I'm trying to get the Timer1 on my PIC18F2550 to fire interrupts every second for countdowns. So far I've been able to establish interrupt priority correctly, as well as other settings, however my ISR only fires once. I've set up my program to print the amount of seconds left in the countdown onto the LCD, so it's obvious to the user how many interrupts have passed. I've made sure to clear the proper interrupt flags in the ISR, since that's what was the source of the problem for the majority of the people having the same issue on google, but this didn't fix it. Everything else about my program has worked fine so far.
(For the sake of testing whether or not the right priority has fired, low counts up instead of down)
Here's the code snippet
//First testing program for PIC18F2550
#include <p18f2550.h>
#include <stdlib.h>
#include <delays.h>
#define _XTAL_FREQ 4915200
#pragma config PLLDIV = 1
#pragma config CPUDIV = OSC1_PLL2
#pragma config FOSC = XT_XT
#pragma config MCLRE = ON
#pragma config BOR = OFF
#pragma config WDT = OFF
#pragma config IESO = OFF
#pragma config PBADEN = OFF
#pragma config LVP = OFF
unsigned char Countdown = 30; (other vars, fcns omitted)
#pragma code high_vector = 0x08
void highvectinterrupt(void){
_asm goto highisr _endasm
}
#pragma code low_vector = 0x18
void lowvectinterrupt(void){
_asm goto lowisr _endasm
}
(...)
void highisr(void){
Countdown--;
PIR1bits.TMR1IF = 0;
}
void lowisr(void){
Countdown++;
PIR1bits.TMR1IF = 0;
}
void main(void){
UCONbits.USBEN=0; //Setup I/O
UCFGbits.UTRDIS=1;
OSCCONbits.SCS1 = 0;
OSCCONbits.SCS0 = 0;
BAUDCONbits.TXCKP=0;
SPBRG = 0x3F; //1200 baud
TRISA = 0x00;
TRISB = 0xFF;
TRISC = 0x00;
beephigh(); //Done, clear the line and beep speaker 1
LATA = 0; //reset
LATC = 0;
Delay10KTCYx(8);
LATAbits.LATA2 = 1; //load the reset
Delay10KTCYx(8);
LATAbits.LATA3 = 1; //stop reset
LATAbits.LATA4 = 1;
LATAbits.LATA2 = 0;
Delay10KTCYx(123);
initlcd();
Delay10KTCYx(10);
setupUI();
LATAbits.LATA0 = 0; //Done, clear the line and beep speaker 2
beeplow();
INTCON = 0; //Reset high interrupt flags and disable interrupts
T1CON = 0x11110100; //16 bit r/w; internal system clock; 8x prescale; timer1 osc disabled
TMR1H = 0; //Clear the counting register
TMR1L = 0;
PIE1bits.TMR1IE = 1; //Enable Interrupts on Timer1
PIR1bits.TMR1IF = 0; //Clear the flag if set
RCONbits.IPEN = 1; //Enable priority levels
INTCON2bits.TMR0IP = 0; //Timer0 on low priority
IPR1bits.TMR1IP = 0; //Timer1 on low priority
IPR1bits.RCIP = 1; //RS232 on high priority
INTCONbits.GIEL = 1; //Enable all low priority interrupts
INTCONbits.GIEH = 1; //Enable all high priority (must be enabled for low priority)
T1CONbits.TMR1ON = 1; //Turn Timer1 on
while(1){ //Idle and refresh screen
Delay10KTCYx(8);
bin2ascii(Countdown);
cmdlcd(0xCE);
putclcd(ConvRegTens);
putclcd(ConvRegOnes);
}
}
Ultimately I'm staring at an LCD with "31" on it. It's likely a single flag that I'm not setting properly that's causing nothing to work right, but as far as I've checked everything's being enabled and being cleared properly. What am I doing wrong? I'd appreciate any help, this is enormously frustrating.
Also: I've cleared all breakpoints so it's not halting anywhere, and program the latest version on compile. So I've ruled out running an old busted version of the code.
Edit: To clarify, this code is only for functionality; it will not fire every second but rather much faster. Once I get it working I'll play the overflow counting to get it to 1 second.
You are not finishing ISR routine correctly!
#pragma code low_vector = 0x18
void lowvectinterrupt(void){
_asm goto lowisr _endasm
}
After ISR execution the interrupt must be switched on again, so the lowvectinterrupt
must be finished with RETFIE
instruction and at least flags and WREG register must be restored.
Normaly is in ISR routine declared like:
static void interrupt isr(void)
{
}