搜索
bottom↓
回复: 12

Zero Cumulative Error MCU-based Real Time Clock

[复制链接]

出0入0汤圆

发表于 2010-6-16 07:16:43 | 显示全部楼层 |阅读模式
there are many real time clocks (RTC) out there. DS1307 for example is widely used for this purposes.

you can generate fairly accurate timing from mcu using delays, or NOP() through polling. However, they increase cpu load and aren't accurate.

you can also use timers and interrupts but interrupt overhead and latency can accumulate over time to create large timing drift.

one approach perfected by Roman Black recognizes that the cumulative timing errors have to be dealt with to generate precise timing. thus the Roman Black Zero Cumulative Error timing algorithm.

the concept is very simple: your timer will be running non-stop. and you have an accumulator that counts the number of "ticks" the timer has experienced. Once the accumulator has reached a pre-determined (and desired) number, you have reached your desired timing. so you subtract from the accumulated number and let the timing errors roll into the next round.

as such, a longer cycle is necessarily followed by a correspondingly shorter cycle. But over a long period of time, your time will never drift - other than that introduced by your oscillator.

the code for Roman Black RTC is very short and highly portable. mostly importantly, it maintains its timing over a long period of time - the timing accuracy depends on the crystal. a 10ppm crystal can be easily obtained at very low cost.

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2010-6-16 08:11:33 | 显示全部楼层
回复【楼主位】millwood0
-----------------------------------------------------------------------

楼主写过此代码?做过实验吗?

出0入0汤圆

 楼主| 发表于 2010-6-16 08:14:09 | 显示全部楼层
here is the code, on a 12F675.

============code===============

//signal generation. based on an approach proposed by roman black
//utilizes tmr0

#include <htc.h>
#include "gpio.h"

__CONFIG(MCLRDIS & BORDIS & WDTDIS & PWRTEN & INTIO);

#define IO_PORT                        GPIO
#define IO_DDR                        TRISIO
#define IO_PIN                        (1<<0)

#define TMR0_PS                        0b001                                //prescaler = 1<<(TMR0_PS+1)
#define Mhz                                000000ul
#define Fxtal                        4Mhz                                //crystal frequency
#define TICKS                        (Fxtal/4)                        //ticks per second
#define TIME_INTERVAL        100                                        //time interval, in ms
#define TMR_MAX                        (TIME_INTERVAL*TICKS/1000ul)        //tmr trigger

unsigned char tmr_flag=0;

void interrupt tmr0_isr(void) {
        static unsigned long tmr_count=0;
       
        T0IF=0;                                                                //clear the tmr flag
        tmr_count += 256<<(1+TMR0_PS);                                        //advance the tmr. tmr0 is 8bit
        if (tmr_count >= TMR_MAX) {
                tmr_count -= TMR_MAX;                        //retaining the error
                tmr_flag=1;                                                //set the tmr flag
        }
}

void tmr0_init(void) {                                        //initialize the tmr0
        T0CS=0;                                                                //tmr source is internal clock
        PSA=0;                                                                //prescaler assigned to tmr0
        PS2=(TMR0_PS & 0b100) >>2;                        //establish the prescaler
        PS1=(TMR0_PS & 0b010) >>1;
        PS0=(TMR0_PS & 0b001) >>0;
        T0IE=1;                                                                //turn on tmr0 interrupt
        GIE=1;                                                                //turn on global interrupt
}

void mcu_init(void) {
        ANSEL=0x00;                                                        //all pins gpio
        CMCON=0x07;                                                        //analog comparators off
        IO_OUT(IO_DDR, IO_PIN);                                //io_pin as output
        IO_CLR(IO_PORT, IO_PIN);                        //clear io_pin
}

void
main(void)
{
        mcu_init();                                                        //initialize the mcu
        tmr0_init();                                                //initialize the timer

        while (1){
                //TODO Auto-generated main function
                if (tmr_flag) {                                        //flag has been set
                        tmr_flag=0;                                        //reset the flag
                        IO_FLP(IO_PORT, IO_PIN);        //flip io_pin
                }
        }
}
=================================================

you can easily change it to another mcu. all it needed is to have a timer and interrupt capabilities. In this case, we used an 8-bit timer, but it can be easily a 16-bit timer as well.

Roman Black is actually multi-talented. a great guy to talk to as well.

出0入0汤圆

 楼主| 发表于 2010-6-16 08:19:23 | 显示全部楼层
you will need to specify two parameters:

1) Fxtal: the crystal frequency at which the mcu is running. in this case, 4Mhz.
2) TIME_INTERVAL: the interval between which the output pin flips. In this case, we wanted it to change once every 100ms.
3) output_pin: we are using GPIO0 (1<<0). You can specify multiple pins and to get complementary output.
4) prescaler: the higher the prescaler is, the lower the cpu load is but more jitter the output is. No impact on cumulative errors, however.

here is the sim.



(原文件名:12F675 RB RTC.PNG)

出0入0汤圆

 楼主| 发表于 2010-6-16 08:23:23 | 显示全部楼层
here is the version with complimentary output, on gpio0 and gpio1.

===========code===================

//signal generation. based on an approach proposed by roman black
//utilizes tmr0

#include <htc.h>
#include "gpio.h"

__CONFIG(MCLRDIS & BORDIS & WDTDIS & PWRTEN & INTIO);

#define IO_PORT                        GPIO
#define IO_DDR                        TRISIO
#define IO_PIN                        (IO_PIN0 | IO_PIN1)
#define IO_PIN0                        (1<<0)
#define IO_PIN1                        (1<<1)

#define TMR0_PS                        0b001                                //prescaler = 1<<(TMR0_PS+1)
#define Mhz                                000000ul
#define Fxtal                        4Mhz                                //crystal frequency
#define TICKS                        (Fxtal/4)                        //ticks per second
#define TIME_INTERVAL        100                                        //time interval, in ms
#define TMR_MAX                        (TIME_INTERVAL*TICKS/1000ul)        //tmr trigger

unsigned char tmr_flag=0;

void interrupt tmr0_isr(void) {
        static unsigned long tmr_count=0;
       
        T0IF=0;                                                                //clear the tmr flag
        tmr_count += 256<<(1+TMR0_PS);                                        //advance the tmr. tmr0 is 8bit
        if (tmr_count >= TMR_MAX) {
                tmr_count -= TMR_MAX;                        //retaining the error
                tmr_flag=1;                                                //set the tmr flag
        }
}

void tmr0_init(void) {                                        //initialize the tmr0
        T0CS=0;                                                                //tmr source is internal clock
        PSA=0;                                                                //prescaler assigned to tmr0
        PS2=(TMR0_PS & 0b100) >>2;                        //establish the prescaler
        PS1=(TMR0_PS & 0b010) >>1;
        PS0=(TMR0_PS & 0b001) >>0;
        T0IE=1;                                                                //turn on tmr0 interrupt
        GIE=1;                                                                //turn on global interrupt
}

void mcu_init(void) {
        ANSEL=0x00;                                                        //all pins gpio
        CMCON=0x07;                                                        //analog comparators off
        IO_OUT(IO_DDR, IO_PIN);                                //io_pin as output
        IO_CLR(IO_PORT, IO_PIN0);                        //clear io_pin0
        IO_SET(IO_PORT, IO_PIN1);                        //set io_pin1
}

void
main(void)
{
        mcu_init();                                                        //initialize the mcu
        tmr0_init();                                                //initialize the timer

        while (1){
                //TODO Auto-generated main function
                if (tmr_flag) {                                        //flag has been set
                        tmr_flag=0;                                        //reset the flag
                        IO_FLP(IO_PORT, IO_PIN);        //flip io_pin
                }
        }
}

出0入0汤圆

 楼主| 发表于 2010-6-16 08:32:58 | 显示全部楼层
I have it running on a 12F675 now.

my long(er) term goal is to make a clock display from it.

出0入0汤圆

发表于 2010-6-16 12:09:22 | 显示全部楼层
回复【5楼】millwood0
-----------------------------------------------------------------------

顶!

出0入0汤圆

发表于 2010-6-16 12:16:03 | 显示全部楼层
好像是时间片轮的方式吧?

论坛没有代码编排功能,看着好累。。

出0入0汤圆

 楼主| 发表于 2010-6-16 18:11:06 | 显示全部楼层
here is the heart of the code:

tmr_count += 256<<(1+TMR0_PS);         //advance the tmr. tmr0 is 8bit
if (tmr_count >= TMR_MAX) {
tmr_count -= TMR_MAX;         //retaining the error
tmr_flag=1;         //set the tmr flag
}

once the tmr0 (8-bit) overflows, the interrupt handler increments tmr_count by 256*prescaler. then it tests to see if it is more than TMR_MAX, the desired timer tick count. if it is, it means that we need to set the flag (tmr_flag) and let the  main() do something, and prepare the tmr_count for the next round by subtracting TMR_MAX from it. so if we overcounted (tmr_count is greater than TMR_MAX) in the last cycle, the error will accumulate to the next round, causing the flag to go up sooner (after fewer timer ticks).

出0入0汤圆

发表于 2010-6-16 19:34:02 | 显示全部楼层
回复【8楼】millwood0  
-----------------------------------------------------------------------

明白点了。。
能否把句子做下断行,英文的话这个论坛处理不好。。

出0入0汤圆

发表于 2010-6-17 09:59:28 | 显示全部楼层
10ppm 晶振也不便宜吧,呵呵! 软件RTC,硬件RTC。。。。

出0入0汤圆

发表于 2010-6-17 11:09:07 | 显示全部楼层
学习了~~

出0入264汤圆

发表于 2010-6-17 11:10:19 | 显示全部楼层
学习了。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-16 02:11

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表