duminică, 28 august 2011

Sistemul de întreruperi

Întreruperile într-un microcontroler sunt văzute ca nişte evenimente speciale generate de elementele periferice.Aceste evenimente pot fi reprezentate de apăsarea unui buton, atingerea unei valor a numărătorului,terminarea de citit a unei valori a convertorului analog digital,etc..Toate acestea sunt gestionate de un sistem intern numit sistem de întreruperi , sistem care are rolul de a oferi procesorului informaţii referitoare la natura întreruperii şi de a opri funcţionarea ciclică tratând întreruperea într-o rutină specială. Această rutină are o adresă specială de început.În cazul chip-ului ATMega8 adresele rutinelor de tratare corespunzătoare întreruperilor sunt următoarele:
Prioritatea de tratare a unei întreruperi în cazul în care avem mai multe cereri de întrerupere este marcată de prima coloana, astfel cea mai mare prioritate o are întreruperea generată de semnalul RESET,apoi întreruperea externă INT0 , întreruperea externă INT1 şi tot aşa.Trebuie să precizez că aceste adrese sunt mascate în CodeVisionAVR dar totuşi e bine să ştiţi cum funcţionează modul intern de tratare al întreruperilor.
Momentan voi trata doar întreruperile externe,INT0 şi INT1, deoarece sunt cele mai simple de configurat.
Pentru a putea folosi orce întrerupere trebuie mai întâi să informăm microcontrolerul că întreruperile sunt activate.Acest lucru se face prin comana sei, comandă care în C se poate scrie astfel:
#asm("sei")
Această comandă nu face altceva decât să seteze valoarea 1 în cel mai semnificativ bit,bitul I, din regist SREG,bit care permite folosirea sistemului de întreruperi.
Pentru dezactivarea întreruperilor se foloseşte comanda cli,în C fiind #asm("cli").
Activarea întreruperilor externe se face prin registrul GICR(General Interrupt Control Register) care are următoarea formă:
Pentru a activa întreruperea externă INT0 trebuie să setăm valoarea 1 în locaţia 6 din registru iar pentru activarea întreruperii externe INT1 trebuie setată valoarea 1 în bitul 7.Primii doi biţi ai registrului, IVCE şi IVSEL se folosesc atunci când în memoria microcontrolerului avem o secţiune boot însă în acest tutorial nu o să folosim aşa ceva.Restul biţilor 2-5 nu sunt folosiţi,ei fiind rezervaţi.
După ce am setat ce întreruperi externe sunt folosite, va trebui să setăm în ce moment să se activeze întreruperea.Acest lucru se poate seta prin registrul MCUCR:
Din acest registru doar primii 4 biţi(0-3) sunt atribuiţi întreruperilor externe,primii doi biţi(ISC00 şi ISC01) fiind atribuiţi întruperii INT0 iar biţii 3 şi 4(ISC10 şi ISC11) sunt atribuiţi întruperii INT1.Următoarea configuraţie este valabilă pentru ambele întreruperi încât biţii ISC11/ISC01 şi ISC00/ISC10 pot lua următoarele valori:
ISC11/ISC01 ISC00/ISC10
00- Nivelul de jos al semnalului va genera o cerere de întrerupere.
01- Orce schimbare a formei semnalului va genera o cerere de
întrerupere.
10- Frontul descrescător al semnalului va genera o cerere de
întrerupere.
11- Frontul crescător al semnalului va genera o cerere de întrerupere.

Dacă întreruperea a fost generată ea va fi semnalată în registrul GIFR:
Dacă pe parcursul rulării programului bitul 6 ia valoarea 0 atunci înseamnă că avem o cerere de întrerupere generată de întreruperea externă 0 iar dacă bitul 7 ia valoarea 0 înseamnă că avem o cerere de întrerupere generată de întreruperea externă 1,aceşti biţi având valoarea 1 atâta timp cât nici o cerere nu a fost lansată.Acest registru se verifică pentru a vedrea sursa întreruperii.Ddupă execuţia rutinei de tratare, bitul corespunzător întreruperii îi va fi automat atribuită valoarea 1,lucru necesar pentru a se evita ciclarea la infinit a rutinei.

În exemplul următor voi incrementa 2 variabile prin apăsarea a 2 butoane legate la pinii corespunzători cererii de întrerupere INT0 şi INT1.Valorile variabilelor care contorizează de câte ori au fost apăsate butoanele vor fi afişate pe un LCD.La unul dintre pinii microrontrolerului va fi legat un LED care îşi va schimba starea din aprins în stins şi invers la intervalul de o secundă.Codul programului îl puteţi descărca de la secţiunea Download-Întreruperi-Aplicaţie 1.

Schema electrică a aplicaţiei este următoarea:
Dacă ne uităm la schema chip-ului observăm că pinii portului D,PD2 şi PD3 sunt folosiţi şi ca pini pentru întreruperi acest lucru înpingându-ne să legăm LCD-ul la portul B.LED-ul îl vom lega la pinul 0 al portului D.Înainte de generarea programului în CodeVision AVR, vom seta LCD-ul pe portul B ,pinul PD0 ca pin de ieşire iar pentru setarea întreruperilor externe vom merge la External IRQ.Vom selecta ambele întreruperi iar în dreptul lor vom alege Rising Edge,adică se va lansa o cerere de întrerupere la trecerea semnalului de intrare de la valoarea 0 la valoarea 1:
Pentru această configurare programul va seta regiştrii de mai sus cu următoarele valori:
GICR =0b11000000 – se activează ambele întreruperi INT0 şi INT1;
MCUCR = 0b00001111 - se vor semnala cereri de întrerupere la trecerea semnalului din starea 0 în starea 1;

După ce aţi generat programul veţi observa că două noi funcţii au fost generate:
interrupt [EXT_INT0] void ext_int0_isr(void) şi interrupt [EXT_INT1] void ext_int1_isr(void) funcţii prin care vom trata apariţia întreruperilor.Codul din interiorul funcţiilor care tratează apăsarea unui buton este următorul:

interrupt
[EXT_INT0] void ext_int0_isr(void)
{
int0++; //la apasare se incrementeaza variabila int0
itoa(int0,convert); //convertim valoarea lui int0 in sir de caractere
strcpy(linie1,end); //atribuim sirului linie1 valoarea '\0'
strcat(linie1,in1); //adaugam la sirul linie1 textul"INT0="
strcat(linie1,convert);//adaugam la sirul linie1 valoarea variabilei int0
lcd_gotoxy(0,0); //mutarea cursorului in LCD la linia 0 coloana 0
lcd_puts(linie1); //afisarea datelor pe LCD
}
interrupt [EXT_INT1] void ext_int1_isr(void)
{
int1++; //la apasare se incrementeaza variabila int1
itoa(int1,convert); //convertim valoarea lui int1 in sir de caractere
strcpy(linie2,end); //atribuim sirului linie1 valoarea '\0'
strcat(linie2,in2); //adaugam la sirul linie1 textul"INT1="
strcat(linie2,convert);//adaugam la sirul linie1 valoarea variabilei int1
lcd_gotoxy(0,1); //mutarea cursorului in LCD la linia 1 coloana 0
lcd_puts(linie2); //afisarea datelor pe LCD
}

In aceste funcţii pe lângă incrementare am convertit variabilele corespunzătoare întreruperilor, din short în sir de caractere, am concatenat şirurile de caractere care conţin sursele întreruperilor şi valorile variabilelor transformate în şir de caractere şi apoi le-am afişat pe LCD.Variabilele cu care am lucrat în aceste funcţii sunt declarate în partea de declarare a variabilelor globale:

unsigned short int0=0,int1=0;
char in1[6] = "INT0=";
char in2[6] = "INT1=";
char convert[4],end[1]="";
char linie1[16],linie2[16];

Este necesară declararea lungimii şirurilor deoarece microcontrolerul nu poate aloca dinamic spaţii de memorie în memoria program de tip flash.
În bucla infinită while avem doar codul care va aprinde şi va stinge LED-ul după o secundă:

while (1)
{
PORTD = 0x00;
delay_ms(1000);
PORTD = 0x01;
delay_ms(1000);
};

Pentru a folosi funcţiile de concatenare şi copiere (strcat şi strcpy) a fost necesară includerea bibliotecii string.h.Pentru conversia din short în şir de caractere,funcţia itoa, am inclus biblioteca stdlib.h.

Poate că vă întrebaţi care e scopul folosirii întreruperilor pentru această aplicaţie când puteam să folosim doar citirea normală la nivel de port.Totuşi dacă veţi încerca să dezvoltaţi aplicaţia fără întreruperi veţi observa că sunt situaţii în care apăsaţi butoanele şi variabilele nu se incrementează.Acest lucru se întâmplă deoarece există situaţia în care programul aşteaptă să treacă o secundă iar dacă se apasă butonul şi se eliberează în acest timp,apăsarea nu este contorizată.Folosind întreruperile se elimină acest neajuns.
Funcţionarea fizică a aplicaţiei o puteţi vedea mai jos:


0 comentarii:

Trimiteți un comentariu

Twitter Delicious Facebook Digg Stumbleupon Favorites More