mdi-chevron-left
Last:STM32与PN532构建NFC近场通信指南(五):一种适用于STM32的通用串口通信架构及与PN532的通信实践
Next:当前STM32开发的几种最流行的开发环境对比
mdi-chevron-right
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