首先,因为的LSE无法启振,所以我使用LSI当作RTCCLK,LSI无法由备用电源供电,故主电源掉电时,RTC走时会暂停。
hal库相关函数
时分秒
typedef struct
{
uint8_t Hours; /*!< Specifies the RTC Time Hour.
This parameter must be a number between Min_Data = 0 and Max_Data = 23 */
uint8_t Minutes; /*!< Specifies the RTC Time Minutes.
This parameter must be a number between Min_Data = 0 and Max_Data = 59 */
uint8_t Seconds; /*!< Specifies the RTC Time Seconds.
This parameter must be a number between Min_Data = 0 and Max_Data = 59 */
} RTC_TimeTypeDef;
闹钟结构体
typedef struct
{
RTC_TimeTypeDef AlarmTime; /*!< Specifies the RTC Alarm Time members */
uint32_t Alarm; /*!< Specifies the alarm ID (only 1 alarm ID for STM32F1).
This parameter can be a value of @ref RTC_Alarms_Definitions */
} RTC_AlarmTypeDef;
初始化结构体
typedef struct
{
uint32_t AsynchPrediv; /*异步预分频器,对RTC时钟源进行分频*/
uint32_t OutPut; /*时钟输出*/
} RTC_InitTypeDef;
日期结构体
typedef struct
{
uint8_t WeekDay; /*!< Specifies the RTC Date WeekDay (not necessary for HAL_RTC_SetDate).
This parameter can be a value of @ref RTC_WeekDay_Definitions */
uint8_t Month; /*!< Specifies the RTC Date Month (in BCD format).
This parameter can be a value of @ref RTC_Month_Date_Definitions */
uint8_t Date; /*!< Specifies the RTC Date.
This parameter must be a number between Min_Data = 1 and Max_Data = 31 */
uint8_t Year; /*!< Specifies the RTC Date Year.
This parameter must be a number between Min_Data = 0 and Max_Data = 99 */
} RTC_DateTypeDef;
句柄
#if (USE_HAL_RTC_REGISTER_CALLBACKS == 1)
typedef struct __RTC_HandleTypeDef
#else
typedef struct
#endif /* (USE_HAL_RTC_REGISTER_CALLBACKS) */
{
RTC_TypeDef *Instance; /*外设地址*/
RTC_InitTypeDef Init; /*初始化结构体*/
RTC_DateTypeDef DateToUpdate; /*初始化上电时间*/
HAL_LockTypeDef Lock; /*配置锁存*/
__IO HAL_RTCStateTypeDef State; /*当前状态*/
#if (USE_HAL_RTC_REGISTER_CALLBACKS == 1)
void (* AlarmAEventCallback)(struct __RTC_HandleTypeDef *hrtc); /*!< RTC Alarm A Event callback */
void (* Tamper1EventCallback)(struct __RTC_HandleTypeDef *hrtc); /*!< RTC Tamper 1 Event callback */
void (* MspInitCallback)(struct __RTC_HandleTypeDef *hrtc); /*!< RTC Msp Init callback */
void (* MspDeInitCallback)(struct __RTC_HandleTypeDef *hrtc); /*!< RTC Msp DeInit callback */
#endif /* (USE_HAL_RTC_REGISTER_CALLBACKS) */
} RTC_HandleTypeDef;
初始化函数
HAL_StatusTypeDef HAL_RTC_Init(RTC_HandleTypeDef *hrtc);
HAL_StatusTypeDef HAL_RTC_DeInit(RTC_HandleTypeDef *hrtc);
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc);
void HAL_RTC_MspDeInit(RTC_HandleTypeDef *hrtc);
设置日期时间
HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);
HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format);
HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format);
闹钟相关函数
HAL_StatusTypeDef HAL_RTC_SetAlarm(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format);
HAL_StatusTypeDef HAL_RTC_SetAlarm_IT(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format);
HAL_StatusTypeDef HAL_RTC_DeactivateAlarm(RTC_HandleTypeDef *hrtc, uint32_t Alarm);
HAL_StatusTypeDef HAL_RTC_GetAlarm(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Alarm, uint32_t Format);
void HAL_RTC_AlarmIRQHandler(RTC_HandleTypeDef *hrtc);
HAL_StatusTypeDef HAL_RTC_PollForAlarmAEvent(RTC_HandleTypeDef *hrtc, uint32_t Timeout);
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc);
hal库实现江科大
首先就是对RTC的初始化,得开启BKP和PWR先关时钟,CubeMx帮我们开启了。
开启RTC时钟源,在时钟树那边设置RTC的时钟源,我选择的是LSI.
BKP的相关函数在RTC和RTC_EX里面,没有专门的hal_BKP文件。
读写BKP函数
void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister, uint32_t Data)
uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister)
对于江科大的相关函数,首先标准库里面很多的函数hal库里面都没有,所以得找个替换的方式,有些相关函数对于hal库是不起作用的,我们也得将他们删去。
//江科大RTC初始化函数
void MyRTC_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);//开启BKP,PWR时钟
PWR_BackupAccessCmd(ENABLE);//使能通道
if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)//判断BKP_DR1内容
{
RCC_LSICmd(ENABLE);//开启LSI时钟
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);//等待时钟就绪
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);//配置LSI到RTC的时钟
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();//等待时钟同步
RTC_WaitForLastTask();
RTC_SetPrescaler(40000 - 1);//配置预分频值
RTC_WaitForLastTask();//等待上个工作完成
MyRTC_SetTime();//设置时间
BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);//写入BKP_DR1
}
else
{
RCC_LSICmd(ENABLE); //即使不是第一次配置,也需要再次开启LSI时钟
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();
RTC_WaitForLastTask();
}
}
这段代码里面,对于LSI和RTC的时钟是不需要我们进行配置的,CubeMX里面已经直接帮我们配置好且开启时钟了,我们只需要先开启BKP和PWR时钟,再对BKP进行读写和设置时间即可。
对于时分秒的计算,RTC的相关函数HAL_RTC_GetTime在函数里面对时间戳进行计算得到对应的时分秒,我们开启秒中断让他的时间戳一秒自增一次就行了。(内部自动进行自增)
秒中断 __HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC);也在rtc_ex文件里面
hal库的日期读写则是通过读取RTC内部的寄存器内容来获取日期,日期也是在外面写入之后RTC内部自己进行维护的。
RTC_DateTypeDef GetData; //获取日期结构体
RTC_TimeTypeDef GetTime; //获取时间结构体
int main(void)
{
HAL_Init();
SystemClock_Config();
OLED_Init();
MX_GPIO_Init();
MX_RTC_Init();
OLED_ShowString(1, 1, "Date:XXXX-XX-XX");
OLED_ShowString(2, 1, "Time:XX:XX:XX");
// OLED_ShowString(3, 1, "CNT :");
// OLED_ShowString(4, 1, "DIV :");
while (1)
{
HAL_RTC_GetTime(&hrtc,&GetTime,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&GetData,RTC_FORMAT_BIN);
OLED_ShowNum(1, 6,(2000+GetData.Year), 4); //显示MyRTC_Time数组中的时间值,年
OLED_ShowNum(1, 11,GetData.Month, 2); //月
OLED_ShowNum(1, 14, GetData.Date, 2); //日
OLED_ShowNum(2, 6, GetTime.Hours, 2); //时
OLED_ShowNum(2, 9, GetTime.Minutes, 2); //分
OLED_ShowNum(2, 12, GetTime.Seconds, 2); //秒
HAL_Delay(1000);
// OLED_ShowNum(3, 6, RTC_GetCounter(), 10); //显示32位的秒计数器,没有相关函数,但是可以通过读写寄存器,或者对时间进行逻辑推导来实现
// OLED_ShowNum(4, 6, RTC_GetDivider(), 10); //显示余数寄存器
}
}
日期的存入读取有问题,我们把日期存入BKP里面,第一次上电初始化的时候写入,其他时间上电从BKP里面读取,这样也能实现日期的正常读写。
void MX_RTC_Init(void)
{
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef DateToUpdate = {0};
__HAL_RCC_BKP_CLK_ENABLE(); //开启后备区域时钟
__HAL_RCC_PWR_CLK_ENABLE(); //开启电源时钟
hrtc.Instance = RTC;
hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1) != 0xA5A5){
sTime.Hours = 15;
sTime.Minutes = 51;
sTime.Seconds = 0;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
__HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC);
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1, 0xA5A5);//写入DR1的判断数据
DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;
DateToUpdate.Month = RTC_MONTH_JUNE;
DateToUpdate.Date = 02;
DateToUpdate.Year = 25;
if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, (uint16_t)DateToUpdate.Year);
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, (uint16_t)DateToUpdate.Month);
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, (uint16_t)DateToUpdate.Date);
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, (uint16_t)DateToUpdate.WeekDay);
}
else{
DateToUpdate.Year = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
DateToUpdate.Month = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);
DateToUpdate.Date = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4);
DateToUpdate.WeekDay = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR5);
if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
__HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC); //开启RTC时钟秒中断
}
}
代码参考了小璇的相关代码
这样也能实现江科大的RTC时钟了