由于STM32F103系列 内部的RTC只是普通的定时器 无法作为断电保存使用,这里将演示一种低成本高兼容性的解决方案,重定向启用time.h头文件,将规范工程结构 节省大量时间
time.h头文件介绍
tm 结构的定义如下:
struct tm {
int tm_sec; /* 秒,范围从 0 到 59 */
int tm_min; /* 分,范围从 0 到 59 */
int tm_hour; /* 小时,范围从 0 到 23 */
int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
int tm_mon; /* 月,范围从 0 到 11 */
int tm_year; /* 自 1900 年起的年数 */
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时 */
};
关键函数功能介绍
clock_t clock(void)//返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。
double difftime(time_t time1, time_t time2)返回 time1 和 time2 之间相差的秒数 (time1-time2)。
time_t time(time_t *timer)计算当前日历时间,并把它编码成 time_t 格式。
struct tm *localtime(const time_t *timer)timer 的值被分解为 tm 结构,并用本地时区表示。
time_t mktime(struct tm *timeptr)把 timeptr 所指向的结构转换为一个依据本地时区的 time_t 值。
需要重写两个接口
// rtc.c
_ARMABI time_t time(time_t * timer)
{
uint32_t nTime=RTC_ReadTimeCounter(&hrtc);
if(timer!=NULL)
{
*timer=nTime;
}
return nTime;
}
_ARMABI clock_t clock(void)
{
return HAL_GetTick();
}
//用户增加 设置时间接口
//设置系统时间 返回时间戳
time_t setTime(struct tm * timeptr)
{
time_t nTime=0;
if(timeptr!=NULL)
{
nTime=mktime(timeptr);
RTC_WriteTimeCounter(&hrtc,nTime);
}
return nTime;
}
重写说明 通过翻阅HAL_RTC源码可知 RTC_ReadTimeCounter RTC_WriteTimeCounter 是真正的用于操作RTC计数器的,cloc() 函数在time.h中的定义就是CPU计数,这里为了规范也一并重写了
RTC_WriteTimeCounter RTC_WriteTimeCounter 这两个函数在 stm32f1xx_hal_rtc.c里被定义为static类型 直接拷贝出来放 cubeMX生成的rtc.c里就行了 后续也不需要直接调用它,所以仍然采用static 定义即可
调用使用演示
//test RTC
LOG_D("\r\n\r\n time.h test");
clock_t ck= clock();
LOG_D("clock:ck:%d",ck);
time_t timer= time(NULL);
LOG_D("time:tm:%d",timer);
/* Read the time counter*/
struct tm st_tm= *gmtime(&timer);
LOG_D("localtime:%04d-%02d-%02d %02d:%02d:%02d",st_tm.tm_year+1900,st_tm.tm_mon,st_tm.tm_mday,st_tm.tm_hour,st_tm.tm_min,st_tm.tm_sec);
初始化流程演示
#define RTC_BKP 0x1235
if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)== RTC_BKP)
{
LOG_D("RTC ok");
return ;
}
else
{
LOG_D("RTC need init");
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,RTC_BKP);
}
struct tm nTime={/*tm_sec*/0,/*tm_min*/0,/*tm_hour*/12,/*tm_mday*/22,
/*tm_mon*/9,/*tm_year */2022-1900};
setTime(&nTime);
待解决问题
- gmtime 无法使用
- 获取到的时间是1970年开始的 不方便直接使用
最后
修改CubeMX生成的rtc.c 完成源码
/**
******************************************************************************
* @file rtc.c
* @brief This file provides code for the configuration
* of the RTC instances.
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2022 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "rtc.h"
/* USER CODE BEGIN 0 */
static uint32_t RTC_ReadTimeCounter(RTC_HandleTypeDef *hrtc);
static HAL_StatusTypeDef RTC_WriteTimeCounter(RTC_HandleTypeDef *hrtc, uint32_t TimeCounter);
/* USER CODE END 0 */
RTC_HandleTypeDef hrtc;
/* RTC init function */
void MX_RTC_Init(void)
{
/* USER CODE BEGIN RTC_Init 0 */
/* USER CODE END RTC_Init 0 */
/* USER CODE BEGIN RTC_Init 1 */
/* USER CODE END RTC_Init 1 */
/** 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 */
#define RTC_BKP 0x1235
if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)== RTC_BKP)
{
LOG_D("RTC ok");
return ;
}
else
{
LOG_D("RTC need init");
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,RTC_BKP);
}
struct tm nTime={/*tm_sec*/0,/*tm_min*/0,/*tm_hour*/12,/*tm_mday*/22,
/*tm_mon*/9,/*tm_year */2022-1900};
setTime(&nTime);
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
/* USER CODE BEGIN RTC_Init 2 */
/* USER CODE END RTC_Init 2 */
}
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 HAL_RTC_MspDeInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspDeInit 0 */
/* USER CODE END RTC_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_RTC_DISABLE();
/* USER CODE BEGIN RTC_MspDeInit 1 */
/* USER CODE END RTC_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
_ARMABI time_t time(time_t * timer)
{
uint32_t nTime=RTC_ReadTimeCounter(&hrtc);
if(timer!=NULL)
{
*timer=nTime;
}
return nTime;
}
_ARMABI clock_t clock(void)
{
return HAL_GetTick();
}
//设置系统时间 返回时间戳
time_t setTime(struct tm * timeptr)
{
time_t nTime=0;
if(timeptr!=NULL)
{
nTime=mktime(timeptr);
RTC_WriteTimeCounter(&hrtc,nTime);
}
return nTime;
}
/**
* @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(RTC_HandleTypeDef *hrtc)
{
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 Enters the RTC Initialization mode.
* @param hrtc pointer to a RTC_HandleTypeDef structure that contains
* the configuration information for RTC.
* @retval HAL status
*/
static HAL_StatusTypeDef RTC_EnterInitMode(RTC_HandleTypeDef *hrtc)
{
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;
}
/**
* @brief Exit the RTC Initialization mode.
* @param hrtc pointer to a RTC_HandleTypeDef structure that contains
* the configuration information for RTC.
* @retval HAL status
*/
static HAL_StatusTypeDef RTC_ExitInitMode(RTC_HandleTypeDef *hrtc)
{
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;
}
/**
* @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(RTC_HandleTypeDef *hrtc, uint32_t TimeCounter)
{
HAL_StatusTypeDef status = HAL_OK;
/* Set Initialization mode */
if (RTC_EnterInitMode(hrtc) != 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(hrtc) != HAL_OK)
{
status = HAL_ERROR;
}
}
return status;
}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
为了避免 别人调用HAL_RTC接口 ,头文件也进行必要改动
/**
******************************************************************************
* @file rtc.h
* @brief This file contains all the function prototypes for
* the rtc.c file
******************************************************************************
* @attention
*
* <h2><center>© Copyright (c) 2022 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __RTC_H__
#define __RTC_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
#define __CLK_TCK 1000
#include <time.h>
/* USER CODE END Includes */
extern RTC_HandleTypeDef hrtc;
/* USER CODE BEGIN Private defines */
//don't use them
#define HAL_RTC_SetTime
#define HAL_RTC_GetTime
#define HAL_RTC_SetDate
#define HAL_RTC_GetDate
/* USER CODE END Private defines */
void MX_RTC_Init(void);
/* USER CODE BEGIN Prototypes */
//重写这部分功能
//获得系统滴答时钟 ms
extern _ARMABI clock_t clock(void);
//获得自1900-01-01 00:00:00 秒
extern _ARMABI time_t time(time_t * /*timer*/);
//设置系统时间 返回时间戳
time_t setTime(struct tm * timeptr);
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __RTC_H__ */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/