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.
Na fotografiách je použité Arduino Pro Mini. To vás nelimituje, môžete použiť v podstate ľubovoľné Arduino.
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á.
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.
Napísal som tri programy. Dva slúžia na otestovanie zapojenia a tretím je samotná simulácia semafóra.
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);
}
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);
}
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 sa nachádza na serveri GitHub.
07.09.2017