WebElektronika

PIC32MX mikrovezérlő megszakításkezelése multivector módban

person access_time 2018.09.19.
Ebben a cikkben megnézzük azt, hogy hogyan lehet a PIC32MX mikrovezérlőnél megszakításokat használni az MpLabX fejlesztőkörnyezetben. Multivector módot fogunk alkalmazni úgy, hogy törekedni fogunk a regiszterek közvetlen írására a makrók használata helyett.


Korábban érintettük a PIC32MX mikrovezérlő megszakításkezelését, átnéztünk néhány egyszerűbb alkalmazást. Ezek a példák még az MCC32-es fordítóval (plib.h makrói) készültek, és a single módót mutatták be.

Ebben a cikkben már az MpLabX-et alkalmazzuk az xc32-es fordítóval, és arra törekszünk, hogy minél kevesebb makró (függvény) hívás legyen, inkább a különböző konfigurációs regisztereket fogjuk írni.

A multivektor módban minden egyes megszakításkérőnek külön interrupt handlere (IT függvény) van. Ezért egy ilyen függvényben nem kell azzal foglalkozni, hogy ki adott megszakításkérést, tehát a feltételvizsgálat elhagyható. Fontos viszont az, hogy az adott modul megszakításkérő flag-jét továbbra is szoftveresen kell törölnünk.

Nézzünk meg most egy példát, ahol a PIC32MX795F512L mikrovezérlő Timer2 perifériája ad mmegszakítási kérést akkor, ha a TMR2 regiszterben tárolt érték megegyezik a PR2 értékével.
Hozzunk létre egy projektet az MpLabX fejlesztőrendszer segítségével, és másoljuk be a következő kódot.

#include <p32xxxx.h>
#include <sys/attribs.h>
 
#define LED2        LATDbits.LATD2
#define LED2Tg()    { LED2 = ~LED2; }
 
void __ISR (_TIMER_2_VECTOR, IPL1SRS) T2Interrupt(void)
{
    LED2Tg();
    IFS0bits.T2IF = 0;
}
 
main()
{
    TRISD = 0x0000;
    LATD = 0x0000;
    T2CONbits.TCKPS = 2;
    TMR2 = 0x0000;
    PR2 = 0x1234;
    
    IPC2bits.T2IP = 1;
    IFS0bits.T2IF = 0;
    IEC0bits.T2IE = 1;
 
    INTCONbits.MVEC = 1;
    asm volatile("ei");
    T2CONbits.ON = 1;
    while(1);    
}

 

Egy fontos különbség már a programunk legelején látható, nem alkalmazzuk a "plib.h" header file-t, mert  hiába találjuk meg ezt a fordító könyvtárában, az ebben található header-ek (pl.: int.h, adc10.h, stb) már nem elérhetőek. Ezért alkalmazzuk a "sys/attribs.h" file-t, ahol megtaláljuk az interrupt használatához szükséges makrókat (1. ábra).

kep
1. ábra   Makrók a sys/attribs.h file-ban
 

Ugyanakkor használnunk kell a "p32xxxx.h"-t is, hiszen (közvetve) itt találjuk a "proc/p32mx795f512l.h" file-t, amely szükséges a PIC32MX795F512L mikrovezérlő alkalmazásához. Ebben a header file-ban találjuk meg azokat a makrókat (2. ábra), amelyeket alkalmazni tudunk majd az adott interrupt handlernél. Ezeknek a segítségével tudjuk kiválasztani, hogy az adott megszakítási függvényhez melyik periféria "tartozik".

kep
2. ábra   Különböző vectorok a proc/p32mx795f512l.h" file-ban
 

Először a "define"-ok segítségével létrehozunk makrókat, majd a T2Interrupt() nevű megszakításkezelő függvényt is. Ebben a handlerben találjuk a "LED2Tg()" makrót, amelynek segítségével a LATD port 2. bitjének az állapotát negáljuk. Ezután a Timer2 interrupt flagjét szoftveresen töröljük.

A "main()" függvényben először beálllítjuk a "D" port irányát, a "TRISD=0x0000;" segítségével kimenetként állítjuk be a D port összes bitjét.
A Timer2 konfigurálásához a T2CON regisztert (3. ábra) kell a működéshez megfelelően beállítani. A TCKPS segítségével állítjuk be a Timer2 előosztójának az értékét, amely ezzel az értékkel 1:4.

kep
3. ábra   A T2CON regiszter felépítése
 

A Timer2 perifériát időzítő módban fogjuk alkalmazni, ehhez a TGATE-nek és a TCS-nek logikai 0-ban kell lenni. Ez az időzítő periféria tud 16, illetve 32 bites módban működni, ha a T32 bit logikai 0-ban van, akkor ez a modul 16 bites módban fog üzemelni.
Ezután a PR2 regiszternek adunk értéket (eddig fog elszámolni ez az időzítő), illetve alapállapotba hozzuk az időzítőt is.

Az időzítő perifériájának a beállítása után adjuk meg azt, hogy a Timer2 megszakításkérést tudjon adni. Ez akkor történik meg (a helyes beállítások mellett), amikor  a TMR2 értéke megegyezik a PR2 regiszterben található számmal. Ha ez a két szám megegyezik, akkor a T2IF flag nullából egybe vált. Viszont ez még nem elég ahhoz, hogy a "T2Interrupt()" handler lefusson. Ahhoz, hogy ez az interrupt handler működésbe lépjen, további beállítások szükségesek.

Be kell állítani a Timer2 interruptprioritását (T2IP), resetelni kell az IT flaget (T2IF), illetve engedélyezni kell a Timer2 megszakításadási lehetőségét (T2IE). Ezek a bitek különböző interrupt regiszterekben találhatók.

Ahhoz, hogy a mikrovezérlőnk ne single módban működjön, logikai 1-et kell írni az INTCON regiszter MVEC bitjébe (4. ábra). Ez felel azért, hogy single vagy multi módban működjön a mikrovezérlőnk.

kep
4. ábra   Az INTCON regiszter felépítése
 

Ezután, hogy legyen egy "függvényhívásunk" is a projektben, az "asm volatile("ei");" adjuk meg, majd elindítjuk a Timer2-t (T2CON regiszter ON bit, 3. ábra), amely a PBCLK periódusváltásait számlálva egyesével fogja növelni a TMR2 regiszter értékét.

A while(1); segítségével megakadályozzuk azt, hogy a "main()" függvény véget érjen.

Hogy lássuk a multivector mód alkalmazását több periféria használatánál, módosítsuk most a programunkat úgy, hogy már ne csak a Timer2, hanem a Timer1 is tudjon megszakításkérést adni. Ezen kívül tudjon adni megszakítást az INT4 is, amely az RA15-ös portbiten található. 

Ezeket a kiegészítéseket is tartalmazó program található itt:

#include <p32xxxx.h>
#include <sys/attribs.h>
 
#define LED1        LATDbits.LATD1
#define LED2        LATDbits.LATD2
#define LED1Tg()    { LED1 = ~LED1; }
#define LED2Tg()    { LED2 = ~LED2; }
 
void __ISR (_TIMER_1_VECTOR, IPL1SRS) T1Interrupt(void)
{
    LED1Tg();
    IFS0bits.T1IF = 0;
}
 
void __ISR (_TIMER_2_VECTOR, IPL1SRS) T2Interrupt(void)
{
    LED2Tg();
    IFS0bits.T2IF = 0; }
 
void __ISR (_EXTERNAL_4_VECTOR, IPL2SRS) INT4Interrupt(void)
{
    LATB++;
    IFS0bits.INT4IF = 0;
}
 
main()
{
    TRISA = 0xFFFF;
    TRISB = 0x0000;
    TRISD = 0x0000;
    LATD = 0x0000;
    
    T1CONbits.TCKPS = 1;
    TMR1 = 0x0000;
    PR1 = 0x1122;
    
    T2CONbits.TCKPS = 4;
    TMR2 = 0x0000;
    PR2 = 0x1234;
    
    IPC1bits.T1IP = 1;
    IFS0bits.T1IF = 0;
    IEC0bits.T1IE = 1;
    
    IPC2bits.T2IP = 1;
    IFS0bits.T2IF = 0;
    IEC0bits.T2IE = 1;
    
    IPC4bits.INT4IP = 2;
    IFS0bits.INT4IF = 0;
    IEC0bits.INT4IE = 1;
    
    INTCONbits.MVEC = 1;
    asm volatile("ei");
    T1CONbits.ON = 1;
    T2CONbits.ON = 1;
    while(1);
}

 

Az INTCON regiszterben találjuk az INT4EP bitet (4. ábra), amely azért felel, hogy az RA15/INT4 bemeneten a 0->1, vagy az 1->0 értékváltás adjon-e interrupt-ot. 
Amikor az RA15-ön lévő nyomógombot lenyomjuk (vagy felengedjük), akkor a LATB regiszterben lévő szám értékét növeljük eggyel.