在做stm32f407rtc实验时,代码是用cubemx生成的,通过串口打印出时间值,1s打印一次。但是结果与料想中的不一致。
发现打印出来的值一直不更新。按下复位键,后时间会更新一次。
一开始一直是51s,按下复位键,发现时间更新了,再按下复位键,时间再次更新。
后来直接在线debug,运行后,一开始时间也是不会更新的,但是后来将hrtc结构体放到观察窗口,并点击了instance成员后,奇迹出现了,发现时间能够正常更新了。
这就非常奇怪了,甚至怀疑了单片机出问题了,结果换了一块板子,还是同样的现象基本可以排除是板子的问题。时间能更新说明晶振都没有啥问题。主函数代码如下;
while (1)
{
HAL_RTC_GetTime(&hrtc,&RTC_TimeStruct,RTC_FORMAT_BIN); printf("Time:%02d:%02d:%02d\r\n",RTC_TimeStruct.Hours,RTC_TimeStruct.Minutes,RTC_TimeStruct.Seconds);
HAL_Delay(1000);
HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
发现了这个奇怪的现象后,感觉很苦恼,先后怀疑了这些情况
后面经过逐一排查,发现没有问题。并且与正点原子的407代码对比了,也没找出问题所在。后来找百度了一下,找到了https://blog.csdn.net/ZLK1214/article/details/103743152这个博客,发现问题现象一致,按照其操作,解决。
问题的表面原因是,我主要函数里面只读取了time信息,没有读取date信息。当把主函数代码改为如下:
while (1)
{
HAL_RTC_GetTime(&hrtc,&RTC_TimeStruct,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&RTC_DateStruct,RTC_FORMAT_BIN);
printf("Time:%02d:%02d:%02d %02d-%02d-%02d %d \r\n",RTC_TimeStruct.Hours,RTC_TimeStruct.Minutes,RTC_TimeStruct.Seconds,\
RTC_DateStruct.Year,RTC_DateStruct.Month,RTC_DateStruct.Date,RTC_DateStruct.WeekDay);
HAL_Delay(1000);
HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
出现需要读取time 和date后,时间才会更新问题的原因是。为了保证数据读取的准确性,rtc设置了一个影子寄存器,每次读取时间和日期的时候,都是读取的这个影子寄存器,只有当读取了日期后,影子寄存器才会更新数据。具体的在sstm32f4xx中文参考手册的23.3.6中有入下描述。
那么有没有办法,我只想读取时间值,而不想读取日期值呢。有两种方法:
配置rtc的时候,需要将RTC_CR寄存器的BYPSHAD位设置为1,这样可以直接读取time值。
配置方法如下:
添加HAL_RTCEx_EnableBypassShadow(&hrtc);将BYPSHAD置1
初始化rtc;
void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
if(rtcHandle->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspInit 0 */
__HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟PWR
HAL_PWR_EnableBkUpAccess();//取消备份区域写保护
/* USER CODE END RTC_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
HAL_RTCEx_EnableBypassShadow(&hrtc);
/* RTC clock enable */
__HAL_RCC_RTC_ENABLE();
/* USER CODE BEGIN RTC_MspInit 1 */
/* USER CODE END RTC_MspInit 1 */
}
}
主函数值读取time值
while (1)
{
HAL_RTC_GetTime(&hrtc,&RTC_TimeStruct,RTC_FORMAT_BIN);
// HAL_RTC_GetDate(&hrtc,&RTC_DateStruct,RTC_FORMAT_BIN);
printf("Time:%02d:%02d:%02d %02d-%02d-%02d %d \r\n",RTC_TimeStruct.Hours,RTC_TimeStruct.Minutes,RTC_TimeStruct.Seconds,\
RTC_DateStruct.Year,RTC_DateStruct.Month,RTC_DateStruct.Date,RTC_DateStruct.WeekDay);
// printf("%02x:%02x:%02x\r\n", (RTC->TR >> 16) & 0xff, (RTC->TR >> 8) & 0xff, RTC->TR & 0xff);
HAL_Delay(1000);
HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
按照上面的博客中,可以直接读取RTC值。也可以正常显示。
printf("%02x:%02x:%02x\r\n", (RTC->TR >> 16) & 0xff, (RTC->TR >> 8) & 0xff, RTC->TR & 0xff);
花了将近一天半的时间解决了这个问题。虽然找到了直接读取time值的方法,不过最好还是按照stm要求的,将time和date同时读取。这样准确性会高很多,毕竟影子寄存器里的时间和日期值是同一时刻的。保证了数据的准确性。