TL; DR: Vea las últimas líneas (y la cita) en la parte inferior.
Solo una palabra sobre mi experiencia: aprendí los microcontroladores el último semestre con el 8051 y he estado aprendiendo los microcontroladores PIC durante el verano. He pasado la mayor parte de mi verano trabajando con el PIC12F683, y apenas estoy empezando a aprender el PIC16F628A. Todo esto es para decir que no soy totalmente nuevo para los microcontroladores o PIC.
Como acabo de comenzar en el 16F628A, decidí comenzar por parpadear un LED. (Estoy usando un PICkit 3 y MPLAB X IDE v3.00 en Linux Mint 17.) Para hacerlo, quería usar el temporizador 1 para crear un retraso de 500 ms. El retraso está sustancialmente fuera de 500 ms, y quiero entender por qué.
Dado que no hay una función de recarga automática (como en el 8051), mi idea fue usar un precalculador 1: 8. Luego, cada vez que el temporizador 1 se desborda, inicialícelo en 0x0BDB. Ese valor crea un intervalo de 0xFFFF - 0x0BDB = 0xF424
antes del desbordamiento (o 62,500, en decimal).
Ya que estoy usando el reloj interno (4 MHz) y el Temporizador se incrementa como FOSC / 4, y estoy usando un preescalador de 1: 8, el período de demora debería ser:
1 / (4 MHz / 4 / 8 / 62,500) = 0.5 sec
Mi LED está parpadeando, pero el retraso no es de 0.5 segundos. Usé mi multímetro para medir la frecuencia, y el resultado se resuelve en un período de aproximadamente 0.417 seg. Además, esto funciona aproximadamente a 144bpm, y cuando configuro un metrónomo a 144bpm, coincide con el LED parpadeante.
Las únicas cosas conectadas a mi placa son Vss (pin 5), Vdd (pin 14), un LED (con una resistencia limitadora de corriente de 10 kΩ) en el pin 6 (RB0) y una resistencia de 47 kΩ entre MCLR y Vdd ( pines 4 y 14). (... y mi PICkit.)
Aquí está mi código:
#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: Sé que detener un temporizador, volver a cargarlo y reiniciarlo (en general) causará algún retraso, pero ¿por qué el PIC acortando mi ¿Intervalo de 500 ms por más de 80 ms?
Creo que he encontrado una pista de por qué esto no funciona, pero no entiendo muy bien los detalles. A partir de la p.53 de la hoja de datos:
7.7 Timer1 Prescaler
El contador del prescaler se borra al escribir en el
Registros TMR1H o TMR1L.
¿Qué significa esto exactamente? ¿El prescaler se convierte en un valor distinto de 1: 8 durante un período de tiempo cuando escribo en TMR1H y TMR1L?
EDIT: También he intentado usar la función Comparar del módulo CCP, y el retraso sigue siendo el mismo valor inexacto. ¿Podría tener un chip malo? ¿Podría el oscilador interno realmente estar tan lejos?