RTC:实时时钟
- 1、实时时钟
- 2、闹钟中断
- 3、秒中断
- 4、输出功能
- 5、BKP的读写
- 6、BKP的侵入事件
1、实时时钟
①RTC.c
#include "RTC.h"
/**
* @brief:RTC初始化函数
*/
RCC_PeriphCLKInitTypeDef RTCPeriphClkInit; //RTC时钟配置结构体
RTC_HandleTypeDef hrtc; //RTC配置结构体
void RTC_Init(void)
{
/* 配置RTC的时钟源 */
RTCPeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC; //需要配置的外设:RTC
RTCPeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; //RTC时钟源:LSE(外部低速时钟)
HAL_RCCEx_PeriphCLKConfig(&RTCPeriphClkInit);
/* RTC的初始化配置 */
__HAL_RCC_RTC_ENABLE(); //使能RTC的时钟
__HAL_RCC_PWR_CLK_ENABLE(); //使能PWR的时钟
__HAL_RCC_BKP_CLK_ENABLE(); //使能BKP的时钟
hrtc.Instance = RTC; //选择RTC
hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND; //时钟源的分频值,1Hz
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE; //无输出
HAL_RTC_Init(&hrtc);
}
/**
* @brief:HAL_RTC_Init()调用函数
*/
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
/* 其他的配置 */
}
/**
* @brief:日期转换为时间戳秒函数
*/
void Time_Set(int Year,int Mon,int Day,int Hour,int Min,int Sec)
{
struct tm DayData; //定义结构体变量DayData
DayData.tm_year = Year - 1900; //设置为2025年
DayData.tm_mon = Mon; //设置为1月
DayData.tm_mday = Day; //设置为2号
DayData.tm_hour = Hour; //设置为20时
DayData.tm_min = Min; //设置为04分
DayData.tm_sec = Sec; //设置为40秒
time_t temp = mktime(&DayData); //将设置的日期转换为时间挫数据
__HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc); //关闭RTC写保护
WRITE_REG(hrtc.Instance->CNTH,(temp >> 16)); //将转换好的时间挫写入RTC计数寄存器高16位
WRITE_REG(hrtc.Instance->CNTL,temp & 0x0000FFFF); //将转换好的时间挫写入RTC计数寄存器低16位
__HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc); //开启RTC写保护
}
/**
* @brief:将RTC中的计数器寄存器值转换为日期
*/
struct tm* Time_Get(void)
{
/* 读取数据寄存器的值 */
time_t temp = (READ_REG(hrtc.Instance->CNTH) << 16) | READ_REG(hrtc.Instance->CNTL);
/* 将计数器的值转换为日期 */
return localtime(&temp);//将时间戳转换为本地的年月日时分秒
}
②RTC.h
#ifndef __RTC_H
#define __RTC_H
#include "stm32f1xx_hal.h"
#include <time.h>
extern RTC_HandleTypeDef hrtc; //RTC配置结构体
void RTC_Init(void);
void Time_Set(int Year,int Mon,int Day,int Hour,int Min,int Sec);
struct tm* Time_Get(void);
#endif
③main.c
#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "RTC.h"
#include "UART.h"
struct tm* Day;
int main(void){
HAL_Init();
HSE_RCC_Init();
UART1_Init(115200);
RTC_Init();
printf("代码测试\r\n");
Time_Set(2025,1,2,21,8,30);//2025-01-02 20:55:30
while(1){
Day = Time_Get();
printf("%d-%d-%d %d:%d:%d\r\n"
,Day->tm_year + 1900,Day->tm_mon,Day->tm_mday,Day->tm_hour,Day->tm_min,Day->tm_sec);
HAL_Delay(1000);
}
}
2、闹钟中断
①RTC.c
#include "RTC.h"
/**
* @brief:RTC初始化函数
*/
RCC_PeriphCLKInitTypeDef RTCPeriphClkInit; //RTC时钟配置结构体
RTC_HandleTypeDef hrtc; //RTC配置结构体
void RTC_Init(void)
{
/* 配置RTC的时钟源 */
RTCPeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC; //需要配置的外设:RTC
RTCPeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; //RTC时钟源:LSE(外部低速时钟)
HAL_RCCEx_PeriphCLKConfig(&RTCPeriphClkInit);
/* RTC的初始化配置 */
__HAL_RCC_RTC_ENABLE(); //使能RTC的时钟
__HAL_RCC_PWR_CLK_ENABLE(); //使能PWR的时钟
__HAL_RCC_BKP_CLK_ENABLE(); //使能BKP的时钟
hrtc.Instance = RTC; //选择RTC
hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND; //时钟源的分频值,1Hz
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE; //无输出
HAL_RTC_Init(&hrtc);
/* 开启闹钟中断 */
__HAL_RTC_ALARM_CLEAR_FLAG(&hrtc, RTC_FLAG_ALRAF); //清除闹钟标志
__HAL_RTC_ALARM_ENABLE_IT(&hrtc, RTC_IT_ALRA); //开启闹钟中断
__HAL_RTC_ALARM_EXTI_ENABLE_IT(); //开启闹钟外部中断线,EXTI17为闹钟事件
__HAL_RTC_ALARM_EXTI_ENABLE_RISING_EDGE(); //上升沿触发
HAL_NVIC_SetPriority(RTC_Alarm_IRQn,3,0);
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}
/**
* @brief:HAL_RTC_Init()调用函数
*/
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
/* 其他的配置 */
}
/**
* @brief:日期转换为时间挫函数
*/
void Time_Set(int Year,int Mon,int Day,int Hour,int Min,int Sec)
{
struct tm DayData; //定义结构体变量DayData
DayData.tm_year = Year - 1900; //设置为2025年
DayData.tm_mon = Mon; //设置为1月
DayData.tm_mday = Day; //设置为2号
DayData.tm_hour = Hour; //设置为20时
DayData.tm_min = Min; //设置为04分
DayData.tm_sec = Sec; //设置为40秒
time_t temp = mktime(&DayData); //将设置的日期转换为时间挫数据
__HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc); //关闭RTC写保护
WRITE_REG(hrtc.Instance->CNTH,(temp >> 16)); //将转换好的时间挫写入RTC计数寄存器高16位
WRITE_REG(hrtc.Instance->CNTL,temp & 0x0000FFFF); //将转换好的时间挫写入RTC计数寄存器低16位
__HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc); //开启RTC写保护
HAL_Delay(20); //等待写保护成功,必须要有
}
/**
* @brief:闹钟时间转换为时间挫函数
*/
void AlarmTime_Set(int Year,int Mon,int Day,int Hour,int Min,int Sec)
{
struct tm DayData; //定义结构体变量DayData
DayData.tm_year = Year - 1900; //设置为2025年
DayData.tm_mon = Mon; //设置为1月
DayData.tm_mday = Day; //设置为2号
DayData.tm_hour = Hour; //设置为20时
DayData.tm_min = Min; //设置为04分
DayData.tm_sec = Sec; //设置为40秒
time_t temp = mktime(&DayData); //将设置的日期转换为时间挫数据
__HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc); //关闭RTC写保护
WRITE_REG(hrtc.Instance->ALRH,(temp >> 16)); //将转换好的时间挫写入RTC闹钟寄存器高16位
WRITE_REG(hrtc.Instance->ALRL,temp & 0x0000FFFF); //将转换好的时间挫写入RTC闹钟寄存器低16位
__HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc); //开启RTC写保护
HAL_Delay(20); //等待写保护成功,必须要有
}
/**
* @brief:将RTC中的计数器寄存器值转换为日期
*/
struct tm* Time_Get(void)
{
/* 读取数据寄存器的值 */
time_t temp = (READ_REG(hrtc.Instance->CNTH) << 16) | READ_REG(hrtc.Instance->CNTL);
/* 将计数器的值转换为日期 */
return localtime(&temp);//将时间戳转换为本地的年月日时分秒
}
②RTC.h
#ifndef __RTC_H
#define __RTC_H
#include "stm32f1xx_hal.h"
#include <time.h>
extern RTC_HandleTypeDef hrtc; //RTC配置结构体
void RTC_Init(void);
void Time_Set(int Year,int Mon,int Day,int Hour,int Min,int Sec);
void AlarmTime_Set(int Year,int Mon,int Day,int Hour,int Min,int Sec);
struct tm* Time_Get(void);
#endif
③main.c
#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "RTC.h"
#include "UART.h"
struct tm* Day;
int main(void){
HAL_Init();
HSE_RCC_Init();
UART1_Init(115200);
RTC_Init();
printf("代码测试\r\n");
Time_Set(2025,1,2,21,8,30); //2025-01-02 20:55:30
AlarmTime_Set(2025,1,2,21,8,35); //闹钟时钟为2025-01-02 20:55:35
while(1){
Day = Time_Get();
printf("%d-%d-%d %d:%d:%d\r\n"
,Day->tm_year + 1900,Day->tm_mon,Day->tm_mday,Day->tm_hour,Day->tm_min,Day->tm_sec);
HAL_Delay(1000);
}
}
④stm32f1xx_it.c
#include "stm32f1xx_hal.h"
#include "stm32f1xx_it.h"
#include "RTC.h"
#include "UART.h"
/**
* @brief:RTC闹钟中断服务函数
*/
void RTC_Alarm_IRQHandler(void)
{
HAL_RTC_AlarmIRQHandler(&hrtc);
}
/******************* 下面的中断的回调函数 ***************************/
/**
* @brief:RTC闹钟中断回调函数函数
*/
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
printf("起床啦!\r\n");
}
3、秒中断
①RTC.c
#include "RTC.h"
/**
* @brief:RTC初始化函数
*/
RCC_PeriphCLKInitTypeDef RTCPeriphClkInit; //RTC时钟配置结构体
RTC_HandleTypeDef hrtc; //RTC配置结构体
void RTC_Init(void)
{
/* 配置RTC的时钟源 */
RTCPeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC; //需要配置的外设:RTC
RTCPeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; //RTC时钟源:LSE(外部低速时钟)
HAL_RCCEx_PeriphCLKConfig(&RTCPeriphClkInit);
/* RTC的初始化配置 */
__HAL_RCC_RTC_ENABLE(); //使能RTC的时钟
__HAL_RCC_PWR_CLK_ENABLE(); //使能PWR的时钟
__HAL_RCC_BKP_CLK_ENABLE(); //使能BKP的时钟
hrtc.Instance = RTC; //选择RTC
hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND; //时钟源的分频值,1Hz
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE; //无输出
HAL_RTC_Init(&hrtc);
/* 开启秒中断 */
HAL_RTCEx_SetSecond_IT(&hrtc); //开启秒中断
HAL_NVIC_SetPriority(RTC_IRQn,3,0);
HAL_NVIC_EnableIRQ(RTC_IRQn);
}
/**
* @brief:HAL_RTC_Init()调用函数
*/
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
/* 其他的配置 */
}
/**
* @brief:日期转换为时间挫函数
*/
void Time_Set(int Year,int Mon,int Day,int Hour,int Min,int Sec)
{
struct tm DayData; //定义结构体变量DayData
DayData.tm_year = Year - 1900; //设置为2025年
DayData.tm_mon = Mon; //设置为1月
DayData.tm_mday = Day; //设置为2号
DayData.tm_hour = Hour; //设置为20时
DayData.tm_min = Min; //设置为04分
DayData.tm_sec = Sec; //设置为40秒
time_t temp = mktime(&DayData); //将设置的日期转换为时间挫数据
__HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc); //关闭RTC写保护
WRITE_REG(hrtc.Instance->CNTH,(temp >> 16)); //将转换好的时间挫写入RTC计数寄存器高16位
WRITE_REG(hrtc.Instance->CNTL,temp & 0x0000FFFF); //将转换好的时间挫写入RTC计数寄存器低16位
__HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc); //开启RTC写保护
HAL_Delay(20); //等待写保护成功,必须要有
}
/**
* @brief:闹钟时间转换为时间挫函数
*/
void AlarmTime_Set(int Year,int Mon,int Day,int Hour,int Min,int Sec)
{
struct tm DayData; //定义结构体变量DayData
DayData.tm_year = Year - 1900; //设置为2025年
DayData.tm_mon = Mon; //设置为1月
DayData.tm_mday = Day; //设置为2号
DayData.tm_hour = Hour; //设置为20时
DayData.tm_min = Min; //设置为04分
DayData.tm_sec = Sec; //设置为40秒
time_t temp = mktime(&DayData); //将设置的日期转换为时间挫数据
__HAL_RTC_WRITEPROTECTION_DISABLE(&hrtc); //关闭RTC写保护
WRITE_REG(hrtc.Instance->ALRH,(temp >> 16)); //将转换好的时间挫写入RTC闹钟寄存器高16位
WRITE_REG(hrtc.Instance->ALRL,temp & 0x0000FFFF); //将转换好的时间挫写入RTC闹钟寄存器低16位
__HAL_RTC_WRITEPROTECTION_ENABLE(&hrtc); //开启RTC写保护
HAL_Delay(20); //等待写保护成功,必须要有
}
/**
* @brief:将RTC中的计数器寄存器值转换为日期
*/
struct tm* Time_Get(void)
{
/* 读取数据寄存器的值 */
time_t temp = (READ_REG(hrtc.Instance->CNTH) << 16) | READ_REG(hrtc.Instance->CNTL);
/* 将计数器的值转换为日期 */
return localtime(&temp);//将时间戳转换为本地的年月日时分秒
}
②RTC.h
#ifndef __RTC_H
#define __RTC_H
#include "stm32f1xx_hal.h"
#include <time.h>
extern RTC_HandleTypeDef hrtc; //RTC配置结构体
void RTC_Init(void);
void Time_Set(int Year,int Mon,int Day,int Hour,int Min,int Sec);
void AlarmTime_Set(int Year,int Mon,int Day,int Hour,int Min,int Sec);
struct tm* Time_Get(void);
#endif
③main.c
#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "RTC.h"
#include "UART.h"
#include "LED.h"
struct tm* Day;
int main(void){
HAL_Init();
HSE_RCC_Init();
UART1_Init(115200);
LED_GPIO_Init();
RTC_Init();
printf("代码测试\r\n");
Time_Set(2025,1,2,21,8,30); //2025-01-02 20:55:30
while(1){
Day = Time_Get();
printf("%d-%d-%d %d:%d:%d\r\n"
,Day->tm_year + 1900,Day->tm_mon,Day->tm_mday,Day->tm_hour,Day->tm_min,Day->tm_sec);
HAL_Delay(1000);
}
}
④stm32f1xx_it.c
#include "stm32f1xx_hal.h"
#include "stm32f1xx_it.h"
#include "RTC.h"
#include "UART.h"
#include "LED.h"
/**
* @brief:RTC秒中断服务函数
*/
void RTC_IRQHandler(void)
{
HAL_RTCEx_RTCIRQHandler(&hrtc);
}
/******************* 下面的中断的回调函数 ***************************/
/**
* @brief:RTC秒中断回调函数
*/
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc)
{
LED_Turn();
}
4、输出功能
①RTC.c文件需要修改的代码如下
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_SECOND; //输出:秒脉冲
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM ; //输出:闹钟脉冲
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_CALIBCLOCK; //输出:32768Hz/64后的脉冲
输出引脚是PC13,但是不用配置它。其他的代码不用修改
5、BKP的读写
BKP的读写需要RTC的配置,但是不需要开启LSE的时钟源。只需要打开RTC时钟+配置RTC总控结构体
①RTC.c文件的代码如下
#include "RTC.h"
/**
* @brief:RTC初始化函数
*/
RTC_HandleTypeDef hrtc; //RTC配置结构体
void RTC_Init(void)
{
/* RTC的初始化配置 */
__HAL_RCC_RTC_ENABLE(); //使能RTC的时钟
__HAL_RCC_PWR_CLK_ENABLE(); //使能PWR的时钟
__HAL_RCC_BKP_CLK_ENABLE(); //使能BKP的时钟
hrtc.Instance = RTC; //选择RTC
HAL_RTC_Init(&hrtc);
}
/**
* @brief:HAL_RTC_Init()调用函数
*/
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
/* 其他的配置 */
}
②main.c文件的代码如下
#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "RTC.h"
#include "UART.h"
uint16_t Data = 0x1234;
int main(void){
HAL_Init();
HSE_RCC_Init();
UART1_Init(115200);
RTC_Init(); //初始化BKP
printf("代码测试\r\n");
HAL_RTCEx_BKUPWrite(&hrtc, 1, Data); //向BKP的第一个数据寄存器写入数据
printf("%x\r\n",HAL_RTCEx_BKUPRead(&hrtc, 1)); //读取BKP的第一个数据寄存器的数据
while(1){
}
}
6、BKP的侵入事件
若发生了侵入事件,会将BKP中的数据清除(主电源断电,侵入事件也有效)。侵入事件检测引脚为PC13且检测电平。但是我们不用配置PC13。
当侵入事件产生后,应该先关闭侵入检测。需要侵入检测则重新调用API函数开启侵入检测。
①RTC.c文件的代码如下
#include "RTC.h"
/**
* @brief:RTC初始化函数
*/
RTC_HandleTypeDef hrtc; //RTC配置结构体
RTC_TamperTypeDef Tamper; //侵入事件配置结构体
void RTC_Init(void)
{
/* RTC的初始化配置 */
__HAL_RCC_RTC_ENABLE(); //使能RTC的时钟
__HAL_RCC_PWR_CLK_ENABLE(); //使能PWR的时钟
__HAL_RCC_BKP_CLK_ENABLE(); //使能BKP的时钟
hrtc.Instance = RTC; //选择RTC
HAL_RTC_Init(&hrtc);
/* 侵入事件配置 */
Tamper.Tamper = RTC_TAMPER_1; //指定引脚:PC13
Tamper.Trigger = RTC_TAMPERTRIGGER_HIGHLEVEL; //高电平触发
HAL_RTCEx_SetTamper_IT(&hrtc,&Tamper); //配置侵入事件,开启中断
HAL_NVIC_SetPriority(TAMPER_IRQn,3,0);
HAL_NVIC_EnableIRQ(TAMPER_IRQn);
}
②main.c文件的代码如下
#include "stm32f1xx_hal.h"
#include "STM32_RCC_Init.h"
#include "RTC.h"
#include "UART.h"
uint16_t Data = 0x1234;
int main(void){
HAL_Init();
HSE_RCC_Init();
UART1_Init(115200);
RTC_Init(); //初始化BKP,初始化侵入事件
printf("代码测试\r\n");
HAL_RTCEx_BKUPWrite(&hrtc, 1, Data); //向BKP的第一个数据寄存器写入数据
printf("%x\r\n",HAL_RTCEx_BKUPRead(&hrtc, 1)); //读取BKP的第一个数据寄存器的数据
while(1){
}
}
③stm32f1xx_it.c文件的代码如下
#include "stm32f1xx_hal.h"
#include "stm32f1xx_it.h"
#include "RTC.h"
#include "UART.h"
/**
* @brief:RTC侵入事件中断服务函数
*/
void TAMPER_IRQHandler(void)
{
HAL_RTCEx_TamperIRQHandler(&hrtc);
}
/******************* 下面的中断的回调函数 ***************************/
/**
* @brief:RTC侵入中断回调函数
*/
void HAL_RTCEx_Tamper1EventCallback(RTC_HandleTypeDef *hrtc)
{
printf("发生侵入事件了\r\n");
HAL_RTCEx_DeactivateTamper(hrtc, RTC_TAMPER_1);//关闭侵入事件
}