软件模拟RTC功能在单片机上的C语言实现

编程入门 行业动态 更新时间:2024-10-16 18:25:56

软件模拟RTC功能在单片<a href=https://www.elefans.com/category/jswz/34/1771422.html style=机上的C语言实现"/>

软件模拟RTC功能在单片机上的C语言实现

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结构体对象

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};
//在获取到实际的本地时间后,调用此函数,传入当前的年月日等参数,得到相对天数
//将从网络获取到的本地时间变为系统内部的相对时间
//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的,所以要减20year=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[i];}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.secuint8_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 %d\n",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 %d\n",day,absoTim.year,absoTim.month,absoTim.day);}else{log_info("rtc time trans success\n");}
}
2.4.2 关于将数据按照时间点排序

很多数据都是按照时间点存下来的,存下来之后,当需要做一些分析的时候,需要用到时间排序,这种设计模式下,系统中的相对天数urtc1_time.day的值越大,代表时间距离2020年1月1日越远。

2.4.3 后续

这两个时间解码和编码函数,我是经过测试的,问题不大,可以直接用。将绝对时间转为相对时间的思想,对于单片机软件层面来说,逻辑会变得简单,尤其是涉及到不同时间的数据存储比较和其它处理。单片机层面不用去关心当前的具体时间已经年月日之间复杂的转换逻辑,只需要满24小时,天数加1即可。下篇文章我会更新一个使用C语言直接模拟rtc,不再使用相对天数的设计逻辑,而是使用最传统的软件模拟rtc思想,实现维护最新本地时间功能。

最后,祝大家2020.10.24节日快乐

更多推荐

软件模拟RTC功能在单片机上的C语言实现

本文发布于:2024-02-25 19:40:44,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1700088.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:机上   语言   功能   软件   RTC

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!