软件:
STM32CubeMX
KEIL5
mcuisp
串口通信助手
硬件:
STM32F103C8Tx
杜邦线,面包板,USB转TTL
框图中浅灰色的部分都是属于备份域的,在VDD掉电时可在VBAT的驱动下继续运行。这部分仅包括 RTC 的分频器,计数器,和闹钟控制器。若 VDD 电源有效,RTC 可以触发RTC_Second(秒中断)RTC_Overflow(溢出事件)和 RTC_Alarm(闹钟中断)。从结构图可以分析到,其中的定时器溢出事件无法被配置为中断。若 STM32 原本处于待机状态,可由闹钟事件或 WKUP 事件(外部唤醒事件,属于 EXTI 模块,不属于 RTC)使它退出待机模式。闹钟事件是在计数器 RTC_CNT 的值等于闹钟寄存器 RTC_ALR 的值时触发的。
在备份域中所有寄存器都是 16 位的, RTC 控制相关的寄存器也不例外。它的计数器RTC_CNT 的 32 位由 RTC_CNTL 和 RTC_CNTH 两个寄存器组成,分别保存定时计数值的低 16 位和高 16 位。在配置 RTC 模块的时钟时,通常把输入的 32768Hz 的 RTCCLK 进行32768分频得到实际驱动计数器的时钟TR_CLK = RTCCLK/32768= 1 Hz,计时周期为1秒,计时器在 TR_CLK 的驱动下计数,即每秒计数器 RTC_CNT 的值加 1。
1.配置RCC
设置高速外部时钟HSE 选择外部时钟源
使能外部晶振LSE
RTC设备因为其独特的运行方式(即掉电依旧运行)使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,资源消耗太大,小小的纽扣电池根本吃不消。没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE
这两个都要点,作用也很明显,先是使能时钟源,再使能RTC日历
第一个是是否使能 tamper(PC13)引脚上输出校正的秒脉冲时钟,
第二个: RTC入侵检测校验功能
下面配置初始时间
此处设置时间为2022/11/2 14:48:00
使用自动配置,初始化时间必须使用BCD data format,原因是库函数存在bug,如果使用Binary data format,月份配置会出错,比如说11月,配置时会赋值为RTC_MONTH_NOVEMBER,而此宏定义值为0x11,也就是说其十进制值为17
4.时钟源设置
- 1.设置RTC时钟为LSE
- 2.选择外部时钟HSE 8MHZ,PLL锁相环倍频9倍
- 3.系统时钟来源选择为PLL
- 4.置APB1分频器为 /2
打开stm32f1xx_hal_rtc.h文件可以看到以下函数
/* RTC Time and Date functions ************************************************/
/** @addtogroup RTC_Exported_Functions_Group2
* @{
*/
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);/*读取系统日期*/
/**
* @}
*/
/* RTC Alarm functions ********************************************************/
/** @addtogroup RTC_Exported_Functions_Group3
* @{
*/
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);
/*时间结构体*/
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
{
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;
在main.c文件中重写fputc函数,完成printf函数的重定向
//添加头文件#include "stdio.h"
int fputc(int ch,FILE *f){
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
return ch;
}
在main.c中定义时间和日期的结构体用来获取时间和日期
RTC_DateTypeDef Date; //获取日期结构体
RTC_TimeTypeDef Time; //获取时间结构体
在main函数的while循环中添加以下代码
//获取RTC时间
HAL_RTC_GetTime(&hrtc, &Time, RTC_FORMAT_BIN);
//获取RTC日期
HAL_RTC_GetDate(&hrtc, &Date, RTC_FORMAT_BIN);
//日期展示格式:年/月/日
printf("%02d/%02d/%02d\r\n",2000 + Date.Year, Date.Month, Date.Date);
//时间展示格式:时:分:秒
printf("%02d:%02d:%02d\r\n",Time.Hours, Time.Minutes, Time.Seconds);
printf("\r\n");
HAL_Delay(1000);
添加星期
switch(Date.WeekDay)
{
case 1:printf("星期一\r\n");break;
case 2:printf("星期二\r\n");break;
case 3:printf("星期三\r\n");break;
case 4:printf("星期四\r\n");break;
case 5:printf("星期五\r\n");break;
case 6:printf("星期六\r\n");break;
case 7:printf("星期七\r\n");
}
HAL库配置RTC时可以看到RTC默认初始时间是0年1月1日零时零分零秒
先将上面代码初始日期时间清零,查看星期数,由于RTC可以自动生成正确的星期数,将输出的星期数与1970年1月1日比较,就可以知道RTC初始时间是否是1970年1月1号
Time.Hours=0,Time.Minutes=0,Time.Seconds=0;//时间清零
Date.Year=0,Date.Month=1,Date.Date=1;//日期清零
HAL_RTC_SetTime(&hrtc,&Time,RTC_FORMAT_BCD);
HAL_RTC_SetDate(&hrtc,&Date,RTC_FORMAT_BCD);
while(1)
{
/* Get the RTC current Time */
HAL_RTC_GetTime(&hrtc, &Time, RTC_FORMAT_BIN);
/* Get the RTC current Date */
HAL_RTC_GetDate(&hrtc, &Date, RTC_FORMAT_BIN);
/* Display date Format : yy/mm/dd */
printf("%02d/%02d/%02d\r\n",Date.Year, Date.Month, Date.Date);
}
查看结果:
已知1970年1月1日为星期4,所以RTC开始日期不是1970年1月1日。
使用CubeMX会使大部分底层原理没有接触到,想要更加深入了解RTC底层构造需要查看官方文档进行进一步学习。