完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
1.前言
最近做到的项目有用到RTC这一功能模块,但是所用的单片机自身不带硬件RTC.所以需要软件模拟一个RTC模块,实现日期更迭功能。 所谓的RTC,即 real time clock,实时时钟系统,通过该模块可以一直获得当前实际的年月日,时分秒,星期的值。rtc模块可以自动实现日期更迭,月份更迭,年份更迭。 下面介绍第一种软件实现rtc模块的设计方法。 2.设计实现 想要实现软件rtc模块,首先单片机本身硬件资源要具备一个最少ms级别的精准的定时器,然后单片机本身跟其它设备之间有通讯接口,可以获得一次当前实时时间,以用来对自身的rtc时间进行校准。该通讯接口可以有很多种形式,比如单片机与手机app之间通过蓝牙通信,手机app获取到当前准确的时间后,通过自定的通讯协议下发到单片机端,单片机接收到了确切的当前时间后,将自身的rtc值校准为当前时间,后续就不用继续校准了。单片机可以用过自身的rtc模块一直维护更迭一个最新的当前日期时间。 第一种设计思想为,单片机端只维护更新一个相对时间,所谓相对时间,可以设计为2020年1月1日距今为止的相对天数,比如单片机端从通讯接口处获得了当前时间为2020年2月1号1点0分0秒,则通过转换函数1将日期转换为相对值32,然后更新到单片机rtc模块端,。命名此函数为Rtc_EncodeTim();后续r如果单片机需要把当前的相对时间转换为绝对日期时间,再通过转换函数2,将系统中的相对时间,转为当前现实世界的绝对日期值。将转换函数2命名为Rtc_DecodeTim(); 下面贴几部分组成代码。 2.1结构体定义 typedef struct { uint16_t year; uint8_t month; uint8_t day; uint8_t hour; uint8_t min; uint8_t sec; } ST_ABSOTIM;//绝对时间值结构体 typedef struct urtcType { uint16_t day; uint8_t hour; uint8_t min; uint8_t sec; unsigned long clock; unsigned char initedFlg; } urtcType;//用于系统内部处理的相对时间结构体 urtcType urtc_time1;//定义rtc结构体对象 1 2.2通过硬件定时器实现时分秒日期的更新 //结构体初始化函数 void URTC_INIT(uint16_t day,uint8_t hour,uint8_t min,uint8_t sec ) { urtc_time1.day=day; urtc_time1.hour = hour; urtc_time1.min =min; urtc_time1.sec =sec; urtc_time1.clock = hour*3600+min*60+sec; //设置标志位为未初始化,向涂鸦模块发送串口命令请求初始化 urtc_time1.initedFlg=0; } //用于更新rtc时间,通过硬件定时器1s调用一次即可 void Urtc_loop() { unsigned long tem=0; //调用涂鸦api同步时间,通过通讯接口获得了当前确切的绝对日期后再启动rtc模块自动更新时间功能 if(!urtc_time1.initedFlg) { Urtc_SynTim(); return; } urtc_time1.clock ++; //计时满一天,相对天数+1 if (urtc_time1.clock >= 86400) { urtc_time1.clock = 0; urtc_time1.day++; } if (urtc_time1.clock >=60) { tem = urtc_time1.clock/60; urtc_time1.sec = urtc_time1.clock % 60; if (tem >=60) { urtc_time1.hour = tem/60; urtc_time1.min = tem%60; } else { urtc_time1.hour = 0; urtc_time1.min = tem; } } else { urtc_time1.hour = 0; urtc_time1.min = 0; urtc_time1.sec = urtc_time1.clock; } } 2.3实现Rtc_EncodeTim()函数,将绝对日期转为相对天数 //设置系统时间函数 void set_URTC_time(uint16_t day,uint8_t hour,uint8_t min,uint8_t sec) { //if (urtc_time1.lock == 0) { urtc_time1.day=day; urtc_time1.hour =hour; urtc_time1.min=min; urtc_time1.sec =sec; urtc_time1.clock = hour*3600+min*60+sec; urtc_time1.lock = 1; //时间同步初始化完成 urtc_time1.initedFlg=1; } } //每月的天数,按照闰年算 unsigned char gu8_MonthDays[12]={31,28,31,30,31,30,31,31,30,31,30,31}; 1 2 //在获取到实际的本地时间后,调用此函数,传入当前的年月日等参数,得到相对天数 //将从网络获取到的本地时间变为系统内部的相对时间 //year:相对于2020年 uint16_t Rtc_EncodeTim(uint8_t year,uint8_t month,uint8_t day,uint8_t hour,uint8_t min,uint8_t sec) { //每四年算一个年份周期,闰年,年份除以4的整数 uint8_t timeCycle=0; //年份除以4的余数 uint8_t offserYear; //自2020年一月1日零时开始距今的天数 uint16_t relatDays=0; //涂鸦api传进来的年份是相对于2000年的,自己写的函数中year的定义是相对于2020的,所以要减20 year=year-20; timeCycle=year/4; offserYear=year%4; //1.计算完整年份天数 relatDays=(timeCycle*(365*3+366)); //例如今年是2022年,则offserYear=2,要加上2020年的366天,再加上2021年的365天 if(offserYear>0) { relatDays=relatDays+366+(offserYear-1)*365; } //2.计算当前年份已经过去的天数 //如果今年是4的整数倍,那就是闰年 //如果今年是闰年且今年月份已经超过2月,需要多加2月份多出来的一天 if(month>2 && offserYear==0) { relatDays+=1; } /* //还没过完的一年,已经过去的天数,统一按照非闰年算,方法1,未验证 for(uint8_t i = 0; i < month; ++i) { relatDays+=gu8_MonthDays; } relatDays += day; */ //还没过完的一年,已经过去的天数,统一按照非闰年算,方法2,已验证 switch (month) { case 1: relatDays+=day; break; case 2: relatDays+=(31+day); break; case 3: relatDays+=(59+day); break; case 4: relatDays+=(90+day); break; case 5: relatDays+=(120+day); break; case 6: relatDays+=(151+day); break; case 7: relatDays+=(181+day); break; case 8: relatDays+=(212+day); break; case 9: relatDays+=(243+day); break; case 10: relatDays+=(273+day); break; case 11: relatDays+=(304+day); break; case 12: relatDays+=(334+day); break; default: break; } //更新本地系统时间 set_URTC_time(relatDays,hour,min,sec); return relatDays; } 2.3实现Rtc_DecodeTim()函数,将系统中的相对日期转为实际的本地时间 //当需要与外部设备进行对接,将系统中的相对时间转为确切的本地绝对时间时,调用此函数,转换结果 //会更新到absoTim结构体对象中 //将系统内部的相对时间解码为实际日期 //absoTim:绝对日期结构体指针 void Rtc_DecodeTime(ST_ABSOTIM *absoTim) { uint8_t yearCycle=0; uint16_t offsetDay=0; uint8_t offsetYear=0; uint16_t relatDays=urtc_time1.day; uint8_t hour=urtc_time1.hour; uint8_t min=urtc_time1.min; uint8_t sec=urtc_time1.sec uint8_t isLeapYear=0;//闰年 //四年为一个周期,以2020年为起始 yearCycle=(relatDays/(366+365*3)); offsetDay=(relatDays%(366+365*3)); if(offsetDay>366) { offsetYear=((offsetDay-366)/365); //得到一年内的剩余日期转换成月份和日 offsetDay=(offsetDay-(366+offsetYear*365)); offsetYear++; isLeapYear=0; } else { offsetYear=0; offsetDay=offsetDay; //闰年 isLeapYear=1; } //offsetDay等于0时,说明今天是该年最后一天,需要特殊处理 if(offsetDay==0) { //得到绝对的年份值 absoTim->year=((yearCycle*4)+offsetYear+2020-1); absoTim->month=12; absoTim->day=31; return; } //得到绝对的年份值 absoTim->year=((yearCycle*4)+offsetYear+2020); //计算实际的月份值和日期值 if(isLeapYear) { if(offsetDay>335){absoTim->month=12;absoTim->day=offsetDay-335;} else if(offsetDay>305){absoTim->month=11;absoTim->day=offsetDay-305;} else if(offsetDay>274){absoTim->month=10;absoTim->day=offsetDay-274;} else if(offsetDay>244){absoTim->month=9;absoTim->day=offsetDay-244;} else if(offsetDay>213){absoTim->month=8;absoTim->day=offsetDay-213;} else if(offsetDay>182){absoTim->month=7;absoTim->day=offsetDay-182;} else if(offsetDay>152){absoTim->month=6;absoTim->day=offsetDay-152;} else if(offsetDay>121){absoTim->month=5;absoTim->day=offsetDay-121;} else if(offsetDay>91){absoTim->month=4;absoTim->day=offsetDay-91;} else if(offsetDay>60){absoTim->month=3;absoTim->day=offsetDay-60;} else if(offsetDay>31){absoTim->month=2;absoTim->day=offsetDay-31;} else {absoTim->month=1;absoTim->day=offsetDay;} } else { if(offsetDay>334){absoTim->month=12;absoTim->day=offsetDay-334;} else if(offsetDay>304){absoTim->month=11;absoTim->day=offsetDay-304;} else if(offsetDay>273){absoTim->month=10;absoTim->day=offsetDay-273;} else if(offsetDay>243){absoTim->month=9;absoTim->day=offsetDay-243;} else if(offsetDay>212){absoTim->month=8;absoTim->day=offsetDay-212;} else if(offsetDay>181){absoTim->month=7;absoTim->day=offsetDay-181;} else if(offsetDay>151){absoTim->month=6;absoTim->day=offsetDay-151;} else if(offsetDay>120){absoTim->month=5;absoTim->day=offsetDay-120;} else if(offsetDay>90){absoTim->month=4;absoTim->day=offsetDay-90;} else if(offsetDay>59){absoTim->month=3;absoTim->day=offsetDay-59;} else if(offsetDay>31){absoTim->month=2;absoTim->day=offsetDay-31;} else {absoTim->month=1;absoTim->day=offsetDay;} } absoTim->hour=hour; absoTim->min=min; absoTim->sec=sec; } 2.4其它 2.4.1 贴一个自己写的用来测试解码编码函数正确与否的测试函数 //将当前系统相对时间从1开始,每次加1,转换成本地绝对时间 //通过打印函数打印出来转换信息,按照自己的设计理念,串口会依次打印 //reladay is 1,year is 2020,month is 1,day is 1 //reladay is 2,year is 2020,month is 1,day is 2 //... //最后再通过编码函数,将当前绝对时间编码为相对时间,两者结果进行对比,结果相等,则说明 //两个函数功能实现都是对的 //循环调用此函数 void Rtc_TransTest() { static uint8_t year=0; static uint8_t month=0; static uint16_t day=1; ST_ABSOTIM absoTim; uint16_t day2=0; day++; Rtc_DecodeTime(day,0, &absoTim); log_info("reladay is %d,year is %d,month is %d,day is %dn",day,absoTim.year,absoTim.month,absoTim.day); day2=Rtc_EncodeTim((absoTim.year-2000),absoTim.month,absoTim.day,0, 0, 0); if(day2!=day) { log_info("err abday is %d,reladay is %d",day,day2); log_info("reladay is %d,year is %d,month is %d,day is %dn",day,absoTim.year,absoTim.month,absoTim.day); } else { log_info("rtc time trans successn"); } } 2.4.2 关于将数据按照时间点排序 很多数据都是按照时间点存下来的,存下来之后,当需要做一些分析的时候,需要用到时间排序,这种设计模式下,系统中的相对天数urtc1_time.day的值越大,代表时间距离2020年1月1日越远。 2.4.3 后续 这两个时间解码和编码函数,我是经过测试的,问题不大,可以直接用。将绝对时间转为相对时间的思想,对于单片机软件层面来说,逻辑会变得简单,尤其是涉及到不同时间的数据存储比较和其它处理。单片机层面不用去关心当前的具体时间已经年月日之间复杂的转换逻辑,只需要满24小时,天数加1即可。下篇文章我会更新一个使用C语言直接模拟rtc,不再使用相对天数的设计逻辑,而是使用最传统的软件模拟rtc思想,实现维护最新本地时间功能。 |
|
|
|
只有小组成员才能发言,加入小组>>
3278 浏览 9 评论
2956 浏览 16 评论
3457 浏览 1 评论
8995 浏览 16 评论
4050 浏览 18 评论
1103浏览 3评论
570浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
568浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2301浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1857浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 17:22 , Processed in 2.197391 second(s), Total 79, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号