Pornind de la tutorialul anterior, cum am putea face astfel încât la o apăsare şi eliberare a butonului, ledul să se aprindă şi să stea aprins până la următoarea apăsare/eliberare a butonului? Adică, dacă tot avem un microcontroller care face aprinderea/stingerea unui led, să ne bucurăm de acest microcontroller.
Ei bine, Ne vom folosi de o altă variabilă astfel încât, în momentul în care se dă drumul butonului, variabila respectivă să rămână cu valoarea dată în momentul apăsării butonului… Hai să vedem codul, pentru că aşa înţelegem mai bine. Şi să începem cu o greşeală…
void loop() { // transferam variabilei stareButon valoarea de la pinul 7 bool stareButon = digitalRead(7); // introducem o noua variabila numita stareLed, unde pastram valoarea 1 sau 0 int stareLed = 0; // este stareButon egal cu 1? if(stareButon==true) { //verifica stareLed switch(stareLed) { //daca valoarea stareLed este 1, atunci schimba stareLed cu 0 case 1: stareLed = 0; break; //daca valoarea stareLed este 0, atunci schimba stareLed cu 1 case 0: stareLed = 1; break; } } //daca stareLed este 1 atunci aprinde ledul if(stareLed == 1) { digitalWrite(13, HIGH); } // ...in caz contrar stinge ledul else { digitalWrite(13,LOW); } }
Ai fi zis că acest cod ar trebui să fie ok. Dacă dai verify/compile şi faci upload, ar trebui să funcţioneze… Dar asta nu se întâmplă. Poţi să îmi spui de ce? Hai, gândeşte-te!
După ce te-ai gândit la răspuns putem trece mai departe…
Acum urmează răspunsul….
Ei bine, noi am definit stareLed ca fiind egal cu 0 în cadrul funcţiei loop(), care funcţie se repetă imediat ce s-a terminat. Ce înseamnă asta? Înseamnă că aproape mereu stareLed va reveni la 0. Hai să mutăm stareLed în afara loop()-ului.
// introducem o noua variabila numita stareLed, unde pastram valoarea 1 sau 0 int stareLed = 0; void setup() { //aici face setarile initiale // Prin pinul 7 (butonul) Arduino va primi din exterior informatii pinMode(7,INPUT); // Prin pinul 13 (ledul) Arduino va trimite in exterior informatii pinMode(13, OUTPUT); } void loop() { // transferam variabilei stareButon valoarea de la pinul 7 bool stareButon = digitalRead(7); // este stareButon egal cu 1? if(stareButon==true) { //verifica stareLed switch(stareLed) { //daca valoarea stareLed este 1, atunci schimba stareLed cu 0 case 1: stareLed = 0; break; //daca valoarea stareLed este 0, atunci schimba stareLed cu 1 case 0: stareLed = 1; break; } } //daca stareLed este 1 atunci aprinde ledul if(stareLed == 1) { digitalWrite(13, HIGH); } // ...in caz contrar stinge ledul else { digitalWrite(13,LOW); } }
Acum, dacă testăm apăsarea/eliberarea butonului ledul ar trebui să rămână aprins. Apoi, la repetarea secvenţei de apăsare/eliberare, ledul ar trebui să se stingă. Dacă faci asta de destule ori ar trebui să meargă. Dar… nu merge chiar de fiecare dată… De ce? Te poţi concentra din nou pentru a găsi singur răspunsul? Dacă nu, îl putem căuta împreună…
Ei bine, întâmplarea are legătură tot cu faptul că totul se întâmplă în loop(). Hai întâi să mai uşurăm codul şi apoi vom da răspunsul… Cine ştie? Poate pe parcurs îţi vei da seama şi de răspuns.
Secvenţa aceasta de cod:
switch(stareLed) { //daca valoarea stareLed este 1, atunci schimba stareLed cu 0 case 1: stareLed = 0; break; //daca valoarea stareLed este 0, atunci schimba stareLed cu 1 case 0: stareLed = 1; break; }
…poate fi scrisă mai scurt aşa:
stareLed = !stareLed;
Pur şi simplu spunem variabilei stareLed să ia valoarea opusă celei pe care o are deja.
Acum hai ca în loc de numărul pinului să ne folosim de o denumire prin care să identificăm mai uşor ce pin aparţine cărui element. Aşadar, unde am definit stareLed, definim şi pinii cu denumiri mai uşor de înţeles
// introducem o noua variabila numita stareLed, unde pastram valoarea 1 sau 0 bool stareLed = 0; int buttonPin = 7; int ledPin = 13; void setup() { // Prin pinul 7 (butonul) Arduino va primi din exterior informatii pinMode(buttonPin,INPUT); // Prin pinul 13 (ledul) Arduino va trimite in exterior informatii pinMode(ledPin, OUTPUT); } void loop() { // transferam variabilei stareButon valoarea de la pinul 7 bool stareButon = digitalRead(buttonPin); // este stareButon egal cu 1? if(stareButon==true) { //verifica stareLed stareLed = !stareLed; } //daca stareLed este 1 atunci aprinde ledul if(stareLed == true) { digitalWrite(ledPin, HIGH); } // ...in caz contrar stinge ledul else { digitalWrite(ledPin,LOW); } }
Acum hai să ne uităm aici:
bool stareButon = digitalRead(buttonPin); if(stareButon==true) { stareLed = !stareLed; }
Nu am putea scrie totul mai simplu? De ce nu am scrie pur şi simplu:
if(digitalRead(buttonPin) == HIGH) { stareLed = !stareLed; }
Şi cu ocazia aceasta, de ce nu am modifica şi partea aceasta de cod:
if(stareLed == true) { digitalWrite(ledPin, HIGH); } // ...in caz contrar stinge ledul else { digitalWrite(ledPin,LOW); }
…cu…
digitalWrite(ledPin,stareLed);
Ok… Hai să ne mai uităm o dată la codul în totalitate, să îl compilăm şi să îl încărcăm pe microcontroller:
// introducem variabile care se aplică la nivel global bool stareLed = 0; int buttonPin = 7; int ledPin = 13; void setup() { // Prin pinul 7 (butonul) Arduino va primi din exterior informatii pinMode(buttonPin,INPUT); // Prin pinul 13 (ledul) Arduino va trimite in exterior informatii pinMode(ledPin, OUTPUT); } void loop() { // transferam variabilei stareButon valoarea de la pinul 7 if(digitalRead(buttonPin) == HIGH) { stareLed = !stareLed; } digitalWrite(ledPin,stareLed); }
Mult mai aerisit… Acum să ne întoarcem la problema noastră. Se pare că nu de fiecare dată când apăsăm/eliberăm butonul ledul se aprinde/stinge. De ce oare? Se pare că oricât de rapidă ar fi apăsarea butonului, în interiorul lui au loc mai multe apăsări/eliberări ale voltajului. Aşadar, ce ne facem? Problema rezidă în faptul că noi nu avem o siguranţă şi rapiditate atât de mare în apăsare încât microcontrollerul să fie sigur asupra acţiunii noastre. Prin urmare, ar trebui să “obligăm” microcontrollerul să aştepte puţin după apăsarea noastră pentru a-şi da seama dacă totul este în regulă. Vom face asta în cadrul unei noi funcţii care va returna o valoare booleană:
boolean debounce(boolean last) { boolean current = digitalRead(buttonPin); if(last != current) { if(last != current) { delay(5); current = digitalRead(buttonPin); } return current; } }
Ce face funcţia aceasta? Funcţia primeşte ca variabilă o valoare care îi spune care a fost situatia butonului la momentul chemării funcţiei (dacă butonul a fost apăsat sau neapăsat). Apoi funcţia verifică dacă în prezent (ţine minte că intre “prezent” şi chemarea funcţiei au trecut deja câteva miimi de secundă în care butonul poate să fi avut deja cateva porniri/opriri care au “scăpat” degetului nostru). Dacă vede că cele două variabile sunt diferite, atunci pesemne că ceva nu este în regulă. Din această cauză, micro-controllerul este pus să aştepte 5 milisecunde şi apoi este pus din nou să citească valoarea pinului buton, valoare care este trimisă înapoi variabilei care a chemat funcţia. Dar ce variabilă cheamă funcţia? Hai să schimbăm din nou loop()-ul pentru a ne folosi de funcţia debounce():
void loop() { currentButton = debounce(lastButton); if(lastButton == LOW && currentButton == HIGH) { stareLed = !stareLed; } lastButton = currentButton; digitalWrite(ledPin,stareLed); }
Acum, pentru a ne folosi de variabilele currentButton şi lastButton trebuie să le definim undeva. Le vom defini printre celelalte variabile globale cu valoarea iniţială LOW. Hai să vedem din nou întreg sketch-ul:
// introducem o noua variabila numita stareLed, unde pastram valoarea 1 sau 0 int buttonPin = 7; int ledPin = 12; bool stareLed = 0; bool lastButton = LOW; bool currentButton = LOW; void setup() { // Prin pinul 7 (butonul) Arduino va primi din exterior informatii pinMode(buttonPin,INPUT); // Prin pinul 13 (ledul) Arduino va trimite in exterior informatii pinMode(ledPin, OUTPUT); } boolean debounce(boolean last) { boolean current = digitalRead(buttonPin); if(last != current) { delay(5); current = digitalRead(buttonPin); } else { return current; } } void loop() { currentButton = debounce(lastButton); // transferam variabilei stareButon valoarea de la pinul 7 if(lastButton == LOW && currentButton == HIGH) { stareLed = !stareLed; } lastButton = currentButton; digitalWrite(ledPin,stareLed); }
Bun… Acum dacă reîncărcăm sketch-ul compilat pe micro-controller totul ar trebui să meargă cum trebuie. Şi aşa se va întâmpla.
Dar stai!!!
Delay-ul acela de 5 milisecunde este unul ales arbitrar. Adică de unde ştim noi că un delay de 5 milisecunde ar rezolva dilema noastră? Poate nu are nevoie de 5 milisecunde. Poate are nevoie de mai puţin. Sau poate are nevoie de mai mult?
Atunci hai să punem un delay de o milisecundă, după care să rechemăm funcţia debounce() înăuntrul funcţiei debounce (funcţie recursivă) pentru a verifica din nou că totul este ok:
boolean debounce(boolean last) { boolean current = digitalRead(buttonPin); if(last != current) { delay(1); debounce(current); } else { return current; } }
Grozav. Hai să revedem codul final:
// introducem o noua variabila numita stareLed, unde pastram valoarea 1 sau 0 int buttonPin = 7; int ledPin = 12; bool stareLed = 0; bool lastButton = LOW; bool currentButton = LOW; void setup() { pinMode(buttonPin,INPUT); pinMode(ledPin, OUTPUT); } boolean debounce(boolean last) { boolean current = digitalRead(buttonPin); if(last != current) { delay(1); debounce(current); } else { return current; } } void loop() { currentButton = debounce(lastButton); // transferam variabilei stareButon valoarea de la pinul 7 if(lastButton == LOW && currentButton == HIGH) { stareLed = !stareLed; } lastButton = currentButton; digitalWrite(ledPin,stareLed); }
Asta-i tot!…
Oups… Îmi cer scuze… Nu vreau să se creadă că am un creier cât o baniţă. Nu… Ideea cu debounce()-ul nu este a mea. Am găsit-o în acest video pe care te invit să îl urmăreşti (este grozav!):