joi, 22 noiembrie 2012

Întreruperi. Programe cu întreruperi. Întreruperea RESET

    
    Intreruperi.

    Întreruperea reprezintă suspendarea procesului normal de execuţie a programului pentru rezolvarea unei probleme prioritare. 
    Întreruperea de regulă este generată ca răspuns la un efect fizic intern sau extern al unui modul periferic. Efectul fizic în sine va reprezenta situaţia de întrerupere sau excepţie care necesită o tratare neintârziată. Asemenea situaţii de întreruperi pot fi ca de exemplu: schimbarea nivelului logic la un pin, sfârşitul unei perioade de timp  sfârşitul unei operaţii de transmisie sau recepţie, sfârşitul unei conversii  etc.
    Majoritatea modulelor periferice pot genera una sau mai multe întreruperi.
    Tratarea situaţiei de întrerupere presupune existenţa unei subrutine definite în acest scop. Odată cu apariţia situaţiei de întrerupere, microcontrollerul va seta într-un registru de stare a perifericului un flag de întrerupere IF, care va fi pastrat pâna la prelucrarea intreruperii date. Pentru ca întreruperea de la situaţia de întrerupere să fie generată va fi nevoie ca în registrul de control al perifericului întreruperea în cauză să fie activată prin setarea unui bit de activare IE. Mecanismul de chemare a subrutinei de prelucrare a întreruperii va fi iniţiat doar în cazul în care:
    1. S-a detectat situaţia de întrerupere reprezentată de flagul acesteia IF==1
    2. Întreruperea de la sursa dată este permisa IE ==1
    3. Bitul de permisiune globală a întreruperilor este setat I==1 (in SREG)
mai simplu, subrutina va fi chemată când relaţia IF & IE & I va fi adevărată.
    Sistemul de întreruperi, de exemplu, va permite un apel de subrutină automat la apăsarea unui buton, luat ca sursă de întrerupere externă EXT_INT.



    Vector de întreruperi

    Odată ce o întrerupere a fost generată sistemul va executa un apel hardware a subrutinei de prelucrare a întreruperii (ISR), care presupune un salt la o adresă unde este realizată această subrutină. Deoarece o asemenea chemare nu este una explicită, ca şi în cazul unui apel clasic cu o instrucţiune de tip CALL unde adresa de salt este parametru al comenzii, apelul hardware presupune existenţa în memoria de programe a unei zone de memorie alocate pentru subrutinele de prelucrare a întreruperilor. Această zonă de memorie de program este numită vector de întreruperi
   Deci vom spune că vectorul de întreruperi reprezintă o zonă de memorie de program rezervată adreselor de referinţă a subrutinelor de prelucrare a întreruperilor. 
    Vectorul de întreruperi, pentru arhitectura AVR, este localizat la începutul memoriei de program, începând cu adresa $0000. Pentru fiecare subrutină de prelucrare a întreruperilor de la o anumită sursă de întrerupere îi este rezervat un spaţiu de 2 comenzi (pentru versiunile mai vechi a nucleului AVR o comanda). 
    În mod evident că acest spaţiu de cele mai deseori nu este destul pentru a implementa o subrutină de prelucrare a întreruperii însă suficient pentru a executa o redirecţionare către implementarea subrutinei cu o instrucţiune de salt necondiţionat de tip JUMP. În cazul în care am dori să protejăm programul de la apeluri accidentale a unei ISR la locaţia respectivă se va înregistra o comandă de returnare din subrutină. Deoarece avem la dispoziţie tocmai două locaţii de program am putea utiliza una din ele pentru tratarea apelurilor nedorite a ISR prin setarea unui indicator, aprinderea unui beculeţ, sau chiar un apel de subrutină special definită în acest scop.

.org 0 ; VI plasat la inceputul MEM programe
    ; redirectionare catre prelucrarea inreruperii RESET
    rjmp Reset
    nop
    ; blocarea intreruperii INT0 prin returnare din VI
    reti 
    nop
    ; chemarea unei rutine de tratare a intreruperii accidentale
    rcall warn_isr 
    reti
    ; setarea unui pin al MCU pentru semnalizarea chemarii ISR
    sbi PORTA, PINA0 
    reti
    ; blocarea ISR
    reti 
    nop
    ; blocarea ISR
    reti 
    nop
    ; blocarea ISR
    reti 
    nop
    ; blocarea ISR
    reti 
    nop
    ...
    ...


     Un microcontroller poate avea până la mai mult de 20 surse de întreruperi distincte în dependenţă de seria lui. De exemplu colecţia de întreruperi pentru ATmega16 va fi următoarea: 
(extras din datasheet-ul producatorului)
    


    Prioritatea Intreruperilor


      În cazul în care două sau mai multe întreruperi intervin concomitent, va fi prelucrată întreruperea cu prioritatea cea mai înaltă. Prioritate mai înaltă vor avea întreruperile adresele de referinţă a cărora în Vectorul de Întreruperi sunt mai mici. Respectiv întreruperea Reset, având adresa de referinţă $0000, va avea cea mai mare prioritate. Luând în consideraţie că frecvenţa de lucru a microcotrollerelor de regulă este mare, probabilitatea ca în acelaşi moment două sau mai multe întreruperi să ceara prelucrare este destul de mică. Însă asemenea cazuri se pot întampla atunci când pe o perioada îndelungată au fost interzise întreruperile prin bitul global de permisiune a întreruperilor - I, posibilă prin resetara lui din program cu comanda CLI pentru a trata o zona critică, sau procesorul s-a reţinut cu prelucrarea unei întreruperi anterioare.






    Mecanismul de apel a subrutinei de prelucrare a întreruperii (ISR)

    După cum am spus mai sus, generarea unei întreruperi va implica un apel hardware a unei ISR. Mecanismul de chemare a unei ISR este identic cu cel de chemare a unei subrutine obişnuite cu excepţia ca pe durata executării ISR bitul global de permisiune a întreruperilor va fi resetat, I=0, fapt care va interzice chemarea unei alte ISR pe durata executării ISR. Deci mecanismul de chemare a unei ISR va fi realizat în mai multe etape.
  • La detectarea situaţiei de întrerupere se va evalua o relaţie de tip IF & IE & I (vezi mai sus). Dacă această relaţie va fi adevărată se va declanşa mecanismul de apel hardware al ISR. În cazul în care Întreruperea a fost detectată în timpul execuţiei unei comenzi, se va aştepta pînă la terminarea execuţiei comenzii curente, după care se va declanşa mecanismul de chemare a ISR.
  • Se va executa apelul hardware al ISR echivalent cu o comandă de forma CALL ISR_Addr  unde ISR_Addr va fi adresa de referinţă din Vectorul de întreruperi pentru Întreruperea dată. În aşa mod se va realiza un salt necondiţionat la adresa ISR_Addr, localizată în vectorul de întreruperi, însoţit de o salvare a Contorului de Program (PC) în stiva (Stack), PC+1->Stack
  • Concomitent cu saltul la vectorul de întreruperi se va reseta bitul global de permisiune a întrerulerilor, I = 0, un echivalent a comenzii CLI, fapt ce va interzice întreruperea execuţiei ISR de o altă ISR detectate în acest timp.
  • Execuţia secvenţei de instrucţiuni din cadrul ISR. Pentru ISR sunt valide recomandările pentru subrutinele obişnuite referitor la păstrarea stării globale a regiştrilor de uz general utilizaţi în cadrul ISR, ce se realizează prin salvarea lor în stivă la începutul subrutinei şi restabilirea acestora înainte de returnarea din subrutină.
  • Returnarea din ISR realizată cu comanda RETI. Această comandă este echivalentă cu comanda de returnare din subrutina obişnuită RET, cu excepţia ca concomitent se va seta bitul de permisiune globală a întreruperilor, I = 1. Putem spune ca RETI este echivalent cu RET+SEI. Deci, returnarea din ISR va implica restabilirea adresei Contorului de Program din stivă, care fusese salvat în procesul de chemare a ISR, PC->Stack şi execuţia programului va continua exact din locul unde a fost suspendat de către întrerupere. 


    Principiul ca o ISR nu poate fi întreruptă de o altă Întrerupere se datorează faptului că pe durata execuţiei acesteia bitul global de permisiune al întreruperilor I este resetat (I=0). Însă această regulă poate fi anulată odată cu setarea în cadrul ISR a bitului de permisiune a Întreruperilor cu comanda SEI (I=1). Această acţiune va permite ca o ISR sa fie întreruptă de o altă întrerupere.

    Intreruperea Reset

    Majoritatea sistemelor digitale, printre care se enumeră şi Microcontrollerul, presupun existenţa unui semnal RESET prin intermediul căruia sistemul se readuce o stare iniţială. Semnalul de RESET ca regulă este aplicat la regiştrii interni, readucînd sistemul la o stare predefinită. În modul normal de funcţionare a dispozitivului semnalul se menţine într-o stare predefinită fie 0 sau 1 menţinută din interiorul sistemului sau din exterior. Îniţierea unui RESET presupune generarea unui puls de o durată anumită de valoare inversă celei din starea normală de funcţionare pe semnalul de Reset.
    Microcontrollerul din seria AVR are în interior un modul specializat pentru generarea semnalului intern de RESET. Acest modul permite generarea pulsului intern de reset în mai multe situaţii cum ar fi  aplicarea alimentării circuitului POWERON RESET, căderea nivelului sursei de alimentare a circuitului, expirarea timpului de time-out de la modulul WATCHDOG (Timer de veghe), sau aplicarea unui puls negativ la pinul extern RESET al microcontrollerului.
    Pe lîngă faptul ca semnalul de RESET intern iniţializează toţi regiştrii interni al microcontrollerului, se generează o situaţie de întrerupere RESET, care va implica prelucrarea acestei întreruperi.
    Întreruperea RESET are cea mai mare prioritate şi deci adresa de referintă a ISR se va plasa în capul Vectorului de Întreruperi, adică la adresa $0000.
    Mai mult decât atât Intreruperea RESET este o întrerupere nemascată, fapt ce îi dă posibilitatea să fie prelucrată chiar dacă întreruperile sunt interzise prin bitul de permisune a întreruperilor (I=0). Deci această întrerupere va putea suspenda o alta întrerupere.
   
 Programe cu intreruperi


      În programarea clasica se presupune ca execuţia programului începe de la adresa $0000, această adresă considerîndu-se punct de intrare în program. Pentru programele cu întreruperi acest fapt este veridic parţial. Execuţia programului va începe de la adresa $0000, dar acest fapt va fi condiţionat de saltul la prelucrarea ISR Reset, adresa de referinţă a căreia este poziţionată la începutul Vectorului de Întreruperi, adică la adresa $0000. Deoarece odată cu pornirea Microcontrollerului se va genera o întrerupere RESET, ceea ce va iniţia un apel a ISR de prelucrare a acestei întreruperi, se va considera ca ISR  RESET este punctul de intrare în program. Mai mult, în ISR RESET va fi încadrat aşa numitul  programul principal din programarea clasică, incluzând partea de iniţializare a sistemului şi bucla infinită care va menţine sub control microcontrollerul pe tot parcursul funcţionării lui. 
     Principiul ca o ISR nu poate fi întreruptă de către o altă întrerupere implică faptul că în cazul în care dorim să scriem programe fără întreruperi, putem să ne permitem să scriem programul începînd cu adresa $0000 şi ignorăm faptul că zona de la început a memoriei de program este rezervată Vectorului de Intreruperi. Aceasta posibilitate vine de la faptul că se execută de fapt ISR Reset şi ea nu poate fi întreruptă (I=0).
     Pentru Programele cu întreruperi, se recomandă evitarea de a înregistra secvenţe de program în zona Vectorului de Întreruperi. În acest scop avem ne vine în ajutor directiva de preprocesare .ORG care permite poziţionarea secvenţelor de cod la anumite adrese. Tot această directivă ne dă posibilitatea de a poziţiona  comanda de redirecţionare către ISR direct la locaţia rezervată Întreruperii în cauză şi deci nu va fi necesar de definit întreg vectorul de întreruperi, ci se poate limita doar la înregistrarea ISR utilizate. 
    În mod evident apelul ISR presupune o iniţializare a stivei. Excepţie sunt Microcontrollerele fără RAM care au un mecanism specializat al stivei.  Fără iniţializarea stivei va fi imposibilă operarea cu întreruperi, mai mult decât atât, Microcontrollerul va trece într-o stare nedefinită la prima situaţie de întrerupere, diferite de RESET.
    Luând în consideraţie cele spuse în acest paragraf, un program cu întreruperi va avea următorele părţi componente:
  1. Definirea vectorului de întrerupere
  2. Programul principal înglobat în întreruperea Reset
    1. Iniţializarea stivei
    2. Iniţializarea configuraţiei microcontrollerului
    3. Pemiterea întreruperilor
    4. Ciclul infinit care menţine MCU sub control continuu numit şi parte de procesare
  3. Subrutine de prelucrare a intreruperilor
  4. Subrutine simple
 Programzl cu întreruperi va avea următorul aspect:

   .include "m16def.inc" ; includem fisierul de adrese la periferice
                    ; pentru ATmega16
   .cseg            ; indicam inceputul segmentului de cod

   ; 1. Definirea vectorului de intreruperi 
   .org 0           ; Inregistrare la adresa rezervata ISR Reset
      rjmp Reset    ; Redirectionare catre ISR Reset

   .org IntxAddr    ; Inregistrare la adresa rezervata ISR Intx
      rjmp Intx_ISR ; Redirectionare catre ISR_Intx

   .org 100         ; Inregistrarea secventelor de program 
                    ; incepand cu o adresa din afara 
                    ; Vectorului de intreruperi

   ; 2. Descrierea programului principal inglobat in ISR Reset
   Reset:
    init:                 ; punctul de intrare in program
   ; 2.1. Initializarea stivei
        ldi R16, LOW(RAMEND) ;
        out SPL, R16
        ldi R16, HIGH(RAMEND)
        out SPH, R16

   ; 2.2. Initializarea configuratiei MCU
        cbi DDRD, PIND
        sbi PORTD, PIND2
        ...
        ...
   ; 2.3. Permiterea Intreruperilor
                   sei
   ; 2.4. Bucla infinit care mentine MCU sub control 
   ;            continuu numit si parte de procesare
    mainLoop:
        ...
        ...
        rjmp mainloop
        reti          ; Returnare din ISR reset
                      ; in mod normal nu se va executa niciodata

   ; 3. Subrutine de prelucrare a Intreruperilor
   Intx_ISR :
        ...
        ...
        reti

   ; 4. Subrutine simple
     sub:
        ...
        ...
        ret