Zápisník experimentátora
Hierarchy: Časovač (timer)
Pomocou timera si môžete zjednodušiť viaceré úlohy. Môžete ho využiť aj na kontrolu stavu tlačidla. V tomto článku si ukážeme, ako môžeme kontrolovať dve tlačidlá, ktoré menia hodnotu premennej. Jedno tlačidlo hodnotu znižuje a druhé ju zvyšuje. Počas stlačenia tlačidla sa všetko deje s minimom kódu a v tomto špeciálnom prípade nie je ani potrebné ošetrovať zákmity na tlačidlách.
Na tento experiment stačí iba pár súčiastok:
Mikrospínače sú pripojené na piny 8 a 6 Arduina. Na druhej strane sú prepojené na GND, preto nezabudnite v kóde vždy nastaviť pin ako vstupný pull-up.
V prvom príklade bude timer1
nastavený na frekvenciu 10 Hz. Využil som na to moju online kalkulačku na výpočet timerov. Prerušenie nastane 10x za sekundu. Pokiaľ držíme stlačené ľavé tlačidlo, 10x za sekundu sa zmenší hodnota premennej a výsledok sa vypíše na sériový port. Pokiaľ držíme stlačené pravé tlačidlo, 10x za sekundu sa zväčší hodnota premennej a výsledok sa vypíše na sériový port.
Definície premenných. Modifikátor volatile
musíme použiť vtedy, keď premennú modifikujeme z hlavného programu a z prerušenia. Ináč by mohol prekladač optimalizovať premennú do registra a dostali by sme podivné výsledky pri čítaní jej obsahu.
#define ledPin 13
#define buttonLeft 8
#define buttonRight 6
volatile int value = 0;
int oldvalue = -999;
Toto je kompletne generovaný kód. V kalkulátore som iba nastavil požadovaný timer a jeho frekvenciu.
void setupTimer1() {
noInterrupts();
// Clear registers
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
// 10 Hz (16000000/((6249+1)*256))
OCR1A = 6249;
// CTC
TCCR1B |= (1 << WGM12);
// Prescaler 256
TCCR1B |= (1 << CS12);
// Output Compare Match A Interrupt Enable
TIMSK1 |= (1 << OCIE1A);
interrupts();
}
Nastavenie pinov. Všimnite si, že piny 8 a 6 sú nastavené ako INPUT_PULLUP
, aby som nemusel použiť externý rezistor. Takto je hodnota pinu HIGH
v nestlačenom stave a LOW
v stlačenom stave.
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
pinMode(buttonLeft,INPUT_PULLUP); // left button
pinMode(buttonRight,INPUT_PULLUP); // right button
setupTimer1();
}
Vypisovanie obsahu premennej na sériový port. Pretože premenná má typ int
, musím zakázať počas jej čítania prerušenie, aby som zaručil atomické prečítanie obsahu. Zvyšok kódu sa stará o to, aby sa vypisovalo iba pri zmene jej hodnoty.
void loop() {
int v;
noInterrupts();
v = value;
interrupts();
if(v!=oldvalue) {
Serial.println(v);
oldvalue = v;
}
}
Obsluha prerušenia. Tento kód je ľahko pochopiteľný. Prerušenie sa vyvolá 10x za sekundu, takže pri stlačení sa pomerne rýchlo vypisujú zmenené hodnoty. Pri taktomto nastavení je ale komplikované nastavovať zmeny napríklad o 1. To zabezpečí príklad číslo 2.
ISR(TIMER1_COMPA_vect) {
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
// left button
if(digitalRead(buttonLeft)==false) {
value--;
}
// right button
if(digitalRead(buttonRight)==false) {
value++;
}
}
V druhom príklade bude timer1
nastavený na frekvenciu 100 Hz. Využil som na to moju online kalkulačku na výpočet timerov. Pokiaľ držíme stlačené ľavé tlačidlo, 2-10-50x za sekundu sa zmenší hodnota premennej a výsledok sa vypíše na sériový port. Pokiaľ držíme stlačené pravé tlačidlo, 2-10-50x za sekundu sa zväčší hodnota premennej a výsledok sa vypíše na sériový port. V oboch prípadoch sa rýchlosť zmeny obsahu premennej postupne zvyšuje.
Definície premenných. Modifikátor volatile
musíme použiť vtedy, keď premennú modifikujeme z hlavného programu a z prerušenia. Ináč by mohol prekladač optimalizovať premennú do registra a dostali by sme podivné výsledky pri čítaní jej obsahu.
#define ledPin 13
#define buttonLeft 8
#define buttonRight 6
volatile int value = 0;
int oldvalue = -999;
int int_counter_left = 0;
int int_counter_right = 0;
Toto je kompletne generovaný kód. V kalkulátore som iba nastavil požadovaný timer a jeho frekvenciu.
void setupTimer1() {
noInterrupts();
// Clear registers
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
// 100 Hz (16000000/((624+1)*256))
OCR1A = 624;
// CTC
TCCR1B |= (1 << WGM12);
// Prescaler 256
TCCR1B |= (1 << CS12);
// Output Compare Match A Interrupt Enable
TIMSK1 |= (1 << OCIE1A);
interrupts();
}
Nastavenie pinov. Všimnite si, že piny 8 a 6 sú nastavené ako INPUT_PULLUP
, aby som nemusel použiť externý rezistor. Takto je hodnota pinu HIGH
v nestlačenom stave a LOW
v stlačenom stave.
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
pinMode(buttonLeft,INPUT_PULLUP); // left button
pinMode(buttonRight,INPUT_PULLUP); // right button
setupTimer1();
}
Vypisovanie obsahu premennej na sériový port. Pretože premenná má typ int
, musím zakázať počas jej čítania prerušenie, aby som zaručil atomické prečítanie obsahu. Zvyšok kódu sa stará o to, aby sa vypisovalo iba pri zmene jej hodnoty.
void loop() {
int v;
noInterrupts();
v = value;
interrupts();
if(v!=oldvalue) {
Serial.println(v);
oldvalue = v;
}
}
Obsluha prerušenia. Prerušenie sa zavolá 100x za sekundu. To je už pomerne rýchle a preto musíme zmeny premennej value
vhodne spomaliť. Takúto frekvenciu som zvolil preto, aby bola obsluha tlačidla svižná a reagovala aj na najmenšie stlačenie tlačidla.
int_counter_xxx
a práca s operátorom modulo.
ISR(TIMER1_COMPA_vect) {
digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
// left button
if(digitalRead(buttonLeft)==false) {
if(int_counter_left<100)
value-=(int_counter_left%50) ? 0 : 1;
else if(int_counter_left<200)
value-=(int_counter_left%10) ? 0 : 1;
else
value-=(int_counter_left%2) ? 0 : 1;
int_counter_left++;
}
else
int_counter_left = 0;
// right button
if(digitalRead(buttonRight)==false) {
if(int_counter_right<100)
value+=(int_counter_right%50) ? 0 : 1;
else if(int_counter_right<200)
value+=(int_counter_right%10) ? 0 : 1;
else
value+=(int_counter_right%2) ? 0 : 1;
int_counter_right++;
}
else
int_counter_right = 0;
}
Zdrojový kód sa nachádza na GitHub.
05.03.2017