Search code examples
timerembeddedcpu-registers

Timer0 count register on PIC18F47K42 increments faster than expected


I'm new to embedded and I have a little problem. I think the problem is my code. But I have gone over it dozens of times and I can't find the glitch.

I have a Timer0 which I can program in 16bits (can count up to 65536). There is a register bit TMR0L and TMR0H which increment at each clock edge or a multiple of clock signal. I want it to increment, let's say, at each 0.00001s.

According to my DATASHEET, I put up these settings:

OSCFRQ = 0x02; //--- HFFRQ 4_MHz
T0CON0 = 10010000; //--- Module Enabled; Timer is 16bits; 1:1 postscaler
T0CON1 = 01010101; //--- Fosc/4; 1:32 prescaler

I'm not extrodinary at maths, but I can certainly do basic aritmethics. My clock is at 4Mhz. I use the clock/4 as an input to my Timer0. So this gives a 1MHz frequency. 1000000/32 = 31250Hz, giving 0.000032 seconds per count. A millisecond (0.001/0.000032 = 31.25counts), so in order to have a miliseconds, I'd have to count approximately 31 times with those parameters. Right?

//Delay function that can delay from 1 milisecond to 2000 miliseconds.
//Uses timer0.

void countDelay(int ms_delay)
{
    //unsigned int oscFreq = ((1<<(00001111&OSCFRQ))*1000000)/4;
    //unsigned int Prescaler = (1<<(00001111&T0CON1));

    unsigned int oscFreq = 4000000;
    unsigned int Prescaler = 32;
    float countTime = (Prescaler/(oscFreq/4)); 

    int countsNum = (int)(((ms_delay/1000)/countTime));

    char endCountDelay = 0;
    TMR0L = 0x00;
    TMR0H = 0x00;
    unsigned int Time16 = 0x0000;

    while(endCountDelay == 0)//PORBLEM
    {
        Time16 = 0;
        Time16 |= TMR0L;
        Time16 |= (TMR0H<<8);
        if (Time16 >= countsNum)
        {
            endCountDelay = 1;
        }
    }

}

And my main code is just the code below. It make a LED blink. I want to make it blonk each seconds. So 31250 counts. That is not a problem, because I checked with another function and the timer is Really 16bits. It counts up to 65k.

void main(void)
{
    // Initialize the device
    SYSTEM_Initialize();

    while (1)
    {

        countDelay(1000);
        LATA0 = 0;
        countDelay(1000);

        LATA0 = 1;

    }
}

With that code, I can see a LED always ON. With the oscilloscope, I check the signal:141.76Hz We should have seen 0.5Hz, having a cycle period of 2seconds, that's 0.5Hz.

So, in brief, we are 283 time too high. Which is close to the 256 of 2^8. So I believe it's an error in my code. Maybe something in my delay function?. Anyone has an idea?

EDIT#1: I have made other tests. I changed the value of my variabes. Doesn't change the outcome. The Signal stays 141Hz, +/- 10Hz. Even if x16 the clock speed.

Changing the prescalar value does almost the same. The signal stays this time, EXACTLY, at 141.76Hz.

EDIT#2: I used the debugger inside my pickit. It looks like when I do this.

int countsNum = (((ms_delay/1000)/(Prescaler/(oscFreq/4))));

The result is 0. Any idea why? It should not.

EDIT#3: It gives me rather 251 milions when I use the long type.

Edit#4: verified clock speed. It is okay. However, this calculation, even if all integers doen't work. The answer to countsNum is 74, but it should be 15.

unsigned int ms_delay = 500;
unsigned long oscFreq = 4000000;
unsigned long Prescaler = 32768;
unsigned int countsNum = ((ms_delay)/((Prescaler)/(oscFreq/4000)));

Solution

  • Its hard to point a finger at the exact problem here. We need to verify a few hardware assumptions first:

    Your master clock is actually running at 4 MHz.

    Your timer is actually counting at 1MHz. Double check the prescalar settings.

    Now, assuming the hardware clocks check out, I see an issue with your time->counts calculation.

    int countsNum = (((ms_delay/1000)/(Prescaler/(oscFreq/4))));
    

    Lets evaluate this line.

    ms_delay is the argument passed in to specific a variable-time delay. It looks like this is in units of ms. But, don't forget we are doing integer maths! So for any value below 1000, ms_delay/1000 will evaluate to ZERO.

    You may need to investigate floating point calculations. Or you need to change the way countsNum is calculated to prevent the division from returning zero.

    @Edit 4:

    unsigned int ms_delay = 500;
    unsigned long oscFreq = 4000000;
    unsigned long Prescaler = 32768;
    unsigned int countsNum = ((ms_delay)/((Prescaler)/(oscFreq/4000)));
    

    Let's evaluate this with integers.

    countsNum = (500)/((32768/(4000000/4000))
    countsNum = (500)/((32768)/(1000))
    countsNum = (500)/(32)
    countsNum = 15
    

    Even if all of these numbers use floating point, the result is 15.259. And you are getting 74 instead?

    The PIC18F47K42 is an 8 bit part, and you are using 'long' values, which makes them 16 bits wide. So the range of these variables are [0, 65535] for unsigned, or [-32768, 32767] for signed. If any result of this arithmetic exceeds this range, the values wrap around. You are probably overflowing or underflowing the variable widths for this calculation. Try including and use types int32_t or uint32_t. If this fixes your problem, it indicates a overflow/underflow issue. You can try to do some compile-time expressions if this proves the issue.

    @comment 2: I re-read the question. I think we may have made this too complicated. AS stated above, your 16 bit timer has a 4MHz clock, with a prescalar of 4, leaving us with an effective 1MHz clock. This has a period of 1 microsecond. With a 16 bit counter, this means we overflow at:

     65536 * 1uS = .065535 seconds = 65.536 millisecond
    

    Now, if we want to find the appropriate amount of counts to run 1 millisecond, then how many counts of 1 microsecond do we need to wait til we have 1 millisecond? Intuitively, we need 1000 microseconds to get 1 millisecond.

    So, we are left with how many ticks @ 1 microsecond do we need to get 1 millisecond? Let's work backwards and then put it in terms of the variables given:

    .001 = ntics * (1 / 1000000)
    .001 = ntics * (1 / (4000000/4))
    .001 = ntics * (1 / (timer_clk / timer_psc))
    -so-
    .001 * 1000000 = ntics
    1000 = ntics
    

    Does this solve your issue?