Sunteți pe pagina 1din 15

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

Proiect 5 Butoane i sistemul de ntreruperi

Coninutul lucrrii pe scurt:


Prezentarea modulului de interconectarea a unui buton la
placa Arduino Uno
Modaliti de configurare a ntreruperilor externe i a
ntreruperilor asociate mecanismelor de tip timer
Exemplu de cronometru cu activare direct a sistemului de
ntreruperi

Echipamente necesare lucrrii:


Plac de dezvoltare Arduino Uno sau compatibil
Cablu de conectare USB
2 breadboard 30 pini sau 1 breadboard 60+ pini + fire de
interconectare
3 butoane, 3 rezistoare 10kohm, 3 condesatoare 100nF
2 shift-register TPIC6B595
2 rezistoare 220ohm
Afiaj pe 7 segmente 2 caractere anod comun

Seciuni lucrare:
Modaliti de conectare a unui buton
Modaliti de manipulare software a intrrilor digitale
Exemplu de manipulare software a intrrilor digitale
Exemplu de numrtor pe dou caractere
Exemplu de cronometru cu activare direct a
sistemului de ntreruperi

45

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

Modaliti
Modaliti de interconectare a unui buton
Butoanele i comutatoarele (butoane
butoane fr revenire) reprezint cele mai simple exemple
de intrri pentru un sistem embedded. Astfel de dispozitive sunt capabile s trag n 0
logic sau 1 logic un pin de intrare de uz general semnaliznd n acest mod sistemului
o comand sau o condiie
ie de intrare.
Imaginea de mai jos prezint schema clasic de conectare a trei butoane la trei pini de
intrare a plcii Arduino (Buton 1 PIN2, Buton 2 PIN3, Buton 3 PIN4) cu comand
pe 0 logic la apsarea unui buton starea pin
pin-ului
ului de intrare va trece din 1 logic n
0 logic.

Microcontrolerele Atmel AVR dispun de o rezisten


rezisten de
pull-up intern care poate fi programat software (pinul
trebui s fie configurat ca intrare i n registrul de ieire
s fie nscris 1). Din acest motiv conectarea unui buton
la placa Arduino se poate face direct fr unei
rezistene de pull-up
up externe (imaginea de pe pagina
urmtoare).
Atenie!!!
ie!!! Conectarea direct a unui buton la un pin al
plcii Arduino fr
r activarea software a rezisten
rezistenei de
pull-up
up intern poate distruge fizic pinul de intrare
datorit curentului tranzitoriu foarte mare care apare la
nchiderea contactului. Nu se recomand utilizarea unei
astfel de schem deoarece o simpl omisiune softw
software
n timpul dezvoltrii poate conduce la distrugeri
hardware.

46

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

O alt problem care apare n cazul utilizrii unei element de


tip buton sau comutator este fenomenul de bounce. Datorit
imperfeciunilor
iunilor mecanice ale contactelor electrice la
nchiderea sau la deschiderea elementelor apare un fenomen
de oscilaie
ie a semnalului care poate conduce la citirea
eronat a comenzii (citire multipl). Pentru a evita aceast
situaie
ie se utilizeaz un condensator (schema de mai jos)
pentru a netezi semnalul de trec
trecere dintr-un nivel logic n
cellalt.

47

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

Modaliti
Modaliti de manipulare software a intrrilor
Mediul Arduino IDE pune la dispoziia programatorului funcia digitalRead(pin)
care returneaz starea pinului dorit. Pinul citit se poate configura n prealabil ca fiind de
intrare pinMode(pin, INPUT) dar avnd n vedere faptul c dup reset toii pinii
microcontrolerului sunt configurai implicit ca intrri acest lucru nu este obligatoriu dect
n cazul n care pinul respectiv a fost configurat anterior ca ieire.
Plecnd de la schema din seciunea anterioar (cu sau fr debouncing hardware dar
cu rezisten extern de pull-up) se poate testa programul urmtorul program pentru o
exemplificare a funciei digitalRead .
const int buton1 = 2;
const int buton2 = 3;
const int buton3 = 4;
int a1 = 0;
int a2 = 0;
int a3 = 0;
void setup() {
pinMode(buton1, INPUT);
pinMode(buton2, INPUT);
pinMode(buton3, INPUT);
Serial.begin(9600);
}
void loop(){
if (digitalRead(buton1) == LOW) {
a1++;
Serial.print("Butonul 1 s-a apasat butonul de ");
Serial.print(a1,DEC);
Serial.print(" ori.");
Serial.println();
//debouncing software
delay(200);}
if (digitalRead(buton2) == LOW) {
a2++;
Serial.print("Butonul 2 s-a apasat butonul de ");
Serial.print(a2,DEC);
Serial.print(" ori.");
Serial.println();
//debouncing software
delay(200);}

48

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

if (digitalRead(buton3) == LOW) {
a3++;
Serial.print("Butonul 3 s-a apasat butonul de ");
Serial.print(a3,DEC);
Serial.print(" ori.");
Serial.println();
//debouncing software
delay(200);
}
}

Se observ c citirea celor trei butoane presupune o bucl infinit care verific continuu
starea pinilor la care sunt conectate cele trei butoane i afieaz pe serial numrul de
apsri al fiecrui buton n parte. Se mai observ c programul introduce i un
debouncing software (o mic ntrziere dup declanarea fiecrei apsri de buton)
pentru a nu prinde evenimentul n mod eronat de mai multe ori. Se poate experimenta i
observa efectul nlturrii debouncing-ului software
O alt modalitate de a prinde un eveniment de tip nchidere circuit / apsare buton se
poate face i prin utilizarea ntreruperilor externe. Mediul Arduino IDE pune la dispoziia
programatorului funcia attachInterrupt(ntrerupere, isr_asociat, mod)
care permite manipularea celor dou ntreruperi externe INT0 i INT1 ale
microcontrolerului. Din pcate acestea sunt singurele ntreruperi care sunt accesibile
prin intermediul limbajului de baz al mediului Arduino IDE. Butoanele trebuie s fie
conectate la pinii 2 (INT0) i 3 (INT1) ai plcii Arduino. Parametrii funciei sunt:
ntrerupere - 0 pentru INT0 i 1 pentru INT1, isr_asociat numele unei proceduri care
s trateze apariia ntreruperii i mod specific evenimentul extern la care s se
declaneze ntreruperea LOW (pinul devine 0 logic), FALLING (pinul trece din 1 n
0), RISING (pinul trece din 0 n 1) sau CHANGE (orice tip de tranziie la buton
acest mod va conduce la declanare dubl la fiecare apsare). Funcia pereche
detachInterrupt(ntrerupere) permite dezactivarea ntreruperii activate cu
ajutorul funciei precedente. Pentru testarea celor dou funcii se poate rula programul
urmtor utiliznd aceiai schem electric efectul programului este identic cu cel
precedent.
include <avr/io.h>
const int buton3 = 4;
volatile int a1 = 0;
volatile int a2 = 0;
int a3 = 0;

49

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

void setup() {
attachInterrupt(0,ISR_b1,FALLING);
attachInterrupt(1,ISR_b2,FALLING);
Serial.begin(9600);
}
void loop(){
if ((PIND & (1<<buton3))==0) {
a3++;
Serial.print("Butonul 3 s-a apasat butonul de ");
Serial.print(a3,DEC);
Serial.print(" ori.");
Serial.println();
//debouncing software
delay(200);
}
}
void ISR_b1() {
a1++;
Serial.print("Butonul 1 s-a apasat de ");
Serial.print(a1,DEC);
Serial.print(" ori.");
Serial.println();
//debouncing software
delay(400);
}
void ISR_b2() {
a2++;
Serial.print("Butonul 2 s-a apasat de ");
Serial.print(a2,DEC);
Serial.print(" ori.");
Serial.println();
//debouncing software
delay(400);
}

Sursa programului utilizeaz dou lucruri importante:

Sistemul de ntreruperi este folosit doar pentru butonul 1 (pin2 INT0) i butonul
2 (pin3 INT1) deoarece funcia attachInterrupt nu permite definirea de
ntreruperi externe pe ali pini. Citirea butonului 3 se face n continuare n bucla
infinit principal.

50

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

Citirea butonului 3 nu se mai face cu ajutorul funciei digitalRead descris


anterior ci, direct, prin intermediul registrului intern PIND care stocheaz starea
pinilor de intrare a portului D pinul 4 al plcii este conectat la pinul PD4 al
microcontrolerului (a se revedea lucrarea 3). Verificarea strii pinului 4 al plcii
const n verificarea strii bitului 4 din registrul PIND. Utilizarea alias-ului PIND
necesit includerea fiierului de definiii avr/io.h . Acest mecanism se afl n
spatele implementrii funciei digitalRead specific mediului Arduino IDE.

Exemplu de activare direct a ntreruperilor externe


Placa de dezvoltare Arduino permite activarea tuturor pinilor I/O ca surse de ntreruperi
externe. Este adevrat c spre deosebire de pinii 2 i 3 (INT0 i INT1) restul pinilor I/0
(PCINTx) nu pot configura ntreruperi externe declanate de prinderea unui eveniment
extern de tip front cresctor, front descresctor sau zero logic (RISING, FALLING,
LOW) ci doar de modificarea nivelului logic (CHANGE). Pentru a configura ntreruperile
externe asociate cu ali pini dect 2 sau 3 este nevoie s configurm n mod direct
registrele interne ale microcontrolerului. Programul urmtor reface comportamentul
programelor anterioare fr a apela la funcia attachInterrupt. Se vor utiliza trei
ntreruperi externe INT0 pin 2 buton 1, INT1 pin 3 buton 2, PCINT2 pin4 =
PCINT20 buton 3.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
volatile int a1 = 0;
volatile int a2 = 0;
volatile int a3 = 0;
void setup() {
sei();
EIMSK |= (1 << INT0);
EIMSK |= (1 << INT1);
PCICR |= (1 << PCIE2);
PCMSK2 |= (1<<PCINT20);
Serial.begin(9600);
}
void loop(){
Serial.print("Butonul 1 s-a apasat de ");

51

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

Serial.print(a1,DEC);
Serial.print(" ori.");
Serial.println();
Serial.print("Butonul 2 s-a apasat de ");
Serial.print(a2,DEC);
Serial.print(" ori.");
Serial.println();
Serial.print("Butonul 3 s-a apasat de ");
Serial.print(a3,DEC);
Serial.print(" ori.");
Serial.println();
delay(1000);
}
ISR(INT0_vect) {
a1++;
_delay_ms(400);
}
ISR(INT1_vect) {
a2++;
_delay_ms(400);
}
ISR(PCINT2_vect) {
a3++;
_delay_ms(1000);
}

Programul include fiierele de definiii avr/interrupt.h [16] i avr/io.h pentru a


putea utiliza denumirile registrelor i biilor care sunt utilizai n configurarea sistemului
de ntreruperi precum i a denumirii standard a subrutinelor de tratare a ntreruperilor
ISR(INT_vect).
Pentru activarea sistemului de ntreruperi (la nivel global) se utilizeaz instruciunea
sei(); care echivaleaz cu setarea bitului I n registrului intern SREG.
Pentru activarea ntreruperilor INT0 i INT1 se seteaz biii INT0 i INT1 din registrul
intern EIMSK, de exemplu: EIMSK |= (1 << INT0).
Pentru activarea ntreruperii externe PCINT2 cu referire la pinul PCINT20 trebuie setai
biii PCIE2 i PCINT20 din registrele PCICR i PCMSK2. Datorit faptului c ntreruperile
de tip PCINT nu au dect modul de funcionare echivalent cu CHANGE fiecare apsare

52

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

de buton 3 va fi prins de dou ori, lucru care poate fi evitat prin mrirea temporizrii de
debounce software.
Funciile ISR pentru cele trei ntreruperi trebuie declarate de forma ISR(INT_vect)
unde INT_vect va fi INT0_vect, INT1_vect, PCINT2_vect. [17]
n cazul ISR-urilor reale (nu generate de mediul Arduino) sistemul de ntreruperi este
dezactivat pe durata execuiei ISR-ului (pentru ca un ISR s nu ntrerup un alt ISR).
Pentru a preveni blocarea programului (n ISR) nu trebuie folosite n cadrul unui ISR
funciile puse la dispoziie de mediul Arduino. De exemplu, utilizarea funciei delay n
cadrul unui ISR (pentru debouncing-ul software) va conduce la blocarea programului
deoarece aceast funcie utilizeaz ntreruperea asociat timer-ului 0 apelat ntr-un
context de dezactivare a sistemului de ntreruperi va introduce un delay infinit
ntreruperea asociat timer-ului 0 nu se va declana niciodat. Aceast funcie poate fi
nlocuit cu macro-ul _delay_ms(nr_ms) prezent n biblioteca util/delay.h .

Exemplu de numrtor pe dou caractere


Avnd n vedere cunotinele noi prezentate vom ncerca s implementm un
numrtor simplu cu ajutorul unui afiaj de dou caractere pe 7 segmente. Dup reset
afiajul va arta 00, la fiecare apsare a butonului 1 numrul se va incrementa
(incrementarea din 99 va duce n 00), la fiecare apsare a butonului 2 numrul de pe
afiaj se va decrementa (decrementarea din 00 va duce n 99), butonul 3 va reseta
numrul la 00 (nu vom fi deranjai de declanarea dubl a evenimentului avnd n
vedere c este o operaie de reset).
Schema electric a numrtorului este prezentat n imaginea urmtoare. Se observ
c datorit utilizrii a dou registre de deplasare nu mai apare efectul de aprindere
slab a unor segmente n cazul afirii de cifre diferite pe cele dou caractere nu se
mai efectueaz comanda prin intermediul aceluiai registru a celor dou caractere.
Comanda de aprindere a segmentelor este stabil fiecare caracter are propriul
registru.

53

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

54

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

Pentru testarea montajului se utilizeaz urmtorul cod surs:


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
const
const
const
const
const
const

int
int
int
int
int
int

latchPin1 = 7;
clockPin1 = 8;
dataPin1 = 9;
latchPin2 = 10;
clockPin2 = 11;
dataPin2 = 12;

volatile int digit1 = 0;


volatile int digit2 = 0;
const int numbers[] = {63, 6, 91, 79, 102, 109, 125, 7, 127, 111};
void setup() {
//activare int0 si int1
EIMSK |= (1 << INT0);
EIMSK |= (1 << INT1);
//activare PCI2 (buton 3 - PCINT20 - PCI2)
PCICR |= (1 << PCIE2);
PCMSK2 |= (1<<PCINT20);
//activare sistem intreruperi
sei();
pinMode(latchPin1, OUTPUT);
pinMode(dataPin1, OUTPUT);
pinMode(clockPin1, OUTPUT);
pinMode(latchPin2, OUTPUT);
pinMode(dataPin2, OUTPUT);
pinMode(clockPin2, OUTPUT);
}
void loop(){
digitalWrite(latchPin1, LOW);
shiftOut(dataPin1, clockPin1, MSBFIRST, numbers[digit1]);
digitalWrite(latchPin1, HIGH);
digitalWrite(latchPin2, LOW);
shiftOut(dataPin2, clockPin2, MSBFIRST, numbers[digit2]);
digitalWrite(latchPin2, HIGH);
delay(1000);
}

55

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

ISR(INT0_vect) {
if (digit1==9) {
if (digit2==9) {
digit1=0;
digit2=0;
}
else {
digit1=0;
digit2++;
}
}
else digit1++;
_delay_ms(400);
}
ISR(INT1_vect) {
if (digit1==0) {
if (digit2==0) {
digit1=9;
digit2=9;
}
else {
digit1=9;
digit2--;
}
}
else digit1--;
_delay_ms(400);}
ISR(PCINT2_vect) {
digit1 = 0;
digit2 = 0;
_delay_ms(400);
}

Exemplu de cronometru cu activare direct a sistemului de ntreruperi


La fel cum se pot activa ntreruperile externe ale plcii Arduino se pot utiliza i
mecanismele de tip timer care au fost utilizate n cadrul lucrrii anterioare prin
intermediul unei biblioteci specializat prin accesarea direct a registrelor interne de
configurare a microcontroler-ului. Pentru a exemplifica acest lucru vom reface exemplu
cu cronometru din lucrarea precedent utiliznd schema de interconectare prezentat
anterior pentru msurarea independent a duratei de o secund nu vom utiliza
56

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

funciile bibliotecii TimerOne ci rutinele implicite asociate timer-ului intern 1.


Funcionarea cronometrului va fi urmtoarea: numrtoarea se va face n zecimal (0099), butonul 1 va porni cronometrul, butonul 2 va opri numrtoarea fr a terge
rezultatul astfel nct numrtoarea s poat fi reluat, butonul 3 va reseta numrul de
secunde la 00 indiferent de modul de funcionare (oprit / pornit). Evenimentele asociate
celor trei butoane vor fi prinse similar ca i n cazul numrtorului (folosind ntreruperi).
Programul ce trebuie ncrcat este urmtorul:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
const
const
const
const
const
const

int
int
int
int
int
int

latchPin1 = 7;
clockPin1 = 8;
dataPin1 = 9;
latchPin2 = 10;
clockPin2 = 11;
dataPin2 = 12;

volatile int digit1 = 0;


volatile int digit2 = 0;
const int numbers[] = {63, 6, 91, 79, 102, 109, 125, 7, 127, 111};
void setup() {
// activare intrerupere Timer1 overflow
TIMSK1 = (1 << TOIE1);
// timer1 oprit
TCCR1A = 0;
TCCR1B = 0;
//activare int0 si int1
EIMSK |= (1 << INT0);
EIMSK |= (1 << INT1);
//activare PCI2 (buton 3 - PCINT20 - PCI2)
PCICR |= (1 << PCIE2);
PCMSK2 |= (1<<PCINT20);
//activare sistem intreruperi
sei();
pinMode(latchPin1, OUTPUT);
pinMode(dataPin1, OUTPUT);
pinMode(clockPin1, OUTPUT);
pinMode(latchPin2, OUTPUT);
pinMode(dataPin2, OUTPUT);
pinMode(clockPin2, OUTPUT); }

57

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

void loop(){
digitalWrite(latchPin1, LOW);
shiftOut(dataPin1, clockPin1, MSBFIRST, numbers[digit1]);
digitalWrite(latchPin1, HIGH);
digitalWrite(latchPin2, LOW);
shiftOut(dataPin2, clockPin2, MSBFIRST, numbers[digit2]);
digitalWrite(latchPin2, HIGH);
delay(1000); }
ISR(TIMER1_OVF_vect) {
if (digit1==9) {
if (digit2==9) {
digit1=0;
digit2=0;
}
else {
digit1=0;
digit2++;
}
}
else digit1++;
}
ISR(INT0_vect) {
_delay_ms(400);
//pornire timer FCPU/256
TCCR1B |= (1 << CS12);
}
ISR(INT1_vect) {
TCCR1B = 0;
_delay_ms(400);}
ISR(PCINT2_vect) {
digit1 = 0;
digit2 = 0;
_delay_ms(400);
}

Activarea ntreruperii de timer overflow pentru timer-ul 1 se face setnd bitul TOIE1 n
registrul TIMSK1. Pentru pornirea i configurarea timer-ului 1 se seteaz biii CS12,
CS11, CS10 din registrul TCCR1B n funcie de modul dorit. Oprirea timer-ului 1 se face
trecnd CS12, CS11, CS10 pe 0. Se recomand ca iniializare TCCR1A = 0 i TCCR1B

58

Elemente practice de baz n dezvoltarea sistemelor cu microprocesoare integrate

= 0. Parametrul INT_vect n funcia ISR pentru timer-ul 1 este TIMER1_OVF_vect.


[18]
Pentru ca timer-ul 1 s fac overflow la fiecare secund trebuie calculat factorul de
scalare (divizare) n funcie de frecvena de ceas a microcontrolerului. La o frecven de
16MHz (cum este n cazul plcii Arduino) se poate folosi un factor de scalare de 256
care va produce overflow la fiecare 1.048576 secunde.
Timp_overflow = Incrementri * (Frecv_CPU/factor_scalare)-1 = 65536 / (16000000 /
256)

59