制作出来的万年历实物图如下,走时非常精准:
提醒:
1、所有标 C1、C2、C3、R1的元件先不用安装,主要为抗干扰预留,若在使用中不正常才可安装。
2、所有电阻阻值前标“*”表示该电阻可根据LED元件性能或个人使用习惯浮动调节。
3、如果使用mini_USB,需要一定的焊接功夫。可先将焊锡加在5个引脚焊盘上。
4、不能使用劣等供电电源,板子上很多芯片都怕高频干扰!
注意:
1、在有GPS连接的情况下,+、-、EXIT 无效,短按MOD可强制GPS校时。
2、冒号闪烁开关、整点报时开关变量保存入24C02,断电不丢失。
3、调时中,秒不可调整。调到分钟时秒自动回零。
4、调时中可随时按EXIT不保存退出,也可以一直按MOD保存退出。
P0 :DS12C887-AD
//max7219
sbit DIN = P1^0;
sbit LOAD = P1^1;
sbit CLK = P1^2;
//控制冒号是否闪烁
sbit BLK = P1^3;
//按键 MOD + - EXIT
sbit K1 = P1^4; //K1-调时设置,长按进入时间设置;短按作为日期时间下一项。当gps有效时,短按以gps时间校时
sbit K2 = P1^5; //K2-调时中作+, 运行中作为报时
sbit K3 = P1^6; //K3-调时中作-, (运行中可留作12/24模式切换)
sbit K4 = P1^7; //K4-调时中作确认、返回;运行中作是否报时选择键
//DS3231,24C02
sbit SCL= P2^0;
sbit SDA= P2^1;
P2^2 : DHT11
//语音报时指示灯
sbit SND = P2^3;
//DS12C887 控制IO
sbit DS = P2^4; //兼做电波钟BPC的P0控制
sbit RW = P2^5;
sbit AS = P2^6;
sbit CS = P2^7;
P3^0 :串口RXD
P3^1 :串口TXD
//DS3231 SQW
sbit INT0= P3^2;
//PWM语音芯片
sbit BUSY = P3^3;
sbit DATA = P3^4;
sbit RST = P3^5;
P3^6 : 红外;
P3^7 : DS18B20;
供电电路:
一、电源预留宽电压输入降压:
1、1117-5
2、7805
若有5V电源,可以将降压芯片输入输出短接。
二、电源输入滤波部分可根据使用的电源质量酌情考虑
三、1.5KE68CA为双向过压保险设计,可无视!
四、部分器件使用3.3V电压,因此使用1117-3.3。输出必须22uF和0.1uF滤波。
24C02是存储冒号是否闪烁及整点报时开关
万年历语音报时芯片与τ 宝原版芯片不一样,地址向后错一位。
原版芯片开机送电产生脉冲即会播报第一个地址“零”,新版定制芯片把第一个地址留空。而且在地址最后额外增加了3个语音:猫叫、老鼠叫、猫叫老鼠叫混合。
语音芯片有3个接口:
SPK1_1、SPK1_2并联,哪个使用方便就接哪个。喇叭建议8Ω/0.25--0.5W
SPK2_1 为放大语音,一般不建议使用。
单片机源程序如下:
- //89C52RC + 11.0592M
- //语音报时,手动调时,gps授时,电脑校时
- //GPS有效信号(VA)稳定达10分钟后自动校时
- //电脑可串口发送数据强制校时 GPRMC,024813.640,,,,,,,,150706,X
- // 标志头 时分秒 日月年 标志尾
- //DS3231 SQW已引入中断P3.2,但本程序未使用外部中断
- //2016.5.10
- #include<reg52.h>
- #include <intrins.h>
- #include <string.h>
- #include "DS3231.h"
- #include "nongli.h"
- #include "iic.h"
- /********************************************************************************************************************
- 以下为GPS部分定义
- 因时间关系,gps数据应该加校验处理,防止出现乱码情况,虽然gps被干扰的情况很罕见。待其他有精力的玩家来解决
- ********************************************************************************************************************/
- //GPS数据存储数组
- u8 time[6]; //时间
- u8 date[6]; //日期
- bit va; //有效
- bit QJ; //电脑发送强制校时信号
- bit tswc; //调时完成标志
- //串口中断需要的变量
- u8 seg_count; //逗号计数器
- u8 dot_count; //小数点计数器
- u8 byte_count; //位数计数器
- u8 mode; //0:结束模式,1:命令模式,2:数据模式
- u8 buf_full; //1:整句接收完成,相应数据有效。0:缓存数据无效。
- u8 cmd[5]; //命令类型存储数组
- typedef struct _TIMER /** 作时区转换时需要用到年、月、日进一 **/
- {
- u8 Hour;
- u8 Min;
- u8 Sec;
- u8 Day;
- u8 Mon;
- u8 Year;
- } TIMER;
- TIMER BjTime;
- void InitBps(); /** 串口初始化 **/
- void UTCToLocal(TIMER *GPS_DataTmp); //时区转换
- void Set_DStime(u8 sel);
- void GetGpsTime(void);
- void FormatString(char *p);
- void updatetime();
- int gpscount=0; //gps有效计时
- /////////////////////////////////////
- sbit DIN = P1^0;
- sbit LOAD = P1^1;
- sbit CLK = P1^2;
- sbit BUSY = P3^3;
- sbit DATA = P3^4;
- sbit RST = P3^5;
- /********按键部分 按键采用定时器配合状态机 **/
- bit TS_Mode;//调时标志
- u8 Tcount; //10ms计数
- xdata uint F_50ms_Loop = 0;
- xdata uint F_500ms_Loop = 0;
- bit F_50ms = 1;
- bit F_500ms = 1;
-
- u8 KeyValue;
- sbit K1 = P1 ^ 4; //K1-调时设置,长按进入时间设置;短按作为日期时间下一项。当gps有效时,短按以gps时间校时
- sbit K2 = P1 ^ 5; //K2-调时中作+, 运行中作为手动报时
- sbit K3 = P1 ^ 6; //K3-调时中作-, 运行中作冒号闪烁开关
- sbit K4 = P1 ^ 7; //K4-调时中作确认、返回;运行中作整点报时开关
- sbit BLK = P1 ^ 3; //冒号是否闪烁
- sbit SND = P2 ^ 3; //语音报时指示灯
- bit bs;
- bit ss,sb; //冒号闪烁标志
- void key_scan(void);
- void Led_Flash(u8 n,bit f);
- u8 DStime[8]={0,0,0,0,0,0,0,0};//秒,分,时,周,日,月,年,温度
- u8 S[7]={0,0,0,0,0,0,0};//调时时的中间变量
- bit sound;//报时指示灯标志 是否允许报时
- u8 spkcount;//报时段参数
- u8 e;//对应调时的位置
- u8 item, max, mini;
- void FormatDT();
- void Delay_50us(uint t) //50us延迟函数
- {
- u8 j;
- for(;t>0;t--)
- for(j=19;j>0;j--);
- }
- void speak(uint z) //赋值变量 Z Z等于几就播放第几段
- {
- RST = 1; //语音芯片的复位脚为高电平//
- Delay_50us(4); //持续 200us//
- RST = 0; //然后复位脚置零//
- Delay_50us(4);
- z++; /*自己定制语音芯片后移一位,如果使用原版报时芯片,此句应删除*/
- while(z > 0) //若 Z等于 0 则不工作,若大于 0则继续自减//
- {
- DATA = 1; //data 脚位为高电平//
- Delay_50us(3); //持续 100us//
- DATA = 0; //然后置零//
- Delay_50us(3); //持续 100us这三句的意思就是发从一个 100us的脉冲(高低各 100us)//
- z--; //z 自减完成后开始播放对应的语音(因为z 是几就播放第几段)//
- }
- }
- // 八点整
- // 八点 零一分
- // 八点 十 分
- // 八点 十一分
- // 八点二十 分
- // 八点二十一分
- // 十一点
- //二十 点
- //二十一点
- void baoshi()//7--21点报时
- {
- if(spkcount<1 || spkcount>9) return;
-
- //第二级MAX7219写入字(16位)
- void WriteWord_2 (u8 addr, u8 num) //发现与第二及第三块相连的数码管闪烁,增加了空操作后就不闪烁了!!
- {
- LOAD = 0;
- _nop_();
- SendChar (NoOp);
- _nop_();
- SendChar (0);
- _nop_();
- SendChar (addr);
- _nop_();
- SendChar (num);
- _nop_();
- SendChar (NoOp);
- _nop_();
- SendChar (0);
- _nop_();
- LOAD = 1;
- }
- // 向第三级MAX7219写入字(16位)
- void WriteWord_1 (u8 addr, u8 num) //发现与第二及第三块相连的数码管闪烁,增加了空操作后就不闪烁了!!
- {
- LOAD = 0;
- _nop_();
- SendChar (NoOp);
- _nop_();
- SendChar (0);
- _nop_();
- SendChar (NoOp);
- _nop_();
- SendChar (0);
- _nop_();
- SendChar (addr);
- _nop_();
- SendChar (num);
- _nop_();
- LOAD = 1;
- }
- void init()
- {
- K1 = 1;
- K2 = 1;
- K3 = 1;
- K4 = 1;
- QJ=0;
- SND=1;//关
- sound=0;
- RST=0;
- DATA=0;
- Tcount=0;
- TS_Mode=0;
- KeyValue=0;
- tswc=0;
- ss=Read_02_Data(0);
- Delay_50us(40);
- SND=Read_02_Data(2);
- Delay_50us(40);
- clrwdt;
- bs=0;
- }
- void InitDis (void)
- {
- WriteWord_1 (ScanLimit,ScanDigit-1); /*设置扫描界限*/
- WriteWord_1 (DecodeMode,DecodeDigit); /*设置译码模式*/
- WriteWord_1 (Intensity,IntensityGrade); /*设置亮度*/
- WriteWord_1 (ShutDown,NormalOperation); /*设置电源工作模式*/
-
- WriteWord_2 (ScanLimit,ScanDigit-2); /*设置扫描界限*/
- WriteWord_2 (DecodeMode,DecodeDigit); /*设置译码模式*/
- WriteWord_2 (Intensity,IntensityGrade); /*设置亮度*/
- WriteWord_2 (ShutDown,NormalOperation); /*设置电源工作模式*/
- WriteWord_3 (ScanLimit,ScanDigit); /*设置扫描界限*/
- WriteWord_3 (DecodeMode,DecodeDigit); /*设置译码模式*/
- WriteWord_3 (Intensity,IntensityGrade); /*设置亮度*/
- WriteWord_3 (ShutDown,NormalOperation); /*设置电源工作模式*/
- }
- /*日期、时间设置函数---------------------------------------------------------*/
- void GetDateTime() //获取DS3231当前日期和时间
- {
- u8 Tmp,i;
- for(i=0;i<7;i++)
- {
- if(i==3) //星期不理睬
- continue;
- Tmp=read_random(i);
- if(i==2)
- Tmp&=0x3f; //时的格式单独处理
- DStime[i] = BCD2HEX(Tmp);
- }
- }
- void ShowTime(bit b) //更新信息
- {
- WriteWord_2 (Digit4, DStime[0]/10);
- WriteWord_2 (Digit5, DStime[0]%10);
- if(gpscount)
- {
- WriteWord_3 (Digit4, BjTime.Sec/10); //测试gps走时
- WriteWord_3 (Digit5, BjTime.Sec%10);
- }
- if(b) //更新全部信息
- {
- if(DStime[3]==0 ||DStime[3]==7) //星期日=8
- DStime[3]=8;
- DStime[7]=read_temp() ;
- WriteWord_2 (Digit0, DStime[2]<10 ? 0x0F:DStime[2]/10);
- WriteWord_2 (Digit1, DStime[2]%10);
- WriteWord_2 (Digit2, DStime[1]/10);
- WriteWord_2 (Digit3, DStime[1]%10);
- WriteWord_1 (Digit0, DStime[6]/10);
- WriteWord_1 (Digit1, DStime[6]%10);
- WriteWord_1 (Digit2, DStime[5]<10?0x0F:1);
- WriteWord_1 (Digit3, DStime[5]%10);
- WriteWord_1 (Digit4, DStime[4]<10?0x0F:DStime[4]/10);
- WriteWord_1 (Digit5, DStime[4]%10);
- WriteWord_1 (Digit6, DStime[3]);
-
- WriteWord_3 (Digit0, month_moon<10? 0x0F:1);
- WriteWord_3 (Digit1, month_moon%10);
- WriteWord_3 (Digit2, day_moon<10? 0x0F:day_moon/10);
- WriteWord_3 (Digit3, day_moon%10);
- if(!gpscount)
- {
- WriteWord_3 (Digit4, 0x0F); //无湿度计
- WriteWord_3 (Digit5, 0x0F);
- }
- if(DStime[7]<128)//正数时直接显示
- {
- WriteWord_3 (Digit6, DStime[7] / 10);
- WriteWord_3 (Digit7, DStime[7] % 10);
- }
- else
- {
- WriteWord_3 (Digit6, 0x0A);//负数显示 -
- WriteWord_3 (Digit7, DStime[7] % 10); //零下只显示一位
- }
- }
- }
- void Led_Flash(u8 n,bit f) //调试时闪烁提示效果
- {
- if(n < 3)
- {
- if(f)
- {
- WriteWord_1 (n*2 + 1, 0x0F);
- WriteWord_1 (n*2 + 2, 0x0F);
- }
- else
- {
- WriteWord_1 (n*2 + 1, S[n] / 10);
- WriteWord_1 (n*2 + 2, S[n] % 10);
- }
- }
- else
- {
- if(f)
- {
- WriteWord_2 ((n-4)*2 + 1, 0x0F);
- WriteWord_2 ((n-4)*2 + 2, 0x0F);
- }
- else
- {
- WriteWord_2 ((n-4)*2 + 1, S[n] / 10);
- WriteWord_2 ((n-4)*2 + 2, S[n] % 10);
- }
- }
- }
- void updatetime()
- {
- u8 i;
- S[3]= Conver_WEEK( S[0], S[1], S[2]);
- if(S[3]== 0)
- S[3]=7;
- for(i = 0; i < 7; i++)
- {
- if(S[i] != DStime[6-i])
- ModifyTime(6-i, S[i]);
- }
- }
- //按键扫描,定时10ms执行一次
- static u8 KeyScan(void)
- {
- if(K1 == 0)return KEY_VALUE_1 ;
- if(K2 == 0)return KEY_VALUE_2 ;
- if(K3 == 0)return KEY_VALUE_3 ;
- if(K4 == 0)return KEY_VALUE_4 ;
- return KEY_NULL ;
- }
- u8 GetKey(void)
- {
- static u8 s_u8KeyState = KEY_STATE_INIT ;
- static u8 s_u8KeyTimeCount = 0 ;
- static u8 s_u8LastKey = KEY_NULL ; //保存按键释放时候的键值
- u8 KeyTemp = KEY_NULL ;
- KeyTemp = KeyScan() ; //获取键值
- switch(s_u8KeyState)
- {
- case KEY_STATE_INIT :
- {
- if(KEY_NULL != (KeyTemp))
- {
- s_u8KeyState = KEY_STATE_WOBBLE ;
- }
- }
- break ;
- case KEY_STATE_WOBBLE : //消抖
- {
- s_u8KeyState = KEY_STATE_PRESS ;
- }
- break ;
- case KEY_STATE_PRESS :
- {
- if(KEY_NULL != (KeyTemp))
- {
- s_u8LastKey = KeyTemp ; //保存键值,以便在释放按键状态返回键值
- KeyTemp |= KEY_DOWN ; //按键按下
- s_u8KeyState = KEY_STATE_LONG ;
- }
- else
- {
- s_u8KeyState = KEY_STATE_INIT ;
- }
- }
- break ;
- case KEY_STATE_LONG :
- {
- if(KEY_NULL != (KeyTemp))
- {
- if(++s_u8KeyTimeCount > KEY_LONG_PERIOD)
- {
- s_u8KeyTimeCount = 0 ;
- KeyTemp |= KEY_LONG ; //长按键事件发生
- s_u8KeyState = KEY_STATE_CONTINUE ;
- }
- }
- else
- {
- s_u8KeyState = KEY_STATE_RELEASE ;
- }
- }
- break ;
- case KEY_STATE_CONTINUE :
- {
- if(KEY_NULL != (KeyTemp))
- {
- if(++s_u8KeyTimeCount > KEY_CONTINUE_PERIOD)
- {
- s_u8KeyTimeCount = 0 ;
- KeyTemp |= KEY_CONTINUE ;
- }
- }
- else
- {
- s_u8KeyState = KEY_STATE_RELEASE ;
- }
- }
- break ;
- case KEY_STATE_RELEASE :
- {
- s_u8LastKey |= KEY_UP ;
- KeyTemp = s_u8LastKey ;
- s_u8KeyState = KEY_STATE_INIT ;
- }
- break ;
- default :
- break ;
- }
- return KeyTemp ; //返回键值
- }
- void KeyProc(void)
- {
- u8 i;
- if(!va && !TS_Mode && KeyValue == (KEY_VALUE_1 | KEY_LONG)) //无gps+长按K1进入调时
- {
- TS_Mode = 1; //进入调时模式
- for(i = 0; i < 7; i++)
- {
- S[i] = DStime[6 - i];
- }
- }
- if(KeyValue == (KEY_VALUE_1 | KEY_DOWN))
- {
- if(TS_Mode) //在调时状态下进入下一项设置
- {
- if(S[e] != DStime[6 - e] || e>4)
- {
- ModifyTime(6 - e, S[e]);
- DStime[6 - e] = S[e];
- }
-
- e++;
- if(e == 3) //跳过调整星期
- e++;
- if (e == 5) //调到分钟时,秒置零
- {
- S[6]=0;
- WriteWord_2 (5, 0);
- WriteWord_2 (6, 0);
- }
- if (e > 5)
- {
- ModifyTime(0, 0);
- e = 0;
- TS_Mode = 0; //修改退出
- tswc=1;
- }
- }
- if(va) //有gps按下即强制校时
- {
- GetGpsTime();
- updatetime();
- }
- }
- if(KeyValue == (KEY_VALUE_2 | KEY_DOWN)) //+
- {
- if(TS_Mode)
- {
- item = S[e];
- item++;//数加 1
- Set_DStime(e);
- }
- else //运行中做报时键
- {
- sound = 1;
- spkcount = 9;
- }
- }
- if(KeyValue == (KEY_VALUE_3 | KEY_DOWN)) //-
- {
- if(TS_Mode)
- {
- item = S[e];
- if(item == 0)
- {
- if(e==4)
- item=23;
- else
- item=59;
- }
- else
- item--;//数减 1
- Set_DStime(e);
- }
- else
- {
- ss=~ss; //闪烁
- sb=1;
- }
- }
- if(KeyValue == (KEY_VALUE_4 | KEY_DOWN)) //EXIT
- {
- if(TS_Mode)
- {
- TS_Mode = 0; //退出调时
- e = 0; //"下一项"计数器清0
- //updatetime();
- tswc=1;
- }
- else
- {
- SND = ~SND; /** 运行模式中 K4 作语音报时切换键 **/
- bs=1;
- }
- }
- }
- void main()//主函数
- {
- u8 Temp, TempT;
- u8 i = 0;
- u8 ee = 0;
- bit d = 0;
- e = 0;
- TempT = Temp = 0;
- init();
- InitDis ();
- InitBps();
- Delayms(100);
- GetDateTime();
- Conversion( DStime[6], DStime[5], DStime[4]);
- DStime[3] = Conver_WEEK( DStime[6], DStime[5], DStime[4]);
- ShowTime(1);
- BLK = 0;
- clrwdt;
-
- while(1)//进入无限循环
- {
- if(TS_Mode == 0) //运行模式
- {
- if(F_50ms) //50ms标志
- {
- GetDateTime();
- F_50ms = 0;
- }
- if(QJ) //电脑发送的校时信息
- {
- GetGpsTime();
- updatetime();
- tswc=1;
- QJ = 0;
- }
- if(Temp != DStime[0]) //不用频繁更新,有变化才更新一次
- {
- if(va && TempT != BjTime.Sec) //如果接收到GPRMC有效数据
- {
- TempT = BjTime.Sec;
- if(gpscount++ == 600) //持续有效达10分钟即自动校时,可根据GPS性能调整
- {
- GetGpsTime();
- updatetime();
- }
- if(gpscount > 86400) //若gps挂机超过1天则回零
- gpscount = 0;
- }
- else
- {
- gpscount = 0;
- va = 0;
- }
- if(DStime[0] == 0 || tswc) //每分钟或调时完成全部刷新
- {
- if(DStime[1] == 0) //每小时
- {
- //Conversion( DStime[6], DStime[5], DStime[4]);
- //DStime[3] = Conver_WEEK( DStime[6], DStime[5], DStime[4]);
-
- if(DStime[2] > 6 && DStime[2] < 22 ) //7--21点报时
- {
- InitDis ();//每小时初始化一次
- if(!SND )
- {
- sound = 1; //整点报时
- spkcount = 9;
- }
- }
- else //夜间节能模式
- {
- WriteWord_1 (Intensity,0);
- WriteWord_2 (Intensity,0);
- WriteWord_3 (Intensity,0);
- }
- }
- Conversion( DStime[6], DStime[5], DStime[4]);
- DStime[3] = Conver_WEEK( DStime[6], DStime[5], DStime[4]);
- ShowTime(1); //每分钟全部更新一次 ,调整完时间退出后全部更新一次
- if(tswc)
- {
- tswc = 0;
- }
-
- }
- else
- ShowTime(0); //只更新秒
- Temp = DStime[0];
- if(sb)
- {
- Write_02_Data(0,ss);
- Delay_50us(40);
- sb=0;
- }
- if(ss)
- BLK=~BLK; //开启冒号闪烁
- }
- if(sound && BUSY)
- {
- baoshi();
- if(spkcount < 1 || spkcount > 20 )
- sound = 0; //退出报时
- }
- if(bs) //报时存储
- {
- Write_02_Data(2,SND);
- Delay_50us(40);
- bs=0;
- }
- }
- else
- {
- //调试状态,led应闪烁指示调整位
- if(F_500ms) //500ms标志
- {
- d = ~d;
- if(e != ee ) //调整下一项时防止上一项不亮
- {
- Led_Flash(ee, 0);
- ee = e;
- }
- Led_Flash(e, d);
- F_500ms = 0;
- }
- //if() //进入调时超过5分钟后自动退出 (该功能略)
- }
- clrwdt; //设置看门狗
- }
- }
- /*-----------------------------日期、时间设置函数-----------------------------*/
- //被调数据加一或减一,并检查数据范围,写入3231指定地址保存
- void ds_w(u8 sel)
- {
- if(item > max) item = mini; //查看数值是否在有效范围之内
- if(item < mini) item = max; //如果数值小于最小值,则自动等于最大值
- S[sel]=item;
- }
- void Set_DStime(u8 sel) //根据选择调整的相应项目加1并写入DS3231,函数参数是按动设置键的次数
- {
- if(sel == 3 || sel == 6) return; //星期不用设置
- /* 秒不用设置,调到分钟时,秒置零
- if(sel == 6)
- {
- max = 59;
- mini = 0;
- ds_w(sel); //被调数据加一或减一函数
- }
- //秒7,按动7次显示 调整秒钟
- //秒钟数据的最大值是59,最小值是0
- */
- if(sel == 5)
- {
- max = 59;
- mini = 0;
- ds_w(sel);
- }
- //分钟6,按动6次显示 调整分钟
- //分钟数据的最大值是59,最小值是0
- if(sel == 4)
- {
- max = 23;
- mini = 0;
- ds_w(sel);
- } //小时5,按动5次显示 调整小时
- //小时数据最大值23,最小值是0
- if(sel == 2)
- {
- if(S[1] == 2 && S[0] % 4 != 0)
- {
- max = 28; //平年2月28天
- mini = 1;
- }
- if(S[1] == 2 && S[0] % 4 == 0)
- {
- max = 29; //闰年2月29天
- mini = 1;
- }
- if(S[1] == 1 || S[1] == 3 || S[1] == 5 || S[1] == 7 || S[1] == 8 || S[1] == 10 || S[1] == 12)
- {
- max = 31; //31天的月份
- mini = 1;
- }
- if(S[1] == 4 || S[1] == 6 || S[1] == 9 || S[1] == 11)
- {
- max = 30; //30天的月份
- mini = 1;
- }
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载:
万年历.7z
(3.73 MB, 下载次数: 185)
|