丁丁 软硬件、前后端全栈开发者。热爱,并将终身学习有关计算机的一切 mdi-home 首页 mdi-language-go Golang mdi-cpu-32-bit STM32 mdi-format-list-bulleted-square 文章列表 mdi-share-variant 分享 mdi-book-open-page-variant 推荐书单 mdi-chat-processing 碎语 mdi-help-box ISSUE About Me mdi-message 知乎 mdi-sina-weibo 微博 mdi-television-play bilibili mdi-rss-box RSS

丁丁的个人网站

mdi-heart mdi-login mdi-logout mdi-settings

STM32F103xx系列芯片使用HAL+CubeMX解决RTC日历问题

STM32目前大家都在使用HAL库和CubeMX开发环境,确实非常方便,我个人也是非常喜欢。但最近碰到一个F103上的问题,在STM32F1系列的芯片上,虽然在CubeMX工具里可以设置RTC日历和时间,但实际上只有时间生效,日期是不生效的,重启后会日期数据会丢失。这个问题,在F4以及F7上不会发生。 F1的这个问题由来已久,百度搜一下能搜到N多关于此问题的结果。很多结论都是指向F1系列的RTC只是一个计时器,言下之意,F4/F7中比较令人舒服的RTC可能是因为其内部拥有更多的资源。个中原因我也不想深究,但既然是一个32bit的计数器,只要它能正常在后备电池的供电下每隔1s自增1,我们理论上就应该能通过它计算一百多年。以秒为单位的32bit数据大小,可以表示136年的时间跨度,这完全够我们使用了。 HAL库在F1系列上至今没有帮我们实现日历功能,感觉就是一个字:懒。 那我们只有自己动手了,在深究了HAL库中关于RTC的几个内部函数后,我自己在CubeMX生成的`rtc.c`文件的基础上,做了完善,整理出了自己的一份`rtc.c`文件,如下: ```C /* * Copyright (c) 2006-2020, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-03-18 newflydd@gmail.com the first version */ #include "app_config.h" #include "rtc.h" #define RTC_WORD 0xA55A static const u8 MONTH_DAY[] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static void MX_RTC_Init(); RTC_HandleTypeDef hrtc; static HAL_StatusTypeDef RTC_WriteTimeCounter(uint32_t TimeCounter); static uint32_t RTC_ReadTimeCounter(); static HAL_StatusTypeDef RTC_EnterInitMode(); static HAL_StatusTypeDef RTC_ExitInitMode(); static u8 getMonthDay(u8 year, u8 month); void InitRTC() { MX_RTC_Init(); } /* RTC init function */ void MX_RTC_Init(void) { /** Initialize RTC Only */ hrtc.Instance = RTC; hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND; hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM; if (HAL_RTC_Init(&hrtc) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN Check_RTC_BKUP */ if ( RTC_WORD == HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1)) { return; } RtcSetDateTime(20, 3, 22, 15, 28, 00); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, RTC_WORD); } void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle) { if (rtcHandle->Instance == RTC) { /* USER CODE BEGIN RTC_MspInit 0 */ /* USER CODE END RTC_MspInit 0 */ HAL_PWR_EnableBkUpAccess(); /* Enable BKP CLK enable for backup registers */ __HAL_RCC_BKP_CLK_ENABLE() ; /* RTC clock enable */ __HAL_RCC_RTC_ENABLE(); /* USER CODE BEGIN RTC_MspInit 1 */ /* USER CODE END RTC_MspInit 1 */ } } void RtcSetDateTime(u8 year, u8 month, u8 date, u8 hour, u8 minute, u8 second) { RTC_WriteTimeCounter((u32)hour * 3600 + (u32)minute * 60 + second + GetDiffDay(101, (u32)year * 10000 + (u32)month * 100 + date) * 3600 * 24); } u32 RtcGetDate() { u32 timecounter = RTC_ReadTimeCounter(); return CalculateDate(101, timecounter / (3600 * 24)); } void RtcGetDateTime(u8* year, u8* month, u8* day, u8* hour, u8* minute, u8* sec) { u32 timecounter = RTC_ReadTimeCounter(); u32 date = timecounter / (3600 * 24); if (hour != NULL && minute != NULL && sec != NULL) { *hour = timecounter / 3600 % 24; *minute = timecounter / 60 % 60; *sec = timecounter % 60; } if (year != NULL && month != NULL && day != NULL) { date = CalculateDate(101, date); *year = date / 10000 % 100; *month = date / 100 % 100; *day = date % 100; } } /** * @brief Read the time counter available in RTC_CNT registers. * @param hrtc pointer to a RTC_HandleTypeDef structure that contains * the configuration information for RTC. * @retval Time counter */ static uint32_t RTC_ReadTimeCounter() { uint16_t high1 = 0U, high2 = 0U, low = 0U; uint32_t timecounter = 0U; high1 = READ_REG(hrtc.Instance->CNTH & RTC_CNTH_RTC_CNT); low = READ_REG(hrtc.Instance->CNTL & RTC_CNTL_RTC_CNT); high2 = READ_REG(hrtc.Instance->CNTH & RTC_CNTH_RTC_CNT); if (high1 != high2) { /* In this case the counter roll over during reading of CNTL and CNTH registers, read again CNTL register then return the counter value */ timecounter = (((uint32_t) high2 << 16U) | READ_REG(hrtc.Instance->CNTL & RTC_CNTL_RTC_CNT)); } else { /* No counter roll over during reading of CNTL and CNTH registers, counter value is equal to first value of CNTL and CNTH */ timecounter = (((uint32_t) high1 << 16U) | low); } return timecounter; } /** * @brief Write the time counter in RTC_CNT registers. * @param hrtc pointer to a RTC_HandleTypeDef structure that contains * the configuration information for RTC. * @param TimeCounter: Counter to write in RTC_CNT registers * @retval HAL status */ static HAL_StatusTypeDef RTC_WriteTimeCounter(uint32_t TimeCounter) { HAL_StatusTypeDef status = HAL_OK; /* Set Initialization mode */ if (RTC_EnterInitMode() != HAL_OK) { status = HAL_ERROR; } else { /* Set RTC COUNTER MSB word */ WRITE_REG(hrtc.Instance->CNTH, (TimeCounter >> 16U)); /* Set RTC COUNTER LSB word */ WRITE_REG(hrtc.Instance->CNTL, (TimeCounter & RTC_CNTL_RTC_CNT)); /* Wait for synchro */ if (RTC_ExitInitMode() != HAL_OK) { status = HAL_ERROR; } } return status; } static HAL_StatusTypeDef RTC_EnterInitMode() { uint32_t tickstart = 0U; tickstart = HAL_GetTick(); /* Wait till RTC is in INIT state and if Time out is reached exit */ while ((hrtc.Instance->CRL & RTC_CRL_RTOFF) == (uint32_t) RESET) { if ((HAL_GetTick() - tickstart) > RTC_TIMEOUT_VALUE) { return HAL_TIMEOUT; } } /* Disable the write protection for RTC registers */ __HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc); return HAL_OK; } static HAL_StatusTypeDef RTC_ExitInitMode() { uint32_t tickstart = 0U; /* Disable the write protection for RTC registers */ __HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc); tickstart = HAL_GetTick(); /* Wait till RTC is in INIT state and if Time out is reached exit */ while ((hrtc.Instance->CRL & RTC_CRL_RTOFF) == (uint32_t) RESET) { if ((HAL_GetTick() - tickstart) > RTC_TIMEOUT_VALUE) { return HAL_TIMEOUT; } } return HAL_OK; } static u8 getMonthDay(u8 year, u8 month) { if (month == 2) { if (((2000 + year) % 4 == 0 && (2000 + year) % 100 != 0) || (2000 + year) % 400 == 0) { return 29; } else { return 28; } } else return MONTH_DAY[month - 1]; } u32 GetDiffDay(u32 dateBegin, u32 dateEnd) { u32 sum = 0; u8 year1, year2, month1, month2, day1, day2 = 0; year1 = dateBegin / 10000; year2 = dateEnd / 10000; month1 = (dateBegin % 10000) / 100; month2 = (dateEnd % 10000) / 100; day1 = dateBegin % 100; day2 = dateEnd % 100; if(dateBegin > dateEnd) return 0; while (1) { if (year2 == year1 && month2 == month1) { sum += day2 - day1; break; } else { sum += day2; month2--; if (month2 < 1 || month2 > 12) { month2 = 12; year2--; } day2 = getMonthDay(year2, month2); } } return sum; } /* calculate date from 2000-01-01 */ u32 CalculateDate(u32 fromDate, u32 diffDay) { u8 year1, month1, day1, day2 = 0; year1 = fromDate / 10000 % 100; month1 = fromDate / 100 % 100; day1 = fromDate % 100; do { if (month1 > 12) { month1 = month1 - 12; year1 = year1 + 1; } day2 = getMonthDay(year1, month1); if (diffDay + day1 > day2) { /* next month */ diffDay = diffDay - (day2 - day1); month1++; day1 = 0; } else { /* current month */ day1 += diffDay; break; } } while (1); return (u32) year1 * 10000 + (u32) month1 * 100 + (u32) day1; } ``` rtc.h: ```C /* * Copyright (c) 2006-2020, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-03-18 newflydd@gmail.com the first version */ #ifndef APPLICATIONS_CORE2_RTC_H_ #define APPLICATIONS_CORE2_RTC_H_ #include "main.h" void InitRTC(); u32 RtcGetDate(); void RtcGetDateTime(u8* year, u8* month, u8* day, u8* hour, u8* minute, u8* sec); void RtcSetDateTime(u8 year, u8 month, u8 day, u8 hour, u8 minute, u8 sec); u32 GetDiffDay(u32 dateBegin, u32 dateEnd); u32 CalculateDate(u32 fromDate, u32 diffDay); #endif /* APPLICATIONS_CORE2_RTC_H_ */ ``` 这样外部就可以通过以上函数来操作RTC时间和日期了,非常方便。
mdi-chevron-left Last:STM32与PN532构建NFC近场通信指南(五):一种适用于STM32的通用串口通信架构及与PN532的通信实践 Next:当前STM32开发的几种最流行的开发环境对比 mdi-chevron-right
Tags JAVA Golang STM32 Links 丁丁喜欢这些网站或者博客 MCU起航 JBlog Advert
Tags JAVA Golang STM32 Links 丁丁喜欢这些网站或者博客 MCU起航 JBlog Advert
{{ $store.state.notice.msg }}