模吧

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

4062查看 | 20回复

[DIY交流] 不准的时钟

[复制链接]
发表于 2020-3-27 22:51:52 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本帖最后由 TIGGER 于 2020-3-27 23:04 编辑

        经过周六、周日两天时间,终于完工了第一个自制时钟

       不准的时钟 新手上路 作者:TIGGER 4484

       不准的时钟 新手上路 作者:TIGGER 1239

  数码管:  这个带时钟点的数码管,翻来覆去,用万用表测了好几次,才把管脚弄明白。两个时钟点引脚,阳极是2,阴极是dp。需要开启大电流,才能正常显示。

  由于对时不方便,增加了两个按键,一个控制小时,另一个控制分钟,实现了简易的对表功能。

  尽管在程序中调整了好多次,走时误差依然较大,每次烧录程序时,单片机的频率也有微小变化,所以精度一直没调上去。

  电路图:
       不准的时钟 新手上路 作者:TIGGER 4117

  代码如下,仅供参考,有不对的,还请大家帮忙指正。
#include <intrins.h>
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
sfr P2M0=0x96;  //声明 P2引脚模式寄存器
sfr P3M0=0xb2;  //声明 P3引脚模式寄存器

sbit a=P3^5;
sbit b=P2^1;
sbit c=P2^3;
sbit d=P3^3;
sbit e=P3^2;
sbit f=P3^6;
sbit g=P2^4;
sbit dp=P2^2;

sbit seg1=P3^4;
sbit seg2=P3^7;
sbit seg3=P2^0;
sbit seg4=P2^5;

sbit key1=P1^6;
sbit key2=P1^5;
uchar key1_action;
uchar key1_old;
uchar key2_action;
uchar key2_old;

void Delay1ms()                //@12.000MHz
{
        uchar i, j;
        
        i = 12;
        j = 169;
        do
        {
                while (--j);
        } while (--i);
}

void Delay_n_ms(uint n) //延时n毫秒函数
{
        while(n)         
        {
                Delay1ms();
                n=n-1;//每循环一次n减小1
        }   
}

void display(unsigned char x)//控制数码管显示内容的函数
{
        //判断x的值来决定显示什么
        if(x==0){a=0;b=0;c=0;d=0;e=0;f=0;g=1; }//显示“0”

        if(x==1){a=1;b=0;c=0;d=1;e=1;f=1;g=1;} //显示“1”

        if(x==2){a=0;b=0;c=1;d=0;e=0;f=1;g=0;} //显示“2”

        if(x==3){a=0;b=0;c=0;d=0;e=1;f=1;g=0;} //显示“3”

        if(x==4){a=1;b=0;c=0;d=1;e=1;f=0;g=0;} //显示“4”

        if(x==5){a=0;b=1;c=0;d=0;e=1;f=0;g=0;} //显示“5”

        if(x==6){a=0;b=1;c=0;d=0;e=0;f=0;g=0;} //显示“6”

        if(x==7){a=0;b=0;c=0;d=1;e=1;f=1;g=1;} //显示“7”

        if(x==8){a=0;b=0;c=0;d=0;e=0;f=0;g=0;} //显示“8”

        if(x==9){a=0;b=0;c=0;d=0;e=1;f=0;g=0;} //显示“9”

}

void main()

{
        unsigned int h1,h2,m1,m2,minute,second,m,h;
        P2M0=0x3F;
        P3M0=0xF4;
        
        h1=0;h2=0;m1=0;m2=0;m=0;h=9;
        h1=h/10;
        h2=h%10;
        key1_old=0; //按键没按下去,是低电平
        key2_old=0;

        while(1)
        {        
                if(h1==1)
                {
                seg1=1;seg2=0;seg3=0;seg4=0;
                display(h1);
                Delay_n_ms(1);
                }

                seg1=0;seg2=1;seg3=0;seg4=0;
                display(h2);
                Delay_n_ms(1);

                seg1=0;seg2=0;seg3=1;seg4=0;
                display(m1);
                Delay_n_ms(1);

                seg1=0;seg2=0;seg3=0;seg4=1;
                display(m2);
                Delay_n_ms(1);
               
                if(key1==0)
                {
                        if(key1_old==1)
                        {
                                key1_action=1;
                                m=m+1;
                        }
               
                }
                key1_old=key1;
                m1=m/10;
                m2=m%10;

                if(key2==0)
                {
                        if(key2_old==1)
                        {
                                key2_action=1;
                                h=h+1;
                        }
               
                }
                key2_old=key2;
                h1=h/10;
                h2=h%10;
                if(h>12){h=0;}

                second=second+1;
                if(second>310)  //1、每1秒慢27.8ms,调整为325-7=318,2、慢32.59ms/S,调整为318-8=310
                {
                        second=0;
                        dp=~dp;
                        minute=minute+1;
                        if(minute>60)
                        {
                                minute=0;
                                m=m+1;
                                m1=m/10;
                                m2=m%10;

                                if(m>60)
                                {
                                        m=0;

                                        h=h+1;
                                        h1=h/10;
                                        h2=h%10;
                                        if(h>12){h=0;}
                                }
                        }
                }


        
        }
}               


发表于 2020-4-28 13:11:30 | 显示全部楼层
时钟需要使用外部高精度晶振,及时程序写在中断里面。也可以外挂一个RTC芯片,可以连万年历都有了
回复 支持 0 反对 1

使用道具 举报

发表于 2020-3-31 13:40:14 | 显示全部楼层
本帖最后由 田不辣 于 2020-3-31 13:43 编辑

我之前也做过,其实跟频率不准有一定的关系,最主要的是要用内部中断走时,你用main函数写,Delay时间会延长。而且代码运行本来也是需要花时间的。
用外部晶振会稍微准点,再者用内部中断。你用定时器0写走时程序,定时器2写显示程序。主程序只写初始化,循环验证个低电压,蜂蜜器啥的。这样代码也简单,程序也准确。
回复 支持 1 反对 0

使用道具 举报

发表于 2020-3-28 12:01:35 | 显示全部楼层
感谢分享请联系moz8com领取论坛赠品
回复 支持 反对

使用道具 举报

发表于 2020-3-28 13:39:57 | 显示全部楼层
34.44354354135753735735735537357357
回复 支持 反对

使用道具 举报

发表于 2020-3-28 14:27:23 | 显示全部楼层
楼主牛皮
回复 支持 反对

使用道具 举报

发表于 2020-3-28 17:38:15 | 显示全部楼层
厉害了不准的时钟 新手上路 作者:XZM1 7124 不准的时钟 新手上路 作者:XZM1 3219
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-28 21:48:54 | 显示全部楼层
本帖最后由 TIGGER 于 2020-3-28 21:53 编辑

2、进一步对时钟进行校准

第一轮时钟精度很低,误差达到27MS/S,为了进一步提高精度,做了如下改进:

首先,采用中断的方式,产生时钟频率。即程序编写中采用T0定时器的方式2,即8位定时器,自动重装初值,以降低用软件反复设置初值,带来的误差。

定时器设置:TL0初值设为6,每250次,产生一次中断。在晶振频率12M下,为250us中断一次。程序中累计4000次为1S。烧录程序时,设定频率12.000MHz,实际烧录频率为12.011MHz。经校准,程序累计次数改为3999后,时钟精度达到:0.2ms/s(手动测量,精度有限),即每小时误差0.72s,一天误差17.28s。

另外,发现两处计算错误,一处为秒循环,一处为分钟循环。应该循环到59,清零,而不是60。

程序如下,如有更好的提高时钟精度的方法,希望加入讨论。

#include <intrins.h>
#include <reg52.h>
#define uchar unsigned char
#define uint unsigned int
sfr P2M0=0x96;  //声明 P2引脚模式寄存器
sfr P3M0=0xb2;  //声明 P3引脚模式寄存器

sbit a=P3^5;
sbit b=P2^1;
sbit c=P2^3;
sbit d=P3^3;
sbit e=P3^2;
sbit f=P3^6;
sbit g=P2^4;
sbit dp=P2^2;

sbit seg1=P3^4;
sbit seg2=P3^7;
sbit seg3=P2^0;
sbit seg4=P2^5;

sbit key1=P1^6;
sbit key2=P1^5;
uchar key1_action;
uchar key1_old;
uchar key2_action;
uchar key2_old;
unsigned int second;

void Delay1ms()                //@12.000MHz
{
        uchar i, j;
        
        i = 12;
        j = 169;
        do
        {
                while (--j);
        } while (--i);
}


void Delay_n_ms(uint n) //延时n毫秒函数
{
        while(n)         
        {
                Delay1ms();
                n=n-1;//每循环一次n减小1
        }   
}


void display(unsigned char x)//控制数码管显示内容的函数
{
        //判断x的值来决定显示什么
        if(x==0){a=0;b=0;c=0;d=0;e=0;f=0;g=1; }//显示“0”
        
        if(x==1){a=1;b=0;c=0;d=1;e=1;f=1;g=1;} //显示“1”
        
        if(x==2){a=0;b=0;c=1;d=0;e=0;f=1;g=0;} //显示“2”
        
        if(x==3){a=0;b=0;c=0;d=0;e=1;f=1;g=0;} //显示“3”

        if(x==4){a=1;b=0;c=0;d=1;e=1;f=0;g=0;} //显示“4”
        
        if(x==5){a=0;b=1;c=0;d=0;e=1;f=0;g=0;} //显示“5”
        
        if(x==6){a=0;b=1;c=0;d=0;e=0;f=0;g=0;} //显示“6”
        
        if(x==7){a=0;b=0;c=0;d=1;e=1;f=1;g=1;} //显示“7”
        
        if(x==8){a=0;b=0;c=0;d=0;e=0;f=0;g=0;} //显示“8”
        
        if(x==9){a=0;b=0;c=0;d=0;e=1;f=0;g=0;} //显示“9”

}

void main()

{
        unsigned int h1,h2,m1,m2,minute,m,h;
        P2M0=0x3F;
        P3M0=0xF4;

        h1=0;h2=0;m1=0;m2=0;m=0;h=9;
        h1=h/10;
        h2=h%10;
        key1_old=0; //按键没按下去,是低电平
        key2_old=0;
        
        TMOD=0x02;//使用T0中断的模式2,自动装初值,减小时钟误差
        TH0=6;   //装初值,
        TL0=6;
        EA=1;
        ET0=1;
        TR0=1;

        while(1)
        {        
                if(h1==1)
                {
                seg1=1;seg2=0;seg3=0;seg4=0;
                display(h1);
                Delay_n_ms(1);
                }


                seg1=0;seg2=1;seg3=0;seg4=0;
                display(h2);
                Delay_n_ms(1);

                seg1=0;seg2=0;seg3=1;seg4=0;
                display(m1);
                Delay_n_ms(1);

                seg1=0;seg2=0;seg3=0;seg4=1;
                display(m2);
                Delay_n_ms(1);
               
                if(key1==0)
                {
                        if(key1_old==1)
                        {
                                key1_action=1;
                                m=m+1;
                        }
               
                }
                key1_old=key1;
                m1=m/10;
                m2=m%10;


                if(key2==0)
                {
                        if(key2_old==1)
                        {
                                key2_action=1;
                                h=h+1;
                        }
               
                }
                key2_old=key2;
                h1=h/10;
                h2=h%10;
                if(h>12){h=1;}
               
                if(second>3999)  //中断一次250us,4000次,1秒(1、慢0.4814MS/S,增减少计数:481/250=1.9256,2、快0.2023MS/S,增加计数:202.3/250=0.8091,不到1 )
                {
                        second=0;
                        dp=~dp;
                        minute=minute+1;
                        if(minute>59)   //60次,1分钟
                        {
                                minute=0;
                                m=m+1;
                                m1=m/10;
                                m2=m%10;


                                if(m>59)  //60次,1小时
                                {
                                        m=0;


                                        h=h+1;
                                        h1=h/10;
                                        h2=h%10;
                                        if(h>12){h=1;}
                                }
                        }
                }

        
        }
}


void T0_time() interrupt 1               

{

        second=second+1;

}



回复 支持 反对

使用道具 举报

发表于 2020-3-29 16:09:28 | 显示全部楼层
lz有恒心   鼓励
回复 支持 反对

使用道具 举报

发表于 2020-3-30 12:41:15 | 显示全部楼层
哈哈,要上时钟芯片才行啊
回复 支持 反对

使用道具 举报

发表于 2020-3-31 14:33:08 | 显示全部楼层
还记得上大学弄过这个,还是用汇编写的
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-3-31 20:56:37 | 显示全部楼层
田不辣 发表于 2020-3-31 13:40
我之前也做过,其实跟频率不准有一定的关系,最主要的是要用内部中断走时,你用main函数写,Delay时间会延 ...

谢谢!程序又修改了一下,把显示部分的延时去掉了,全部用T0中断来控制。但还是不理想。另外,想外接一个时钟晶振,但时钟晶振频率是32.768KH ,与单片机的机器周期(12),无法进行15分频得到秒。
回复 支持 反对

使用道具 举报

发表于 2020-3-31 22:16:45 | 显示全部楼层
加油,完美了可以用泰勒公式去计算周几
回复 支持 反对

使用道具 举报

发表于 2020-4-1 00:02:33 | 显示全部楼层
加个外部晶振不香么
回复 支持 反对

使用道具 举报

发表于 2020-4-1 01:04:23 | 显示全部楼层
用ESP8266他不香吗,都不用你对时,自己网络获取时间
回复 支持 反对

使用道具 举报

发表于 2020-4-3 11:44:38 | 显示全部楼层
我也考虑过自己做一个,你可以再加一个6块钱的时钟芯片,一天误差0.45秒以内,我属于那种可以折腾,但折腾后这东西要可以长久稳定运行,我会弄个8266wifi模块矫时或者gps模块校对,之后用个弄个电池作为备用电源接移动电源板
回复 支持 反对

使用道具 举报

发表于 2020-4-3 18:13:23 | 显示全部楼层
准不准无所谓,关键是学习过程
回复 支持 反对

使用道具 举报

发表于 2020-4-3 19:16:28 | 显示全部楼层
单片机学习都是这样开始的,反正初级阶段还是要学习一下
回复 支持 反对

使用道具 举报

发表于 2020-4-4 20:32:19 | 显示全部楼层
不知道提高芯片工作频率能不能提高一点精度?
回复 支持 反对

使用道具 举报

发表于 2020-4-6 17:24:58 | 显示全部楼层
本帖最后由 田不辣 于 2020-4-6 17:46 编辑

看你一直在更新这个帖子,特别看了你的代码,你参考:1、定时器选为12T,最长单次中断是60ms,这样中断次数很少了。
2、走时计算尽量写在中断里面,原因是你的中断second变量已经变成N+X了,但是main函数还在运行second的第N次,可以把时间运算全部放在中断0中。
3、display放到定时器2里面,每20ms切换一次显示位(具体是不是20MS不记得了),这样来保证显示准确。
4、主函数除了按钮程序,尽量空出来。其实按钮函数可以一并放在中断2里面(这个看个人,但是这样就可以不再使用delay函数,delay函数一般只是程序初始化的时候使用)。
5、变量定义过多,可以将key,keyaction设为bit型,减少U16类型(比如second),将hour,min,second设为全局变量(u8即可)!
这样一个定时器管走时,一个定时器管显示。相对独立,各不干扰。程序也相对轻便。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2020-4-27 21:05:42 | 显示全部楼层
田不辣 发表于 2020-4-6 17:24
看你一直在更新这个帖子,特别看了你的代码,你参考:1、定时器选为12T,最长单次中断是60ms,这样中断次数 ...

       根据MR.田的建议,将走时的计算,写进了T0中断程序里,第一次测试,中断4000次为1s,经24小时测量,快1.9MS/S; 经再次计算,调整为中断4007次为1s后,走时精度达到了一天快2秒,即 0.023 MS/S。上一次的最好成绩是0.2MS/S, 此次走时精度提高了10倍。
在此对MR.甜不辣 表示感谢!!!


中断程序代码如下:

void T0_time() interrupt 1
{

        us=us+1;
        ds=ds+1;

                if(us>(4007))  //中断一次250us,4000次,1秒(测量1、快1.9MS/S,增计数:1900/250=7.6;    测量2、快0.023MS/S)
                {
                        us=0;
                        dp=~dp;
                        s=s+1;
                        if(s>59)   //60次,1分钟
                        {
                                s=0;
                                m=m+1;
                                m1=m/10;
                                m2=m%10;

                                if(m>59)  //60次,1小时
                                {
                                        m=0;

                                        h=h+1;
                                        h1=h/10;
                                        h2=h%10;
                                        if(h>12){h=1;}
                                }
                        }

                }
}

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|关于模吧|APP下载|广告报价|手机版|企业会员|商城入驻|联系我们|模吧 ( 黔ICP备2022002348号-1 )

© 2013-2020 Moz8.com 模吧,玩出精彩!