目录
1、什么是RTC
实时时钟的缩写是RTC(Real_Time Clock)。RTC 是集成电路,通常称为时钟芯片。
可编程的预分频系数:分频系数高为220。
32位的可编程计数器,可用于较长时间段的测量。
2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟 频率的四分之一以上)。
可以选择以下三种RTC的时钟源:
HSE时钟除以128;
LSE振荡器时钟;
LSI振荡器时钟
2个独立的复位类型:
APB1接口由系统复位;
RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位
3个专门的可屏蔽中断:
1.闹钟中断,用来产生一个软件可编程的闹钟中断。
2.秒中断,用来产生一个可编程的周期性中断信号(长可达1秒)。
3.溢出中断,指示内部可编程计数器溢出并回转为0的状态。
RTC时钟源:
三种不同的时钟源可被用来驱动系统时钟(SYSCLK):
HSI振荡器时钟
HSE振荡器时钟
PLL时钟
这些设备有以下2种二级时钟源:
40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。 RTC用于从停机/待机模式下自动唤醒系统。
32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。
RTC具体流程:
RTCCLK经过RTC_DIV预分频,RTC_PRL设置预分频系数,然后得到TR_CLK时钟信号,我们一般设置其周期为1s,RTC_CNT计数器计数,假如1970设置为时间起点为0s,通过当前时间的秒数计算得到当前的时间。RTC_ALR是设置闹钟时间,RTC_CNT计数到RTC_ALR就会产生计数中断,
RTC_Second为秒中断,用于刷新时间。
RTC_Overflow是溢出中断。
RTC Alarm 控制开关机
RTC时钟选择
使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,因此没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE,频率为实时时钟模块中常用的32.768KHz,因为32768
= 2^15,分频容易实现,所以被广泛应用到RTC模块.(在主电源VDD有效的情况下(待机),RTC还可以配置闹钟事件使STM32退出待机模式).
RTC复位过程
除了RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器外,所有的系统寄存器都由系统复位或电源复位进行异步复位。
RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器仅能通过备份域复位信号复位。系统复位后,禁止访问后备寄存器和RCT,防止对后卫区域(BKP)的意外写操作
RTC中断
秒中断: 这里时钟自带一个秒中断,每当计数加一的时候就会触发一次秒中断,。注意,这里所说的秒中断并非一定是一秒的时间,它是由RTC时钟源和分频值决定的“秒”的时间,当然也是可以做到1秒钟中断一次。我们通过往秒中断里写更新时间的函数来达到时间同步的效果
闹钟中断: 闹钟中断就是设置一个预设定的值,计数每自加多少次触发一次闹钟中断
2、创建项目
①、设置RCC
设置高速外部时钟HSE 选择外部时钟源
使能外部晶振LSE
RTC设备因为其独特的运行方式(即掉电依旧运行)使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,资源消耗太大,小小的纽扣电池根本吃不消。没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE
②、配置RTC
Activate Clock Source 激活时钟源
Activate calendar激活日历
此处的时间为默认值,便于验证初始时间,之后想要设置为其他时间时可以再继续更改
③、使能串口
使能一下串口,便于发送日期到上位机
④、时钟源设置
1、选择外部时钟HSE 8MHz
2、PLL锁相环倍频9倍
3、系统时钟来源选择为PLL
4、设置APB1分频器为 /2
5、使能CSS监视时钟
6、设置RTC时钟为LSE
⑤、项目文件设置
3、验证初始时间
①、代码:
在main函数中的while循环中加入如下代码:
/* Get the RTC current Time */
HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
/* Get the RTC current Date */
HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
/* Display date Format : yy/mm/dd */
printf("%02d/%02d/%02d\r\n",2000 + GetData.Year, GetData.Month, GetData.Date);
/* Display time Format : hh:mm:ss */
printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds);
printf("\r\n");
HAL_Delay(1000);
因为在上述代码中使用了printf函数,所以我们还要重写fputc函数,完成printf函数的重定向
在main.c文件中添加如下函数
#include "stdio.h"
int fputc(int ch,FILE *f){
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
return ch;
}
注意,当在这里重写了printf函数时,我们要勾选Use MiscroLiB
②、结果
根据预期,输出的结果应该是从1970/1/1 00:00:00开始,但是输出确实从2000年开始的。但是CubeMX中的初始值又是这个值。所以,我认为应该是用cubeMx生成的原因。
4、RTC时间调整为当前时间
①、修改rtc.c
这里是将时间设置为2021年1月29日,20点16分0秒,是星期一
大家可以自己调节
我将时间调成了2022-11-1 23:21:0 星期二
sTime.Hours = 0x23;
sTime.Minutes = 0x21;
sTime.Seconds = 0x0;
DateToUpdate.WeekDay = RTC_WEEKDAY_TUESDAY;
DateToUpdate.Month = RTC_MONTH_NOVEMBER;
DateToUpdate.Date = 0x01;
DateToUpdate.Year = 0x22;
②、结果
5、输出星期几
①、修改代码
只需要在while循环中在加上如下代码
if(GetData.WeekDay==1){
printf("Monday\r\n");
}else if(GetData.WeekDay==2){
printf("Tuesday\r\n");
}else if(GetData.WeekDay==3){
printf("Wednesday\r\n");
}else if(GetData.WeekDay==4){
printf("Thursday\r\n");
}else if(GetData.WeekDay==5){
printf("Friday\r\n");
}else if(GetData.WeekDay==6){
printf("Saturday\r\n");
}else if(GetData.WeekDay==7){
printf("Sunday\r\n");
}
则,此时while循环中变成:
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* Get the RTC current Time */
HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
/* Get the RTC current Date */
HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
/* Display date Format : yy/mm/dd */
printf("%02d/%02d/%02d\r\n",2000 + GetData.Year, GetData.Month, GetData.Date);
/* Display time Format : hh:mm:ss */
printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds);
/* Display date Format : weekday */
if(GetData.WeekDay==1){
printf("Monday\r\n");
}else if(GetData.WeekDay==2){
printf("Tuesday\r\n");
}else if(GetData.WeekDay==3){
printf("Wednesday\r\n");
}else if(GetData.WeekDay==4){
printf("Thursday\r\n");
}else if(GetData.WeekDay==5){
printf("Friday\r\n");
}else if(GetData.WeekDay==6){
printf("Saturday\r\n");
}else if(GetData.WeekDay==7){
printf("Sunday\r\n");
}
printf("\r\n");
HAL_Delay(1000);
}