Home > Software design >  PIC16F877A timer1 interrupt time is not as expected
PIC16F877A timer1 interrupt time is not as expected

Time:09-29

Implemented interrupt function on TIMER1 on PIC16F877A MCU on PIC-DIP40 development board. Configured the timer Prescaler to 1 and auto preload value to 55536 so that the interrupt time is 0.01s. Using a counter of 100 to count 1s interval. The Fosc is 4Mhz. So my calculation is :

interrupt time = (4 / Fosc) * (65536 - 55536) = (4/4000000) * (65536 - 55536) = 0.01 s

And used a counter of 100 to generate a 1s interval. Currently, I have no oscilloscope to test the actual 1s interval so, I am blinking an LED (LED2) on the timer interrupt and another LED (LED1) on the same time interval 1s using __delay_ms(1000); function.

So as expected the two LEDs will blink synchronously (Turn ON and OFF at the same Time). But for some first iterations, they blink synchronously. After some iterations, there is a clear difference in time between their blinking time (Turning ON and OFF time). After several minutes the difference is almost 1s. So the timer interrupt is not working as expected.

So is my calculation wrong for interrupt time or I am missing something in the timer1 configuration?

The overall goal is to generate a 1s time interval and test the validity without using an oscilloscope.

Here is my code :

// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#include <xc.h>
#include <pic16f877a.h>
#define _XTAL_FREQ  4000000

#define LED1_ON PORTDbits.RD7 = 0
#define LED1_OFF PORTDbits.RD7 = 1 
#define LED2_ON PORTDbits.RD6 = 0
#define LED2_OFF PORTDbits.RD6 = 1 

#define LED2_TOGGLE PORTDbits.RD6 = ~PORTDbits.RD6

uint16_t preloadValue  = 55536 ; 
uint16_t counter  = 0 ; 
uint16_t secCounter1 = 100 ; 

void io_config() {
    TRISD  &= ~((1 << _PORTD_RD7_POSITION) | (1 << _PORTD_RD6_POSITION)) ; //RD7 and RD6 are output LEDs
}

void timer1_init(){
    TMR1 = preloadValue ; //loading the preload value
    T1CON &= ~((1 << _T1CON_T1CKPS1_POSN) | (1 << _T1CON_T1CKPS0_POSN) | (1 << _T1CON_TMR1CS_POSN)) ; //prescalar is 1 clock is Fosc
    T1CONbits.TMR1ON = 1 ; //timer 1 is ON
    LED2_ON ; 
}

void interrupt_en_configure(){
    INTCON |=  (1 << _INTCON_GIE_POSITION) | (1 << _INTCON_PEIE_POSITION) ; //global and peripheral interrupt on
    PIE1 |= _PIE1_TMR1IE_MASK ;  //timer 1 interrupt enable
    TMR1IF = 0 ; //clearing interupt flag
}

void __interrupt() ISR(){
    if(TMR1IF){
        counter     ; 
        if (counter == secCounter1){
            counter = 0 ;
            LED2_TOGGLE ; 
        }
        
        TMR1 = preloadValue ;
        TMR1IF = 0 ; 
    }
}

void main(void) {
    io_config();
    interrupt_en_configure() ; 
    timer1_init() ;
    
    while (1) {
        LED1_ON ; 
        __delay_ms(1000);
        LED1_OFF ;
        __delay_ms(1000);
    }
}

CodePudding user response:

__delay_ms() delays by running an empty loop, but it commonly cannot be exact. You would need to look into the actual machine code that is run to calculate the real delay. BTW, this is not rocket science and a great learning task. (Been there, done that.)

Now the rest of your loop (LED switching, looping) adds to this. Therefore, your pure software driven blinker is not exact.

However, your interrupt driven blinker is not, too. You reset the timer at the end of the ISR, after several clock cycles have passed. You need to take this into account, and don't forget the interrupt latency. Even worse, depending on the conditional statement, the reset happens at different times after the timer overflow.

Producing exact timing is difficult, especially with such a simple device.

The solution is to avoid software at all for the reset of the timer. Please read chapter 8 of the data sheet and use the capture/compare/PWM module to reset the timer on the appropriate value.

The worst thing that could still happen is some jitter, just because the ISR might have different latencies. But the timer runs as exactly as your system's crystal. In average your LED will blink correctly.

Anyway, if your timing requirements are not that hard, consider to live with some inaccuracy. Then use the most simple solution you like best.

  • Related