STM32CubeMX之RTC电子钟
1.RTC简介
实时时钟是一个独立的定时器。 RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, RTC的设置和时间维持不变。
2.特性
● 可编程的预分频系数:分频系数最高为220。
● 32位的可编程计数器,可用于较长时间段的测量。
● 2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟频率的四分之一以上)。
● 可以选择以下三种RTC的时钟源:
─ HSE时钟除以128;
─ LSE振荡器时钟;
─ LSI振荡器时钟;
● 3个专门的可屏蔽中断:
─ 闹钟中断,用来产生一个软件可编程的闹钟中断。
─ 秒中断,用来产生一个可编程的周期性中断信号(最长可达1秒)。
─ 溢出中断,指示内部可编程计数器溢出并回转为0的状态。
3.功能概述
RTC由两个主要部分组成(见下图)。第一部分(APB1 接口 )用来和APB1 总线相连。此单元还包含一组16位寄存器,可通过APB1 总线对其进行读写操作。 APB1 接口由APB1 总线时钟驱动,用来与APB1 总线接口。另一部分(RTC核心)由一组可编程计数器组成,分成两个主要模块。第一个模块是RTC的预分频模块,它可编程产生最长为1 秒的RTC时间基准TR_CLK。 RTC的预分频模块包含了一个20位的可编程分频器(RTC 预分频器) 。如果在 RTC_CR寄存器中设置了相应的允许位,则在每个TR_CLK周期中RTC产生一个中断(秒中断)。第二个模块是一个32位的可编程计数器,可被初始化为当前的系统时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时间相比较,如果RTC_CR控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。
4.RTC配置过程
必须设置RTC_CRL寄存器中的CNF位 ,使RTC进入配置模式后 ,才能写入RTC_PRL 、RTC_CNT、 RTC_ALR寄存器。另外,对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是’1’时,才可以写入RTC寄存器。
- 配置过程:
- 选择RTC时钟源
- 查询RTOFF位,直到RTOFF的值变为’1’;
- 置CNF值为1 ,进入配置模式;
- 对一个或多个RTC寄存器进行写操作;
- 清除CNF标志位,退出配置模式;
- 查询RTOFF,直至RTOFF位变为’1’以确认写操作已经完成。仅当CNF标志位被清除时,写操作才能进行,这个过程至少需要3个RTCCLK周期;
5.软件设计
5.1 选择RTC时钟源
5.2 RTC配置
开启RTC功能,开启日历功能,设置时间和日期,设置预分频系数,产生秒中断。
5.3 配置RTC时钟源
RTC时钟产生可配置三种时钟源:
外部高速时钟HSE(8MHZ)进行128分频(HSE/128);
外部低速时钟LSE(32.768KHZ);
内部低速时钟LSI(40KHZ);
下面配置选择为外部低速时钟LSE,32.768KHZ作为RTC时钟源。
6.生成代码
6.1 RTC初始化
由于RTC部分配置寄存器是处于后备区域,在有后备电池供电情况下,为了保证掉电后电子钟数据持续更新,我们需要开启后备驱动。
/* RTC init function */
void MX_RTC_Init(void)
{
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef DateToUpdate = {0};
/** 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 */
/*检查后备区域是否写入数据*/
if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)==0x12)
{
return ;
}
else HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,0x12);//写入标志位,表示RTC时间配置好,防止重置时间
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 0;
sTime.Minutes = 0;
sTime.Seconds = 0;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)//设置时间
{
Error_Handler();
}
DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;
DateToUpdate.Month = RTC_MONTH_JUNE;
DateToUpdate.Date = 30;
DateToUpdate.Year = 21;
if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)//设置日期
{
Error_Handler();
}
}
6.2 开启RTC开时钟,开RTC中断
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();//RTC时钟
/* RTC interrupt Init */
HAL_NVIC_SetPriority(RTC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(RTC_IRQn);
/* USER CODE BEGIN RTC_MspInit 1 */
HAL_RTCEx_SetSecond_IT(rtcHandle);//开启RTC中断
/* USER CODE END RTC_MspInit 1 */
}
}
6.3 配置RTC中断
extern u8 flag;
void RTC_IRQHandler(void)
{
/* USER CODE BEGIN RTC_IRQn 0 */
u8 buff[30];
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef DateToUpdate = {0};
/* USER CODE END RTC_IRQn 0 */
HAL_RTCEx_RTCIRQHandler(&hrtc);
/* USER CODE BEGIN RTC_IRQn 1 */
if(flag==1)
{
if(HAL_RTC_GetTime(&hrtc,&sTime,RTC_FORMAT_BIN)==HAL_OK)
{
// printf("%d:%d:%d ",sTime.Hours,sTime.Minutes,sTime.Seconds);
snprintf((char *)buff,sizeof(buff),"%02d:%02d:%02d",sTime.Hours,sTime.Minutes,sTime.Seconds);
OLED_Display_str(32,0,8,16,buff);
}
if(HAL_RTC_GetDate(&hrtc,&DateToUpdate,RTC_FORMAT_BIN)==HAL_OK)
{
printf("%d/%d/%d 星期:%d\r\n",DateToUpdate.Year,DateToUpdate.Month,DateToUpdate.Date,DateToUpdate.WeekDay);
snprintf((char *)buff,sizeof(buff),"%02d/%02d/%02d week:%d",DateToUpdate.Year,DateToUpdate.Month,DateToUpdate.Date,DateToUpdate.WeekDay);
OLED_Display_str(0,2,8,16,buff);
}
}
/* USER CODE END RTC_IRQn 1 */
}
6.4 主体功能
主函数中实现OLED屏幕初始化、RTC电子钟初始化等;实现实时时间显示,可通过串口进行时间校准。
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM4_Init();
MX_USART1_UART_Init();
MX_RTC_Init();
/* USER CODE BEGIN 2 */
OLED_Init();
OLED_Displey_Imag(48,0,32,32,(u8 *)android);
Delay_Ms(500);
Delay_Ms(500);
OLED_Clear();//清屏
flag=1;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)//轮询
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(usart1_flag)
{
printf("%s\r\n",usart1_buff_rx);
if(usart1_buff_rx[0]== '*')
{
DateToUpdate.Year=(usart1_buff_rx[3]-'0')*10+(usart1_buff_rx[4]-'0')*1;//年
DateToUpdate.Month=(usart1_buff_rx[5]-'0')*10+(usart1_buff_rx[6]-'0')*1;//月
DateToUpdate.Date=(usart1_buff_rx[7]-'0')*10+(usart1_buff_rx[8]-'0')*1;//日
sTime.Hours=(usart1_buff_rx[9]-'0')*10+(usart1_buff_rx[10]-'0')*1;
sTime.Minutes=(usart1_buff_rx[11]-'0')*10+(usart1_buff_rx[12]-'0')*1;
sTime.Seconds=(usart1_buff_rx[13]-'0')*10+(usart1_buff_rx[14]-'0')*1;
printf("%d/%d/%d -- %d:%d:%d\r\n",DateToUpdate.Year,DateToUpdate.Month,DateToUpdate.Date,sTime.Hours,sTime.Minutes,sTime.Seconds);
HAL_RTC_SetDate(&hrtc, &DateToUpdate,RTC_FORMAT_BIN);
HAL_RTC_SetTime(&hrtc,&sTime,RTC_FORMAT_BIN);
}
usart1_flag=0;
usart1_count=0;
}
}
/* USER CODE END 3 */
}
7.运行效果
8.相关接口函数
//读取和写入后备域数据标签:CODE,HAL,RTC,STM32CubeMX,电子钟,USER,DateToUpdate,sTime From: https://blog.51cto.com/u_15688123/5885574
uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister)
HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister, uint32_t Data)
//设置和获获取时间
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秒中断
HAL_StatusTypeDef HAL_RTCEx_SetSecond_IT(RTC_HandleTypeDef *hrtc)