你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32CubeIDE实时时钟(RTC)经验分享

[复制链接]
攻城狮Melo 发布时间:2023-4-6 16:45
一、RTC简介
        实时时钟的缩写是RTC(Real_Time Clock),核心是晶振,晶振频率一般为32768 Hz 。它为分频计数器提供精确的与低功耗的实基信号。它可以用于产生秒、分、时、日等信息。为了确保时钟长期的准确性,晶振必须正常工作,不能够受到干扰。RTC的晶振又分为:外部晶振和内置晶振。

        RTC时间信息存储在后备寄存器(RTC_BKUP)中,在STM32中,通常采用一个32位计数器来计时,而不是用年月日时分秒的分组寄存器,因此在处理STM32的时间信息时(设置或读取),通常要求先处理时分秒时间,再处理年月日时间,以确保时间和日期值之间的一致性。

        RTC和后备寄存器一般不会被系统或电源复原源复位,当从待机模式唤醒时,也不会复位,但是要注意的是,停止供电会出现重置,数值恢复到初始设置,因此如果要保持长期有效,尤其是断电重启依然有效,需要配置独立供电电源。

        RTC时间是以振荡频率来计算的。故它本质上是一个计数器,只是通过换算呈现出日历时间信息。而一般的计数器都是16位的。又因为时间的准确性很重要,故震荡次数越低,时间的准确性越低。所以必定是个高次数。2^15 = 32768 ,而 32768 Hz = 2^15 即分频15次后为1Hz,周期 = 1s,因此RTC的时间基准是1秒。16位计数器最大值0XFFFFFFFF,按秒计算,能支持136年时长计算。


二、工程创建及配置
        本文采用STM32L496VGTx-ali开发板来实现,该开发板是没有给RTC独立供电的,因此断电后会出现时间重置,但功能模拟演示还是OK的。本文假设已经移植了前面工程,并实现了lpuart1串口通信与电脑端的调试输出以及按键、LED灯驱动

        假设已经实现了lpuart1串口通信及按键、LED灯功能,现在双击.ioc打开cubeMX配置界面,开启RTC功能。数据格式支持二进制格式和BCD格式,在HAL中采用RTC_FORMAT_BIN(0x00000000u)和RTC_FORMAT_BCD(0x00000001u)标识。

1927c21adfaa4522bbf8ef9117ae2483.png

         BCD(Binary Coded Decimal),即二-十进制编码,是用四位二进制码的 10 种组合表示十进制数0-9。这种编码至少需要用四位二进制码元,而四位二进制码元可以有16种组合。当用这些组合表示十进制数0-9时,有六种组合不用。

        RTC功能开启后,时钟树视图可以关于RTC的选择器可以选择外部高速时钟、外部低速时钟、内部低速时钟,本文选择外部低速时钟(LSE)。

77fc485a203d4e63a71c21cde13eb335.png

         完成配置保存输出按钮(本文是为每个外设生成独立.h/.c源码),在生成的rtc.c文件中可以看到RTC初始化函数关于年月日、时分秒的设值和cubeMX上配置信息一致。

        备注:如果在cubeMX配置界面没有选择数据格式,年日、时分秒默认是0X前缀的。

629d8cf91bdc4be8b721bdc3bae4b3f7.png

三、驱动代码设计实现
        【1】在Core/Src/rtc.c文件中,调整RTC初始化函数,在RTC设置初始化时间、日期之前,加入后备寄存器的读取,判定是否已经进行过初始化,避免重复设置时间、日期而改写本该持续计数的RTC计数。
  1. void MX_RTC_Init(void)
  2. {

  3.   /* USER CODE BEGIN RTC_Init 0 */

  4.   /* USER CODE END RTC_Init 0 */

  5.   RTC_TimeTypeDef sTime = {0};
  6.   RTC_DateTypeDef sDate = {0};

  7.   /* USER CODE BEGIN RTC_Init 1 */
  8.     //新增
  9.   __HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟PWR
  10.    HAL_PWR_EnableBkUpAccess();//取消备份区域写保护
  11.   /* USER CODE END RTC_Init 1 */

  12.   /** Initialize RTC Only
  13.   */
  14.   hrtc.Instance = RTC;
  15.   hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  16.   hrtc.Init.AsynchPrediv = 127;
  17.   hrtc.Init.SynchPrediv = 255;
  18.   hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  19.   hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
  20.   hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  21.   hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  22.   if (HAL_RTC_Init(&hrtc) != HAL_OK)
  23.   {
  24.     Error_Handler();
  25.   }

  26.   /* USER CODE BEGIN Check_RTC_BKUP */
  27.     //新增
  28.   if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)!=0X8888)//判断是否首次上电
  29.   {
  30.           HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,0X8888); //标记数值(写入上电检查数值)
  31.   /* USER CODE END Check_RTC_BKUP */

  32.   /** Initialize RTC and set the Time and Date
  33.   */
  34.   sTime.Hours = 12;
  35.   sTime.Minutes = 30;
  36.   sTime.Seconds = 30;
  37.   sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  38.   sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  39.   if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
  40.   {
  41.     Error_Handler();
  42.   }
  43.   sDate.WeekDay = RTC_WEEKDAY_TUESDAY;
  44.   sDate.Month = RTC_MONTH_DECEMBER;
  45.   sDate.Date = 20;
  46.   sDate.Year = 22;

  47.   if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
  48.   {
  49.     Error_Handler();
  50.   }
  51.   /* USER CODE BEGIN RTC_Init 2 */
  52.     //新增
  53. //  printf("手动初始化测试!\r\n");//显示初始化信息
  54.   }
  55.   /* USER CODE END RTC_Init 2 */

  56. }
复制代码

        在main.c文件中,加入按键、LED灯、串口外设驱动头文件,
  1. /* USER CODE BEGIN Includes */
  2. #include "../../ICore/key/key.h"
  3. #include "../../ICore/led/led.h"
  4. #include "../../ICore/print/print.h"
  5. #include "../../ICore/usart/usart.h"
  6. /* USER CODE END Includes */
复制代码

        在main函数中声明RTC日期、时间读取缓存变量。
  1.   /* USER CODE BEGIN 1 */
  2.   RTC_DateTypeDef RtcDate;
  3.   RTC_TimeTypeDef RtcTime;
  4.   uint8_t time_set = 0;//用户设置时间标记
  5.   /* USER CODE END 1 */
复制代码

        在main函数中开启串口lpuart1的中断接收。
  1.   /* USER CODE BEGIN 2 */
  2.   ResetPrintInit(&hlpuart1);
  3.   HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
  4.   HLPUSART_RX_STA = 0;
  5.   printf("APP reset now!\r\n");
  6.   /* USER CODE END 2 */
复制代码

        在main函数循环体中,实现按键KEY1获取RTC时间以及按键KEY2设置RCT时间,注意RTC设置时间、日期和RTC读取时间、日期时,数据格式保持一致,最好和CubeMX设置时一致,例如读取、写入RTC时间日期都采用RTC_FORMAT_BCD格式。
  1. /* USER CODE BEGIN WHILE */
  2.   while (1)
  3.   {
  4.           if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
  5.                     printf("lpuart1:%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
  6.                     if(time_set){
  7.                             //将超级终端发过来的数据换算并写入RTC,读取数据为字符内容需要转换为数值
  8.                     RtcDate.Year =  (HLPUSART_RX_BUF[2]-0x30)*10+HLPUSART_RX_BUF[3]-0x30;//减0x30后才能得到十进制0~9的数据
  9.                   RtcDate.Month =  (HLPUSART_RX_BUF[4]-0x30)*10+HLPUSART_RX_BUF[5]-0x30;
  10.                   RtcDate.Date =  (HLPUSART_RX_BUF[6]-0x30)*10+HLPUSART_RX_BUF[7]-0x30;
  11.                   RtcTime.Hours =  (HLPUSART_RX_BUF[8]-0x30)*10+HLPUSART_RX_BUF[9]-0x30;
  12.                   RtcTime.Minutes =  (HLPUSART_RX_BUF[10]-0x30)*10+HLPUSART_RX_BUF[11]-0x30;
  13.                   RtcTime.Seconds =  (HLPUSART_RX_BUF[12]-0x30)*10+HLPUSART_RX_BUF[13]-0x30;
  14.                   //写入格式RTC_FORMAT_BCD或RTC_FORMAT_BIN,确保写入及读取数据格式一致即可
  15.                     if (HAL_RTC_SetTime(&hrtc,  &RtcTime, RTC_FORMAT_BCD) != HAL_OK)//将数据写入RTC程序
  16.                     {
  17.                         printf("写入时间失败!\r\n"); //显示写入失败
  18.                     }else if (HAL_RTC_SetDate(&hrtc,  &RtcDate, RTC_FORMAT_BCD) != HAL_OK)//将数据写入RTC程序
  19.                     {
  20.                         printf("写入日期失败!\r\n"); //显示写入失败
  21.                     }else printf("写入成功!\r\n");//显示写入成功
  22.                     time_set=0;
  23.                     }
  24.                     HLPUSART_RX_STA=0;//接收错误,重新开始
  25.                     HAL_Delay(100);//等待
  26.             }
  27.             if(KEY_1())
  28.           {
  29.                     HAL_RTC_GetTime(&hrtc, &RtcTime,  RTC_FORMAT_BCD);//读出时间值
  30.           HAL_RTC_GetDate(&hrtc, &RtcDate,  RTC_FORMAT_BCD);//一定要先读时间后读日期
  31.           printf(" RTC实时时钟测试\r\n");
  32.           printf(" 实时时间:%04d-%02d-%02d  %02d:%02d:%02d  \r\n",2000+RtcDate.Year,
  33.                           RtcDate.Month, RtcDate.Date,RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds);//显示日期时间
  34.           }
  35.             if(KEY_2())
  36.           {
  37.                     printf(" 请输入设置时间,格式20221220183030,按回车键确定! \r\n");
  38.                     time_set=1;
  39.           }
  40.     /* USER CODE END WHILE */
复制代码

         注意和前面简介说过,确保时间和日期值之间的一致性,先处理时间(时分秒),在处理日期(年月日),在stm32l4xx_hal_rtc.c的HAL_RTC_GetTime函数注释中也有明确要求如下,“您必须在HAL_RTC_GetTime()之后调用HAL_RTC_GetDate()来解锁值以确保时间和日期值之间的一致性。读取RTC当前时间锁定日历阴影寄存器中的值,直到读取当前日期以确保时间和日期值之间的一致性。”:

27887a24a62d46a1b27a1e4efe8855d6.png


四、编译及测试
        和本专栏其他博文一样,设置好工程输出目标格式支持,配置好运行调试支持后,点击编译工程及运行按钮,完成工程编辑及下载。      

e451e4c7dd9b4c5fa03817fca447c916.png


        打开串口助手工具,连接到开发板,测试

        【1】按键KEY1,读取RTC时间,读取出的时间就去前面在cubeMX 设置的时间,并开始计数,因此分秒有差异。

fb4cb2634b1c469cabfdac88376d1c3b.png

         【2】按键KEY2设置时间,并按格式输入时间,然后在按键KEY1读取新的RTC时间,如下图所示,设置时间生效。

30dc865389204e619affac8dc1687db7.png

         【3】按开发板上的Reset按键,系统复位,再按键KEY2读取RTC时间,时间依然正常计数

1862d58464bb469ab8f231cf549be7c0.png


         【4】断电一会,然后重新上电,再按键KEY2读取RTC时间,时间恢复到CubeMX设置的初始时间(即为断电后,原来写入的标识无法保持,HAL_RTCEx_BKUPRead读取标记失败重新初始化)。

6d8f19e2a6ac4eaf959ccae14e9e980d.png

————————————————
版权声明:py_free-物联智能如有侵权请联系删除



收藏 评论1 发布时间:2023-4-6 16:45

举报

1个回答
SMT64 回答时间:7 天前

瞎J13乱发,傻福玩意

F1系列的RTC是计时器,你帖子里用的不是STM32L496VGT吗?看过这个芯片的数据手册吗?MD,抄都TM抄不明白,下边这张图看得懂吗?傻福

image.png

image.png
image.png

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版