Skocz do zawartości

Miganie diodami z własnej funkcji


Pomocna odpowiedź

To już tylko krótkie 😃 wyjaśnienie.

Mam nadzieję, że budowanie w domu " pół-samoczynnego semafora 5 komorowego" nie jest zabronione prawem, bo brzmi co najmniej tak groźnie jak "elektromagnetyczna wyrzutnia pocisków podkalibrowych" 😉

Rzeczywiście brzmi to trochę jak nazwa jakiegoś Uzi.

Jednak komora - to w tym przypadku (zupełnie nie nabojowa) oprawa świetlna.

A pół-samoczynny - bo sygnał zezwalający na przejazd pociągu włącza dyżurny ruchu z nastawni, a gdy pociąg minie semafor - to semafor samoczynnie przełącza się na sygnał S1 ("Stój"). W odróżnieniu od semaforów samoczynnych SBL (Samoczynna Blokada Liniowa), w której przy torach są czujniki "masy" i semafor wykrywając nadjeżdżający pociąg - sam wyświetla mu odpowiedni sygnał w zależności od "zajętości" torów na kolejnych dwóch odcinkach szlaku kolejowego.

Semafora oczywiście budować w domu nie zamierzam.

Tylko jego model. Z siedmiu LED ustawionych pionowo: zielona, żółta, czerwona, znowu żółta, biała, żółta i zielona. Ostatnie dwie (dolne) imitują tzw. pas dolny.

Pierwsze dwie od góry (zielona i żółta) mogą świecić ciągle, migać lub być wygaszone. Oczywiście każda z nich świeci lub miga oddzielnie - albo jedna albo druga.

Pozostałe albo świecą ciągle (jedna z nich lub dwie) albo są wygaszone. Dodatkowo piąta (licząc od góry) biała LED może także migać.

Tak więc: wersja minimum to tylko jedna LED świeci lub miga. Wersja maksimum to dwie LED świecą i trzecia świeci lub miga.

Jak nic nie świeci ani nie miga - to znaczy że się coś... popsuło 😉

Brzmi to może trochę zawile - ale jak to wygląda można zobaczyć tutaj

Użyte tam określenia "pierwsza droga jazdy" i "druga droga jazdy" oznaczają odpowiednio: odcinek torów za mijanym właśnie semaforem (do następnego semafora) oraz odcinek torów za następnym semaforem (do kolejnego semafora).

Link to post
Share on other sites

Fajne 🙂 Kolej była zawsze dla mnie tajemniczym terenem, z dziwnymi ale jak się okazuje prostymi i w każdym calu nastawionymi na bezpieczeństwo procedurami. Ja odróżniam jedynie lokomotywę ET od EP, ale to żaden wyczyn - wystarczy spojrzeć co ciągnie za sobą... No, i oczywiście parowóz od całej reszty 🙂

Dobra, warto przed siądnięciem(?) do pisania kodu narysować schemat lub przynajmniej jakiś plan podłączenia wszystkiego co przewidujesz w sprzęcie. To pomaga zastanowić się co program ma obsługiwać, co jest ważne a co mniej. Z tego wynikają podziały zadań i sposoby obsługi zdarzeń. Jeżeli to będą same diodki plus sygnalizacja na LCD plus kilka przycisków, sprawa wydaje się prosta. Poradzisz sobie na pewno, a... w razie zaciachów, pytaj.

EDIT: Czy obecne semafory pracują wciąż na żarówkach? To wygląda dość staroświecko i podoba mi się. Przemyśl sprawę powolnego zapalania i gaszenia diodek tak, by udawać lampy żarowe. Procesor nie musi robić tylko dwóch stanów. Za pomocą generacji PWM może "udawać" dowolne jasności LEDów a gdyby symulował krzywe zapalania i gaszenia żarówek, wrażenie byłoby dużo lepsze. Kiedyś zrobiłem regulator dla modelarzy lotniczych w którym dioda na stateczniku pionowym była właśnie tak sterowana - zupełnie inne wrażenie niż "elektronicznie" zapalane w zerowym czasie diody. Gdybyś zechciał zapłacić za każdy punkt świetlny 2.5zł zamiast 20gr, mógłbyś wstawić "inteligentne" diody RGB. Każda z nich może świecić dowolnym kolorem i dowolną jasnością a cały łańcuch sterowany jest tylko jednym wyjściem procesora. Kabli wtedy jest mniej, wiele semaforów możesz podłączyć pod jedno wyjście (jeżeli zepniesz je w łańcuch) a do każdego będziesz puszczał jedynie 4 druty: zasilanie (+5V i GND, wejście sygnału z procesora lub z poprzedniego semafora i wyjście do następnego). Taki mechanizm sprzętowy uwalnia Cię od masy kabli, umożliwia wygodne sterowanie jasnością i kolorem dowolnego punktu (komory?) a ciężar rozwiązania przerzuca na program procesora. A tu będziesz mógł pokazać co potrafisz. Prędzej czy później to opanujesz, nie mam wątpliwości. Niestety te diody nie są małe (5x5mm). Jeżeli ma to być makieta do kolejki H0, trzeba by sprawdzić czy zmieścisz się w skali.

Link to post
Share on other sites

1. To nie będzie makieta do kolejki H0. Więc skala jest... nieistotna.

2.

(...) Jeżeli to będą same diodki plus sygnalizacja na LCD plus kilka przycisków (...)

No właśnie nie. Będą to same diodki (LED) plus sygnalizacja na LCD (początkowo Monitor Portu Szeregowego).

Ale nie "kilka przycisków" - tylko... jeden przycisk. Po jego wciśnięciu wyświetlane przez LED-ki sygnały (oraz ich opisy na LCD lub w Monitorze Portu Szeregowego) będą się zmieniały "sekwencyjnie" - czyli po kolei, z jednego na drugi.

3.

(...) Czy obecne semafory pracują wciąż na żarówkach? (...)
Nie mam pojęcia.
(...) Procesor nie musi robić tylko dwóch stanów. Za pomocą generacji PWM może "udawać" dowolne jasności LEDów a gdyby symulował krzywe zapalania i gaszenia żarówek, wrażenie byłoby dużo lepsze. (...) Kiedyś zrobiłem regulator dla modelarzy lotniczych w którym dioda na stateczniku pionowym była właśnie tak sterowana - zupełnie inne wrażenie niż "elektronicznie" zapalane w zerowym czasie diody. (...)
Jakie powinny być opóźniena (w ms) zapalania i gaszenia LED aby to wyglądało na tradycyjną żarówkę?

Próba zbudowania "w domu modelu pół-automatycznego semafora 5-komorowego" (jeden przycisk, 7 LED) na płytce stykowej i Arduino UNO - udała się za pierwszym razem. W głównej pętli "loop" co 10 ms wywoływany jest taki kod:

void loop() {
  uint8_t klik, sygnal, pinLED, stanLED;
  klik = wykryj_klik();
  sygnal = glowny_fsm(klik);
  podaj(sygnal, klik);
  delay(10);
}

Funkcja "wykryj_klik()" wykrywa (nioeomylnie) naciśnięcie przycisku. Funkcja "glowny_fsm()" - sekwencyjnie (z jednego na następny i z ostatniego na pierwszy) zmienia aktualny sygnał. Funkcja "podaj(sygnal, klik)" w zależności od wartości zmiennej "sygnal" wywołuje funkcję "obsluga_led" (odrębną dla każdej z 7 LED) z odpowiednim parametrem.

Napisałeś:

(...) Jeżeli myślisz o rozbudowaniu programu o raporty dotyczące aktualnego stanu systemu to wystarczy, byś stworzył funkcję np. raport_stanu(led_mode) która będzie wykrywać (oczywiście na podstawie prywatnie zapamiętanego stanu poprzedniego) zmianę stanu i tylko wtedy wypisywać komunikat na Serial.println(). (...)
No tak. Ale jeśli mam dwie "znaczące" zmienne: "sygnal" i "klik" to moja funkcja "podaj(sygnal, klik)" - włącza odpowiednie LED w odpowiednim "trybie" i do tego w warunku "if (klik)" (jeśli wciśnięto przycisk) - wywołuje kolejną funkcję "raport(sygnal), która wyświetla w Monitorze Portu Szeregowego opis aktualnie wyświetlanego na LED-ach sygnału. Oczywiście tylko wtedy, gdy wyświetlany sygnał się zmienił.

Nie jestem (ani nie czuję się) programistą - i wyszedł mi jakiś taki... "tasiemiec".

Na pewno sporo zrobiłem "nie tak", sporo można by poprawić, inaczej pogrupować kod, inaczej skonstruować całość.

Dodatkowo na wielkość kodu (ilość jego linii) - niewątpliwie ma wpływ ilość możliwych do wyświetlenia sygnałów. Razem z tym sygnałem zastępczym ("Sz") - jest ich razem... osiemnaście.

Póki co - mój szkic (program) działa i wygląda tak:

 #define pinPrzycisku 2
 #define LED_OFF 0     // stany LED
 #define LED_ON 1
 #define LED_BLINK 2
 #define GREEN 12      // nr pinów LED
 #define ORANGE_UP 11
 #define RED 10
 #define ORANGE_DN 9
 #define WHITE 8
 #define PAS_ORANGE 7
 #define PAS_GREEN 6
 #define S1 1          // nr sygnałów
 #define S2 2
 #define S3 3
 #define S4 4
 #define S5 5  
 #define S6 6
 #define S7 7
 #define S8 8
 #define S9 9
 #define S10 10
 #define S10a 11
 #define S11 12
 #define S11a 13
 #define S12 14
 #define S12a 15
 #define S13 16
 #define S13a 17
 #define Sz 18

void setup() {
 pinMode(13, OUTPUT);  // Żeby wbudowana LED na pinie 13 nie świeciła
 pinMode (pinPrzycisku, INPUT_PULLUP);
 pinMode(GREEN, OUTPUT);
 pinMode(ORANGE_UP, OUTPUT);
 pinMode(RED, OUTPUT);
 pinMode(ORANGE_DN, OUTPUT);
 pinMode(WHITE, OUTPUT);
 pinMode(PAS_ORANGE, OUTPUT);
 pinMode(PAS_GREEN, OUTPUT);
 Serial.begin(9600); //Ustawienie prędkości transmisji
 Serial.println("S1 - Stop.");
 Serial.println("Zakaz dalszej jazdy!");
 Serial.println("\n");  
}

void loop() {
  uint8_t klik, sygnal, pinLED, stanLED;
  klik = wykryj_klik();
  sygnal = glowny_fsm(klik);
  podaj(sygnal, klik);
  delay(10);
}

// Funkcja opisująca w Monitorze portu szeregowego
// wyświetlany sygnał - tylko przy zmianie sygnału
void raport (uint8_t sygnal)
 {
  switch(sygnal)
     {
       case S1:
         Serial.println("S1 - Stop.");
         Serial.println("Zakaz dalszej jazdy!");
         Serial.println("\n");
        break;

       case S2:
         Serial.println("S2 - Jazda.");
         Serial.println("V maksymalna dozwolona.");
         Serial.println("Kolejny semafor: V maksymalna dozwolona.");
         Serial.println("\n");
        break;

       case S3:
         Serial.println("S3 - Jazda");
         Serial.println("V max: 160km/h.");
         Serial.println("Kolejny semafor: ograniczenie do 100 km/h.");
         Serial.println("\n");
        break;

       case S4:
         Serial.println("S4 - Jazda");
         Serial.println("V max: Zwolnij.");
         Serial.println("Kolejny semafor: ograniczenie do 60 lub 40 km/h.");
         Serial.println("\n");
        break;

       case S5:
         Serial.println("S5 - Jazda");
         Serial.println("V max: Zwolnij.");
         Serial.println("Kolejny semafor: wskazuje stop.");
         Serial.println("\n");
        break;

       case S6:
         Serial.println("S6 - Jazda");
         Serial.println("V max: 100 km/h.");
         Serial.println("Kolejny semafor: V max.");
         Serial.println("\n");
        break;

       case S7:
         Serial.println("S7 - Jazda");
         Serial.println("V max: 100 km/h.");
         Serial.println("Kolejny semafor: ograniczenie 100 km/h.");
         Serial.println("\n");
        break;

       case S8:
         Serial.println("S8 - Jazda");
         Serial.println("V max: 100 km/h.");
         Serial.println("Kolejny semafor: ograniczenie 60 lub 40 km/h.");
         Serial.println("\n");
        break;

       case S9:
         Serial.println("S9 - Jazda");
         Serial.println("V max: 100 km/h.");
         Serial.println("Kolejny semafor: wskazuje stop.");
         Serial.println("\n");
        break;

       case S10:
         Serial.println("S10 - Jazda");
         Serial.println("V max: 40 km/h.");
         Serial.println("Kolejny semafor: V max.");
         Serial.println("\n");
        break;

       case S10a:
         Serial.println("S10a - Jazda");
         Serial.println("V max: 60 km/h.");
         Serial.println("Kolejny semafor: V max.");
         Serial.println("\n");
        break;

       case S11:
         Serial.println("S11 - Jazda");
         Serial.println("V max: 40 km/h.");
         Serial.println("Kolejny semafor: ograniczenie 100 km/h.");
         Serial.println("\n");
        break;

       case S11a:
         Serial.println("S11a - Jazda");
         Serial.println("V max: 60 km/h.");
         Serial.println("Kolejny semafor: ograniczenie 100 km/h.");
         Serial.println("\n");
        break;

       case S12:
         Serial.println("S12 - Jazda");
         Serial.println("V max: 40 km/h.");
         Serial.println("Kolejny semafor: ograniczenie 60 lub 40 km/h.");
         Serial.println("\n");
        break;

       case S12a:
         Serial.println("S12a - Jazda");
         Serial.println("V max: 60 km/h.");
         Serial.println("Kolejny semafor: ograniczenie 60 lub 40 km/h.");
         Serial.println("\n");
        break;

       case S13:
         Serial.println("S13 - Jazda");
         Serial.println("V max: 40 km/h.");
         Serial.println("Kolejny semafor: wskazuje Stop.");
         Serial.println("\n");
        break;

       case S13a:
         Serial.println("S13a - Jazda");
         Serial.println("V max: 60 km/h.");
         Serial.println("Kolejny semafor: wskazuje Stop.");
         Serial.println("\n");
        break;

       case Sz:
         Serial.println("Sz - Jazda z ostroznoscia");
         Serial.println("V max: do 40 km/h.");
         Serial.println("Gotowosc do naglego zatrzymania.");
         Serial.println("\n");
        break;
     }   
 }


// Funkcja wyświetlająca odpowiedni obraz na semaforze
// w zależności od zmienej sygnał - wywołuje "obsluga_led()"
// oraz "raport()" z odpowiednimi parametrami.
void podaj(uint8_t sygnal, uint8_t klik)
 {
   switch(sygnal)
     {

       case S1:
         obsluga_led1(LED_OFF);
         obsluga_led2(LED_OFF);
         obsluga_led3(LED_ON);
         obsluga_led4(LED_OFF);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

       case S2:
         obsluga_led1(LED_ON);
         obsluga_led2(LED_OFF);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_OFF);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

       case S3:
         obsluga_led1(LED_BLINK);
         obsluga_led2(LED_OFF);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_OFF);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

       case S4:
         obsluga_led1(LED_OFF);
         obsluga_led2(LED_BLINK);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_OFF);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

       case S5:
         obsluga_led1(LED_OFF);
         obsluga_led2(LED_ON);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_OFF);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

       case S6:
         obsluga_led1(LED_ON);
         obsluga_led2(LED_OFF);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_ON);
         if (klik)
           raport(sygnal);
        break;

       case S7:
         obsluga_led1(LED_BLINK);
         obsluga_led2(LED_OFF);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_ON);
         if (klik)
           raport(sygnal);
        break;

       case S8:
         obsluga_led1(LED_OFF);
         obsluga_led2(LED_BLINK);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_ON);
         if (klik)
           raport(sygnal);
        break;

      case S9:
         obsluga_led1(LED_OFF);
         obsluga_led2(LED_ON);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_ON);
         if (klik)
           raport(sygnal);
        break;

      case S10:
         obsluga_led1(LED_ON);
         obsluga_led2(LED_OFF);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

      case S10a:
         obsluga_led1(LED_ON);
         obsluga_led2(LED_OFF);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_ON);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

      case S11:
         obsluga_led1(LED_BLINK);
         obsluga_led2(LED_OFF);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

      case S11a:
         obsluga_led1(LED_BLINK);
         obsluga_led2(LED_OFF);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_ON);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

      case S12:
         obsluga_led1(LED_OFF);
         obsluga_led2(LED_BLINK);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

      case S12a:
         obsluga_led1(LED_OFF);
         obsluga_led2(LED_BLINK);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_ON);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

      case S13:
         obsluga_led1(LED_OFF);
         obsluga_led2(LED_ON);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

      case S13a:
         obsluga_led1(LED_OFF);
         obsluga_led2(LED_ON);
         obsluga_led3(LED_OFF);
         obsluga_led4(LED_ON);
         obsluga_led5(LED_OFF);
         obsluga_led6(LED_ON);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

      case Sz:
         obsluga_led1(LED_OFF);
         obsluga_led2(LED_OFF);
         obsluga_led3(LED_ON);
         obsluga_led4(LED_OFF);
         obsluga_led5(LED_BLINK);
         obsluga_led6(LED_OFF);
         obsluga_led7(LED_OFF);
         if (klik)
           raport(sygnal);
        break;

     }
 }


// Główna funkcja zmieniająca sekwencyjnie stan sygnału
uint8_t glowny_fsm(uint8_t klik)
 {
   // Zmienna statyczna - to przypisanie wykona sie tylko raz,
   // na początku programu.
   static uint8_t sygnal_state = S1;

   switch(sygnal_state)
     {
       case S1:
         if (klik)
           sygnal_state = S2;
         break;
       case S2:
         if (klik)
           sygnal_state = S3;
         break;
       case S3:
         if (klik)
           sygnal_state = S4;
         break;
       case S4:
         if (klik)
           sygnal_state = S5;
         break;

       case S5:
         if (klik)
           sygnal_state = S6;
         break;

       case S6:
         if (klik)
           sygnal_state = S7;
         break;

       case S7:
         if (klik)
           sygnal_state = S8;
         break; 

       case S8:
         if (klik)
           sygnal_state = S9;
         break; 

       case S9:
         if (klik)
           sygnal_state = S10;
         break; 

       case S10:
         if (klik)
           sygnal_state = S10a;
         break;

       case S10a:
         if (klik)
           sygnal_state = S11;
         break;

       case S11:
         if (klik)
           sygnal_state = S11a;
         break;

       case S11a:
         if (klik)
           sygnal_state = S12;
         break;

       case S12:
         if (klik)
           sygnal_state = S12a;
         break;

       case S12a:
         if (klik)
           sygnal_state = S13;
         break;

       case S13:
         if (klik)
           sygnal_state = S13a;
         break;

       case S13a:
         if (klik)
           sygnal_state = Sz;
         break;

       case Sz:
         if (klik)
           sygnal_state = S1;
         break;   
      }
      return sygnal_state;
   }

// Obsługa LED1
void obsluga_led1( uint8_t stanLED)
 {
   static uint8_t licznik1; 
   switch(stanLED)
     {
       default:
       case LED_OFF:
         digitalWrite(GREEN, LOW);
         licznik1 = 0;
       break;

       case LED_ON:
             digitalWrite(GREEN, HIGH);
             licznik1 = 0;
             break;

        case LED_BLINK:
             if (++licznik1 >= 100) // Licznik działa w cyklu 100
               licznik1 = 0;
             if (licznik1 < 50) // Wypełnienie 50 na 50 
               digitalWrite(GREEN, HIGH);
             else
               digitalWrite(GREEN, LOW);
             break;
         }
     }

// Obsługa LED2
void obsluga_led2( uint8_t stanLED)
 {
   static uint8_t licznik2; 
   switch(stanLED)
     {
       default:
       case LED_OFF:
         digitalWrite(ORANGE_UP, LOW);
         licznik2 = 0;
       break;

       case LED_ON:
             digitalWrite(ORANGE_UP, HIGH);
             licznik2 = 0;
             break;

        case LED_BLINK:
             if (++licznik2 >= 100) // Licznik działa w cyklu 100
               licznik2 = 0;
             if (licznik2 < 50) // Wypełnienie 50 na 50 
               digitalWrite(ORANGE_UP, HIGH);
             else
               digitalWrite(ORANGE_UP, LOW);
             break;
         }
     }

// Obsługa LED3
void obsluga_led3( uint8_t stanLED)
 {
   switch(stanLED)
     {
       default:
       case LED_OFF:
         digitalWrite(RED, LOW);
       break;

       case LED_ON:
         digitalWrite(RED, HIGH);
       break;
         }
     }

// Obsługa LED4
void obsluga_led4( uint8_t stanLED)
 {
   switch(stanLED)
     {
       default:
       case LED_OFF:
         digitalWrite(ORANGE_DN, LOW);
       break;

       case LED_ON:
         digitalWrite(ORANGE_DN, HIGH);
       break;
         }
     }

// Obsługa LED5
void obsluga_led5( uint8_t stanLED)
 {
   static uint8_t licznik5; 
   switch(stanLED)
     {
       default:
       case LED_OFF:
         digitalWrite(WHITE, LOW);
         licznik5 = 0;
       break;

       case LED_ON:
             digitalWrite(WHITE, HIGH);
             licznik5 = 0;
       break;

       case LED_BLINK:
             if (++licznik5 >= 100) // Licznik działa w cyklu 100
               licznik5 = 0;
             if (licznik5 < 50) // Wypełnienie 50 na 50 
               digitalWrite(WHITE, HIGH);
             else
               digitalWrite(WHITE, LOW);
        break;
         }
     }

// Obsługa LED6
void obsluga_led6( uint8_t stanLED)
 {
   switch(stanLED)
     {
       default:
       case LED_OFF:
         digitalWrite(PAS_ORANGE, LOW);
       break;

       case LED_ON:
         digitalWrite(PAS_ORANGE, HIGH);
       break;
         }
     }

// Obsługa LED7
void obsluga_led7( uint8_t stanLED)
 {
   switch(stanLED)
     {
       default:
       case LED_OFF:
         digitalWrite(PAS_GREEN, LOW);
       break;

       case LED_ON:
         digitalWrite(PAS_GREEN, HIGH);
       break;
         }
     }


// Odkłocanie przycisku
uint8_t sito()
 {
  const uint8_t czas_odbijania = 5;
  static uint8_t licznik_opoznienia, poprzedni, stabilny;

  int odczyt = digitalRead(pinPrzycisku);

  if (odczyt != poprzedni)
     licznik_opoznienia = czas_odbijania;

  if (licznik_opoznienia)
     licznik_opoznienia--;
  else
     stabilny = odczyt;

  poprzedni = odczyt;
  return stabilny;
}

// Wykrywanie wciśnięcia przycisku:
uint8_t wykryj_klik()
 {
   static uint8_t nowy_stan, poprzedni_stan;

   nowy_stan = sito();
   if (nowy_stan < poprzedni_stan)
     {
       poprzedni_stan = nowy_stan;
       return 1;
     }
   else
     {
       poprzedni_stan = nowy_stan;
       return 0;
     }
 }

I jeszcze pytanie:

W podręcznikach poświęconych programowaniu (np. PHP) wyczytałem, że w instrukcji wielokrotnego wyboru "switch ... case", w poszczególnych przypadkach (zdefiniowanych przez "case(x):" - można użyć tylko... jednej instrukcji. Tymczasem (i w moim w/w szkicu - i w Twoich przykładach) pomiędzy "case(x):" i "break;" jest więcej niż jedna instrukcja. To działa. Ale czy to jest poprawne?

Link to post
Share on other sites

Jestem pod wrażeniem 🙂

Widać, że trudno Ci ogarnąć cały program i sprawić, by był "ładny" w sensie programistycznym, ale absolutnie nie jest tragiczną mieszaniną przypadkowych linii kodu (którą z łatwością mógłby być). Struktura jest, funkcje są i nawet maszyna stanów wydaje się poprawna. Super.

Nie mieszałbym różnych języków ze sobą. Jeżeli podręcznik języka A mówi N, to w języku B to samo może wyglądać jak M. Znajdź dobry, odpowiadający Ci organizacją podręcznik do C/C++ i opieraj się na nim. Jest dużo książek, które nie są suchymi opisami składni, a raczej wprowadzeniem do sposobu dobrego myślenia/programowania np. w C. Podejrzewam, że nawet na naszym Forum były już recenzje takich książek. A jeśli oprócz tego znajdziesz coś, co będzie ukierunkowane na mikrokontrolery (a jeszcze lepiej na rodzinę AVR od której zacząłeś) to już pełnia szczęścia.

Gdybym miał coś poprawiać, to jedynie podział zadań a dokładniej to, że niepotrzebnie połączyłeś obsługę LEDów z wypisywaniem komunikatów. Nowe, osobne urządzenie - port szeregowy to nowa, osobna funkcja. Teraz musiałeś wepchnąć do podaj() sprawdzanie zdarzenia "klik" i wywoływanie (w wielu miejscach) wypisywacza report(). Gdyby podaj() zajęła się tylko diodkami - jak tego byśmy oczekiwali (jedna funkcja - jedno zadanie), to wystarczałby jej tylko stan systemu (bo to jedyna rzecz potrzebna do sterowania diodkmi) a report() wywoływany z pętli głównej powinien tylko wypisywać komunikaty - także jedynie na podstawie otrzymanego stanu.

Skoro jest jedna maszyna stanów i na główny stan systemu ma wpływ tylko jedno zdarzenie - klik, to niech klik wchodzi tylko tam. Proste zależności upraszczają przepływ informacji i ułatwiają zrozumienie kodu.

Nowy report() powinien samodzielnie rozpoznawać zmianę stanu i wypisywać odpowiedni komunikat:

void raport (uint8_t sygnal) 
{ 
  static uint8_t
     poprzedni_sygnal = 255;
  if (poprzedni_sygnal != sygnal)
  {
     switch(sygnal) 
        { 
          case S1: 
            Serial.println("S1 - Stop."); 
            Serial.println("Zakaz dalszej jazdy!"); 
            Serial.println("\n"); 
           break; 
        ...... tutaj reszta wielkiego switcha
        }
     poprzedni_sygnal = sygnal;
  }
}

Chodzi o to, by maksymalnie izolować od siebie obszary "kompetencji" funkcji. Wyobraź sobie, że nagle zapragnąłeś zmieniać stany semafora nie tylko za pomocą przycisku, ale np. na skutek przejazdu lokomotywy (switch na torach?) lub choćby przysłanego znaku przez port szeregowy. W Twojej wersji musisz zacząć śledztwo od wyszukania wszystkich miejsc gdzie jest używane zdarzenie klik i ich modyfikacji. W wersji gdzie klik wchodzi tylko do maszyny stanów, zmieniasz tylko tam. Wszystko inne korzysta wyłącznie ze stanu systemu i jest odizolowane od źródeł sygnałów i decyzji o jego zmianach. Gdy za rok wrócisz do projektu i będziesz chciał zrobić zmiany, proste podziały funkcji bardzo ułatwią poruszanie się po kodzie.

Zauważ też, że inicjalizowałem poprzedni_stan dość dziwną wartością. Dzięki temu już pierwsze wywołanie tej funkcji spowoduje automatyczne wypisanie stanu przez Serial i niepotrzebne staje się to w init().

Bezparametrowe wywołanie:

Serial.println();

wysyła znak nowej linii.

Mimo "życzeniowego" wypisywania w setup() informacji o stanie S1, zmienna sygnał będąca stanem systemu startuje od (arytmetycznego) zera. Powinna być jawnie inicjowana na S1, bo bez tego maszyna stanów nie wejdzie nigdy do żadnego case'a i całość nie ruszy. Hm, w sumie dziwne, że teraz działa... Ja zwykle takie maszyny uzupełniam o specjalną klauzulę z domyślnym przypadkiem obejmującym wszystkie nieuwzględnione numery stanów, powodującym automatyczne i bezwarunkowe przejście do jakiegoś stanu podstawowego:

default:
     sygnal_state = S1; 
  break;

lub alarmujący o takiej sytuacji:

default:
     Serial.println("Nieznany stan!"); 
     sygnal_state = S1; 
  break;

Teraz, nawet gdy "zapomnisz" inicjalizować stan lub gdy program na skutek błędu popsuje go sobie, zadzieje się coś sensownego.

Dobra, dosyć marudzenia, naprawdę gratuluję sukcesu 🙂 Jakieś zdjęcia?

Link to post
Share on other sites

Dziękuję za cenne uwagi i porady. Zastosuję się.

Fotek... nie będzie. Przynajmniej na tę chwilę.

Bo moja "próba zbudowania w domu modelu (...)" - oznaczała na razie tylko złożenie prototypu układu na płytce stykowej i napisaniu kodu 😉

EDIT: Napisałeś

(...) Nie mieszałbym różnych języków ze sobą (...)
No jasne. Przecież to było wyjaśnione już w filmie Barei pod tytułem "Miś":

"Nie mieszajmy myślowo dwóch różnych systemów walutowych. Nie bądźmy peweksami." 🤣

EDIT 2:

W nawiązaniu do tego, że "Fotek... nie będzie (...)". Bo to na razie tylko 7 LED-ów i 1 przycisk, wpięte w płytkę stykową:

Ten mój model semafora - w zamyśle miał być taką "materialną wizualizacją" sygnałów możliwych do wyświetlenia na semaforze " pół-samoczynnym 5-komorowym", które można zobaczyć na wskazanej wcześniej przeze mnie stronie www. Miałoby to służyć do nauki (zapamiętania) wyglądu i znaczenia poszczególnych sygnałów. Stąd ten (docelowo) wyświetlacz LCD 16x2.

Ale przecież sygnałów świetlnych na kolei jest więcej. Są także: semafory SBL, tarcze ostrzegawcze, sygnały powtarzające, tarcze manewrowe.

Tylko, że różne kolory opraw (komór) świetlnych są w poszczególnych semaforach inaczej rozmieszczone (pomijając fakt, że semafory " pół-samoczynne" umieszczane są na masztach pomalowanych w biało-czerwone pasy, a cała reszta na masztach pomalowanych na kolor szary).

Napisałeś wcześniej:

(...) Gdybyś zechciał zapłacić za każdy punkt świetlny 2,50 zł zamiast 20 gr, mógłbyś wstawić "inteligentne" diody RGB. Każda z nich może świecić dowolnym kolorem i dowolną jasnością, a cały łańcuch sterowany jest tylko jednym wyjściem procesora. (...)
I tu pojawiła się... nowa inspiracja: przecież budując taki fizyczny model semafora (pomijając sposób malowania jego masztu) i używając w tym modelu "inteligentnych LED RGB" oraz większego niż 16x2 wyświetlacza LCD - mógłbym zobrazować na tym modelu wszystkie występujące na szlakach kolejowych sygnały świetlne.

Wtedy przydałaby się też rozbudowa "interfejsu" z jednego do dwóch przycisków. Jeden przycisk miałby zmieniać sygnały "w przód" (na następny sygnał), a drugi miałby zmieniać sygnały "w tył" (na poprzedni sygnał).

Czyli w takim (tego rozbudowanego typu) modelu sygnalizacji kolejowej byłoby: 8 "inteligentnych LED RGB" połączonych w łańcuch oraz 2 przyciski ("poprzedni sygnał" i "następny sygnał").

No, tak. Tylko jak to zrobić w Arduino IDE? 😉

Jak zaimplementować obsługę dwóch przycisków tak, żeby jeden z nich robił "++sygnal", a drugi "--sygnal"?

No i jak (w Arduino IDE) obsługiwać (adresować?) takie "inteligentne LED RGB" połączone w łańcuch? Jak to zrobić, żeby np. pierwsza w łańcuchu taka LED zaświeciła (lub migała) na zielono, a np. trzecia świeciła na biało (ale nie "w ogóle"=zawsze - a tylko w czasie wyświetlania jednego, konkretnego sygnału) ?

Cały czas pamiętam też to, co napisałeś na samym początku:

(...) Może się zdarzyć, że na Forum nikt nie ma czasu by pisać podręcznik o tym jak rozwiązać Twój problem (...)
Więc i nie oczekuję gotowego "podręcznika". Tylko jakiejś... podpowiedzi (chociażby tylko sugerującej) gdzie mogę się czegoś (albo wręcz: czegokolwiek) na ten temat dowiedzieć (koniecznie z przykładami kodu!). No i najlepiej w jęz. polskim...

Bo na pewno nie sposób się tego dowiedzieć z kursów na stronach www albo z książek (tam "królują" proste przykłady prostych czynności, zawsze z użyciem "bez ograniczeń" funkcji "delay()") 😉

Link to post
Share on other sites

Nie masz dość? 😉 Bardzo proszę, możesz wymyślać co tam potrzebujesz. To może.. po kolei, zacznijmy od przycisków.

Dla drugiego klawiszka oczywistym rozwiązaniem jest napisanie drugiej funkcji sito(), mającej wszystko tak samo: taki sam algorytm i taki sam zestaw zmiennych lokalnych (mogą się nawet tak samo nazywać - przecież ich nazwy "żyją" tylko w obrębie tej funkcji) oprócz wczytywania stanu innego wejścia. Nie wygląda to źle, choć zestaw takich funkcji obsługujący np. 8 przycisków wygląda już przerażająco. Możesz dla wprawy, na próbę zrobić sito1() i sito2() wczytujące stany 2 wejść, ale ja proponuję jednak podejść do sprawy bardziej kompleksowo. No i mam teraz problem. Możemy zacząć rozmawiać o portach, bitach, rejestrach itp rzeczach a możemy podejść do sprawy bardziej "Arduinowo". Moim zdaniem na wnikanie w procesor masz jeszcze czas, a obecnie chyba wystarczy gdy ze światem zewnętrznym będziesz rozmawiał wciąż za pomocą funkcji digitalRead/Write. Napisz co o tym myślisz.

Jeśli chodzi o maszynę stanów: weźmy dla przykładu jeden jej stan:

        case S3: 
         if (klik) 
           sygnal_state = S4; 
         break; 

A mówiąc po ludzku: jesteśmy w stanie S3 i będziemy w nim dopóki nie zdarzy się zdarzenie klik. Po jego zajściu następuje przejście do stanu S4. No to teraz wyobraź sobie, że masz dwa zdarzenia w systemie klik1 i klik2, które wpływają na Twoją maszynę np. w bardzo prosty sposób:

        case S3: 
         if (klik1) 
           sygnal_state = S4; 
         else
            if (klik2) 
              sygnal_state = S2; 
         break; 

🙂

Sterowaniem diodkami RGB zajmiemy się na końcu. Wbrew pozorom to bardzo proste.

Link to post
Share on other sites

Nie mam dość. A wręcz odwrotnie: czuję niedosyt 🙂

Wcześniej napisałeś:

(...) Mimo "życzeniowego" wypisywania w setup() informacji o stanie S1, zmienna sygnał będąca stanem systemu startuje od (arytmetycznego) zera. Powinna być jawnie inicjowana na S1, bo bez tego maszyna stanów nie wejdzie nigdy do żadnego case'a i całość nie ruszy. Hm, w sumie dziwne, że teraz działa... (...)
Chyba wiem dlaczego działa, chociaż niby nie powinno: początek funkcji glowny_fsm()?
// Zmienna statyczna - to przypisanie wykona się tylko raz,
// na początku programu.
static uint8_t sygnal_state = S1;

Wracając do Twojego ostatniego postu: niestety, wciąż jeszcze musimy podchodzić do sprawy w sposób tylko "Arduinowy".

Próbowałem, zgodnie z Twoimi wcześniejszymi sugestiami, zmienić kod tak, żeby używając tylko jednej funkcji wykryj_klik() i tylko jednej funkcji sito() odkłócać oraz wykrywać wciśnięcie dwóch przycisków. Oczywiście próbowałem tylko w sposób "Arduinowy". Nie udało się. Bo nie mogło się udać. Pomimo tego, że funkcja sito() dostawała jako parametr - nr pinu odpowiedniego przycisku.

Funkcja wykryj_klik() i funkcja sito() mają, co prawda, swoje "własne" zmienne lokalne. Ale przecież są to jednocześnie zmienne statyczne (typ static).

Napisałeś:

(...) Dla drugiego klawiszka oczywistym rozwiązaniem jest napisanie drugiej funkcji sito(), mającej wszystko tak samo: taki sam algorytm i taki sam zestaw zmiennych lokalnych (mogą się nawet tak samo nazywać - przecież ich nazwy "żyją" tylko w obrębie tej funkcji) oprócz wczytywania stanu innego wejścia (...)
Otóż wydaje mi się, że... jednak nie. Zmienne statyczne, "lokalne" w różnych funkcjach - nie mogą się tak samo nazywać. Bo choć są dostępne tylko dla "swojej" funkcji, to po zakończeniu jej działania pozostają w pamięci. A w tym samym czasie mogą przechowywać różne wartości. Dlatego tak napisany program albo nie będzie działał w ogóle albo będzie działał nieprawidłowo.

Czyż nie tak ❓

Ten problem można zapewne łatwo wyeliminować podchodząc do tematu w sposób bardziej zaawansowany niż ten "Arduinowy". Ale na to u mnie jeszcze nie czas... 😉

Chociaż w zasadzie mogłoby się to udać (jedna funkcja wykryj_klik() i jedna funkcja sito() dla wielu przycisków) i na sposób "Arduinowy" - o ile tu możliwe jest wewnątrz funkcji modyfikowanie nazwy zmiennej lokalnej poprzez "doklejenie" do tej nazwy przyrostka, przekazanego do funkcji jako jeden z jej parametrów (dostępne w innych językach programowania ❓ ). Ale żeby się o tym przekonać muszę "postudiować" podręcznik C/C++ (którego wciąż jeszcze nie mam) 😉

Póki co, musiałem napisać nie tylko dwie funkcje: sito1() i sito2() - ale także dwie funkcje wykryj_klik1() i wykryj_klik2(). Każda z osobna dla odpowiedniego przycisku (DALEJ i WSTECZ).

Napisałeś dalej:

(...) Nie wygląda to źle, choć zestaw takich funkcji obsługujący np. 8 przycisków wygląda już przerażająco (...)
Zaczynam to rozumieć 😉 Przycisków wcale nie musi być aż 8. Wystarczy, że zechcemy dodać jeden przycisk więcej niż było wcześniej - dla głównego FSM o... sporej ilości możliwych stanów układu. I ile to już "automatycznie" koniecznych zmian w kodzie...

Żeby nie wklejać tu mojego "tasiemca" 🙂 z "wielkimi" switchami dla 18 stanów FSM - napisałem takiego "skrótowca" dla układu, w którym są tylko dwa przyciski.

Główny FSM ( "główny" - bo przecież FSM-ami są też funkcje sito(), wykryj_klik() oraz raport()) - zmienia stan układu w "odpowiednią stronę", zależnie od tego, który przycisk został wciśnięty.

Są tu tylko 3 możliwe stany układu.

Tylko wtedy gdy nastąpi zmiana stanu układu - funkcja raport(sygnal) w oknie "Monitora szeregowego" podaje wartość aktualnego stanu układu.

  // Przyciski:
 #define DALEJ 3
 #define WSTECZ 2
 // Wartości stanu układu dla głównego FSM:
 #define S1 1
 #define S2 2
 #define S3 3

void setup() {
 pinMode(13, OUTPUT);  // Żeby wbudowana LED na pinie 13 nie świeciła
 pinMode (DALEJ, INPUT_PULLUP);
 pinMode (WSTECZ, INPUT_PULLUP);
 Serial.begin(9600); // Ustawienie prędkości transmisji
}

void loop() {
  uint8_t klik1, klik2, sygnal;
  klik1 = wykryj_klik1(sito1());
  klik2 = wykryj_klik2(sito2());
  sygnal = glowny_fsm(klik1, klik2);
  raport(sygnal);
  delay(10);
}

// Wykrywanie wciśnięcia przycisku DALEJ:
uint8_t wykryj_klik1(uint8_t odsiane)
 {
   static uint8_t nowy_stan1, poprzedni_stan1;

   nowy_stan1 = odsiane;
   if (nowy_stan1 < poprzedni_stan1)
     {
       poprzedni_stan1 = nowy_stan1;
       return 1;
     }
   else
     {
       poprzedni_stan1 = nowy_stan1;
       return 0;
     }
 }

// Wykrywanie wciśnięcia przycisku WSTECZ:
uint8_t wykryj_klik2(uint8_t odsiane)
 {
   static uint8_t nowy_stan2, poprzedni_stan2;

   nowy_stan2 = odsiane;
   if (nowy_stan2 < poprzedni_stan2)
     {
       poprzedni_stan2 = nowy_stan2;
       return 1;
     }
   else
     {
       poprzedni_stan2 = nowy_stan2;
       return 0;
     }
 }

// Odkłócanie przycisku DALEJ
uint8_t sito1()
 {
  const uint8_t czas_odbijania = 5;
  static uint8_t licznik_opoznienia1, poprzedni1, stabilny1;

  int odczyt = digitalRead(DALEJ);

  if (odczyt != poprzedni1)
     licznik_opoznienia1 = czas_odbijania;

  if (licznik_opoznienia1)
     licznik_opoznienia1--;
  else
     stabilny1 = odczyt;

  poprzedni1 = odczyt;
  return stabilny1;
}

// Odkłócanie przycisku WSTECZ
uint8_t sito2()
 {
  const uint8_t czas_odbijania = 5;
  static uint8_t licznik_opoznienia2, poprzedni2, stabilny2;

  int odczyt = digitalRead(WSTECZ);

  if (odczyt != poprzedni2)
     licznik_opoznienia2 = czas_odbijania;

  if (licznik_opoznienia2)
     licznik_opoznienia2--;
  else
     stabilny2 = odczyt;

  poprzedni2 = odczyt;
  return stabilny2;
}

// Główna funkcja zmieniająca sekwencyjnie stan układu
uint8_t glowny_fsm(uint8_t klik1, uint8_t klik2)
 {
   /* Zmienna statyczna - to przypisanie wykona się tylko raz,
      na początku programu. */
   static uint8_t sygnal_state = S1;

   switch(sygnal_state)
     {
       case S1:
         if (klik1)
           sygnal_state = S2;
         else
            if (klik2)
              sygnal_state = S3;
         break;

       case S2:
         if (klik1)
           sygnal_state = S3;
         else
            if (klik2)
              sygnal_state = S1;
         break;

       case S3:
         if (klik1)
           sygnal_state = S1;
         else
            if (klik2)
              sygnal_state = S2;
         break;

       default:
           sygnal_state = S1;
         break; 
     }
   return sygnal_state;
 }

 /* Funkcja wyświetlająca w oknie Monitora szeregowego
    wartość aktualnego stanu układu */
void raport(uint8_t sygnal)
 {
   static uint8_t poprzedni_sygnal = 255;
   if (poprzedni_sygnal != sygnal)
     {
       Serial.println(sygnal);
       Serial.println();
     }
   poprzedni_sygnal = sygnal; 
 }    
Link to post
Share on other sites

Tak, oczywiście, przypisanie początkowego stanu przegapiłem.

Zmienne zadeklarowane w funkcji - niezależnie czy mają atrybut static, register czy jeszcze jakiś inny - są widoczne tylko w obrębie tej funkcji. Nie jest ważne, że zostają w pamięci, kompilator nad tym czuwa i analizując dany fragment kodu "wie" które nazwy gdzie obowiązują. Pierwszym rozdziałem który powinieneś przeczytać w podręczniku (gdy już go zdobędziesz) będzie ten o zakresie dostępności nazw (scope).

BTW: Żeby jeszcze bardziej zakręcić, zakres widoczności symboli ogranicza każdy blok {}. Zmienna w nim zadeklarowana dostępna jest tylko w tym bloku:

void akuku(void)
{
  static uint8_t licznik = 0;

  licznik++;
  if (licznik>5)
  {
     uint8_t licznik;
        for (licznik = 0; licznik<100; licznik++)
           tablica[licznik] = 0;
  }
  else
  {
     static uint8_t licznik;
        if (licznik)
           licznik--;
  }

To głupi przykład, bo tak piszącego programistę należałoby wywalić z roboty, ale wszystko jest OK: w swoim ciele funkcja używa statycznego licznika, który jest inkrementowany za każdym wywołaniem akuku(). W obu blokach po if używane są inne liczniki - jeden do obracania pętli - ten powstaje tylko na chwilę, i drugi (statyczny!) jest dekrementowany gdy wykonuje się blok po else i ten licznik jest > 0. Żaden z tych "dodatkowych" liczników nie niszczy ani nie jest mylony (przez kompilator) z tym "głównym". Żaden z trzech liczników nie jest też dostępny na zewnątrz funkcji. Dla człowieka to jednak masakra...

Zrobiłeś dwa komplety funkcji i OK - to też działa a w przypadku dwóch przycisków nie jest to jeszcze grzech ciężki 😉 Jutro pomyślimy jak zrobić to zgrabniej, choć wciąż po arduinowemu.

Jeżeli wciąż jesteś napalony na 8 punktów świetlnych, każdy z niezależnie programowanym kolorem i jasnością, pomyśl nad kupieniem czegoś takiego:

http://botland.com.pl/paski-led-adresowane/2902-adafruit-neopixel-stick-pasek-led-rgb-8-x-ws2812-5050.html

Można kupować osobno te diodki i próbować klecić listwę/taśmę/semafor samodzielnie, ale nie wiem jak u Ciebie jest z lutowaniem a tu masz gotowy, sztywny moduł akurat (chyba?) odpowiedniej długości. W każdym razie będzie Ci potrzebne kilka diodek połączonych szeregowo. Liczba w łańcuchu jest praktycznie nieograniczona, więc jeśli kupisz więcej takich 8-punktowych "klocków" lub np. jakiś odcinek miękkiej tasiemki, będziesz mógł ją pociąć, połączyć trzema kabelkami a potem zbudować i jednocześnie sterować niezależnym zapalaniem kilku semaforów. Oczywiście zaczniemy od jednego 🙂

Czy w takim 8-komorowym niektóre lampy będą mrugać? Zaczyna być ciekawie 🙂

Link to post
Share on other sites

marek1707, Właśnie dowiedziałem się że nawiasy nie muszą być przyczepione do if, while itd. aby ograniczały zakres zmiennych:

http://stackoverflow.com/questions/5072845/scope-with-brackets-in-c

Nie spodziewałem się tego, dzięki!

Nie myślałeś nad założeniem bloga czy czegoś takiego? Czytałbym bardzo.

Link to post
Share on other sites

A wydawało mi się, że to rozumiem 😉

W swoim przykładzie pod tytułem: "żeby jeszcze bardziej zakręcić" - pokazujesz zmienne lokalne różnych (2 ?) typów o takich samych nazwach. Ale w obrębie jednej funkcji.

Czy żeby to rozumieć prawidłowo, mam myśleć że zmiennna typu static o nazwie licznik zadeklarowana w funkcji akuku() i pozostawiona w pamięci mikrokontrolera po zakończeniu działania tej funkcji - nie zostanie "nadpisana" (pomylona?) ze zmienną typu static o takiej samej nazwie licznik zadeklarowanej w innej funkcji o nazwie np. kukuryku()?

Jeśli tak, to jest to faktycznie lekko... nielogiczne.

W tym moim "rozwinięciu tematu" mówimy przecież wciąż o zestawie 8 punktów świetlnych.

Z tym, że programowany (bo zmienny) ma być tylko ich kolor.

Jasność świecenia każdego punktu ma być taka sama (stała).

I tu poprawka: jeśli kolor będzie "programowany" to wystarczy 7 punktów świetlnych. Bo za tzw. pas dolny w semaforze będzie robiła jedna diodka świecąca raz na zielono a raz na pomarańczowo.

Z "lutowaniem u mnie" - myślę, że dobrze.

Myślałem raczej o zakupie czegoś takiego: http://botland.com.pl/led-rgb/2438-dioda-led-5-mm-rgb-ws2811-adresowana-10-szt.html

A to dlatego, że tylko sześć takich LED RGB miałoby być umieszczonych w pionie. Siódma taka dioda miałaby być umieszczona obok tego sześcio-LED-owego "słupka", na "wysokości" trzeciej (licząc od góry) diody.

W syglnalizacji kolejowej w Polsce występuje sygnalizator o nazwie "tarcza ostrzegawcza przejazdowa", który wyświetla dwa sygnały "Osp1" lub "Osp2"(http://www.kontrakt-bhp.com.pl/paul/kolej/sygn_top.php ). I właśnie ze względu na sygnał "Osp1" konieczne jest takie rozmieszczenie diod w modelu (przy założeniu, że model semafora ma wyświetlać wszystkie występujące na szlakach kolejowych w Polsce sygnały świetlne).

Model semafora ma być tylko jeden.

Stąd będzie tylko 7 takich LED RGB połączonych w jeden łańcuch.

Tak, niektóre lampy będą mrugać. Tylko te (od góry licząc): pierwsza, druga, piąta.

Ale zawsze (jeśli w ogóle coś będzie mrugać) - to tylko jedna lampa będzie mrugać.

Dodatkowo (oprócz tej jednej mrugającej) - jedna lub dwie lampy mogą (ale nie muszą) świecić światłem ciągłym.

"Potrzebne" kolory to: zielony, czerwony, pomarańczowy (żółty), biały, niebieski.

"Zaczyna być ciekawie". No pewnie 😉

Link to post
Share on other sites

Nielogiczne - jeśli podchodzisz do tego jak do pisania małego programiku, gdzie spokojnie możesz ogarnąć cały kod jednym spojrzeniem. Możesz wtedy z łatwością wymyślać niepowtarzalne nazwy zmiennych i byłoby wręcz dziwne, gdybyś chciał "przykrywać" jedne nazwy innymi. Niestety programy komputerowe są wielkie i pisane przez wielu ludzi jednocześnie. Twórcy C (i wielu innych języków) poświęcili mnóstwo czasu na wbudowanie w gramatykę i semantykę języka takich mechanizmów, które ograniczałyby błąd człowieka. Nie chodzi oczywiście o zły algorytm - tego kompilator nie jest w stanie przewidzieć, bo gdyby wiedział co chcesz zrobić mógłby za Ciebie napisać program. Zakres dostępności symboli jest - oprócz ścisłej kontroli typów i ogólnej struktury funkcyjnej kodu - jednym z takich podstawowych mechanizmów. Pisząc duży program nie jesteś w stanie myśleć globalnie. Po prostu ludzie nie mają takich możliwości percepcyjnych. Skupiasz się na "tu i teraz": robisz funkcję która w naturalny sposób jest pewną zamkniętą całością. Jeżeli zadeklarujesz w niej jakieś symbole (zmienne, typy itd) to chcesz, by to właśnie one w niej obowiązywały i działały zgodnie z Twoją intencją. Zmienne statyczne powinny zostawać "statycznie" w normalnej pamięci RAM, rejestrowe siedzieć w rejestrach procesora a automatyczne pojawiać się na stosie i znikać gdy funkcja się "zwija". Po napisaniu funkcji przechodzisz na wyższy poziom abstrakcji. Zaczynasz pisać kod, który z tych funkcji korzysta i nie chcesz wciąż zaglądać do ich tekstu by sprawdzić jakimi nazwami tam się posłużyłeś. W nowych funkcjach, wołających te wczorajsze robisz nowe zmienne i one znowu powinny działać zgodnie z Twoim nowym punktem widzenia. I tak kolejno, przechodząc na coraz wyższe warstwy dochodzisz do main() (lub loop() w Arduino) nie będąc zmuszanym do rozumienia całego kodu na raz. Co więcej, poprawki wnoszone później nawet do najgłębszych funkcji nie będą się odbijały - właśnie dzięki ograniczonemu zasięgowi obowiązywania nazw - na wyższych warstwach. Jest cała ogromna nauka zwana inżynierią oprogramowania, która zajmuje się od strony teoretycznej procesem powstawania programów. Od planowania, analizy problemu, poprzez tworzenie struktur danych, algorytmów, samo kodowanie, uruchamianie, testowanie i późniejsze używanie i konserwację kodu. O tym też warto poczytać. Już dawno temu języki programowania przestały być jedynie syntetycznym opisem algorytmów a stały się specjalizowanymi narzędziami o rozbudowanej strukturze właśnie po to, by coraz trudniej było popełniać wredne do znalezienia błędy. A to właśnie uruchamianie kodu jest - wbrew pozorom - najkosztowniejszym etapem tworzenia programu. Co ciekawsze samo kodowanie jest uważane za pracę rzemieślniczą, podrzędną - przełożyć obmyślony wcześniej algorytm na zestaw instrukcji danego języka potrafi każdy w miarę rozumny człowiek a nawet.. jakieś graficzne środowisko typu scratch. To jak w każdym przedsięwzięciu - im później wykryjesz głupotę - tym drożej będzie Cie kosztować jej poprawienie. Siadanie do klawiatury bez dobrego planu na strukturę programu jest czystym (choć bardzo pociągającym) marnowaniem czasu. Jeżeli jakiś babol siedzi głęboko albo co gorsza okaże się, że odpuściłeś/zaoszczędziłeś czas/pieniądze w poprzednich etapach planowania, to być może cała struktura kodu jest zła i nie ma ratunku: ani czas ani budżet nie zostaną dotrzymane. Widać to jak na dłoni w wielu "programach" wrzucanych tu na Forum. "Zrobiłem program do stopera LCD sterowanego trzema przyciskami, działa i liczy ale nie wiem jak wstawić do niego mruganie diodką w momencie gdy liczy czas, pomóżcie!" - to typowy przykład napisanego na kolanie, wg. pierwszego impulsu programu z bezmyślną strukturą opartą o zakodowanie wprost algorytmu w jedną wielką funkcję loop(). W takim przypadku trywialne wydawałoby się dodanie mrugającej diodki może okazać się po prostu niemożliwe bez kompletnej przebudowy struktury. Nie wszyscy to rozumieją, ogłupieni zasłyszanym hasłem prostego tworzenia programów. Owszem, bardzo proste programy są proste, ale bez zachowania pewnych zasad bardzo szybko komplikują się w niewyobrażalnym stopniu.

I dlatego warto czytać. Mimo pojawiania się wciąż nowych publikacji: "Czysty kod" i "Mistrz czystego kodu" Roberta Martina, które już dziś są kultowymi pozycjami (choć jednak dla bardziej zaawansowanych programistów), to polecam także cegły z przeszłości: "Praktyka programowania" Dennie van Tassel'a - fajna i lekka książka dla początkujących otwierająca oczy na sztukę tworzenia programów (przykłady w Algolu, COBOLu czy BASICu trochę już trącą myszką, ale to bez znaczenia - niektóre problemy są uniwersalne) czy takie pozycje jak "Umiejętność programowania" Dijkstry (trochę wyższa półka) czy wręcz "The Art of Computer Programming" Donalda Knutha - pięciotomowe dzieło, które każdy z branży powinien kiedyś łyknąć i mieć na swojej półce (koniecznie - wygląda jak sto pięćdziesiąt 😉 ). Drogie - cóż, wiedza kosztuje. Nie wiem czy wyszło polskie wydanie. Wydaje mi się niemożliwe, żeby nie. Koledzy bardziej biegli w sztuce programowania niż ja na pewno jeszcze coś dorzucą. Może coś o troszkę nowszym i lekko innym świecie mikrokontrolerów?

OK, kupuj osobne diodki i buduj to swoje cudo choć moim zdaniem kupienie 10cm tasiemki z kilkoma inteligentnymi RGB jest dobrym zaczątkiem testów - tańsze niż gotowe PCB a od razu działa a przecież do prób nie musi wyglądać. Pamiętaj o kondensatorach blokujących zasilanie i daj znać gdy będziesz miał zmontowany zestaw. Może jakiś kawałek płytki uniwersalnej? Bo "na pająka" to jednak dość ryzykowne, szczególnie przy dzieciach w pobliżu...

O tej jasności tylko wspomniałem jako o dodatkowym plusie. Skoro każda składowa RGB jest w tych diodkach reprezentowana liczbą 0-255 to naturalnym jest, że możesz zmieniać jasność (a zatem pobór prądu - czasem nie trzeba świecić ile fabryka dała) mnożąc każdą ustawianą składową koloru przez jakąś wspólną stałą mniejszą od 1. Na szczęście można to przecież ustawić w programie jako parametr kompilacji i nie obsługiwać "na bieżąco". Do zabawy i pierwszych testów można ustawić jasność małą by nie dawało po oczach i nie drażniło, a potem w docelowej instalacji podkręcić do wymagań oświetlenia zewnętrznego. Hm, można też zrobić automatykę z czujnikiem natężenia światła w otoczeniu.. Dobra, spokojnie, żartowałem 🙂

Link to post
Share on other sites

witam

Jeśli o mnie chodzi to bym to zrobił w innej pętli( jednej+ zdarzenia)

zamiast definiować tyle zmiennych , użył 3 tablic

1 zmienne stale jn

2 zmienne dynamiczne (można dowolnie zwiększyć w momencie dojścia nowych argumentów)

3 kody tekstów wyświetlanych jw

Na zasadzie wywołania zdarzenia odsyłał główną pętle do podprogramu( obiektu) i aktualizował .

Obecnie program przypomina zasadę bombelkową sortowania, czyli najdłuższy możliwy algorytm.

Jeśli się zastosujesz do danego schematu program będzie miał znamiona obiektowego

co ułatwia dodawanie nowych warunków w pętli głównej oraz dodawania nowych bibliotek

w wypadku powiększenia pola działania programu (dodatkowe semafory,zwrotnice itp).

Zdefiniowane tablice na początku będą dostępne wszystkim podprogramom co ułatwi dopisywanie lub odbieranie argumentów (liczniki , opóźnienia , wielokrotności).

Link to post
Share on other sites

Witam ponownie - znowu upłynął ponad tydzień?

Na wstępie chcę podziękować koledze z forum o nicku szrek2003 za podpowiedź będącą innym spojrzeniem (oszczędzającym) na to, co chcę osiągnąć w moim "projekcie".

Przepraszam też, za mój głupawy i egoistyczny komentarz dotyczący nielogiczności zasięgu lokalnych zmiennych statycznych.

Sądząc po długości Twojego ostatniego wpisu - to chyba teraz Ty się zirytowałeś. I słusznie, bo: programy pisane dla mikrokontrolerów AVR (a także dla innych mikrokontrolerów) potrafią na pewno realizować dużo więcej celów, niż ten mój problem. Ale dało mi to do myślenia i sporo wyjaśniło.

Rozumiem też to, że nie wolno pisać programu dla mikrokontrolera bez przemyślenia planu, funkcjonalności i "na kolanie". Tworząc kod cały czas trzeba myśleć, i pisać ten kod tak, żeby potem zwiększanie jego funkcjonalności (np. dodanie nie jednego więcej - ale kliku więcej przycisków i ich obsługi) nie było... mordęgą.

Mam dwie wiadomości: dobrą i złą.

Zacznę może od tej... dobrej:

Cały czas czytam, czytam, czytam i...czytam 🙂

I na obecną chwilę, wydaje mi się, że rozumiem dlaczego w pierszym poście nazwałeś platformę Arduino "piaskownicą".

W każdym razie: rozumiem już czym są linie, porty, rejestry i operacje bitowe. Czym są przerwania wewnętrzne i zewnętrzne, czym są wewnętrzne timery mikroprocesora. I jak z nich korzystać. A przynajmniej tak mi się wydaje. 🙂

Wcześniej napisałeś:

(...) Zrobiłeś dwa komplety funkcji i OK - to też działa, a w przypadku dwóch przycisków nie jest to jeszcze grzech ciężki 😉 Jutro pomyślimy jak zrobić to zgrabniej, choć wciąż po arduinowemu.(...)

Domyślam się rozwiązania, które choć zapowiedziane... nie nastąpiło.

I tu nie chodzi mi o mnie - tylko o innych początkujących (w "piaskownicy" ADRUINO IDE) czytających ten temat na forum. Dla nich byłoby dobrze, żeby to rozwiązanie jednak zostało pokazane. Oczywiście w sposób "arduinowy" 🙂

A ta zła wiadomość:

Napisałeś:

(...) OK, kupuj osobne diodki i buduj to swoje cudo choć moim zdaniem kupienie 10 cm tasiemki z kilkoma inteligentnymi RGB jest dobrym zaczątkiem testów - tańsze niż gotowe PCB a od razu działa a przecież do prób nie musi wyglądać. Pamiętaj o kondensatorach blokujących zasilanie i daj znać gdy będziesz miał zmontowany zestaw. Może jakiś kawałek płytki uniwersalnej? Bo "na pająka" to jednak dość ryzykowne, szczególnie przy dzieciach w pobliżu... (...)

Tak "BTW": moje dzieci mają po 30 lat. I tak na co dzień raczej nie są "w pobliżu". Może chodziło Ci bardziej o moje wnuki? 😉

Wracając do tematu: koszty i Twojej i mojej opcji - są podobne. Około 30 zł.

Do tego, pomimo, że napisałem iż wydaje mi się że rozumiem już programowanie mikrokontrolera w języku "C" - to, pomijając to, że mam tylko klona Arduino UNO oraz LED-y, rezystory, kondensatory - trzeba jeszcze kupić: programator (myślę o USBasp), mikrokontroler AVR ATmega 328P-PU (dodatkowy poza tym z Arduino - to jakieś 15 zł), "zewnętrzny" kwarc (jak w Arduino Uno czyli: 12 MHz). itp, itd.

Dla Ciebie (i być może dla innych początkujących czytających ten temat) - te kwoty mogą wydawać się po prostu... śmieszne.

Jednak dla mnie w tym momencie - w aspekcie ostatnich wydarzeń z życia mojego i mojej rodziny - o których nie chciałbym tu pisać na tym forum (które przecież jest jakoś... publiczne) - są to koszty na tę chwilę... nieosiągalne.

Dlatego tym postem nie daję znać, że mam "zmontowany zestaw". Bo na tę chwilę nie mam. I chyba... nieprędko będę miał.

Dlatego, mając wciąż "z tyłu głowy" początkujących (jak ja) czytających ten temat, proszę o "rozwinęcie tematu" pod tytułem: "jak z Arduino UNO (lub z mikrokontrolera) sterować "inteligentnymi" LED-ami RGB (jak sterować ich kolorem i jasnością). Oraz jak odwoływać się do adresów (i skąd wziąć te adresy) połączonych w linię "inteligentnych" LED RGB?

O ile to możliwe - proszę o rozwinięcie tematu w sposób zrozumiały dla tych "bez "zmontowanego zestawu"" 😉

Link to post
Share on other sites

Hej, jak dla mnie obie informacje od Ciebie są dobre. Zarówno to, że czytasz i wiesz coraz więcej jak i to, że wciąż planujesz rozwój siebie i swojej konstrukcji trudno uznać za porażki. W każdej działalności są lepsze i gorsze chwile a w zwykłym życiu tym bardziej (rany, ale truizm) więc spoko, zajmuj się ważnymi w danej chwili rzeczami a diodki.. mogą poczekać.

Nie pisałem, bo nie chcę zamieniać Twojego - bądź co bądź - wątku w kurs programowania wg jakichś moich wyobrażeń. I tak pojawiają się tu dłużyzny nie do zniesienia. Wolałbym, żeby zachowało to formę typową dla Forum: pytanie - odpowiedź. OK, rozumiem jednak, że jesteś ciekaw jak ożywić inteligentne LEDy i chciałbys o tym przeczytać nawet "na sucho", bez możliwości interaktywnego tworzenia i uruchamiania własnego kodu. Dobra. Teraz muszę coś skończyć na szybko dla firmy, ale jutro (jeśli mnie ktoś nie ubiegnie - przecież nie jest to żadna wiedza tajemna) coś wrzucę z przykładowymi kodami. Mam tu gdzieś nawet kilka diodek skleconych w linijkę więc możemy się umówić, że przykłady będą pracować na 5 punktach RGB. Zaczniemy od prostego mrugania wg schematów, ew. przełączanych przyciskami a potem opiszę jak zrobić w systemie 10ms pętli głównej symulację powolnych żarówek "dorosłego" semafora. Znika wtedy nienaturalna i zaskakująca oko szybkość LEDów a lampki zaczynają wyglądać jak wielkie żarówy 🙂

Jeśli chcesz tanio robić urządzenia ze wsparciem systemu Arduino, to pomyśl o gotowych płytkach Mini Pro:

http://allegro.pl/listing/listing.php?order=p&string=arduino%20mini%20pro%20328&bmatch=base-relevance-uni-1-3-0229

To pełnoprawna ATmega328 na malutkiej płyteczce ze stabilizatorem napięcia, ale bez interfejsu USB. Aby podłączyć to do komputera i móc programować poprzez wbudowany bootloader wprost z IDE Arduino, musisz mieć albo komputer z portem COM (i konwerter RS232-TTL) albo przejściówkę USB-UART. Kupujesz ją tylko raz (zamiast np. na każdym kolejnym UNO) a potem do woli podłączasz do kolejnych płytek Mini Pro.

Do tej pory kod pisałem z głowy wiedząc, że i tak za chwilę go sprawdzisz i wytkniesz błędy. Teraz nie mogę(?) na to liczyć, więc właśnie na takim Mini Pro przetestuję wrzucane programy.

Link to post
Share on other sites

hej

Widzę kolega dalej męczy zmienne ...ok

jak się wystarczająco spocisz to daj se spokój i skup się na tablicach.

jako podpowiedz zapodam przykłada .. 10 elementowa tablica o głębokości 255 zajmuje 10 bajtów

możesz zapisać w niej kombinacje 2 do 8+8+8+8+8+8+8+8+8+8 kombinacji 🙂 pakowanie szeregowe

bez stratne .

10 elementów równoległych - bedzie równocześnie pamietać 10 urządzeń w 255 pozycjach do których masz dostęp z kazdego miejsca programu i mozliwośc modyfikacji bez przenoszenia wartosci poszczególnych zmiennych ( 10 semaforów z 255 nuzkami lub swiatłkami 0-1)+ dekoder.

bez dekodera sterujesz besposrednio 10 * 8 led ( zapalic zgasic) dokładajac 5 bajtów i dzieląc je na dwa kazdy , mozesz ustawic strumien natężenia swiatła 0-15, 16-255.( 4 wystopniowane rezystory)

REASUMUJAC 45 bajtów daje ci mozliwosc sterowania 10 parami po 8 ledów razy 3 kolory w 15 stopniach natęzenia swiecenia !!!

KOMPRENDE????

[ Dodano: 24-03-2016, 23:48 ]

Jesli pamiętam atmega8 ma 21 wysterowanych bitów 0-1 cyfrowych.

8+3+10 - sterowanie bezposrednie do jw schematu .

dodając 1 moduł z podwójną bramką AND 4 kanałową uzyskasz 4 dodatkowe sterowania warunkowe.

np: jesli semafor 4 i 7 zadzialają to aktywuj w takim samym stanie semafor 11.

jesli działa 11 i 3 aktywuj stan 3 na semaforze 12 ........

to tak na szybko...... 😋

bramki do wydłubania ze starych kart sterujacych: atapi ide lpt , itp

Link to post
Share on other sites
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.