Čtení stavu tlačítka pomocí timeru

Zápisník experimentátora

Hierarchy: Časovač (timer)

Pomocí timeru si můžete zjednodušit několik úkolů. Můžete ho využít i ke kontrole stavu tlačítka. V tomto článku si ukážeme, jak můžeme kontrolovat dvě tlačítka, které mění hodnotu proměnné. Jedno tlačítko hodnotu snižuje a druhé ji zvyšuje. Během stisknutí tlačítka se vše děje s minimem kódu a v tomto speciálním případě není ani nutné ošetřovat zákmity na tlačítkách.

Použité součástky

Na tento experiment stačí pouze pár součástek:

  • Arduino Pro Mini (link) - Použil jsem ho proto, že se vejde i na nejmenší zkušební pole.
  • Breadboard 400 Holes (link) - Na něm máme dost místa na dva mikrospínače.
  • Převodník CP2102 USB to Serial (link) - Převodník slouží k naprogramování Arduina.
  • Mikrospínače (linklink) - potřebujeme dva kusy. Do zkušebního pole se dají použít i čtyřpinový i dvojpinové. Někdy jsou piny dlouhé, nebo mají podivné výstupky. Nebojte se ty konce odstřihnout, aby se lépe daly zastrčit.

Mikrospínače jsou připojeny na piny 8 a 6 Arduina. Na druhé straně jsou propojeny na GND, proto nezapomeňte v kódu vždy nastavit pin jako vstupní pull-up.

Příklad 1

V prvním příkladu bude timer1 nastaven na frekvenci 10 Hz. Využil jsem na to mou online kalkulačku na výpočet časovačů. Přerušení nastane 10x za sekundu. Pokud držíme stisknuté levé tlačítko, 10x za sekundu se zmenší hodnota proměnné a výsledek se vypíše na sériový port. Pokud držíme stisknuté pravé tlačítko, 10x za sekundu se zvětší hodnota proměnné a výsledek se vypíše na sériový port.

Definice proměnných. Modifikátor volatile musíme použít tehdy, když proměnnou modifikujeme z hlavního programu a z přerušení. Jinak by mohl překladač optimalizovat proměnnou do registru a dostali bychom podivné výsledky při čtení jejího obsahu.

#define ledPin 13
#define buttonLeft 8
#define buttonRight 6

volatile int value = 0;
int oldvalue = -999;

Toto je kompletně generován kód. V kalkulátoru jsem pouze nastavil požadovaný timer a jeho frekvenci.

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();
}

Nastavení pinů. Všimněte si, že piny 8 a 6 jsou nastaveny jako INPUT_PULLUP, abych nemusel použít externí rezistor. Takto je hodnota pinu HIGH v nestlačeném stavu a LOW ve stlačeném stavu.

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(buttonLeft,INPUT_PULLUP); // left button
  pinMode(buttonRight,INPUT_PULLUP); // right button
  setupTimer1();
}

Vypisování obsahu proměnné na sériový port. Protože proměnná má typ int, musím zakázat během jejího čtení přerušení, abych zaručil atomické přečtení obsahu. Zbytek kódu se stará o to, aby se vypisovalo pouze při změně její hodnoty.

void loop() {
  int v;
  noInterrupts();
  v = value;
  interrupts();
  if(v!=oldvalue) {
    Serial.println(v);
    oldvalue = v;
  }
}

Obsluha přerušení. Tento kód je snadno pochopitelný. Přerušení se vyvolá 10x za sekundu, takže při stlačení se poměrně rychle vypisují změněné hodnoty. Při taktomto nastavení je ale komplikované nastavovat změny například o 1. To zajistí pří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++;
  }
}

Příklad 2

Ve druhém příkladu bude timer1 nastaven na frekvenci 100 Hz. Využil jsem na to mou online kalkulačku na výpočet časovačů. Pokud držíme stisknuté levé tlačítko, 2-10-50x za sekundu se zmenší hodnota proměnné a výsledek se vypíše na sériový port. Pokud držíme stisknuté pravé tlačítko, 2-10-50x za sekundu se zvětší hodnota proměnné a výsledek se vypíše na sériový port. V obou případech se rychlost změny obsahu proměnné postupně zvyšuje.

Definice proměnných. Modifikátor volatile musíme použít tehdy, když proměnnou modifikujeme z hlavního programu a z přerušení. Jinak by mohl překladač optimalizovat proměnnou do registru a dostali bychom podivné výsledky při čtení jejího 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 kompletně generován kód. V kalkulátoru jsem pouze nastavil požadovaný timer a jeho frekvenci.

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();
}

Nastavení pinů. Všimněte si, že piny 8 a 6 jsou nastaveny jako INPUT_PULLUP, abych nemusel použít externí rezistor. Takto je hodnota pinu HIGH v nestlačeném stavu a LOW ve stlačeném stavu.

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(buttonLeft,INPUT_PULLUP); // left button
  pinMode(buttonRight,INPUT_PULLUP); // right button
  setupTimer1();
}

Vypisování obsahu proměnné na sériový port. Protože proměnná má typ int, musím zakázat během jejího čtení přerušení, abych zaručil atomické přečtení obsahu. Zbytek kódu se stará o to, aby se vypisovalo pouze při změně její hodnoty.

void loop() {
  int v;
  noInterrupts();
  v = value;
  interrupts();
  if(v!=oldvalue) {
    Serial.println(v);
    oldvalue = v;
  }
}

Obsluha přerušení. Přerušení se zavolá 100x za sekundu. To je již poměrně rychlé a proto musíme změny proměnné value vhodně zpomalit. Takovou frekvenci jsem zvolil proto, aby byla obsluha tlačítka svižná a reagovala i na nejmenší stisknutí tlačítka.

  • Během první sekundy stisknutí se obsah proměnné změní pouze 2x. Celou magii nám zajistí vhodné nastavování proměnné int_counter_xxx a práce s operátorem modulo.
  • Během druhé sekundy stisknutí se obsah proměnné změní 10x.
  • Od třetí sekundy se obsah proměnné mění 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 se nachází na GitHub.


17.07.2018


Menu