42. RTC

您所在的位置:网站首页 闹钟怎么让它停止 42. RTC

42. RTC

2024-07-11 06:23:03| 来源: 网络整理| 查看: 265

42.5.2.2. 代码分析¶

RTC实验配置相关宏定义

在这个RTC实验中的bsp_rtc.h文件中添加了一些宏定义用于切换工程的配置,见 代码清单:RTC-6 。

代码清单:RTC-6:RTC实验配置相关的宏定义(bsp_rtc.h文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12//是否使用LCD显示日期 #define USE_LCD_DISPLAY //使用LSE外部时钟 或 LSI内部时钟 //#define RTC_CLOCK_SOURCE_LSE #define RTC_CLOCK_SOURCE_LSI #define RTC_BKP_DRX BKP_DR1 // 写入到备份寄存器的数据宏定义 #define RTC_BKP_DATA 0xA5A5 //北京时间的时区秒数差 #define TIME_ZOOM (8*60*60)

以上代码定义的宏介绍如下:

USE_LCD_DISPLAY:这个宏可以用于切换本工程是否使用液晶屏显示时间,把它注释掉可以关闭液晶显示,方便移植到没有液晶的应用中。

RTC_CLOCK_SOURCE_LSE/LSI:这两个宏用于选择使用LSE作外部时钟还是LSI作外部时钟。 提供两种选择主要是因为STM32的LSE晶振在批量产品时容易不起振, 而LSI则在主电源关闭后计时时间不会继续增加。

RTC_BKP_DRX和RTC_BKP_DATA:这两个宏用于在备份域寄存器设置RTC已配置标志,本实验中初始化RTC后,向备份域寄存器写入一个数字, 若下次芯片上电检测到该标志,说明RTC之前已经配置好时间,所以不应该再设置RTC,而如果备份域电源也掉电,备份域内记录的该标志也会丢失, 所以芯片上电后需要重新设置时间。这两个宏的值中,BKP_DR1是备份域的其中一个寄存器,而0xA5A5则是随意选择的数字,只要写入和检测一致即可。

TIME_ZOOM:这个宏用于设置时区的秒数偏移,例如北京时间为(GMT+8) 时区,即相对于格林威治时间(GMT) 早8个小时, 此处使用的宏值即为8个小时的秒数(8*60*60),若使用其它时区,修改该宏即可。

关于这些宏的作用,在后面的C源代码中都会有体现。

初始化RTC

在本工程中,我们编写了RTC_Configuration函数对RTC进行初始化,见 代码清单:RTC-7 。

代码清单:RTC-7:RTC_Configuration函数(bsp_rtc.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94/* * 函数名:RTC_Configuration * 描述 :配置RTC * 输入 :无 * 输出 :无 * 调用 :外部调用 */ void RTC_Configuration(void) { /* 使能 PWR 和 Backup 时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* 允许访问 Backup 区域 */ PWR_BackupAccessCmd(ENABLE); /* 复位 Backup 区域 */ BKP_DeInit(); //使用外部时钟还是内部时钟(在bsp_rtc.h文件定义) //使用外部时钟时,在有些情况下晶振不起振 //批量产品的时候,很容易出现外部晶振不起振的情况,不太可靠 #ifdef RTC_CLOCK_SOURCE_LSE /* 使能 LSE */ RCC_LSEConfig(RCC_LSE_ON); /* 等待 LSE 准备好 */ while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) { } /* 选择 LSE 作为 RTC 时钟源 */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); /* 使能 RTC 时钟 */ RCC_RTCCLKCmd(ENABLE); /* 等待 RTC 寄存器 同步 * 因为RTC时钟是低速的,内环时钟是高速的,所以要同步 */ RTC_WaitForSynchro(); /* 确保上一次 RTC 的操作完成 */ RTC_WaitForLastTask(); /* 使能 RTC 秒中断 */ RTC_ITConfig(RTC_IT_SEC, ENABLE); /* 确保上一次 RTC 的操作完成 */ RTC_WaitForLastTask(); /* 设置 RTC 分频: 使 RTC 周期为1s */ /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) = 1HZ */ RTC_SetPrescaler(32767); /* 确保上一次 RTC 的操作完成 */ RTC_WaitForLastTask(); #else /* 使能 LSI */ RCC_LSICmd(ENABLE); /* 等待 LSI 准备好 */ while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) { } /* 选择 LSI 作为 RTC 时钟源 */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); /* 使能 RTC 时钟 */ RCC_RTCCLKCmd(ENABLE); /* 等待 RTC 寄存器 同步 * 因为RTC时钟是低速的,内环时钟是高速的,所以要同步 */ RTC_WaitForSynchro(); /* 确保上一次 RTC 的操作完成 */ RTC_WaitForLastTask(); /* 使能 RTC 秒中断 */ RTC_ITConfig(RTC_IT_SEC, ENABLE); /* 确保上一次 RTC 的操作完成 */ RTC_WaitForLastTask(); /* 设置 RTC 分频: 使 RTC 周期为1s ,LSI约为40KHz */ /* RTC period = RTCCLK/RTC_PR = (40 KHz)/(40000-1+1) = 1HZ */ RTC_SetPrescaler(40000-1); /* 确保上一次 RTC 的操作完成 */ RTC_WaitForLastTask(); #endif }

在这个初始化函数里,分成了使用LSE和LSI的初始化配置,这两种配置的初始化过程类似,都直接调用了前面介绍的各种RTC相关的库函数

这个初始化的流程如下:使用RCC_APB1PeriphClockCmd使能PWR和BKP区域(即备份域)的时钟系统,使用PWR_BackupAccessCmd设置允许对BKP区域的访问,使能LSE时钟或LSI时钟,选择LSE或LSI作为RTC的时钟源并使能RTC时钟,利用库函数RTC_WaitFor Synchro对备份域和APB进行同步,用RTC_ITConfig使能秒中断,使用RTC_SetPrescaler分频配置把RTC时钟频率设置为1Hz。那么RTC每个时钟周期都会产生一次中断对RTC的每一个初始化参数都是使用相应的库函数来配置的。

经过这样的配置后,RTC每秒产生一次中断事件,实验中在中断设置标志位以便更新时间。

时间管理结构体

RTC初始化完成后可以直接往它的计数器写入时间戳,但是时间戳对用户不友好,不方便配置和显示时间, 在本工程中,使用bsp_date.h文件的rtc_time结构体来管理时间, 见 代码清单:RTC-8 。

代码清单:RTC-8:时间管理结构体的定义¶ 1 2 3 4 5 6 7 8 9struct rtc_time { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; };

这个类型的结构体具有时、分、秒、日、月、年及星期这7个成员。当需要给RTC的计时器重新配置或显示时间时,使用这种容易接受的时间表示方式。

在配置RTC时,使用这种类型的变量保存用户输入的时间,然后利用函数由该时间求出对应的UNIX时间戳,写入RTC的计数器;RTC正常运行后, 需要输出时间时,利用函数通过RTC的计数器获取UNIX时间戳,转化成这种友好的时间表示方式保存到变量输出。

其实在C语言标准库ANSI C中,也具有类似这样的结构体struct tm ,位于标准的time.h文件中, 而具有以上功能的转化函数则为mktime和localtime,它们分别把tm格式的时间转化成时间戳和用时间戳转化成tm格式。 而在本实验中直接使用了开源的万年历算法源码,便于修改和学习。

时间格式转换

在本实验中,tm格式转时间戳使用mktimev函数,时间戳转tm格式使用to_tm函数,这两个函数都定义在bsp_date.c文件中, 见 代码清单:RTC-9 。

代码清单:RTC-9:mktimev和to_tm函数(bsp_date.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. * * [For the Julian calendar (which was used in Russia before 1917, * Britain & colonies before 1752, anywhere else before 1582, * and is still in use by some communities) leave out the * -year/100+year/400 terms, and add 10.] * * This algorithm was first published by Gauss (I think). * * WARNING: this function will overflow on 2106-02-07 06:28:16 on * machines were long is 32-bit! (However, as time_t is signed, we * will already get problems at other places on 2038-01-19 03:14:08) * */ u32 mktimev(struct rtc_time *tm) { if (0 >= (int) (tm->tm_mon -= 2)) { /* 1..12 -> 11,12,1..10 */ tm->tm_mon += 12; /* Puts Feb last since it has leap day */ tm->tm_year -= 1; } return ((( (u32) (tm->tm_year/4 - tm->tm_year/100 + tm->tm_year/400 + 367*tm->tm_mon/12 + tm->tm_mday) + tm->tm_year*365 - 719499 )*24 + tm->tm_hour /* now have hours */ )*60 + tm->tm_min /* now have minutes */ )*60 + tm->tm_sec; /* finally seconds */ } void to_tm(u32 tim, struct rtc_time * tm) { register u32 i; register long hms, day; day = tim / SECDAY; /* 有多少天 */ hms = tim % SECDAY; /* 今天的时间,单位s */ /* Hours, minutes, seconds are easy */ tm->tm_hour = hms / 3600; tm->tm_min = (hms % 3600) / 60; tm->tm_sec = (hms % 3600) % 60; /* Number of years in days */ /*算出当前年份,起始的计数年份为1970年*/ for (i = STARTOFTIME; day >= days_in_year(i); i++) { day -= days_in_year(i); } tm->tm_year = i; /* Number of months in days left */ /*计算当前的月份*/ if (leapyear(tm->tm_year)) { days_in_month(FEBRUARY) = 29; } for (i = 1; day >= days_in_month(i); i++) { day -= days_in_month(i); } days_in_month(FEBRUARY) = 28; tm->tm_mon = i; /* Days are what is left over (+1) from all that. *//*计算当前日期*/ tm->tm_mday = day + 1; /* * Determine the day of week */ GregorianDay(tm); }

关于日期计算的细节此处不作详细分析,其原理是以1970年1月1日0时0分0秒为计时基点,对日期和以秒数表示时间戳进行互相转化,转化重点在于闰年的计算。

这两个函数都是以格林威治时间(GMT)时区来计算的,在调用这些函数时我们会对输入参数加入时区偏移的运算,进行调整。

配置时间

有了以上的准备,接下来学习一下Time_Adjust函数,见 代码清单:RTC-10

代码清单:RTC-10:Time_Adjust函数(bsp_rtc.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22/* * 函数名:Time_Adjust * 描述 :时间调节 * 输入 :用于读取RTC时间的结构体指针(北京时间) * 输出 :无 * 调用 :外部调用 */ void Time_Adjust(struct rtc_time *tm) { /* RTC 配置 */ RTC_Configuration(); /* 等待确保上一次操作完成 */ RTC_WaitForLastTask(); /* 由日期计算时间戳并写入到RTC计数寄存器 */ RTC_SetCounter(mktimev(tm)-TIME_ZOOM); /* 等待确保上一次操作完成 */ RTC_WaitForLastTask(); }

Time_Adjust函数用于配置时间,它先调用前面的RTC_Configuration初始化RTC,接着调用库函数RTC_SetCounter向RTC计数器写入要设置时间的时间戳值, 而时间戳的值则使用mktimev函数通过输入参数tm来计算,计算后还与宏TIME_ZOOM运算,计算时区偏移值。此处的输入参数tm是北京时间, 所以“mktimev(tm)-TIME_ZOOM”计算后写入到RTC计数器的是格林威治时区的标准UNIX时间戳。

检查并配置RTC

上面的Time_Adjust函数直接把参数写入到RTC中修改配置,但在芯片每次上电时,并不希望每次都修改系统时间, 所以我们增加了RTC_CheckAndConfig函数用于检查是否需要向RTC写入新的配置,见。

代码清单:RTC-11:RTC_CheckAndConfig函数(bsp_rtc.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74/* * 函数名:RTC_CheckAndConfig * 描述 :检查并配置RTC * 输入 :用于读取RTC时间的结构体指针 * 输出 :无 * 调用 :外部调用 */ void RTC_CheckAndConfig(struct rtc_time *tm) { /*在启动时检查备份寄存器BKP_DR1,如果内容不是0xA5A5, 则需重新配置时间并询问用户调整时间*/ if (BKP_ReadBackupRegister(RTC_BKP_DRX) != RTC_BKP_DATA) { printf("\r\n\r\n RTC not yet configured...."); printf("\r\n\r\n RTC configured...."); /* 使用tm的时间配置RTC寄存器 */ Time_Adjust(tm); /*向BKP_DR1寄存器写入标志,说明RTC已在运行*/ BKP_WriteBackupRegister(RTC_BKP_DRX, RTC_BKP_DATA); } else { /* 使能 PWR 和 Backup 时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* 允许访问 Backup 区域 */ PWR_BackupAccessCmd(ENABLE); /*LSE启动无需设置新时钟*/ #ifdef RTC_CLOCK_SOURCE_LSI /* 使能 LSI */ RCC_LSICmd(ENABLE); /* 等待 LSI 准备好 */ while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET) { } #endif /*检查是否掉电重启*/ if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET) { printf("\r\n\r\n Power On Reset occurred...."); } /*检查是否Reset复位*/ else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET) { printf("\r\n\r\n External Reset occurred...."); } printf("\r\n No need to configure RTC...."); /*等待寄存器同步*/ RTC_WaitForSynchro(); /*允许RTC秒中断*/ RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR, ENABLE); /*等待上次RTC寄存器写操作完成*/ RTC_WaitForLastTask(); } /*定义了时钟输出宏,则配置校正时钟输出到PC13*/ #ifdef RTCClockOutput_Enable /* 禁止 Tamper 引脚 */ /* 要输出 RTCCLK/64 到 Tamper 引脚, tamper 功能必须禁止 */ BKP_TamperPinCmd(DISABLE); /* 使能 RTC 时钟输出到 Tamper 引脚 */ BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock); #endif /* 清除复位标志 flags */ RCC_ClearFlag(); }

在本函数中,会检测备份域寄存器RTC_BKP_DRX内的值是否等于RTC_BKP_DATA而分成两个分支。

若不等,说明之前没有配置RTC所以直接调用Time_Adjust函数初始化RTC并写入时间戳进行计时,配置完成后向备份域寄存器RTC_BKP_DRX写入值RTC_BKP_DATA作为标志, 这样该标志就可以指示RTC的配置情况了,因为备份域不掉电时,RTC和该寄存器的值都会保存完好,而如果备份域掉电,那么RTC配置和该标志都会一同丢失;

若本函数的标志判断相等,进入else分支,不再调用Time_Adjust函数初始化RTC,而只是使用RTC_WaitForSynchro和RTC_ITConfig同步RTC域和APB以及使能中断, 以便获取时间。如果使用的是LSI时钟,还需要使能LSI时钟,RTC才会正常运行,这是因为当主电源掉电和备份域的情况下,LSI会关闭,而LSE则会正常运行,驱动RTC计时。

转换并输出时间

RTC正常运行后,可以使用Time_Display函数转换时间格式并输出到串口及液晶屏, 见 代码清单:RTC-12 。

代码清单:RTC-12:Time_Display函数(bsp_rtc.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71/* * 函数名:Time_Display * 描述 :显示当前时间值 * 输入 :-TimeVar RTC计数值,单位为 s * 输出 :无 * 调用 :内部调用 */ void Time_Display(uint32_t TimeVar,struct rtc_time *tm) { static uint32_t FirstDisplay = 1; uint32_t BJ_TimeVar; uint8_t str[200]; // 字符串暂存 /* 把标准时间转换为北京时间*/ BJ_TimeVar =TimeVar + TIME_ZOOM; to_tm(BJ_TimeVar, tm);/*把定时器的值转换为北京时间*/ if ((!tm->tm_hour && !tm->tm_min && !tm->tm_sec) || (FirstDisplay)) { GetChinaCalendar((u16)tm->tm_year, (u8)tm->tm_mon, (u8)tm->tm_mday, str); printf("\r\n 今天新历:%0.2d%0.2d,%0.2d,%0.2d", str[0], str[1], str[2], str[3]); GetChinaCalendarStr((u16)tm->tm_year,(u8)tm->tm_mon,(u8)tm->tm_mday,str); printf("\r\n 今天农历:%s\r\n", str); if (GetJieQiStr((u16)tm->tm_year, (u8)tm->tm_mon, (u8)tm->tm_mday, str)) printf("\r\n 今天农历:%s\r\n", str); FirstDisplay = 0; } /* 输出时间戳,公历时间 */ rintf(" UNIX时间戳 = %d 当前时间为: %d年(%s年) %d月 %d日 (星期%s) %0.2d:%0.2d:%0.2d\r",TimeVar, tm->tm_year, zodiac_sign[(tm->tm_year-3)%12], tm->tm_mon, tm->tm_mday, WEEK_STR[tm->tm_wday], tm->tm_hour, tm->tm_min, tm->tm_sec); #ifdef USE_LCD_DISPLAY //时间戳 sprintf((char *)str," UNIX TimeStamp = %d ",TimeVar); ILI9341_DispStringLine_EN(LINE(3),(char*)str); //日期 sprintf((char *)str," Date: %d-%d-%d ", tm->tm_year, tm->tm_mon, tm->tm_mday); ILI9341_DispStringLine_EN(LINE(5),(char*)str); //生肖 sprintf((char *)str," Chinese %s year ",en_zodiac_sign[(tm->tm_year-3)%12]); ILI9341_DispStringLine_EN(LINE(6),(char*)str); //星期 sprintf((char *)str," %s ",en_WEEK_STR[tm->tm_wday]); ILI9341_DispStringLine_EN(LINE(7),(char*)str); //时间 sprintf((char *)str," Time: %0.2d:%0.2d:%0.2d", tm->tm_hour, tm->tm_min, tm->tm_sec); ILI9341_DispStringLine_EN(LINE(8),(char*)str); #endif }

本函数的核心部分已加粗显示,主要是使用to_tm把时间戳转换成日常生活中使用的时间格式,to_tm以BJ_TimeVar作为输入参数, 而BJ_TimeVar对时间戳变量Time_Var进行了时区偏移,也就是说调用Time_Display函数时,以RTC计数器的值作为TimeVar作为输入参数即可, 最终会输出北京时间。

利用to_tm转换格式后,调用bsp_calendar.c文件中的日历计算函数,求出星期、农历、生肖等内容,然后使用串口和液晶屏显示出来。

中断服务函数

一般来说,上面的Time_Display时间显示每秒中更新一次,而根据前面的配置,RTC每秒会进入一次中断, 本实验中的RTC中断服务函数见 代码清单:RTC-13 。

代码清单:RTC-13:中断服务函数(stm32f10x_it.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18/** * @brief This function handles RTC interrupt request. * @param None * @retval None */ void RTC_IRQHandler(void) { if (RTC_GetITStatus(RTC_IT_SEC) != RESET) { /* Clear the RTC Second interrupt */ RTC_ClearITPendingBit(RTC_IT_SEC); /* Enable time update */ TimeDisplay = 1; /* Wait until last write operation on RTC registers has finished */ RTC_WaitForLastTask(); } }

RTC的秒中断服务函数只是简单地对全局变量TimeDisplay置1,在main函数的while循环中会检测这个标志,当标志为1时, 就调用Time_Display函数显示一次时间,达到每秒钟更新当前时间的效果。

main函数

代码清单:RTC-14:RTC例程中的main函数(main.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55/*时间结构体,默认时间2000-01-01 00:00:00*/ struct rtc_time systmtime= { 0,0,0,1,1,2000,0 }; extern __IO uint32_t TimeDisplay ; /** * @brief 主函数 * @param 无 * @retval 无 */ int main() { //可使用该宏设置是否使用液晶显示 #ifdef USE_LCD_DISPLAY ILI9341_Init (); //LCD 初始化 LCD_SetFont(&Font8x16); LCD_SetColors(RED,BLACK); ILI9341_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH); ILI9341_DispStringLine_EN(LINE(0)," BH RTC demo"); #endif USART_Config(); Key_GPIO_Config(); /* 配置RTC秒中断优先级 */ RTC_NVIC_Config(); RTC_CheckAndConfig(&systmtime); while (1) { /* 每过1s 更新一次时间*/ if (TimeDisplay == 1) { /* 当前时间 */ Time_Display( RTC_GetCounter(),&systmtime); TimeDisplay = 0; } //按下按键,通过串口修改时间 if ( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) { struct rtc_time set_time; /*使用串口接收设置的时间,输入数字时注意末尾要加回车*/ Time_Regulate_Get(&set_time); /*用接收到的时间设置RTC*/ Time_Adjust(&set_time); //向备份寄存器写入标志 BKP_WriteBackupRegister(RTC_BKP_DRX, RTC_BKP_DATA); } } }

main函数的流程非常清晰,初始化了液晶、串口等外设后,调用RTC_CheckAndConfig函数初始化RTC,若RTC是第一次初始化,就使用变量systmtime中的默认时间配置, 若之前已配置好RTC,那么RTC_CheckAndConfig函数仅同步时钟系统,便于获取实时时间。在while循环里检查中断设置的TimeDisplay是否置1, 若置1了就调用Time_Display函数,它的输入参数是库函数RTC_GetCounter的返回值,也就是RTC计数器里的时间戳, Time_Display函数把该时间戳转化成北京时间显示到串口和液晶上。

使用串口配置时间

在main函数的44-54行,是一个按键检测分支,当检测到开发板上的KEY1被按下时,会调用Time_Regulate_Get函数通过串口获取配置时间, 然后把获取得的时间输入到Time_Adjust函数把该时间写入到RTC计数器中,更新配置, Time_Regulate_Get函数内容见 代码清单:RTC-15 。

代码清单:RTC-15:Time_Regulate_Get函数(bsp_rtc.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38/* * 函数名:Time_Regulate_Get * 描述 :保存用户使用串口设置的时间, * 以便后面转化成时间戳存储到RTC 计数寄存器中。 * 输入 :用于读取RTC时间的结构体指针 * 注意 :在串口调试助手输入时,输入完数字要加回车 */ void Time_Regulate_Get(struct rtc_time *tm) { uint32_t temp_num = 0; uint8_t day_max=0 ; printf("\r\n=========================设置时间=================="); do { printf("\r\n 请输入年份(Please Set Years), 范围[1970~2038],输入字符后请加回车:"); scanf("%d",&temp_num); if (temp_num 2038) { printf("\r\n 您输入的数字是:%d,不符合要求",temp_num); } else { printf("\n\r 年份被设置为: %d\n\r", temp_num); tm->tm_year = temp_num; break; } } while (1); do { printf("\r\n 请输入月份(Please Set Months):范围[1~12],输入字符后请加回车:"); scanf("%d",&temp_num); if (temp_num 12) { printf("\r\n 您输入的数字是:%d,不符合要求",temp_num); } else { printf("\n\r 月份被设置为: %d\n\r", temp_num); tm->tm_mon = temp_num; break; } } while (1); /*...以下省略日期、时间获取的代码*/ }

Time_Regulate_Get函数的本质是利用重定向到串口的C标准数据流输入函数scanf获取用户输入,若获取得的数据符合范围, 则赋值到tm结构体中,在main函数中再调用Time_Adjust函数把tm存储的时间写入到RTC计数器中。

注意

必须强调的是,使用scanf通过串口输入时,每次输入完毕后都要加入回车,这样才能正常接收,见图 使用串口配置时间的注意事项 。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭