KEIL-MDK的时间戳之time.h 的应用
1 时间戳介绍
现在物联网产品的在进行通讯的时候,需要加入时间戳的这个信息参数,方便服务器和产品之间交换时间信息。
时间戳是计算机系统中用来表示日期和时间的一种方式,通常是一个数字或者一串字符,表示从某个特定时间点(通常是“纪元”或“起始时间”)开始经过的秒数或者毫秒数。时间戳通常是以协调世界时(UTC)为基准的,但也可以根据需要使用其他时区。
时间戳的优点是它提供了一种统一的方式来表示时间,无论用户所在的时区如何,都可以通过时间戳来精确地表示一个特定的时间点。这在计算机系统中进行时间计算、排序和存储时非常有用。
时间戳通常用于记录事件发生的时间,例如在日志文件中记录系统活动的时间、在数据库中存储数据的创建或修改时间等。它也经常用于计算时间间隔,例如计算两个事件之间经过的时间,或者用于定时任务的调度。
在很多编程语言和操作系统中,时间戳通常是以自某个特定日期开始的秒数来表示,这个特定日期被称为“纪元”(epoch)。UNIX 系统通常使用1970年1月1日作为纪元,而Windows系统则使用1601年1月1日。
KEIL的time文件采用UNIX 系统通常使用1970年1月1日作为纪元。
2 RTC模块介绍
GD32F1系列是基于ARM Cortex-M3内核的通用微控制器系列,由中国的光电半导体(GigaDevice)公司开发。该系列包括了具有丰富外设和功能的微控制器,其中也包括了RTC(Real-Time Clock,实时时钟)模块。
RTC模块是用来提供精确的时间信息的硬件设备,在嵌入式系统中通常用于实现实时时钟、日历功能和时间戳等应用。以下是关于GD32F1系列的RTC模块的一般介绍。
这里以gd32f1的RTC为例。 gd32f1的集成了一个RTC的模组,可以非常方便自动记录时间。
2.1 电路设计
为了达到比较精确的时间记录,为RTC增加了一个32.768kHz的晶振。 这样比较保存时间。
2.2 驱动代码开发
开发RTC(Real-Time Clock,实时时钟)驱动代码的配置思路如下:
选择合适的时钟源:
RTC通常需要一个稳定的时钟源来提供时间基准。这可以是外部晶振或者内部RC振荡器。在配置RTC之前,需要确定使用哪种时钟源,并相应地配置系统时钟。
初始化RTC模块:
首先,需要初始化RTC模块,包括启用RTC时钟、选择时钟源、配置分频系数等。这通常涉及设置RTC相关的寄存器,具体操作可参考MCU的技术手册。
设置时间:
在RTC初始化后,需要设置当前时间。这可以通过设置RTC的年、月、日、时、分、秒等寄存器来完成。一般情况下,需要提供一个函数接口,允许用户设置初始时间。
配置闹钟功能(可选):
如果需要使用RTC的闹钟功能,需要配置相应的闹钟寄存器。这包括设置闹钟触发时间、选择触发模式(例如每秒、每分钟、每小时等)、启用闹钟中断等。
处理RTC中断:
如果RTC模块支持中断功能,需要编写中断服务程序(ISR)来处理RTC中断。这可能涉及到处理时间更新中断、闹钟触发中断等。在中断服务程序中,通常需要清除相应的中断标志位。
备份寄存器的使用(可选):
一些MCU的RTC模块提供了备份寄存器,用于在系统掉电时保存特定的数据。如果需要使用备份寄存器,需要相应地初始化和配置备份寄存器。
测试与调试:
完成RTC驱动代码后,需要进行测试和调试。可以使用仿真器或者实际硬件进行测试,并检查RTC功能是否按预期工作。
优化与稳定性提升:
在测试过程中,可以对RTC驱动代码进行优化,提高代码的稳定性和性能。例如,考虑使用更有效率的算法或者减少对寄存器的访问次数来优化代码。
通过以上步骤,可以完成对RTC模块的驱动代码配置,实现所需的实时时钟功能,并确保代码的可靠性和稳定性。
void rtc_configuration(void)
{/* enable PMU and BKPI clocks */rcu_periph_clock_enable(RCU_BKPI);rcu_periph_clock_enable(RCU_PMU);/* allow access to BKP domain */pmu_backup_write_enable();/* reset backup domain */bkp_deinit();/* enable LXTAL */rcu_osci_on(RCU_LXTAL);/* wait till LXTAL is ready */rcu_osci_stab_wait(RCU_LXTAL);/* select RCU_LXTAL as RTC clock source */rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);/* enable RTC Clock */rcu_periph_clock_enable(RCU_RTC);/* wait for RTC registers synchronization */rtc_register_sync_wait();/* wait until last write operation on RTC registers has finished */rtc_lwoff_wait();// /* enable the RTC second interrupt*/
// rtc_interrupt_enable(RTC_INT_SECOND);// /* wait until last write operation on RTC registers has finished */
// rtc_lwoff_wait();/* set RTC prescaler: set RTC period to 1s */rtc_prescaler_set(32767);/* wait until last write operation on RTC registers has finished */rtc_lwoff_wait();
}
3 字符串转化成为时间戳
将字符串转换为时间戳的过程通常需要以下步骤,具体实现方式取决于所用编程语言和库:
解析字符串:首先,需要解析输入的字符串,提取其中的年、月、日、时、分、秒等时间信息。这可能需要使用字符串处理函数或正则表达式来提取有效的时间信息。
转换为结构化时间:将提取出的时间信息转换为结构化时间,即包含年、月、日、时、分、秒等字段的时间数据结构。不同编程语言和库提供了不同的函数或类来表示结构化时间。
转换为时间戳:最后,将结构化时间转换为时间戳,即从某个特定时间点(通常是纪元时间,如1970年1月1日)开始的秒数或毫秒数。通常可以使用编程语言提供的时间转换函数来实现这一步骤。
具体实现步骤
3.1 字符串转化成为年、月、日、时、分、秒等时间信息
// 时间字符串转化成为时间 "19/05/29,02:05:29+32" "07/10/25,11:33:44+08"
int DateString_to_TM(Bat_Struct *p_bat)
{if(rt_strncmp(p_bat->rtcstr,"\0",2)==0) // 判断输入不为空{return 1; // 输入错误}else{
// struct tm myTime = {0}; char arr[32];strcpy(arr,p_bat->rtcstr); // 复制数据rt_kprintf("arr is %s\r\n",arr);const char * sep = "/,:+"; // 分割符号char *str = NULL; uint32_t n=1;int val;for( str=strtok(arr, sep);str!=NULL;str=strtok(NULL,sep)){switch(n){case 1: {val = atoi(str);if(val !=0){p_bat->date.tm_year=val+2000; // 获取年n++;break;}else{return 2; // 解析错误}}case 2: {val = atoi(str); p_bat->date.tm_mon=val; // 获取月n++;break;}case 3: {val = atoi(str); p_bat->date.tm_mday=val; // 获取日n++;break;}case 4: {val = atoi(str); p_bat->date.tm_hour=val; // 获取hour n++;break;}case 5: {val = atoi(str); p_bat->date.tm_min=val; // 获取minn++;break;}case 6: {val = atoi(str); p_bat->date.tm_sec=val; // 获取minn++;break;}default: break; } }if(n<6){return 2; // 解析错误} else{return 0; // 成功}}
}
3.2 利用mktime变成时间戳,同时考虑时区问题。
其中tm_diff 用来调整时区,北京时区是东八区
uint32_t time_regulate(int syear,int smon,int sday,int hour,int min,int sec)
{time_t time_t_Set_Time; //定义time_t类型的设置时间结构体struct tm tm_Set_Time; //定义tm结构的设置时间结构体tm_Set_Time.tm_year = (uint32_t)(syear-1900); //从1900年开始算起tm_Set_Time.tm_mon = (uint32_t)(smon-1); //月tm_Set_Time.tm_mday = (uint32_t)sday; //日tm_Set_Time.tm_hour = (uint32_t)hour; //时tm_Set_Time.tm_min = (uint32_t)min; //分tm_Set_Time.tm_sec = (uint32_t)sec; //秒tm_Set_Time.tm_isdst=0;struct tm diff;diff.tm_year = (uint32_t)(1970-1900); //从1900年开始算起diff.tm_mon = 0; //月diff.tm_mday = 1; //日diff.tm_hour = 8; //时区diff.tm_min = 0; //分diff.tm_sec = 0; //秒diff.tm_isdst=0;time_t_Set_Time=mktime(&tm_Set_Time)-mktime(&diff); // 插入 return time_t_Set_Time; // 存储时间戳}
3.3 写入寄存器
把时间戳的值写入到寄存器中,可以让RTC电路自动进行累加。
rtc_lwoff_wait();/* change the current time */rtc_counter_set(time_regulate(data->tm_year,data->tm_mon,data->tm_mday,data->tm_hour,data->tm_min,data->tm_sec));/* wait until last write operation on RTC registers has finished */rtc_lwoff_wait();
3.4 读取时间戳
sysBat.timestamp=rtc_counter_get(); // 获取时间戳
4 把时间戳变成字符串
// 定义时间戳time_t timestamp = 1644202496; // 2024-02-07 12:34:56// 使用localtime函数将时间戳转换为tm结构体struct tm *timeinfo;timeinfo = localtime(×tamp);// 使用strftime函数将tm结构体格式化为字符串char buffer[80];strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);// 输出格式化后的字符串printf("Formatted date and time: %s\n", buffer);
5 总结
RTC的实际使用结合时间戳比较应用价值,很多参考案把f1当作只能记录一天时间的读秒器,失去这个RTC应用意义。希望这篇文章帮助读者更好应用RTC的这个模组。