1
\$\begingroup\$

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?

\$\endgroup\$
3
\$\begingroup\$

There does not appear to be anything wrong with your code. Here is the delay with a 4.000MHz clock using MPSIM:

enter image description here

Try putting a low impedance bypass capacitor and a larger capacitor in parallel (eg. 100nF ceramic and 100uF electrolytic) directly across the power (Vss-Vdd) to the microcontroller.

The section you quote refers to the prescaler counter, not the prescaler selection, so no big effect.

\$\endgroup\$
  • 1
    \$\begingroup\$ I added those two capacitors, and the delay is much better: about 505 ms, compared to my desired 500 ms. I'm now reading more about these "decoupling capacitors." I'm surprised this has such a big impact on the oscillator frequency. Could I perhaps use other values to get it even closer to the desired value? \$\endgroup\$ – Daniel Charles Jul 31 '15 at 5:24
  • \$\begingroup\$ The internal oscillator is only good to about 1%, so that sounds fine. A reminder to always use bypass capacitors! No, more capacitors won't have any effect. If you need better accuracy use a crystal (and consider improving your timing algorithm, but it's pretty good as-is). \$\endgroup\$ – Spehro Pefhany Jul 31 '15 at 5:25
  • 1
    \$\begingroup\$ Thanks for the help. This experience turned out to be a good lesson. When I first learned microcontrollers (with the 8051), we always used a development board with an onboard crystal, so I never had a chance to notice this problem. I just installed a 20 MHz crystal and got even better results. Looks like a crystal is indeed the way to go. \$\endgroup\$ – Daniel Charles Jul 31 '15 at 5:50
  • \$\begingroup\$ Also, I think the 100uF might be unnecessary in this situation. After adding those two capacitors (100nF film and 100uF electrolytic), I pulled out the 100uF and noticed no measurable change in the delay. (I was measuring frequency with a multimeter, and the reading didn't change at all.) Since my breadboard is cramped enough as is, I may just leave the 100uF capacitor out. Should I reconsider? Is that capacitor necessary? \$\endgroup\$ – Daniel Charles Jul 31 '15 at 6:09
  • \$\begingroup\$ Probably not, depending on your power source. I just wanted to give you the best chance with one test. \$\endgroup\$ – Spehro Pefhany Jul 31 '15 at 6:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.