Používame sekvencer - semafór

Zápisník experimentátora

Obľúbeným projektom pre Arduino je riadenie svetelnej križovatky. V dnešnom článku si takýto semafór naprogramujeme. Nebudeme to robiť klasicky, ale využijeme sekvencer, pomocou ktorého sa táto úloha zmení na veľmi jednoduchú úlohu. Sekvencer sme si programovali aj v minulosti v článku o generovaní signálu SOS.

Nákup súčiastok

Na fotografiách je použité Arduino Pro Mini. To vás nelimituje, môžete použiť v podstate ľubovoľné Arduino.

  • Arduino Pro Mini (link) - Toto Arduino som použil.
  • Breadboard (link) - Sem som zastrčil všetky súčiastky.
  • 2x zelená, žltá a červená LED dióda - Farby som vybral tak, aby sa čo najviac podobali na svetlá na semafore. Žltá LED je ako náhrada za oranžovú farbu.
  • 6x rezistor 1k - Rezistor obmedzuje veľkosť prúdu, ktorý tečie cez LED diódu. Pretože používame vysokosvietivé LED diódy, stačí nám prúd pár miliampérov.

Semafór

Náš príklad simuluje dva semafóry. Na oboch sa nachádzajú tri svetlá. Tri svetlá sa starajú o to, aby sa autá na križovatke nezrazili. Čiže na jednom svieti zelené svetlo a na druhom červené. V pravidelných intervaloch sa svetlá na semafóroch vymenia.

Typický semafor na Slovensku funguje nasledovne. Na semafore svieti 10-20 sekúnd zelená farba. Tá sa prepne na oranžovú a následne na červenú. Potom svieti červená farba 10-20 sekúnd. Súčasne s červenou farbou sa zapne oranžová farba. Potom obe farby zhasnú a zapne sa zelená farba. Takto sa to opakuje stále dokola. Na druhom semafore sú farby posunuté. Vždy na jednom semafore svieti červená farba a na druhom semafore zelená.

Sekvencer

Sekvencery sú veľmi jednoduché nástroje. V pravidelných intervaloch posielajú na výstup požadovanú hodnotu. V našom prípade vytvoríme sekvencer, ktorý bude simulovať predchádzajúci príklad. V intervaloch 1 s zapne alebo vypne LED diódy. Sekvencer má tu výhodu, že pokiaľ by došlo ku zmene požiadaviek na program, samotný program sa nemusí zmeniť a zmení sa iba sekvencia.

Program

Napísal som tri programy. Dva slúžia na otestovanie zapojenia a tretím je samotná simulácia semafóra.

sequencer_traffic_light_01

Prvý program slúži na testovanie zapojenia. Postupne zapne každú LED diódu. Ak sa všetky rozsvietia, máte to zapojené správne. Zapojenie jednotlivých LED diód nájdete v zdrojovom texte programu.

int leds[] = {2, 3, 4, 5, 6, 7};
int step = 0;

void setup() {
  for (int i = 0; i < 6; i++)
    pinMode(leds[i], OUTPUT);
}

void loop() {
  for (int i = 0; i < 6; i++)
    digitalWrite(leds[i], i == step ? HIGH : LOW);
  step++;
  step %= 6;
  delay(1000);
}

sequencer_traffic_light_02

Druhý program slúži na testovanie zapojenia a testovanie sekvencera. Sekvencer je vytvorený v triede Sequencer. Má len dve funkcie. V konštruktore si poznačíme údaje o sekvencii a vo funkcii next sa zakaždým posunieme dopredu v sekvencii a podľa toho nastavíme LED diódu na HIGH alebo LOW. Všimnite si, na ktoré piny v Arduine sú pripojené LED diódy. Sú to piny 2, 3, 4, 5, 6 a 7. Prečo som si vybral práve tieto piny? Pretože všetky tieto piny sa nachádzajú na porte D a môžem ich ovládať jedným priradením hodnoty priamo na port

int leds[] = {2, 3, 4, 5, 6, 7};

//  222111xx
//  GYRGYR
const char PROGMEM sequence[] = {
  0b00000100,
  0b00001000,
  0b00010000,
  0b00100000,
  0b01000000,
  0b10000000,
};
const int sequence_length = sizeof(sequence) / sizeof(char);

class Sequencer {
    char *data;
    int len;
    int pos;

  public:
    Sequencer(char *_data, int _len)
      : data(_data), len(_len), pos(0)
    {}

    void next() {
      char b = pgm_read_byte(&data[pos]);
      PORTD = b;
      pos++;
      if (pos == len)
        pos = 0;
    }
};

Sequencer seq(sequence, sequence_length);

void setup() {
  for (int i = 0; i < 6; i++)
    pinMode(leds[i], OUTPUT);
}

void loop() {
  seq.next();
  delay(1000);
}

sequencer_traffic_light_03

Tretí program je samotná simulácia svetiel na križovatke. Predchádzajúci príklad bol kvôli prehľadnosti časovaný pomocou funkcie delay. Teraz už môžeme náš program urobiť maximálne efektívne a na časovanie využijeme timer1. Pomocou kalkulátora si nastavíme timer na frekvenciu 1 Hz (každých 1000 ms) a funkciu next z triedy Sequencer budeme volať z prerušenia TIMER1_COMPA_vect.

int leds[] = {2, 3, 4, 5, 6, 7};

//  222111xx
//  GYRGYR
const char PROGMEM sequence[] = {
  0b10000100, // red, green
  0b10000100,
  0b10000100,
  0b10000100,
  0b10000100,
  0b10000100,
  0b10000100,
  0b10000100,
  0b10000100,
  0b10000100,
  0b01000100, // red, orange
  0b01000100,
  0b00100100, // red, red
  0b00101100, // red+orange, red
  0b00110000, // green, red
  0b00110000,
  0b00110000,
  0b00110000,
  0b00110000,
  0b00110000,
  0b00110000,
  0b00110000,
  0b00110000,
  0b00110000,
  0b00101000, // orange, red
  0b00101000,
  0b00100100, // red, red
  0b01100100, // red, red+orange
};
const int sequence_length = sizeof(sequence) / sizeof(char);

class Sequencer {
    char *data;
    int len;
    int pos;

  public:
    Sequencer(char *_data, int _len)
      : data(_data), len(_len), pos(0)
    {}

    void next() {
      char b = pgm_read_byte(&data[pos]);
      PORTD = b;
      pos++;
      if (pos == len)
        pos = 0;
    }
};

Sequencer seq(sequence, sequence_length);

void setupTimer1() {
  noInterrupts();
  // Clear registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // 1 Hz (16000000/((15624+1)*1024))
  OCR1A = 15624;
  // CTC
  TCCR1B |= (1 << WGM12);
  // Prescaler 1024
  TCCR1B |= (1 << CS12) | (1 << CS10);
  // Output Compare Match A Interrupt Enable
  TIMSK1 |= (1 << OCIE1A);
  interrupts();
}

void setup() {
  for (int i = 0; i < 6; i++)
    pinMode(leds[i], OUTPUT);
  setupTimer1();
}

void loop() {
}

ISR(TIMER1_COMPA_vect) {
  seq.next();
}

Zdrojový kód

Zdrojový kód sa nachádza na serveri GitHub.


07.09.2017


Menu