Count seconds and minutes with MCU timer/interrupt

2019-06-03 08:21发布

I am trying to figure out how to create a timer for my C8051F020 MCU. The following code uses the value passed to init_Timer2() with the following formula:

65535-(0.1 / (12/2000000)=48868.

I set up the timer to count every time it executes and for every 10 counts, count one second. This is based on the above formula. 48868 when passed to init_Timer2 will produce a 0.1 second delay. It would take ten of them per second. However, when I test the timer it is a little fast. At ten seconds the timer reports 11 seconds, at 20 seconds the timer reports 22 seconds. I would like to get as close to a perfect second as I can.

Here is my code:

#include <compiler_defs.h>
#include <C8051F020_defs.h>

void init_Clock(void);
void init_Watchdog(void);
void init_Ports(void);
void init_Timer2(unsigned int counts);
void start_Timer2(void);
void timer2_ISR(void);

unsigned int timer2_Count;
unsigned int seconds;
unsigned int minutes;

int main(void)
{
    init_Clock();
    init_Watchdog();
    init_Ports();
    start_Timer2();

    P5 &= 0xFF;

    while (1);
}

//=============================================================
//Functions
//=============================================================
void init_Clock(void)
{
    OSCICN = 0x04;  //2Mhz
    //OSCICN = 0x07;    //16Mhz
}

void init_Watchdog(void)
{
    //Disable watchdog timer
    WDTCN = 0xDE;
    WDTCN = 0xAD;
}

void init_Ports(void)
{
    XBR0    = 0x00;
    XBR1    = 0x00;
    XBR2    = 0x40;

    P0      = 0x00;
    P0MDOUT = 0x00;
    P5      = 0x00; //Set P5 to 1111
    P74OUT  = 0x08; //Set P5 4 - 7 (LEDs) to push pull (Output)
}

void init_Timer2(unsigned int counts)
{
    CKCON = 0x00;   //Set all timers to system clock divided by 12

    T2CON = 0x00;   //Set timer 2 to timer mode

    RCAP2 = counts; 

    T2 = 0xFFFF;    //655535

    IE |= 0x20;     //Enable timer 2

    T2CON |= 0x04;  //Start timer 2
}

void start_Timer2(void)
{
    EA = 0;

    init_Timer2(48868);

    EA = 1;
}

void timer2_ISR(void) interrupt 5
{    
    T2CON &= ~(0x80);

        P5 ^= 0xF0;

        timer2_Count++;

        if(timer2_Count % 10 == 0)
        {
            seconds++;
        }

        if(seconds % 60 == 0  && seconds != 0)
        {
            minutes++;
        }
}

4条回答
爷的心禁止访问
2楼-- · 2019-06-03 08:32

Open the datasheet for this specific microcontroller and look for the timetick interrupt. Find out how to issue an interrupt every n ms. Usually the formula for calculatinng this is depended on the clock speed, and It's often described in the datasheet/programming guide for the specific microcontroller.

查看更多
Rolldiameter
3楼-- · 2019-06-03 08:47

You are using internal oscillator. It is very inaccurate, not 2 mhz exactly.

查看更多
Fickle 薄情
4楼-- · 2019-06-03 08:48

There's likely nothing in your code that is causing the problem.

You are assuming that the internal oscillator is at exactly 2Mhz. Most likely, your micro isn't there. If you look at the datasheet's tolerances, you will see that when configured for 2Mhz, the spec'ed min is 1.5Mhz and the max is 2.4Mhz. So you could be running anywhere between 75% and 120% of your intended frequency. (This usually changes over temperature also...)

So the 10% error you are seeing in your counter could just be the hardware.

Do you have a second micro to flash your code into? It's osc may run at a different frequency and may give you a different sense of time.

Side note: I recommend changing

if(seconds % 60 == 0  && seconds != 0)
{
    minutes++;
}

to

if(seconds >= 60)
{
    seconds = 0;
    minutes++;
}

The equality check is less expensive in cycles than the modulo, and without clearing out the seconds your reported seconds will be incorrect after the first minute.

查看更多
ら.Afraid
5楼-- · 2019-06-03 08:50

I cannot take full credit for answering this question. Everyone contributed. I ended up using the external clock at 22.1184Mhz. The following snippets of code are the sections of the original post code that has changed to make it work:

void init_Clock(void)
{
    OSCXCN = 0x67;  //External 22.1184Mhz

    while ( !(OSCXCN & 0x80) );

    OSCICN = 0x88;
}

and

void start_Timer2(void)
{
    EA = 0;

    //External clock
    init_Timer2(47103);

    EA = 1;
}

From my tests this is very accurate.

查看更多
登录 后发表回答