TL;DR: See the last few lines (and the quote) at the bottom.
Just a word about my background: I learned microcontrollers last semester with the 8051 and have been learning PIC microcontrollers over the summer. I've spent most of my summer working with the PIC12F683, and I'm just starting to learn the PIC16F628A. This is all to say that I'm not totally new to microcontrollers or PICs.
Since I'm just starting out on the 16F628A, I decided to start by blinking a LED. (I'm using a PICkit 3 and MPLAB X IDE v3.00 on Linux Mint 17.) To do so, I wanted to use Timer 1 to create a 500ms delay. The delay is substantially off from 500ms, and I want to understand why.
Since there's no auto-reload feature (like on the 8051), my idea was to use a 1:8 prescaler. Then, every time Timer 1 overflows, initialize it to 0x0BDB. That value creates an interval of 0xFFFF - 0x0BDB = 0xF424
before the overflow (or 62,500, in decimal).
Since I'm using the internal clock (4 MHz), and the Timer increments as FOSC/4, and I'm using a 1:8 prescaler, the period of the delay should be:
1 / (4 MHz / 4 / 8 / 62,500) = 0.5 sec
My LED is blinking, but the delay is not 0.5 seconds. I used my multimeter to measure the frequency, and the result works out to a period of about 0.417 sec. Also, this works out to about 144bpm, and when I set a metronome to 144bpm, it matches with the blinking LED.
The only things wired to my breadboard are Vss (pin 5), Vdd (pin 14), an LED (with a 10kΩ current-limiting resistor) on pin 6 (RB0), and a 47kΩ resistor between MCLR and Vdd (pins 4 and 14). (...and my PICkit.)
Here is my code:
#include <xc.h>
#pragma config FOSC = INTOSCIO //oscillator selection; use internal oscillator
#pragma config WDTE = OFF //disable watchdog Timer
#pragma config PWRTE = OFF //power-up timer disabled
#pragma config MCLRE = OFF //use MCLR pin (internally tied to Vdd) as a digital input
#pragma config BOREN = OFF //disable Brown-Out Reset
#pragma config LVP = OFF //disable Low-Voltage Programming
#pragma config CPD = OFF //data code protection disabled
#pragma config CP = OFF //code protection disabled
bit LEDstatus;
void main(void)
{
LEDstatus=1; //initialize LED off (active-low)
TRISB0=0; //use RB0 as digital output
T1CON=0b00110000; //Timer 1 off; 1:8 prescaler; use internal clock
TMR1H=0x0B; //initialize Timer 1 to get a 500ms delay
TMR1L=0xDB;
INTCONbits.PEIE=1; //enable peripheral interrupts (i.e., Timer 1)
PIR1bits.TMR1IF=0; //clear Timer 1 interrupt flag
PIE1bits.TMR1IE=1; //enable Timer 1 interrupts
INTCONbits.GIE=1; //enable all interrupts
T1CONbits.TMR1ON=1; //turn on Timer 1
while(1); //wait for interrupts
return;
}
void interrupt ISR()
{
if (PIR1bits.TMR1IF==1)
{
PIR1bits.TMR1IF=0; //clear interrupt flag
T1CONbits.TMR1ON=0; //turn off Timer 1
TMR1H=0x0B; //initialize Timer 1 for 500ms delay
TMR1L=0xDB;
T1CONbits.TMR1ON=1; //turn on Timer 1
LEDstatus=~LEDstatus; //toggle LED
RB0=LEDstatus;
}
}
TL;DR: I know that stopping a timer, reloading it, and restarting it will (in general) cause some delay, but why is the PIC shortening my 500ms delay interval by over 80ms?
I think I've found a hint as to why this isn't working, but I don't quite understand the details. From p.53 of the datasheet:
7.7 Timer1 Prescaler
The prescaler counter is cleared on writes to the
TMR1H or TMR1L registers.
What exactly does this mean? Does the prescaler become some value other than 1:8 for some amount of time when I write to TMR1H and TMR1L?
EDIT: I've also just tried using the Compare function of the CCP module, and the delay is still the same inaccurate value. Could I have a bad chip? Could the internal oscillator really be that far off?