Čítanie stavu tlačidla pomocou timera

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.

Použité súčiastky

Na tento experiment stačí iba pár súčiastok:

  • Arduino Pro Mini (link) - Použil som ho preto, lebo sa zmestí aj na najmenšie skúšobné pole.
  • Breadboard 400 Holes (link) - Na ňom máme dosť miesta na dva mikrospínače.
  • Prevodník CP2102 USB to Serial (link) - Prevodník slúži na naprogramovanie Arduina.
  • Mikrospínače (link, link) - potrebujeme dva kusy. Do skúšobného pola sa dajú použiť aj štvorpinové aj dvojpinové. Niekedy sú piny dlhé, alebo majú podivné výstupky. Nebojte sa tie konce odstrihnúť, aby sa lepšie dali zastrčiť.

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.

Príklad 1

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++;
  }
}

Príklad 2

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.

  • Počas prvej sekundy stlačenia sa obsah premennej zmení iba 2x. Celú mágiu nám zabezpečí vhodné nastavovanie premennej int_counter_xxx a práca s operátorom modulo.
  • Počas druhej sekundy stlačenia sa obsah premennej zmení 10x.
  • Od tretej sekundy sa obsah premennej mení 50x za sekundu.
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

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


05.03.2017


Menu