Zápisník experimentátora
Oblíbeným projektem pro Arduino je řízení světelné křižovatky. V dnešním článku si takový semafor naprogramujeme. Nebudeme to dělat klasicky, ale využijeme sekvencer, pomocí kterého se tato úloha změní na velmi jednoduchou úlohu. Sekvencer jsme si programovali iv minulosti v článku o generování signálu SOS.
Na fotografiích je použito Arduino Pro Mini. To vás nelimituje, můžete použít v podstatě libovolné Arduino.
Náš příklad simuluje dva semafory. Na obou se nacházejí tři světla. Tři světla se starají o to, aby se auta na křižovatce nesrazily. Čili na jednom svítí zelené světlo a na druhém červené. V pravidelných intervalech se světla na semaforech vymění.
Typický semafor na Slovensku funguje následovně. Na semaforu svítí 10-20 sekund zelená barva. Ta se přepne na oranžovou a následně na červenou. Potom svítí červená barva 10-20 sekund. Současně s červenou barvou se zapne oranžová barva. Potom obě barvy zhasnou a zapne se zelená barva. Takto se to opakuje stále dokola. Na druhém semaforu jsou barvy posunuté. Vždy na jednom semaforu svítí červená barva a na druhém semaforu zelená.
Sekvencery jsou velmi jednoduché nástroje. V pravidelných intervalech posílají na výstup požadovanou hodnotu. V našem případě vytvoříme sekvencer, který bude simulovat předchozí příklad. V intervalech 1 s zapne nebo vypne LED diody. Sekvencer má tu výhodu, že pokud by došlo ke změně požadavků na program, samotný program se nemusí změnit a změní se pouze sekvence.
Napsal jsem tři programy. Dva slouží k otestování zapojení a třetím je samotná simulace semaforů.
První program slouží k testování zapojení. Postupně zapne každou LED diodu. Pokud se všechny rozsvítí, máte to zapojeno správně. Zapojení jednotlivých LED diod najdete v zdrojovém textu 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 slouží k testování zapojení a testování sekvenceru. Sekvencer je vytvořen ve třídě Sequencer
. Má pouze dvě funkce. V konstruktoru
si poznačit údaje o sekvenci a ve funkci next
se pokaždé posuneme vpřed v sekvenci a podle toho nastavíme LED diodu na HIGH nebo LOW. Všimněte si, na které piny v Arduine jsou připojeny LED diody. Jsou to piny 2, 3, 4, 5, 6 a 7. Proč jsem si vybral právě tyto piny? Protože všechny tyto piny se nacházejí na portu D a mohu je ovládat jedním přiřazením hodnoty přímo 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);
}
Třetí program je samotná simulace světel na křižovatce. Předchozí příklad byl kvůli přehlednosti časovaný pomocí funkce delay
. Nyní už můžeme náš program udělat maximálně efektivně a na časování využijeme timer1. Pomocí kalkulátora si nastavíme timer na frekvenci 1 Hz (každých 1000 ms) a funkci next
z třídy Sequencer budeme volat z přerušení 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 se nachází na serveru GitHub.
12.09.2017