Przerwania typu PCINT w Arduino są niezwykle przydatne w praktyce. Oczywiście można wykorzystywać przerwania zewnętrzne (External Interrupts) jednak w Arduino Uno mamy do dyspozycji jedynie dwa przerwania zewnętrzne – w wielu przypadkach to za mało. Przerwania typu PCINT można użyć na każdym pinie Arduino – daje to niesamowite możliwości!
Przykład – klawiatura złożona z trzech przycisków.
Przyciski podłączamy zgodnie z poniższym schematem – Rys.1 (oczywiście numery pinów podłączeń przycisków są dowolne – jednak zmianę trzeba uwzględnić w kodzie).
Rys.1. Przykładowy sposób podłączenia przycisków do Arduino.
W przykładzie wybrano następujące podłączenie:
- przycisk S1 – pin Arduino nr 11,
- przycisk S2 – pin Arduino nr 7,
- przycisk S3 – pin Arduino nr 9.
Następnie włączamy generator przerwań typu PCINT, aby błyskawicznie wygenerować niezbędny fragment kodu (Link do generatora przerwań typu PCINT).
W generatorze zaznaczamy odpowiednie piny Arduino czyli: D11, D7 oraz D9 (tak jak na Rys.2).
Rys.2. Sposób wyboru pinów Arduino, na których obsługiwane będą przerwania typu PCINT.
Wynikiem generacji jest poniższy kod:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// Code generated form Arduinowo.pl #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) boolean flag=0; boolean PIN_STATE_D7=HIGH; boolean PIN_STATE_D9=HIGH; boolean PIN_STATE_D11=HIGH; void setup() { pinMode(7, INPUT_PULLUP); pinMode(9, INPUT_PULLUP); pinMode(11, INPUT_PULLUP); sbi (PCICR, PCIE2); sbi (PCMSK2, PCINT23); sbi (PCICR, PCIE0); sbi (PCMSK0, PCINT1); sbi (PCMSK0, PCINT3); } void loop() { if(flag==1){ flag=0; //do something after pin change } } ISR(PCINT2_vect) { flag=1; PIN_STATE_D7=!bitRead(PIND,7); } ISR(PCINT0_vect) { flag=1; PIN_STATE_D9=!bitRead(PINB,1); PIN_STATE_D11=!bitRead(PINB,3); } |
W tym przypadku zmienna “flag” powinna być typu volatile.
Zmienne PIN_STATE_D7, PIN_STATE_D9, PIN_STATE_D11 – wskazują nam, czy przyciski są aktualnie wciśnięte czy też nie (wartość 1 dla wciśniętego przycisku, wartość 0 dla zwolnionego przycisku).
Zadaniem natychmiastowej aktualizacji stanów powyższych zmiennych zajmują się właśnie zdefiniowane przerwania typu PCINT. To olbrzymie ułatwienie, ponieważ aktualizacja przebiega w tle, niezależnie od realizowanego programu głównego. W każdym miejscu programu mamy dostęp do aktualnych wartości. Praktycznie nie musimy się martwić, że program nie zauważy wciśnięcia przycisku, bo wykonuje jakąś inną czasochłonną funkcję.
W dalszym kroku, aby klawiatura działała poprawnie, musimy programowo wyeliminować wpływ efektu drgań styków. Ustalono opóźnienie 50ms (stała: t_delay).
Przykładowy program obsługujący klawiaturę (zliczający przyciśnięcia) wykorzystujący przerwania PCINT przedstawiono poniżej:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
// Code generated form Arduinowo.pl #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) boolean PIN_STATE_D7=HIGH; boolean PIN_STATE_D9=HIGH; boolean PIN_STATE_D11=HIGH; volatile boolean flag=0; volatile unsigned int sumaS1=0; volatile unsigned int sumaS2=0; volatile unsigned int sumaS3=0; boolean prevS1=0; boolean prevS2=0; boolean prevS3=0; unsigned long t_S1=millis(); unsigned long t_S2=millis(); unsigned long t_S3=millis(); const unsigned int t_delay=50; void setup() { pinMode(7, INPUT_PULLUP); pinMode(9, INPUT_PULLUP); pinMode(11, INPUT_PULLUP); sbi (PCICR, PCIE2); sbi (PCMSK2, PCINT23); sbi (PCICR, PCIE0); sbi (PCMSK0, PCINT1); sbi (PCMSK0, PCINT3); Serial.begin(9600); } void loop() { if(flag==1){ flag=0; Serial.print("Licznik wcisniec przyciskow:"); Serial.print(" S1: ");Serial.print(sumaS1); Serial.print(" S2: ");Serial.print(sumaS2); Serial.print(" S3: ");Serial.println(sumaS3); } } ISR(PCINT2_vect) { PIN_STATE_D7=!bitRead(PIND,7); if(PIN_STATE_D7!=prevS2) { if(PIN_STATE_D7==HIGH&&millis()-t_S2>t_delay) { flag=1; sumaS2++; } if(PIN_STATE_D7==LOW)t_S2=millis(); } prevS2=PIN_STATE_D7; } ISR(PCINT0_vect) { PIN_STATE_D9=!bitRead(PINB,1); if(PIN_STATE_D9!=prevS3) { if(PIN_STATE_D9==HIGH&&millis()-t_S3>t_delay) { flag=1; sumaS3++; } if(PIN_STATE_D9==LOW)t_S3=millis(); } prevS3=PIN_STATE_D9; PIN_STATE_D11=!bitRead(PINB,3); if(PIN_STATE_D11!=prevS1) { if(PIN_STATE_D11==HIGH&&millis()-t_S1>t_delay) { flag=1; sumaS1++; } if(PIN_STATE_D11==LOW)t_S1=millis(); } prevS1=PIN_STATE_D11; } |
Efekt działania programu, gdy wciskamy nasze przyciski (odpowiedź na porcie szeregowym) przedstawiono na Rys.3.
Rys.3. Odpowiedź z portu szeregowego na przyciskanie przycisków klawiatury.
Gdyby były problemy ze zrozumieniem powyższego kodu proszę o kontakt na stronie Facebook Arduino Polska.