WebElektronika

Megszakítások használata a PIC18F családban

person access_time 2014.03.13.
Megismerjük most a PIC18F mikrovezérlők megszakításkezelését, és készítünk két egyszerű példaprogramot is. Ezek az egy- és a kétszintű megszakításkezelést mutatják be, de sablonként is jól használhatók saját fejlesztéseknél is.


A mikrovezérlők elengedhetetlen tulajdonsága a megszakításkezelés (interrupt) lehetősége. A PIC18F családban a megszakításkezelés kétszintű, azaz, hozzá tudunk rendelni prioritási szintet az adott perifériához, amely majd kiváltja a megszakítást. Lehet például az AD átalakítás végén magasszintű megszakítást kiváltani, míg egy időzítő túlcsordulásakor alacsonyt (vagy fordítva). A PIC18F családban minden periféria (pl.: AD átalakító, SSP, Timer1, stb) képes megszakítást kiváltani.
A magasszintű megszakítás a 0x0008 címen, az alacsonyszintű megszakítási vektor pedig a 0x0018 címen található.


1. ábra   Interrupt a PIC18F csalában
 

A Microchip által közölt ábrán (1. ábra) láthatjuk a megszakításkezelés "logikáját". Minden megszakítástkiváltó perifériához három bit (xxxIF, xxxIP, xxxIE) tartozik. Nézzük meg a Timer0 időzítő IT bitjeit.

  • TMR0IF : interrupt flag, ha megszakítás történik, 0-ból 1-be vált. Szoftveresen kell törölni
  • TMR0IP : interrupt prioritás, ha 1, akkor magasszintű, ha 0, akkor alacsonyszintű lesz az IT
  • TMR0IE : interrupt engedélyezés. Nem elég globálisan engedélyezni a megszakítást, az adott modul IE bitjét is logikai 1-be kell állítani.

Ahhoz, hogy a megszakításkezelést használni tudjuk, nem elég az adott perifériának ezt a lehetőségét engedélyezni, hanem "globálisan" is engedélyeznünk kell. Erre szolgál a GIE bit, amely az INTCON regiszterben találunk.

Ebben a mintakódban csak a Timer0 ad túlcsorduláskor megszakítást. 

#include <p18f14k50.h>
#define    LED0    LATCbits.LATC0

void InterruptHandler();

main()
{
    INTCON = 0x20;          // 0010 0000 : TMR0IE=1, engedélyezzük
                            // a TMR0 megszakításkérését
    INTCON2 = 0x84;         // 1000 0100 : TMR0IP = 1, magasszintű IT
    RCONbits.IPEN = 0;      // nem engedélyezzük a kétszintű megszakítást
    TMR0H = 0;              // töröljük a TMR0 számlálót
    TMR0L = 0;
    T0CON = 0x80;           // 1000 0000 : TMR0ON=1, TMR0 engedélyezve
                            // 16bites mód, belső CLK, 1:2 osztás
    INTCONbits.GIEH = 1;    // engedélyezzük a megszakítást
    TRISC = 0x00;

    while(1)
    {
        // ide írhatjuk a kódot, amely állandóan
        // meghívásra kerül, innen "lépünk" ki
        // a megszakításkor
    }
}

#pragma code InterruptVector = 0x08
void InterruptVector()
{
    _asm
        goto InterruptHandler
    _endasm
}

#pragma code
#pragma interrupt InterruptHandler

void InterruptHandler()
{
    // Timer0 adta a megszakítást?
    if(INTCONbits.TMR0IF)
    {
        INTCONbits.TMR0IF = 0;   // szoftveresen töröljük az IT flaget
        LED0 = !LED0;            // villogtatjuk a LED0-t
    }
}

 

Kétszintű megszakításkezelés

Most nézzük meg azt, amikor kétszintű megszakítást alkalmazunk. Magasszintű megszakítást ad a Timer0 a túlcsorduláskor, alacsonyszintűt pedig az AD átalakító, amikor véget ér az analóg átalakítás.

#include <p18f14k50.h>

void InterruptHandlerHigh();
void InterruptHandlerLow();

main()
{
    INTCON = 0x20;          // 0010 0000 : TMR0IE=1, engedélyezzük
                            // a TMR0 megszakításkérését
    INTCON2 = 0x84;         // 1000 0100 : TMR0IP = 1, magasszintű IT
    PIE1bits.ADIE = 1;      // AD átalakító megszakításának engedélyezése
    IPR1bits.ADIP = 0;      // alacsonyszintű megszakítás

    // AD beállítása
    ADCON0 = 0B00101001;    // AN10 a bemenő jel, engedélyezve az AD
    ADCON1 = 0B00000000;    // ref fesz: Vdd, GND
    ADCON2 = 0B10101010;    // jobbra igazítás, 12Tad, Fosc/32

    RCONbits.IPEN = 1;      // engedélyezzük a kétszintű megszakítást
    TMR0H = 0;              // töröljük a TMR0 számlálót
    TMR0L = 0;
    T0CON = 0x80;           // 1000 0000 : TMR0ON=1, TMR0 engedélyezve
                            // 16bites mód, belső CLK, 1:2 osztás
    INTCONbits.GIEH = 1;    // engedélyezzük a megszakítást
    TRISC = 0x00;
    ADCON0bits.GO = 1;      // AD átalakítás indítása

    while(1)
    {
        // ide írhatjuk a kódot, amely állandóan
        // meghívásra kerül, innen "lépünk" ki
        // a megszakításkor
    }
}

#pragma code InterruptVectorHigh = 0x08
void InterruptVectorHigh()
{
    _asm
        goto InterruptHandlerHigh
    _endasm
}

#pragma code InterruptVectorLow = 0x18
void InterruptVectorLow()
{
    _asm
        goto InterruptHandlerLow
    _endasm
}

#pragma code
#pragma interrupt InterruptHandlerHigh
void InterruptHandlerHigh()
{

}

#pragma code
#pragma interrupt InterruptHandlerLow
void InterruptHandlerLow()
{    
}

Azok a függvények, amelyek megszakításkor meghívásra kerülnek, üresek, azokat majd az Olvasó töltheti ki. Fontos azonban, hogy a megszakítást jelző flag-eket (pl.: PIR1bits.ADIF, INTCONbits.TMR0IF) szoftveresen törölni kell.