Interrupt
Nogle gange har man brug for, hvad man måske kunne kalde en slags multitasking, hvor man udover den normale kode, der kører FOREVER loop også har noget kode der skal afvikles på bestemte tidspunkter. Det kan man gøre med interrupt.
Hvad sker der ved interrupt
Interrupt betyder at afbryde, og det der netop sker er, at den kode der afvikles bliver afbrudt af en hændelse (tid, skift på et ben, AD færdig eller hvad det kan være).
Når koden afbrydes, så sker det ved at der kaldes en speciel adresse / rutine.
Hvis man arbejder med interrupt i assembler, så sker der det, at man kalder adresse 4 i PIC'en (kaldet en interrupt-vector), hvorefter man som programmør fuldt ud har ansvaret for hvad der så skal ske (typisk skal man gemme CPU-status og W-registeret), så skal man udføre noget og til slut reetablere det man har gemt, og returnere fra interruptet.
Hvis man arbejder i JAL, så er det noget enklere, da man blot skriver en interrupt-rutine, hvor man først tjekker på om det er den givne hændelse der har interruptet, og hvis det er, så udfører man det man ønsker.
Man kan godt have flere interrupt-rutiner i JAL. Det eneste man skal være klar over er, at man ikke kvæler processoren i for mange interrupts, samt at man skal huske at afstille det der interruptede.
Eksempel på anvendelse af interrupt
Dette eksempel gennemgår hvordan man kan lave et timer-interrupt, der sker hvert 4. millisekund, så man ud fra dette kan lave et ur der tæller sekunder, minutter og timer.
Det centrale i interruptet er interrupt-rutinen der kan skrives som følger:
procedure interrupt_handler is pragma interrupt
if intcon_tmr0if then
TMR0 = timer_value -- Sæt timeren til den afpassede værdi
tim_int
-- Slet TOIF for at re-enable timer interrupt
intcon_tmr0if = False
end if
end procedureNavnet på proceduren betyder ikke noget, men det er vigtigt at der står pragma interrupt, da det er disse keywords der angiver at det er en rutine der skal kaldes, når der sker et interrupt.
Der undersøges så på det bit der hedder TMR0IF, som ligger i INTCON. Det er TiMeR 0 Interrupt Flag, altså et bit der sættes, når det er timer 0 der har udløst interruptet.
Hvis det er Timer 0, så starter man med at skrive en ny værdi i TMR0, så den kan tælle videre derfra (beskrives senere).
Så kaldes den procedure tim_int, der håndterer selv tids-registreringen, også mere om dette senere.
Når vi har serviceret dette, så er det vigtigt at TMR0IF bliver afstillet igen, ellers vil der komme et nyt interrupt fra samme kilde igen, lige efter vi har returneret fra proceduren.
For at der overhovedet kan komme et interrupt fra timeren, så skal interruptet slåes til. Det gøres som følger:
-- Initialiser interruptet
intcon_tmr0ie = True -- Sæt timerinterrupt aktivt
intcon_inte = False -- Passiver Port B ben 0 interrupt
intcon_rabie = False -- Passiver Port B ben 4-7 kanttrigget interrupt
intcon_gie = True -- Enable interrupt generelt
--Her er det TMR0IE der bliver sat i INTCON. Det betyder at TiMeR 0 Interrupt Enable sættes, altså at dette interrupt nu er aktivt. Bare for en sikkerheds skyld afstilles de andre interrupts.
Den anden ting der skal stilles for at der kan komme interrupt er at det generelle interrupt slås til ved at sætte GIE (General Interrupt Enable) i INTCON.
Specielt for Timerinterrupt
Nu er det sikret at interruptet kan komme, og at det bliver serviceret - tilbage i indstillingerne er at bestemme hvor tit det skal komme. Her skal vi have fat i OPTION_REG og TMR0. Koden ser ud som følger:
-- Starten af initialiseringskoden
-- Bit 7 til 1 : Port B pull up disabled
-- Bit 6 til 0 : Port B-0 interrupt på faldende kant
-- Bit 5 til 0 : Clock til TMR0 fra den interne clock (4MHz / 4)
-- Bit 4 til 0 : Kant for clock til TMR0 (ikke relevant)
-- Bit 3 til 0 : Timer rate til TMR0 (hvis den er 1 er det WDT)
-- Bit 2-0 Timer rate
-- 000 2
-- 001 4
-- 010 8
-- 011 16
-- 100 32
-- 101 64
-- 110 128
-- 111 256
OPTION_REG = 0b_1000_0011 -- Se ovenstående eller datablad for nærmere detaljer
TMR0 = timer_valueBit 7 og 6 har ikke noget med Timer 0 at gøre, men ligger i dette register, så de er også nævnt her.
Bit 5 angiver at takten der skal tælles med stammer fra den takt der angiver processorens hastighed - hvis man anvender en anden clock-frekvens skal man naturligvis til at ændre sine beregninger for clocken. I beregningen skal man tage højde for at der deles med 4 i forhold til den oprindelige clock-frekvens, så når vi bruger 4 MHz er den frekvens vi starter med her altså 1 MHz.
Bit 4 er ikke relevant i denne sammenhæng, det er kun hvis man bruger et eksternt ben til at give clock-signaler, så kan det betyde noget om det er på forkant eller på bagkant det sker.
Bit 3 angiver at den timer-hastigehed vi angiver faktisk også kommer ind i Timer 0.
Bit 0-2 angiver med 3 bit hvilken delingsfaktor der skal på interruptet. Her har jeg valgt 011, altså at den 1 MHz vi starter med deles med 16. Det angiver hvert tællerskridt.
Det er altså denne værdi der skrives i OPTION_REG Her angivet som en binær værdi for nemheds skyld).
Den værdi der skrives i TMR0 er 6. Grunden til dette er at vi gerne vil have interrupt for hver 4. millisekund. Det får vi ved at TMR0 er et Byte register, og da timer 0 tæller opad, så vil det komme til at tælle 250 gange (256 - 6) inden der sker overflow, og altså interrupt. Da vi starter med 1 MHz svarer tiden til 1 mikrosekund, og hvert tælleskridt er altså 16 mikrosekunder, og når vi har 250 af dem, så får vi 250 * 16 = 4000 mikrosekunder, altså 4 millisekunder mellem hvert interrupt, under forudsætning af at vi stiller TMR0 til 6 hver gang der sker et interrupt, men det gør vi jo også i interrupt-proceduren.
Dette var forklaringen på alle værdierne, men for at gøre det lidt lettere, så kan man få omregnet sine ønsker til koden ved at gå ind på en pic timer calculator for at få sine værdier, og rent faktisk også får koden (godt nok i C), men det er et godt skridt i den rigtige retning.
Endelig så er der tilbage at forklare hvordan vi kan få sekunder, minutter og timer til at fungere. Det sker i proceduren tim_int, der ser ud som følger:
-- Procedure der i interruptet håndterer optællingen af tid til et ur.
procedure tim_int is
ms_4 = ms_4 + 1
if (ms_4 == 250) then
ms_4 = 0
sekund = sekund + 1
if (sekund == 60) then
sekund = 0
minut = minut + 1
if (minut == 60) then
minut = 0
time = time + 1
if (time == 24) then
time = 0
end if
end if
end if
end if
end procedureVariablen ms4 tæller fra 0 til 250, og hver gang den nulstilles er der gået 1000 millisekunder, altså et sekund, og det betyder at sekund-variablen tælles en op.
Når sekundvariablen når 60 så nulstilles den, og minutvariablen tælles en op. Tilsvarende gøres med minutvariablen, så der tælles timer op, og timerne nulstilles så ved 24.
Hvis man ønskede at bygge dato på, så skulle der bygges videre på denne procedure til at håndtere dag, måned og år, hvis man ville det.
Den samlede kode, der er implementeret med display-visning ved hjælp af ALCD-modulet ligger i en zip-fil her.
Andre interruptkilder
Som standard har de PIC-kredse vi bruger følgende interrupts:
Timer0 som vist i eksemplet før.
Eksternt interrupt fra et ben (kan indstilles til forkant og bagkant)
Interrupt, når EEPROM brænding er færdig.
Interrupt på niveauskift på et antal ben - hvilke ben det er kan ses i tabellen herunder.
Ud over det, så kan de PIC-kredse vi arbejder med andre ting med interruptet - man er selvfølgelig nødt til at gå i databladet for den enkelte processor for at se hvordan det virker, så dette er bare en oversigt over hvilke muligheder der er.
| PIC16F84 | PIC16F628 | PIC12F675 | PIC16F684 | PIC16F690 | PIC16F877 | |
|---|---|---|---|---|---|---|
| AD | Nej | Nej | Ja | Ja | Ja | Ja |
| Comperator | Nej | Nej | Ja | 2 stk | 2 stk | Nej |
| EEPROM | Ja | Ja | Ja | Ja | Ja | Ja |
| INT - eksternt ben | Ja | Ja | Ja | Ja | Ja | Ja |
| Niveau skift | Port B4-7 | Port B4-7 | Port A0-5 | Port A0-5 | Port A0-5 og Port B4-7 | Port B4-7 |
| Timer 0 (8 bit) | Ja | Ja | Ja | Ja | Ja | Ja |
| Timer 1 (16 bit) | Nej | Ja | Ja | Ja | Ja | Ja |
| Timer 2 (8 bit) | Nej | Ja | Nej | Ja | Ja | Ja |
| USART (seriel) | Nej | Ja | Nej | Nej | Ja | Ja |
| Capture Compare | Nej | Ja | Nej | Ja | Ja | 2 stk |
| Oscillator Fail | Nej | Nej | Nej | Ja | Ja | Nej |
| Synkron seriel | Nej | Nej | Nej | Nej | Ja | Ja |
| Parallel slave port | - | - | - | - | - | Ja |
| Bus Collision | - | - | - | - | - | Ja |
Demo-eksempler på interrupt
Demo-eksemplerne er ikke lavet til at udføre noget praktisk, men udelukkende tænkt til at demonstrere hvordan interruptet initialiseres (indstilles og sættes i gang) og lige så meget kode i interruptet til at man kan se at det fungerer - det er simple funktioner der udføres som at vippe med en udgang, så man kan måle / se reaktionen.
Koderne kan hentes i denne ZIP-fil.
For at interruptet kan fungere skal det slås til. Det er dels de enkelte interrupts, men også det globale interrupt GIE der skal slås til.
Ud over dette har hovedkoden blot et tomt forever loop.
INTCON_GIE = true -- Enable alle interrupt
forever loop
-- I denne kode sker der intet,
-- men der kunne også udføres kode der ikke er så tidskritisk
end loopInterrupt koden er skrevet ud i hvert sit modul, der i princippet kan fungere individuelt. Hvert modul har sin egen interrupt-rutine, men JAL sørger for at alle interrupt-rutiner bliver serviceret.
include timer1
include timer2
include intTimer 0 interrupt
Dette interrupt er sat op til at vippe med pin_a4 for hver 64,000 ms ved en clockfrekvens på 4 MHz.
De 64 ms er beregnet ud fra prescaleren og den værdi TMR0 reloades med samt den clock-frekvens PIC'en arbejder med delt med 4.
Ved 4 MHz får timeren en frekvens på 1 MHz, så er det så simpelt at produktet af prescaleren og den tid man reloader TMR0 med er det antal mikrosekunder man ønsker.
Tid = PS * Timercount = 256 * 250 = 64000 mikrosekunder
Det timercount man får opnår man ved at reloade tælleren med en værdi, således at den kommer til at tælle 250 gange inden den løber over, så reload-værdien er 256 minus det ønskede timercount.
Reload TMR0 = 256 - 250 = 6
Koden ser ud som følger:
-- Demo til Timer 0 Interrupt
pin_a4_direction = output
INTCON_TMR0IE = true -- Aktiver Timer 0 interrupt
INTCON_TMR0IF = false -- Afstil interruptet
OPTION_REG_T0CS = false -- Timer 0 source fra Fosc/4
OPTION_REG_T0SE = false -- Rising edge for T0 clock
OPTION_REG_PSA = false -- Prescaler til Timer 0
OPTION_REG_PS = 0b_111 -- Prescaler til 256
-- Interruptrutine - kaldes automatisk
procedure timer_0_int is pragma interrupt
if INTCON_TMR0IF then -- Reager på Timer 0 interrupt
TMR0 = 6
INTCON_TMR0IF = false
pin_a4 = ! pin_a4
end if
end procedureDet giver et output på pin_a4 som følger:

Output fra demo-program på timer0-interrupt
Timer 1 interrupt
Dette interrupt er sat op til at vippe med pin_c5 for hvert halve sekund.
-- Demo til Timer 0 Interrupt
pin_C5_direction = output
PIE1_TMR1IE = true -- Enable interrupt
PIR1_TMR1IF = false
INTCON_PEIE = true -- Aktiver ekstra interrupt (bl.a. timer 1)
T1CON_TMR1CS = false -- Timer 0 source fra Fosc/4
T1CON_TMR1ON = true -- Rising edge for T0 clock
T1CON_T1OSCEN = true -- Prescaler til Timer 0
T1CON_T1CKPS = 0b_11 -- Prescaler til 8
-- Interruptrutine - kaldes automatisk
procedure timer_1_int is pragma interrupt
if PIR1_TMR1IF then -- Reager på Timer 0 interrupt
TMR1 = 65535 - 62500
PIR1_TMR1IF = false
pin_C5 = ! pin_C5
end if
end procedureDet giver et output på pin_c5 som følger:

Output fra demo-program på timer1-interrupt
Timer 2 interrupt
Dette interrupt er sat op til at vippe med pin_a0 med forskelligt interval, startende med 1,8 ms og op til 65 ms.
-- Demo til Timer 2 Interrupt
pin_a0_direction = output
-- Indstil timer 2 til at bestemme pulslængde
-- Her er demonstreret hvordan man kan få samme tider med forskellige oscillatorer
if target_clock == 4_000_000 then
T2CON_TOUTPS = 0b_0010 -- Postscaler til / 3
elsif target_clock == 8_000_000 then
T2CON_TOUTPS = 0b_0101 -- Postscaler til / 6
elsif target_clock == 20_000_000 then
T2CON_TOUTPS = 0b_1110 -- Postscaler til / 15
else
pragma error "Oscillatorfrekvens ikke defimeret"
end if
-- Her sættes tiden til langsomst muligt
T2CON_TOUTPS = 0b_1111 -- Postscaler til / 16
T2CON_TMR2ON = true -- Tænd timer 2
T2CON_T2CKPS = 0b_10 -- Prescale 16
PIE1_TMR2IE = true -- Enable interrupt
INTCON_PEIE = true -- Aktiver ekstra interrupt (bl.a. timer 2)
-- Interruptrutine - kaldes automatisk
procedure timer_2_int is pragma interrupt
if PIR1_TMR2IF then -- Reager på Timer 2 interrupt
PIR1_TMR2IF = false
PR2 = PR2 + 1 -- Lav en skiftende tid på interruptet
if PR2 == 0 then
PR2 = 5
end if
pin_a0 = ! pin_a0
end if
end procedureDet giver et output på pin_a0 som følger:

Output fra demo-program på timer2-interrupt
Set i en anden timebase ser det ud som følger:

Output fra demo-program på timer2-interrupt timebase 10 ms
Kanttrigget RA2 interrupt
Dette er et af de oprindelige interrupts fra de tidlige PIC-kredse, hvor man kan indstille om det er positivt gående eller negativt gående der skal interruptes på, men man kan ikke ændre på hvilket ben det er forbundet til. På PIC12F675, PIC16F684 og PIC16F690 er interruptet placeret på pin_a2, på PIC16F84, PIC16F628 og PIC16F877 er interruptet placeret på pin_b0.
I dette eksempel er interruptet er sat op til at vippe med pin_a1 når der kommer en forkant på pin_a2.
-- Demo til kanttrigget Interrupt
pin_a2_direction = input
pin_a1_direction = output
INTCON_INTE = true -- Aktiver eksternt interrupt på RA2
INTCON_INTF = false -- Afstil interruptet
OPTION_REG_INTEDG = true -- Rising edge of int
-- Interruptrutine - kaldes automatisk
procedure int_interrupt is pragma interrupt
if INTCON_INTF then -- Reager på pin A2 kant interrupt
INTCON_INTF = false
pin_a1 = ! pin_a1
end if
end procedureFor at illustrere at der sker et skift på alle forkanter af pin_a2, så sættes pin_a2 til pin_a0, der er timer 2, som giver en varierende puls, som så bliver halveret over til pin_a1.
Det giver et output hvor pin_a0 ar den blå kurve, og pin_a1 er den røde kurve som følger:

Output fra demo-program på kant trigget interrupt på RA2
Niveau-skift interrupt
Dette interrupt er sat op til at vippe med pin_c6 når der kommer en forkant på pin_b4.
-- Demo til Ports A B Change Interrupt
pin_c6_direction = output
pin_b4_direction = input
INTCON_RABIE = true -- Aktiver Timer 0 interrupt
INTCON_RABIF = false -- Afstil interruptet
-- Mulige input er RA0 til RA5 - vær opmærksom på at oscillator og reset kan blokere:
-- RA4 og RA5 virker ikke, når der bruges ekstern oscillator
-- RA3 virker ikke ved ekstern reset
-- Andre mulige input er RA4 til RA7 - her kan også ligge andre funktioner
IOCB_IOCB4 = true -- Enable interrupt fra pin_a3
-- Interruptrutine - kaldes automatisk
procedure porta_int is pragma interrupt
if INTCON_RABIF then -- Reager på Port A Change interrupt
if pin_b4 then -- reager kun på et skift til høj
pin_c6 = ! pin_c6
end if
INTCON_RABIF = false
end if
end procedureFor at demonstrere dette interrupt hvor der sker et skift på alle forkanter af pin_b4, så sættes pin_a2 til pin_a0, der er timer 2, som giver en varierende puls, som så bliver halveret over til pin_a1. Denne forbindes videre til pin_b4, som så giver yderligere en halvering ud til pin_c6. På denne måde deles frekvensen med 4 - og puls-pause-tiderne ganges med 4 som det kan ses på følgende optagelse:

Output fra demo-program på chamge interrupt på pin_c6
Serielt modtage interrupt
Dette interrupt er til den serielle port i asynkront mode. Der skal indstilles hvordan den serielle port skal kommunikere med hastighed og antal bit. Når der ikke kommunikeres er den serielle linje høj. En asynkron seriel karakter består af en lav startbit og 8 databit samt en høj startbit som vist her:

Asynkron seriel karakter med en lav startbit, 8 databit og en høj stopbit
For at kunne kommunikere via interruptet skal man først have indstillet baudrate. Det gør man her ved at inkludere baudrate. Derefter slås de forskellige ting til for at kunne modtage asynkront og interrupte.
include baudrate
pin_b5_direction = input -- RX/DT pin input (modtagerben)
pin_b7_direction = output -- TX/CK pin output (senderben)
PIE1_RCIE = true -- Aktiver serielt receive interrupt
PIR1_RCIF = false -- Afstil interruptet
INTCON_PEIE = true -- Aktiver ekstra interrupt (bl.a. seriel modtagelse)
RCSTA_SPEN = true -- Enable den serielle port
TXSTA_SYNC = false -- Sæt den serielle port til Asynkron mode
RCSTA_CREN = true -- Enable modtagelse
TXSTA_TXEN = true -- Enable sending på den serielle port til echoet
var byte tx_antal = 0 -- Tæller der bruges til sending
var byte rc_byte -- Den modtagne byteNår dette er indstillet vil man kunne modtage en byte serielt på RX-benet. Modtagelsen af en byte som den viste udløser dette interrupt.
-- Interruptrutine - kaldes automatisk
procedure serial_rc_interrupt is pragma interrupt
if PIR1_RCIF then -- Reager på serielt modtage interrupt
PIR1_RCIF = false
if RCSTA_FERR then
RCSTA_SPEN = false
RCSTA_SPEN = true
elsif RCSTA_FERR then
RCSTA_SPEN = false
RCSTA_SPEN = true
else
rc_byte = RCREG -- Hent den modtagne byte
tx_antal = 0
TXREG = rc_byte -- Send den igen som echo (vil starte TX-interrupt)
end if
end if
end procedureI interruptet sendes den modtagne byte tilbage på TX-benet. Dette kaldes at lave Echo.
Når man gør det i jaledits serielle terminal ser det ud som følger:

Visning af hvordan serielt modtage-interrupt reagerer ved at give et echo til terminalen
Serielt sende interrupt
Som med modtage-interruptet kan man på den serielle port sende en byte i asynkront mode. Indstillinger med hastighed og bits gælder også for senderen. Sendingen sker ved at der skrives en karakter til senderegisteret TXREG. Dette interrupt kommer når registeret er klar til at modtage en ny karakter, så man på denne måde kan få sendt karakterer hurtigt efter hinanden.
Der skal igen laves en række indstillinger for at få sende-interruptet til at fungere:
pin_b5_direction = input -- RX/DT pin input (modtagerben)
pin_b7_direction = output -- TX/CK pin output (senderben)
PIE1_TXIE = true -- Aktiver serielt receive interrupt
INTCON_PEIE = true -- Aktiver ekstra interrupt (bl.a. seriel modtagelse)
RCSTA_SPEN = true -- Enable den serielle port
TXSTA_SYNC = false -- Sæt den serielle port til Asynkron mode
TXSTA_TXEN = true -- Enable sending på den serielle port til echoet
PIR1_TXIF = false -- Afstil interruptetI dette eksempel arbejder sende-interruptet sammen med det modtage-interruptet der er beskrevet ovenfor. Når der modtages en karakter, så sendes den tilbage som et echo. Når TXREG er klar til en ny karakter kommer der et TX-interrupt, hvor man så kan fortsætte med at sende, og her sendes den samme karakter så 4 gange mere, og der sendes CR og LF som giver en ny linje i terminalvinduet. Dette er implementeret som følger:
-- Interruptrutine - kaldes automatisk
procedure serial_TX_interrupt is pragma interrupt
if PIR1_TXIF then -- Reager på serielt sende interrupt
PIR1_TXIF = false
if tx_antal < 6 then
if tx_antal < 4 then
TXREG = rc_byte -- Send den sidst den modtagne byte
else
if tx_antal < 5 then
TXREG = 0x0D -- Send CR
else
TXREG = 0x0A -- Send LF
end if
end if
tx_antal = tx_antal + 1
end if
end if
end procedure
Terminal-vinduet der viser at hver karakter returneres 5 gange efterfulgt af et linjeskift
Synkron seriel port interrupt
PIC'en kan også arbejde med synkron kommunikation - ikke testet.
AD konvertering interrupt
Når man anvender AD-konvertering, så sætter man AD-konverteren i gang, og alt efter indstillingerne, så går der noget tid inden man kan aflæse det konverterede. I ADC holst biblioteket venter man blot indtil konverteringen er færdig.
Dette interrupt kan indstilles til at ske når konverteringen er færdig, så man kan få PIC'en til at lave andre ting mens konverteringen sker.
Her illustreres den opsætning der skal laves for at indstille AD-konverteringen til at fungere på pin_c2 der har tilknyttet analog kanal 6:
pin_c2_direction = input -- Analog kanal 6 input
jansel_ans6 = true -- Sæt kanal 6 som AD
adcon1_adcs = 0b_110 -- 16 us clock ved 4 MHz (176 us konvertering)
adcon0_vcfg = false -- Vref til Vdd
adcon0_adfm = false -- Venstre-justeret output
adcon0_chs = 0b_0110 -- Konverter kanal 6
adcon0_adon = true -- Slå AD'en tilHerefter ses det der skal til for at AD-konverteren interrupter:
PIR1_ADIF = false -- Afstil interruptet
PIE1_ADIE = true -- Aktiver AD interrupt
INTCON_PEIE = true -- Aktiver ekstra interrupt (bl.a. seriel modtagelse)For at teste sættes pin_c3 som output og der defineres en variabel til AD-konverterings resultatet. Som det sidste startes AD-konverteringen for at sætte gang i festen.
pin_c3_direction = output -- til test
var byte ad_byte -- Den modtagne byte
adcon0_go = true -- Start konverteringenSelve interruptet aflæser AD-resultatet og vipper med pin_c3 til testen, og den næste konvertering sættes i gang.
-- Interruptrutine - kaldes automatisk
procedure serial_ad_interrupt is pragma interrupt
if PIR1_ADIF then -- Reager på AD interrupt
PIR1_ADIF = false
ad_byte = ADRESH -- Hent den konverterede byte
pin_c3 = ! pin_c3 -- Lav en indikation i hardwaren
adcon0_go = true -- Sæt en ny konvertering i gang
end if
end procedureSom det kan ses her løber den rundt på 210 us. Grunden til at tiden er længere end 176 us er at interruptet skal reagere og der udføres lidt kode inde i interruptet inden den næste konvertering startes.

Illustration der viser at AD-interrupt sker med 210 us mellemrum
Compare/Capture Timer 1 interrupt
Dette interrupt har mange forskellige indstillinger til timer 1 sammen med CCPR1 der er et sammenlignings-register, som kan udløse interrupt når registeret og timer 1 har samme værdi. Indstillingsmulighederne ligger på hvad der skal ske med timer 1 når der er lighed, og hvordan interruptet skal dannes (deling med 1, 4 eller 16).
EEPROM write operation interrupt
En skrivning til EEPROM tager en vis tid. Dette interrupt anvendes til at registrere hvornår skrivningen er færdig, så man kan skrive næste byte.
Comperator C1 interrupt
Den analoge comperator C1 kan indstilles til sammenligning med en spænding enten eksternt eller internt. Ved enten positivt eller negativt skift kan det indstilles til at give et interrupt.
Comperator C2 interrupt
Den analoge comperator C2 kan indstilles til sammenligning med en spænding enten eksternt eller internt. Ved enten positivt eller negativt skift kan det indstilles til at give et interrupt.
Oscillator fejl interrupt
Der kan internt i PIC'en registreres om oscillatoren har fungeret korrekt (givet konstant frekvens). Hvis der har været en fejl kan der udløses et interrupt, men selvfølgelig først efter oscillatoren er kommet i gang igen, da det kræver oscillator at gennemføre kode, altså også et interrupt.