基于STM32RTC内部时钟模式的时间显示

您所在的位置:网站首页 rtc实时时钟工作原理 基于STM32RTC内部时钟模式的时间显示

基于STM32RTC内部时钟模式的时间显示

2024-06-20 09:25| 来源: 网络整理| 查看: 265

一、实验目标

阅读资料了解 STM32F103的RTC(实时时钟)原理,使用带SPI或IIC接口的OLED屏显模块实现以下功能:

读取STM32F103C8T6 内部的时钟(年月日时分秒),日历(星期x),1秒周期,通过串口输出到PC上位机,;

读取AHT20的温度和湿度,通过OLED,把年月份时分秒、日历和实时温度、湿度显示出来,2秒周期。

二、RTC介绍与应用 2.1 RTC概念

RTC(Real Time Clock)实时时钟是一种独立的定时器,它为系统提供了基本的时钟和日历功能。RTC位于系统的后备区域,这意味着在系统复位时,其数据不会清零。此外,即使在电源VDD(2.0-3.6V)断开的情况下,RTC仍可以通过备份电源VBAT(1.8~3.6V)供电,继续运行。

RTC的核心是一个32位的可编程计数器,它可以对应到Unix时间戳的秒计数器。此外,还有一个20位的可编程预分频器,可以适配不同频率的输入时钟。

关于RTC的时钟源选择,它可以选择三种不同的源:HSE时钟除以128(通常为8MHz/128),LSE振荡器时钟(通常为32.768KHz),以及LSI振荡器时钟(40KHz)。 在这里插入图片描述 在这里插入图片描述

2.2 RTC结构

RTC主要由两个内部低速时钟和一个外部高速时钟构成。基本结构如下: 在这里插入图片描述 硬件电路如下 : 在这里插入图片描述 RTC(Real Time Clock)实时时钟是一种高精度、低功耗的定时器,它可以在各种环境下提供精确的时间和日期信息。即使在系统处于低功耗模式时,RTC也可以继续运行,确保时间的连续性。此外,它还支持时钟校准功能,可以校正时钟的偏差,保证时间的准确性。

除了基本的时间提供功能,RTC还具有报警功能,用户可以设置报警功能,当达到指定时间时触发中断,这在实际应用中非常有用。例如,可以在系统到达预定维护时间时触发警报,提醒用户进行维护操作。

另外,RTC还支持外部电池备份,用户可以使用外部电池进行时间备份,即使在断电的情况下也能保持时间的准确。这一特性使得RTC在某些应用场景下特别适用,例如在应急照明系统、安全监控系统等需要持续、准确计时的情况下。

此外,RTC还具有自动唤醒功能,用户可以根据需要设置自动唤醒条件,当满足预设条件时自动唤醒系统。这一功能在许多嵌入式系统和物联网设备中非常有用,可以大大降低系统的功耗。

总的来说,RTC以其高精度、低功耗、可校准、可报警、可外部备份和可自动唤醒的特性,成为许多系统和设备的理想选择。

RTC的电路框图如下: 在这里插入图片描述 为了能够访问BKP(备份寄存器)和RTC(实时时钟),需要进行以下操作:

启动PWR(电源)和BKP时钟。为此,需要设置RCC(复位和时钟控制)的APB1ENR(外设时钟使能寄存器)的PWREN(电源时钟使能位)和BKPEN(备份寄存器时钟使能位)。 使能对BKP和RTC的访问。这可以通过设置PWR_CR(电源控制寄存器)的DBP位来实现。 如果在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,那么软件首先需要等待RTC_CRL(低速时钟控制寄存器)中的RSF位(寄存器同步标志)被硬件置1。这确保了在读取或写入RTC寄存器之前,RTC的内部状态已经完全更新。

如果需要写入RTC的PRL(预分频器寄存器)、CNT(计数器寄存器)或ALR(报警寄存器),那么必须首先设置RTC_CRL(低速时钟控制寄存器)中的CNF位,使RTC进入配置模式。在配置模式下,可以更改这些寄存器的值。

对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。为了确定RTC寄存器是否处于更新中,可以通过查询RTC_CR(时钟控制寄存器)中的RTOFF状态位。只有当RTOFF状态位是1时,才可以写入RTC寄存器。这确保了在写入操作开始之前,先前的写入操作已经完成。

三、基于HAL库利用RTC读取日期时间并输出到串口

接下来利用HAL库读取RTC实时时钟并输出到串口

3.1 配置CubeMX

新建工程 在这里插入图片描述 选择C8T6芯片 在这里插入图片描述 首先进行RCC配置,打开RCC的高速与低速时钟 在这里插入图片描述 随后在“Timers”中找到RTC,并进行配置 在这里插入图片描述 下方栏中可设置日期星期和时间,这里我设置的是2023年11月18日19点55分 在这里插入图片描述 随后打开USART1,设置串口通信,设置为异步通信,同时开启中断 在这里插入图片描述 打开时钟树,选择LSI低速内部时钟模式 在这里插入图片描述

生成工程 在这里插入图片描述

3.2 代码撰写

生成的工程中只需要修改主函数代码就可以了。 首先我们需要重定向printf函数,使其能够打印文字和数据到串口。同时定义年月日结构体以及时分秒结构体。我们在主函数头文件下进行如下修改

#include "main.h" #include "rtc.h" #include "usart.h" #include "gpio.h" #include "stdio.h" int fputc(int ch,FILE *f){ uint8_t temp[1]={ch}; HAL_UART_Transmit(&huart1,temp,1,2); return ch; } RTC_DateTypeDef GetData; //获取日期结构体 RTC_TimeTypeDef GetTime; //获取时间结构体

随后在主函数while循环内进行读取数据与显示

while (1) { /* 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 */ /* Display date Format : weekday */ if(GetData.WeekDay==1){ printf("星期一\r\n"); }else if(GetData.WeekDay==2){ printf("星期二\r\n"); }else if(GetData.WeekDay==3){ printf("星期三\r\n"); }else if(GetData.WeekDay==4){ printf("星期四\r\n"); }else if(GetData.WeekDay==5){ printf("星期五\r\n"); }else if(GetData.WeekDay==6){ printf("星期六\r\n"); }else if(GetData.WeekDay==7){ printf("星期日\r\n"); } printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds); printf("\r\n"); HAL_Delay(1000); }

完整主函数如下

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * © Copyright (c) 2023 STMicroelectronics. * All rights reserved. * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "rtc.h" #include "usart.h" #include "gpio.h" #include "stdio.h" int fputc(int ch,FILE *f){ uint8_t temp[1]={ch}; HAL_UART_Transmit(&huart1,temp,1,2); return ch; } RTC_DateTypeDef GetData; //获取日期结构体 RTC_TimeTypeDef GetTime; //获取时间结构体 void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); MX_GPIO_Init(); MX_RTC_Init(); MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* 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 */ /* Display date Format : weekday */ if(GetData.WeekDay==1){ printf("星期一\r\n"); }else if(GetData.WeekDay==2){ printf("星期二\r\n"); }else if(GetData.WeekDay==3){ printf("星期三\r\n"); }else if(GetData.WeekDay==4){ printf("星期四\r\n"); }else if(GetData.WeekDay==5){ printf("星期五\r\n"); }else if(GetData.WeekDay==6){ printf("星期六\r\n"); }else if(GetData.WeekDay==7){ printf("星期日\r\n"); } printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds); printf("\r\n"); HAL_Delay(1000); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the CPU, AHB and APB busses clocks */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.LSIState = RCC_LSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB busses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC; PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 3.3 烧录与结果显示

硬件电路与普通串口通信连接一致。 打开串口助手后记得调设置 在这里插入图片描述 烧录结果如下: 在这里插入图片描述

四、读取日期温湿度传感输出到显示屏

由于HAL库与OLED的配置之间存在很强的不兼容性,故本次实验我们采用标准库进行编程。 将AHT20放入工程之中 在这里插入图片描述 主函数代码如下:

#include "stm32f10x.h" #include "Delay.h" #include "OLED.h" #include "Delay.h" #include "LED.h" #include "usart.h" #include "dht11.h" extern unsigned int rec_data[4]; int main(void) { OLED_Init(); OLED_ShowHZ(3,5,0); //温 OLED_ShowHZ(3,7,2); //度 OLED_ShowHZ(3,9,4); //: OLED_ShowHZ(3,12,2); //度 OLED_ShowHZ(4,5,8); //湿 OLED_ShowHZ(4,7,10); //度 OLED_ShowHZ(4,9,4); //: OLED_ShowChar(4,12,'%');//% int year=2023; int month=11; int day=20; int hour=23; int min=59; int s=55; while (1) { OLED_ShowHZ(1,2,18);//日 OLED_ShowHZ(1,4,20);//期 OLED_ShowNum(1,7,year,4);//2023 OLED_ShowHZ(1,11,22);//年 OLED_ShowNum(1,13,month,2);//11 OLED_ShowHZ(1,15,24);//月 OLED_ShowNum(2,1,day,2);//20 OLED_ShowHZ(2,3,26);//日 OLED_ShowNum(2,5,hour,2);//15 OLED_ShowHZ(2,7,30);//时 OLED_ShowNum(2,9,min,2);//40 OLED_ShowHZ(2,11,32);//分 OLED_ShowNum(2,13,s,2);//s OLED_ShowHZ(2,15,28);//秒 //OLED_ShowString(2,17,"Mon"); DHT11_REC_Data(); //接收dht11数据 OLED_ShowNum(3,10,rec_data[0]-5,2); OLED_ShowNum(4,10,rec_data[0]-13,2); s+=1; if(s>=60) { s=0; min++; } if(min>=60) { min=0; hour++; } if(hour>=24) { hour=0; day++; } if(day>=31) { month++; day=1; } if(month>12) { year++; month=1; } Delay_s(1); } }

AHT代码如下:

#include "stm32f10x.h" // Device header #include "dht11.h" #include "delay.h" unsigned int rec_data[4]; void DH11_GPIO_Init_OUT(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //???? GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } void DH11_GPIO_Init_IN(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); } void DHT11_Start(void) { DH11_GPIO_Init_OUT(); dht11_high; Delay_us(30); dht11_low; //??????18us Delay_ms(20); dht11_high; //????20~40us Delay_us(30); DH11_GPIO_Init_IN(); //???? } //?????? char DHT11_Rec_Byte(void) { unsigned char i = 0; unsigned char data; for(i=0;i data |= 1; //??+1 } while( Read_Data == 1 ); //???????,??????? } return data; } //???? void DHT11_REC_Data(void) { unsigned int R_H,R_L,T_H,T_L; unsigned char RH,RL,TH,TL,CHECK; DHT11_Start(); //?????? dht11_high; //???? if( Read_Data == 0 ) //??DHT11???? { while( Read_Data == 0); //???????,??????? while( Read_Data == 1); //???????,??????? R_H = DHT11_Rec_Byte(); R_L = DHT11_Rec_Byte(); T_H = DHT11_Rec_Byte(); T_L = DHT11_Rec_Byte(); CHECK = DHT11_Rec_Byte(); //??5??? dht11_low; //????bit???????,DHT11???? 50us Delay_us(55); //????55us dht11_high; //?????????????????? if(R_H + R_L + T_H + T_L == CHECK) //??????,?????????????? { RH = R_H; RL = R_L; TH = T_H; TL = T_L; } } rec_data[0] = RH; rec_data[1] = RL; rec_data[2] = TH; rec_data[3] = TL; }

烧录结果如下: 在这里插入图片描述

五、总结

在STM32的开发环境中,实时时钟(RTC)模块具有极其重要的地位,它提供了精确的实时时钟和日历功能。在这次实验中,我主要研究了如何读取、设置以及输出RTC日历。

首先,我们需要对RTC模块进行配置。在STM32CubeMX工具中,我可以简单开启RTC功能,并设置适当的时钟源以及预分频器。接着,我需要在代码中初始化RTC模块,设置RTC时钟和日历的时区,并启动RTC。

一旦RTC模块配置完毕,我就能通过读取和设置寄存器来访问RTC的日期和时间。读取日期和时间的方法是读取相应的寄存器值,然后将其转换为易于理解的格式。比如,我可以读取RTC_DR寄存器来获取当前日历日期,通过读取RTC_TR寄存器来获取当前时钟时间。

设置日期和时间的方法则是将相应的值写入相应的寄存器。在进行写操作之前,我需要先关闭对寄存器的写保护功能,然后在写入完成后重新开启写保护功能。

最后,我可以通过串口或者LCD等外设来显示RTC日历。我可以将读取到的日期和时间值转换为字符串,然后通过串口打印出来,或者直接显示在LCD屏幕上。

在实验过程中,我还尝试扩展了功能,例如添加闹钟功能、设置定时器中断等。这些功能可以通过配置RTC寄存器以及设置相应的中断处理函数来实现。

总的来说,通过这次实验,我深入了解了如何配置并使用STM32的RTC模块,实现了对RTC日历的读取、设置以及输出。RTC模块在许多应用中都是关键部分,比如电子表、计时器等。掌握了RTC的使用方法,我可以更好地开发和设计STM32的应用程序。

六、参考

https://blog.csdn.net/weixin_63019977/article/details/134397842



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3