Table of Contents

Întreruperi

O intrerupere reprezinta un semnal sincron sau asincron de la un periferic ce semnalizeaza aparitia unui eveniment care trebuie tratat de catre procesor. Tratarea intreruperii are ca efect suspendarea firului normal de executie al unui program si lansarea in executie a unei rutine de tratare a intreruperii (RTI).

Intreruperile hardware au fost introduse pentru a se elimina buclele pe care un procesor ar trebui sa le faca in asteptarea unui eveniment de la un periferic. Folosind un sistem de intreruperi, perifericele pot atentiona procesorul in momentul producerii unei intreruperi (IRQ), acesta din urma fiind liber sa-si ruleze programul normal in restul timpului si sa isi intrerupa executia doar atunci cand este necesar.

Inainte de a lansa in executie o RTI, procesorul trebuie sa aiba la dispozitie un mecanism prin care sa salveze starea in care se afla in momentul aparitiei intreruperii. Aceasta se face prin salvarea intr-o memorie, de cele mai multe ori organizata sub forma unei stive, a registrului contor de program (Program Counter), a registrelor de stare precum si a tuturor variabilelor din program care sunt afectate de executia RTI. La sfarsitul executiei RTI starea anterioara a registrelor este refacuta si programul principal este reluat din punctul de unde a fost intrerupt.

Pentru a asocia o intrerupere cu o anumita rutina din program, procesorul foloseste tabela vectorilor de intrerupere (TVI). Fiecarei intreruperi ii este asociata o adresa la care programul va face salt in cazul aparitiei acesteia. Aceste adrese sunt predefinite si sunt mapate in memoria de program intr-un spatiu contiguu care alcatuieste TVI. Adresele intreruperilor in TVI sunt setate in functie de prioritatea lor, cu cat adresa este mai mica cu atat prioritatea este mai mare.

Pentru ATMega16, TVI este data in tabelul de mai jos:

Se observa ca TVI este plasata de la prima adresa a memoriei de program si ca intreruperile sunt puse din doua in doua adrese consecutive. Prioritatea cea mai mare o are intreruperea de RESET, de la adresa 0, apoi intreruperea externa 0 (INT0).

Perifericele care pot genera intreruperi la ATMega16 sunt timerele, interfata seriala (USART), convertorul analog-digital (ADC), controllerul de memorie EPROM, comparatorul analog si interfata seriala I2C. Deasemenea, procesorul poate sa primeasca cereri de intreruperi externe din trei surse (INT0, 1 si 2) ce corespund unui numar egal de pini exteriori.

Activarea/Dezactivarea intreruperilor

Intreruperile pot fi activate sau dezactivate de utilizator in program prin setarea individuala a bitilor de interrupt enable pentru fiecare periferic folosit si prin setarea flagului de “Global Interrupt Enable” (I) din Status Register (SREG).

Tratarea unei intreruperi pentru ATMega16 se face ca in figura de mai jos.

Sa presupunem ca programul nostru primeste intreruperea externa INT0 in timp ce executa instructiunea ldi R16,0xFF. Dupa efectuarea instructiunii, registrul contor program (PC) este automat salvat in stiva si apoi initializat la valoarea corespunzatoare adresei lui INT0 ($002). Acest lucru se traduce prin 
saltul programului de la adresa 
curenta ($123) la adresa $002. Aici, 
programul gaseste instructiunea  jmp 
$2FF care-i permite sa sara la rutina efectiva de tratare a intreruperii. La sfarsitul oricarei RTI trebuie sa existe instructiunea reti care reface din stiva registrul PC (programul sare inapoi la adresa de unde ramasese inainte de intrerupere).

In timpul executiei unei intreruperi, bitul I din SREG este setat la 0 si resetat la iesire, adica orice alta intrerupere care poate aparea in timpul intreruperii curente nu va fi luata in seama. Cu toate acestea, utilizatorul poate sa reseteze bitul I din software, permitand astfel executia de intrerupere in intrerupere.

Concret, o secventa de cod care trateaza o intrerupere este data mai jos:

.cseg                        ; aici incepe segmentul de cod 
 
 
 ;tabela de intreruperi
.org 0
  jmp main                   ;  Intreruperea de reset
  jmp int0_handler           ;  Intrerupere externa 0
  jmp int1_handler           ;  Intrerupere externa 1
 
 
 
main:
  ldi r16, HIGH(RAMEND)      ; initializeaza stiva 
  out SPH, r16 
  ldi r16, LOW(RAMEND) 
  out SPL, r16 
 
  sei                        ; "set enable interrupt" - 
                             ; dam drumul ingreruperilor setand bitul “I”  din registrul SREG 
 
loop:                        ;  bucla principala a programului
  [..cod program principal..]
  rjmp loop
                             ;  RUTINA DE TRATARE INTRERUPERI
int0_handler: 
  push  r16                  ; salvam un registru in stiva 
  in r16, SREG               ; citim valoarea din  SREG in registrul general r16 
  push r16                   ; punem pe stiva valoarea 
  push r17                   ; salvam pe stiva r17 
 
  in r16, PIND               ; in registrul  r16 incarcam  valoarea din registrul PIND 
  ldi r17, 234               ; Incarcam 234 in r17
  add r16, r17               ; adunare fara carry; 
  ; -- continutul registrelor, inclusiv SREG, este modificat
 
  ; -- refacerea continutului initial al registrelor
  pop r17                    ; scoate din stiva r17 
  pop r16                    ; scoate din stiva r16 
  out SREG, r16              ; reface SREG 
  pop r16                    ; reia valoarea lui r16 din stiva 
  reti                       ; se reintoarce din intrerupere 

Registre pentru tratarea intreruperilor externe

Acest registru este raspunzator pentru plasarea tabelului vectorului de intreruperi.

Intreruperile externe

Aceste intreruperi sunt generate prin intermediul pinilor INT0, INT1 si INT2. Ele sunt activate chiar daca acesti pini sunt setati ca fiind de output. Modul in care se pot genera intreruperile externe poate fi setat prin configurarea registrelor MCUCR si MCUCSR.

De exemplu, pentru a folosi intreruperea externa INT2 sunt necesare urmatoarele configurari:

  1. Bitul I din SREG trebuie sa fie setat (global interrupt enable)
  2. Bitul INT2 din GICR trebuie setat (INT2 enable)
  3. Daca ISC2 este initializat cu valoarea zero INT2 va fi activata pe front descrescator (tranzitie din in 0 a pinului INT2), in caz contrar ea va fi activata pe front crescator.

Perifericele care pot genera intreruperi sunt: timerele, convertorul analog-digital, interfetele seriale (RS232, I2C, SPI) etc.

Timer

Principiul de functionare al unui Timer

Timerul/Counterul, dupa cum ii spune si numele ofera facilitatea de a masura intervale fixe de timp si de a genera intreruperi la expirarea intervalului masurat. Un timer, odata initializat va functiona independent de unitatea centrala (core μP). Acest lucru permite eliminarea buclelor de delay din programul principal. Principiul de functionare a unui Timer poate fi descris in linii mari de cele trei unitati:

  1. Registrul numarator (Timer Counter, TCNT) care masoara efectiv intervalele de timp si care este incrementat automat cu o frecventa data.
  2. Avand ceasul intern sau alt ceas conectat pentru a obtine diferite intervale folosim un “Prescaler”. Acesta are menirea de a diviza in functie de necesitatile aplicatiei frecventa de ceas si odata cu divizarea sa incrementeze registrul TCNT.
  3. La fiecare incrementare a TCNT are loc o comparatie intre acest registru si o valoare stocata in registrul OCRn. Aceasta valoare poate fi incarcata prin software de utilizator. Daca are loc egalitatea se genereaza o intrerupere, in caz contrar intrementarea continua.

Timerele sunt prevazute cu mai multe canale astfel in paralel se pot desfasura diferite numaratori. ATmega16 este prevazut cu 3 unitati de timer: doua de opt biti si una de numarare pe saisprezece biti.

Principalele caracteristici sunt:

Majoritatea registrelor si referintelor de biti din aceasta sectiune sunt prezentate intr-o forma generala.Un “n” mic inlocuieste numarul timerului si un “x” mic inlocuieste unitatea de comparare de output. Atunci cand se foloseste registrul sau bitul definit intr-un program, trebuie folosită forma precisă.

Registre

TCNT1, OCR1A/B si ICR1 sunt toate registre de 16 biti. Proceduri speciale trebuie urmate cand se acceseaza registre de 16 biti.

Registrele de control T/C(TCCR1A/B) sunt registre de 8 biti si nu au restrictii de acces CPU. Rolul lor este de-a configura Timerul pentru un anumit mod de functionare. Toate intreruperile sunt activate/dezactivate individual in TIMSK( nu sunt prezentati in figura deoarece aceste registre sunt folosite si de alte unitati timer). T/C este incrementat intern prin intermediul ceasului dat de unitatea de prescaler, sau de catre o sursa externa de ceas conectata la pinul T1. Bloc-ul logic de selectare a ceasului controleaza ce sursa de ceas este folosita sau limita pana la care T/C numara. T/C este inactiv cand nu este selectata nici o sursa de ceas.

Registrele de comparare ale output-ului (OCR1A/B) sunt comparate cu valoarea T/C la fiecare incrementare a acestuia. Rezultatul comparatiei poate fi folosit de catre generatorul de unde pentru a genera un semnal PWM la pinul de Comparare a Output-ului(OC1A/B). Operatia de comparare va seta si semnalul de potrivire-comparare (OCF1A/B) la generarea unei cereri de intrerupere de comparare output.

Definitii

Valoarea TOP poate fi desemnata astfel incat sa fie una din aceste valori fixe: 0x00FF, 0X01FF, 0X003FF sau valoarea memorata in registrele OCR1A sau ICR.

Accesarea registrelor pe 16 biti

TCNT1, OCR1A/B si ICR1 sunt registre de 16 biti care pot fi accesate de catre AVR CPU cu ajutorul bus- ului de date de 8 biti. Un registru de 16 biti poate sa sa fie accesat folosind doua operatii, fie de scriere, fie de citire pe 8 biti. Pentru a executa o scriere de 16 biti, byte-ul HIGH trebuie sa fie scris inaintea byte-ului LOW (bug de compilator, nu feature hardware).

Lucrul cu memoria – SRAM

SRAM-ul este in esenta memoria cu care este inzestrat cipul(ATmega16) . In timp ce registrele generale sunt folosite pentru operatii, SRAM-ul este folosit pentru a depozita date in timpul executiei. Ca sa putem utiliza SRAM-ul trebuie sa stim cateva aspect legate de spatial de adresare, de directivele asm si instructiunile care opereaza pe SRAM. Toate locatiile din memorie pot fi accesate fie direct fie indirect.

Adresarea directa

In cazul incarcarii in memorie sau din memorie de la o adresa directa stim exact de unde sau unde sunt datele. Exemplu: Folosind adresa 0x60 ca sa adresam valoarea dintr-un byte(de la adresa “digit”), putem folosi instructiunile sts si lds.

lds r16, digit     ; (incarcam in registrul general r16 continutul de la adresa 0x60 din memoria SRAM )
sts digit,r16      ; (punem valoarea din registrul general r16 in SRAM la adresa digit)

Adresarea indirecta

Adresarea indirecta se face similar cu cea cu ajutorul pointerilor in C sau Pascal: perechile de registre pointeri (r26:r27 sunt numiti X, r28:r29 Y, r30:r31 Z) pot fi folositi sa faca referire la spatial de adresare AVR. Daca de exemplu X(r28:r29) ia ca valoare 0x60 el va pointa la “digit” si poate fi folosit pentru a face diferite operatii pe valoarea lui “digit”.

ldi XL, 0X60     ; incarcam in r26(XL) cu valoarea low(0x60)
ldi XH, 0X00     ; incarcam in r27(XH) cu valoarea high(0x60)
ld r16,X         ; incarca in r16 valoarea la care pointeaza X

In diagrama de mai sus observam ca prima adresa de SRAM este 0x60. Exemplu ( diferenta intre adresarea indirect si adresarea directa):

Stim ca registrul de I/O PORTA se gaseste in tabelul de registre :

</latex> cum f(t) este o forma de unda dreptunghiulara valoarea sa maxima se atinge pentru 0<t<D*T. Tipuri de PWM

Multe circuite digitale pot genera semnale PWM. Majoritatea microcontrollerelor dispun de aceasta facilitate. Pentru a implementa o asemenea facilitate ele se folosesc de un numărator care este incrementat periodic (conectat direct sau indirect la o unitate de ceas) si care este resetat la sfarsitul fiecărei perioade a PWM-ului. Cand valoarea numaratorului este mai mare decat valoarea de referinta, iesirea PWM(output-ul) trece din starea inalta in stare joasa (sau invers).

Exerciții

In schema de mai sus este reprezentat schematic suportul hardware pe care trebuie sa-l interfatam in acest laborator.Dupa cum se observa, cu ajutorul microcontrollerului si mai precis folosind facilitatile oferite de: sistemul de intreruperi, timere, memoria microcontrollerului, vom interfata un afisaj cu leduri, un buzzer(sounder in schema) si doua butoane.

  1. Folosind scheletul din lab2_1.asm, construiți un cronometru peste afișajul cu LEDuri.
    1. inițializați portul B (la care e legat afișajul cu LEDuri) ca port de ieșire (completați init_portb).
    2. completați funcția init_digit pentru a afișa 0 pe afișajul cu LEDuri.
    3. configurați Timer/Counter TCNT1 (completați în init_timer1) să declanșeze o dată pe secundă:
      • Timerul poate fi setat în mai multe moduri: normal, clear timer on compare match, fast PWM, phase correct PWM. Configurați-l să funcționeze în mod clear timer on compare match (CTC).
      • folosiți un prescaler de 1024 și o valoare pentru limita superioară TIME_LIMIT de 15625: (16MHz = 1024 * 16525Hz) pentru a declanșa întreruperea cmp_handler atunci când numărătorul timerului atinge valoarea TIME_LIMIT.
    4. completați cmp_handler (invocată la fiecare secundă – când numărătorul timerului atinge valoarea TIME_LIMIT) să modifice și să afișeze noua cifră.lab2_1schelet.asm
  2. Folosind scheletul de cod lab2_2.asm, construiți o suita de tonalitati generate la iesire de buzzer configurand timerul2 pe modul fast PWM.lab2_2schelet.asm