用户工具

站点工具


rtc实时时钟实验_显示日期和时间

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

后一修订版
前一修订版
rtc实时时钟实验_显示日期和时间 [2019/11/29 09:52]
zhangzheng 创建
rtc实时时钟实验_显示日期和时间 [2022/03/22 10:19] (当前版本)
sean
行 1: 行 1:
-[[http://www.cnblogs.com/xiaomagee/p/5034453.html]]+|  **银杏科技有限公司旗下技术文档发布平台** ​ |||| 
 +|技术支持电话|**0379-69926675-801**||| 
 +|技术支持邮件|Gingko@vip.163.com||| 
 +^  版本 ​ ^  日期 ​ ^  作者 ​ ^  修改内容 ​ ^ 
 +|  V1.0  |  2020-07-04 ​ |  gingko ​ |  初次建立 ​ |  
 + 
 +===== 实验十:RTC实时时钟实验——显示日期和时间 ===== 
 + 
 +==== 一、 实验目的与意义 ==== 
 + 
 +  - 了解STM32 RTC结构。 
 +  - 了解STM32 RTC特征。 
 +  - 掌握RTC的使用方法。 
 +  - 掌握STM32 HAL库中RTC属性的配置方法。 
 +  - 掌握KEIL MDK 集成开发环境使用方法。 
 +==== 二、 实验设备及平台 ==== 
 + 
 +  - iCore4 双核心板[[https://item.taobao.com/item.htm?​spm=a1z10.1-c-s.w4004-22598974120.15.5923532fsFrHiE&​id=551864196684|点击购买]]。 
 +  - JLINK(或相同功能)仿真器[[https:​//item.taobao.com/​item.htm?​id=554869837940|点击购买]]。 
 +  - Micro USB线缆。 
 +  - Keil MDK 开发平台。 
 +  - STM32CubeMX开发平台。 
 +  - 装有WIN XP(及更高版本)系统的计算机。 
 +==== 三、 实验原理 ==== 
 + 
 +=== 1、STM32F767 RTC时钟简介 === 
 + 
 +  * STM32F767的实时时钟(RTC)相对于STM32F1来说,改进了不少,带了日历功能了,STM32F767的RTC,是一个独立的BCD定时器/​计数器。RTC提供一个日历时钟(包含年月日时分秒信息)、两个可编程闹钟(ALARMA和ALARMB)中断,以及一个具有中断功能的周期性可编程唤醒标志。RTC还包含用于管理低功耗模式的自动唤醒单元。 
 +  * 两个32位寄存器(TR和DR)包含二进码十进数格式(BCD)的秒、分钟、小时(12或24小时制)、星期、日期、月份和年份。此外,还可提供二进制格式的亚秒值。 
 +  * STM32F767的RTC可以自动将月份的天数补偿为28、29(闰年)、30和31天。并且还可以进行夏令时补偿。 
 +  * RTC模块和时钟配置是在后备区域,即在系统复位或从待机模式唤醒后RTC的设置和时间维持不变,只要后备区域供电正常,那么RTC将可以一直运行。但是在系统复位后,会自动禁止访问后备寄存器和RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前,先要取消备份区域(BKP)写保护。 
 +=== 2、RTC主要特性 === 
 +  * RTC单元的主要特性如下: 
 +    *  包含亚秒、秒、分钟、小时(12/​24小时制)、星期几、日期、月份和年份的日历。 
 +    *  软件可编程的夏令时补偿。 
 +    *  具有中断功能的可编程闹钟。可通过任意日历字段的组合触发闹钟。 
 +    *  自动唤醒单元,可周期性地生成标志以触发自动唤醒中断。 
 +    *  参考时钟检测:可使用更加精确的第二时钟源(50Hz或60Hz)来提高日历的精确度。 
 +    *  利用亚秒级移位特性与外部时钟实现精确同步。 
 +    *  数字校准电路(周期性计数器调整):精度为0.95ppm,在数秒钟的校准窗口中获得。 
 +    *  用于事件保存的时间戳功能。 
 +    *  带可配置过滤器和内部上拉的入侵检测事件。 
 +    *  可屏蔽中断/​事件: 
 +    *  –闹钟A 
 +    *  –闹钟B 
 +    *  –唤醒中断 
 +    *  –时间戳 
 +    *  –入侵检测 
 +    *  32备份寄存器。 
 + 
 +=== 3、RTC框图 === 
 +{{ :​icore4:​icore4_arm_hal_10_1.png?​direct |}}  
 + 
 +=== 4、时钟和分频 === 
 + 
 +  * 首先,我们看STM32F767的RTC时钟分频。STM32F767的RTC时钟(RTCCLK)通过时钟控制器,可以从LSE时钟、LSI时钟以及HSE时钟三者中选择(通过RCC_BDCR寄存器选择)。一般我们选择LSE,即外部32.768Khz晶振作为时钟源(RTCCLK),而RTC时钟核心,要求提供1Hz的时钟,所以,我们要设置RTC的可编程预分配器。STM32F767的可编程预分配器(RTC_PRER)分为2个部分: 
 +    * (1)一个通过RTC_PRER寄存器的PREDIV_A位配置的7位异步预分频器。 
 +    * (2)一个通过RTC_PRER寄存器的PREDIV_S位配置的15位同步预分频器。 
 +  * RTC框图中,ck_spre的时钟可由如下计算公式计算: 
 +  * Fck_spre=Frtcclk/​[(PREDIV_S+1)*(PREDIV_A+1)] 
 +  * 其中,Fck_spre即可用于更新日历时间等信息。PREDIV_A和PREDIV_S为RTC的异步和同步分频器。且推荐设置7位异步预分频器(PREDIV_A)的值较大,以最大程度降低功耗。 
 +=== 5、日历时间(RTC_TR)和日期(RTC_DR)寄存器 === 
 + 
 +  * STM32F767的RTC日历时间(RTC_TR)和日期(RTC_DR)寄存器,用于存储时间和日期(也可以用于设置时间和日期),可以通过与PCLK1(APB1时钟)同步的影子寄存器来访问,这些时间和日期寄存器也可以直接访问,这样可避免等待同步的持续时间。 
 +  * 每隔2个RTCCLK周期,当前日历值便会复制到影子寄存器,并置位RTC_ISR寄存器的RSF位。我们可以读取RTC_TR和RTC_DR来得到当前时间和日期信息,不过需要注意的是:时间和日期都是以BCD码的格式存储的,读出来要转换一下,才可以得到十进制的数据。 
 +=== 6、可编程闹钟 === 
 + 
 +  * STM32F767提供两个可编程闹钟:闹钟A(ALARM_A)和闹钟B(ALARM_B)。通过RTC_CR寄存器的ALRAE和ALRBE位置1来使能闹钟。当日历的亚秒、秒、分、小时、日期分别与闹钟寄存器RTC_ALRMASSR/​RTC_ALRMAR和RTC_ALRMBSSR/​RTC_ALRMBR中的值匹配时,则可以产生闹钟(需要适当配置)。本章我们将利用闹钟A产生闹铃,即设置RTC_ALRMASSR和RTC_ALRMAR即可。 
 +=== 7、周期性自动唤醒 === 
 +  * STM32F767的RTC不带秒钟中断,但是多了一个周期性自动唤醒功能。周期性唤醒功能,由一个16位可编程自动重载递减计数器(RTC_WUTR)生成,可用于周期性中断/​唤醒。 
 +  * 我们可以通过RTC_CR寄存器中的WUTE位设置使能此唤醒功能。唤醒定时器的时钟输入可以是:2、4、8或16分频的RTC时钟(RTCCLK),也可以是ck_spre时钟(一般为1Hz)。 
 +  * 本实验中,通过软件对RTC计数器进行相关的配置,可以提供时钟功能,通过修改计时器的值可以调整时钟。最终通过串口在终端显示时间。 
 +==== 四、 实验程序 ==== 
 + 
 +=== 1、主函数 === 
 +<code c> 
 +int main(void) 
 +
 +  RTC_TimeTypeDef sTime; 
 +  RTC_DateTypeDef sDate; 
 + 
 +  int second_bak = 0; 
 +  /* MCU 配置 */ 
 +  /* 重置所有外围设备,初始化Flash接口和Systick. */ 
 +  HAL_Init();​ 
 +  /* 配置系统时钟 */ 
 +  SystemClock_Config();​ 
 +  /* 初始化所有已配置的外围设备*/​ 
 +  MX_GPIO_Init();​ 
 +  MX_RTC_Init();​ 
 +  MX_USART6_UART_Init();​ 
 +  usart6.initialize(115200); ​          //​串口波特设置 
 +  usart6.printf("​\x0c"​); ​              //​清屏 
 +  usart6.printf("​\033[1;​32;​40m"​); ​      //​设置终端字体为绿色 
 +  usart6.printf("​ Hello, I am iCore4!\r\n"​);​ //​串口信息输出 
 +  LED_GREEN_ON;​  
 +   //​设置RTC日期和时间 
 +  my_rtc.set_date(17,​8,​11,​5);​ 
 +  my_rtc.set_time(17,​43,​20);​ 
 +  /* 无限循环 */ 
 +  while (1) 
 +  { 
 +        HAL_Delay(100);​ 
 +        //​读取RTC日期和时间 
 +        HAL_RTC_GetTime(&​hrtc,​ &sTime, RTC_FORMAT_BIN);​ 
 +        HAL_RTC_GetDate(&​hrtc,​ &sDate, RTC_FORMAT_BIN);​ 
 +        if(second_bak != sTime.Seconds){ 
 +            usart6.printf("​ %02d:​%02d:​%02d ​ ",​sTime.Hours,​sTime.Minutes,​sTime.Seconds);​ 
 +            usart6.printf("​20%02d-%02d-%02d ​ \r",​sDate.Year,​sDate.Month,​sDate.Date);​ 
 +            second_bak = sTime.Seconds; ​        
 +        } 
 +  } 
 +
 +  
 +</​code>​ 
 +=== 2、RTC初始化 === 
 +<code c> 
 +void MX_RTC_Init(void) 
 +
 +  RTC_TimeTypeDef sTime; 
 +  RTC_DateTypeDef sDate; 
 +  /​**仅初始化RTC */ 
 +  hrtc.Instance = RTC; 
 +  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;//​ RTC设置为24小时格式 
 +  hrtc.Init.AsynchPrediv = 127;  //RTC 异步分频系数 
 +  hrtc.Init.SynchPrediv = 255;   //RTC 同步分频系数 
 +  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;​ 
 +  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;​ //​输出信号的极性 
 +  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;//​RTC输出引脚模式 
 +  if (HAL_RTC_Init(&​hrtc) != HAL_OK)//​初始化RTC外设 
 +  { 
 +    _Error_Handler(__FILE__,​ __LINE__);​ 
 +  } 
 +    /​**初始化RTC并设置时间和日期 */ 
 +  if(HAL_RTCEx_BKUPRead(&​hrtc,​ RTC_BKP_DR0) != 0x32F2){ 
 +  sTime.Hours = 0x16;//​小时 
 +  sTime.Minutes = 0x19;//​分钟 
 +  sTime.Seconds = 0x0;//秒 
 +  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;​ 
 +  sTime.StoreOperation = RTC_STOREOPERATION_RESET;​ 
 +  if (HAL_RTC_SetTime(&​hrtc,​ &sTime, RTC_FORMAT_BCD) != HAL_OK) 
 +  { 
 +    _Error_Handler(__FILE__,​ __LINE__);​ 
 +  } 
 +  sDate.WeekDay = RTC_WEEKDAY_MONDAY;​ 
 +  sDate.Month = RTC_MONTH_AUGUST;​ 
 +  sDate.Date = 0x7; 
 +  sDate.Year = 0x0; 
 +  if (HAL_RTC_SetDate(&​hrtc,​ &sDate, RTC_FORMAT_BCD) != HAL_OK) 
 +  { 
 +    _Error_Handler(__FILE__,​ __LINE__);​ 
 +  } 
 +    HAL_RTCEx_BKUPWrite(&​hrtc,​RTC_BKP_DR0,​0x32F2);​ 
 +  } 
 +    /​**使能唤醒 */ 
 +  if (HAL_RTCEx_SetWakeUpTimer(&​hrtc,​ 0, RTC_WAKEUPCLOCK_RTCCLK_DIV16) != HAL_OK) 
 +  { 
 +    _Error_Handler(__FILE__,​ __LINE__);​ 
 +  } 
 +
 + 
 +</​code>​ 
 +  * 该函数用来初始化RTC配置以及日期和时钟,这里设置时间和日期,分别是通过 HAL_RTC_SetTime函数和 HAL_RTC_SetDate函数来实现。 
 +=== 3、设置时间和日期 === 
 + 
 +  * 主函数中调用设置时间和日期的自定义函数rtc_set_time和rtc_set_date来设置初始时间日期,这两个函数实际就是调用库函数里面的HAL_RTC_SetTime函数和HAL_RTC_SetDate函数来实现的,它们的定义如下: 
 +<code c> 
 +static int rtc_set_time(unsigned char hour,​unsigned char min,​unsigned char sec) 
 +
 +    RTC_TimeTypeDef sTime; 
 +    sTime.Hours = hour;//​小时 
 +    sTime.Minutes = min; //分钟 
 +    sTime.Seconds = sec; //秒 
 +    sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;​ 
 +    sTime.StoreOperation = RTC_STOREOPERATION_RESET;​ 
 +    if (HAL_RTC_SetTime(&​hrtc,​ &sTime, RTC_FORMAT_BIN) != HAL_OK) 
 +    { 
 +        while(1); 
 +    } 
 +    return 0; 
 +
 +static int rtc_set_date(unsigned char year,​unsigned char month,​unsigned char date,​unsigned char week) 
 +
 +    RTC_DateTypeDef sDate; 
 +    sDate.WeekDay = week; //星期 
 +    sDate.Month = month; //月 
 +    sDate.Date = date; //日 
 +    sDate.Year = year; //年 
 +  if (HAL_RTC_SetDate(&​hrtc,​ &sDate, RTC_FORMAT_BIN) != HAL_OK) 
 +  { 
 +    while(1); 
 +  }    
 +    return 0; 
 +
 + 
 +</​code>​ 
 +=== 4、获取时间和日期 === 
 +<code c> 
 + /​*获取RTC当前时间*/​ 
 +HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format) 
 +
 +  uint32_t tmpreg = 0; 
 +  /* 检查参数*/​ 
 +  assert_param(IS_RTC_FORMAT(Format));​ 
 +  /* 从对应的寄存器中获取亚秒值*/​ 
 +  sTime->​SubSeconds = (uint32_t)(hrtc->​Instance->​SSR);​ 
 +  /* 从相应的寄存器字段中获取SecondFraction结构字段/​ 
 +  sTime->​SecondFraction = (uint32_t)(hrtc->​Instance->​PRER & RTC_PRER_PREDIV_S);​ 
 +  /* 获取TR寄存器 */ 
 +  tmpreg = (uint32_t)(hrtc->​Instance->​TR & RTC_TR_RESERVED_MASK);​  
 +  /​*用读取的参数填充结构字段 */ 
 +  sTime->​Hours = (uint8_t)((tmpreg & (RTC_TR_HT | RTC_TR_HU)) >> 16); 
 +  sTime->​Minutes = (uint8_t)((tmpreg & (RTC_TR_MNT | RTC_TR_MNU)) >>​8);​ 
 +  sTime->​Seconds = (uint8_t)(tmpreg & (RTC_TR_ST | RTC_TR_SU));​ 
 +  sTime->​TimeFormat = (uint8_t)((tmpreg & (RTC_TR_PM)) >> 16);  
 +  /* 检查输入参数格式 */ 
 +  if(Format == RTC_FORMAT_BIN) 
 +  { 
 +    /​*将时间结构参数转换为二进制格式*/​ 
 +    sTime->​Hours = (uint8_t)RTC_Bcd2ToByte(sTime->​Hours);​ 
 +    sTime->​Minutes = (uint8_t)RTC_Bcd2ToByte(sTime->​Minutes);​ 
 +    sTime->​Seconds = (uint8_t)RTC_Bcd2ToByte(sTime->​Seconds); ​  
 +  } 
 +  return HAL_OK; 
 +
 + /​*获取RTC当前日期*/​ 
 +HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format) 
 +
 +  uint32_t datetmpreg = 0; 
 +  /* 检查参数 */ 
 +  assert_param(IS_RTC_FORMAT(Format));​ 
 +  /* 获取DR寄存器 */ 
 +  datetmpreg = (uint32_t)(hrtc->​Instance->​DR & RTC_DR_RESERVED_MASK);​  
 +  /* 用读取的参数填充结构字段 */ 
 +  sDate->​Year = (uint8_t)((datetmpreg & (RTC_DR_YT | RTC_DR_YU)) >> 16); 
 +  sDate->​Month = (uint8_t)((datetmpreg & (RTC_DR_MT | RTC_DR_MU)) >> 8); 
 +  sDate->​Date = (uint8_t)(datetmpreg & (RTC_DR_DT | RTC_DR_DU));​ 
 +  sDate->​WeekDay = (uint8_t)((datetmpreg & (RTC_DR_WDU)) >> 13);  
 +  /* 检查输入参数格式 */ 
 +  if(Format == RTC_FORMAT_BIN) 
 +  {     
 +    /* 将日期结构参数转换为二进制格式 */ 
 +    sDate->​Year = (uint8_t)RTC_Bcd2ToByte(sDate->​Year);​ 
 +    sDate->​Month = (uint8_t)RTC_Bcd2ToByte(sDate->​Month);​ 
 +    sDate->​Date = (uint8_t)RTC_Bcd2ToByte(sDate->​Date); ​  
 +  } 
 +  return HAL_OK; 
 +
 +  
 +</​code>​ 
 +==== 五、 实验步骤 ==== 
 + 
 +  - 把仿真器与iCore4的SWD调试口相连(直接相连或者通过转接器相连); 
 +  - 把iCore4通过Micro USB线与计算机相连,为iCore4供电; 
 +  - 打开Keil MDK 开发环境,并打开本实验工程; 
 +  - 烧写程序到iCore4上; 
 +  - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 
 +==== 六、 实验现象 ==== 
 +  * 在终端屏幕上可以看到显示的时间和日期。如图所示。 
 + {{ :​icore4:​icore4_arm_hal_10_2.png?​direct |}}  
rtc实时时钟实验_显示日期和时间.1574992376.txt.gz · 最后更改: 2019/11/29 09:52 由 zhangzheng