【蓝桥杯——物联网设计与开发】基础模块8 - RTC
目录
一、RTC
(1)资源介绍
🔅简介
(以下资料来源于STM32L0_参考手册(L0x1))
- RTC提供自动唤醒来管理所有低功耗模式。
- 实时时钟(RTC)是一个独立的BCD定时器/计数器。RTC提供了一个带有可编程报警中断的时间时钟/日历。
- RTC还包括一个具有中断能力的周期性可编程唤醒标志。
- 两个32位寄存器包含秒、分、小时(12或24小时格式)、天(星期中的一天)、日期(月中的一天)、月和年,以二进制编码的十进制格式(BCD)表示。分秒值也可以以二进制格式提供。
- 28天、29天(闰年)、30天和31天的补偿将自动执行。还可以执行夏令时补偿。
- 额外的32位寄存器包含可编程报警子秒,秒,分钟,小时,日和日期。
- 数字校准功能可用于补偿晶体振荡器精度的任何偏差。
- 在RTC域重置后,所有RTC寄存器都受到保护,防止可能的寄生写访问。
- 只要电源电压保持在工作范围内,无论设备状态(运行模式、低功耗模式或复位),RTC都不会停止。
🔅时钟与分频(十分重要‼️)
RTC时钟源(RTCCLK)是通过LSE时钟、LSI振荡器时钟和HSE时钟之间的时钟控制器来选择的。
一个可编程的预分频器阶段产生一个1Hz的时钟,用于更新日历。为了尽量减少功耗,预分频器被分成2个可编程的预分频器:
- 通过RTC_PRER寄存器的PREDIV_A位配置的7位异步预分频器;
- 通过RTC_PRER寄存器的PREDIV_S位配置的15位同步预分频器;
⚠️注意:当同时使用两个预分频器时,建议将异步预分频器配置为较大的值,以减少消耗。
LSE频率为32.768 kHz时,将异步预分频器的分频因子设置为128,将同步预分频器的分频因子设置为256,得到的内部时钟频率为1Hz(ck_spre) 。即:(默认参数)
PREDIV_A = 127;
PREDIV_S = 255;
但是,在蓝桥杯物联网竞赛实训平台上,RTC的时钟只能通过LSI RC(37kHz)来获得,所以默认的参数不适宜本平台,需要进行修改‼️
在这里,根据以异步预分频器值较大为优先原则,计算出一个合理的参数值,即:
PREDIV_A = 124;
PREDIV_S = 295;
(2)STM32CubeMX 软件配置
🔅“工程建立、时钟树配置、Debug 串行线配置、代码生成配置” 在下文中有讲解,这里不再赘述❗️
1️⃣点击"Timers" → 点击"RTC" → 勾选"Activate Clock Source" 和 "Activate Calendar" → 修改异步预分配值为124,同步预分频值为295 → 修改日历时间23时59分55秒;
2️⃣使能OLED;
3️⃣使能TIM2;
4️⃣生成代码即可;
(3)代码编写
🟢️main 函数
/* USER CODE BEGIN PV */
uint8_t puc_buf[17]; // OLED显示缓存区
RTC_TimeTypeDef now_time; // RTC时间结构体
RTC_DateTypeDef now_date; // RTC日期结构体
uint8_t hour = 23, minute = 59, second = 55; // 定时器计时
uint16_t cnt_1s; // 定时器计时1秒
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
/* 定时器计时1000次1ms,即1秒,更新一次数据 */
if(++cnt_1s == 1000)
{
cnt_1s = 0;
if(++second == 60)
{
second = 0;
if(++minute == 60)
{
minute = 0;
if(++hour == 24)
hour = 0;
}
}
}
}
}
/**
* @brief The application entry point.
* @retval int
*/
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 */
HAL_RCC_EnableCSS();
/* 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_I2C3_Init();
MX_RTC_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
OLED_Init();
HAL_TIM_Base_Start_IT(&htim2); // 开启定时器2中断
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* 获取RTC值 */
HAL_RTC_GetTime(&hrtc, &now_time, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &now_date, RTC_FORMAT_BIN);
/* OLED显示 */
// 第一行显示RTC的时间
sprintf((char*)puc_buf, " %2d : %2d : %2d ", now_time.Hours, now_time.Minutes, now_time.Seconds);
OLED_ShowString(0, 0, puc_buf, 16);
// 第二行显示定时器的时间
sprintf((char*)puc_buf, " %2d : %2d : %2d ", hour, minute, second);
OLED_ShowString(0, 2, puc_buf, 16);
HAL_Delay(200);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
(4)实验现象
- RTC更新的时间较快;
- 计时器更新的时间与手机时钟相同;
二、RTC接口函数封装
🟡️RTC更新时钟函数
void RTC_Get(void)
{
HAL_RTC_GetTime(&hrtc, &now_time, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &now_date, RTC_FORMAT_BIN);
}
🟡️定时器中断更新时钟函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
/* 定时器计时1000次1ms,即1秒,更新一次数据 */
if(++cnt_1s == 1000)
{
cnt_1s = 0;
if(++second == 60)
{
second = 0;
if(++minute == 60)
{
minute = 0;
if(++hour == 24)
hour = 0;
}
}
}
}
}
三、踩坑日记
(1)RTC时间读取问题
🔅RTC读取需要先读取时间,后必须读取日期,否则读取的时间不能更新❗️❗️❗️
即需要调用下面两个函数,否则无法正常更新时间:
HAL_RTC_GetTime(&hrtc, &now_time, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &now_date, RTC_FORMAT_BIN);
(2)RTC分频系数修改问题
🔅由于开发板上RTC的时钟不为32.768kHz,所以不能使用默认的分频系数,根据其LSI RC(37kHz)时钟源,计算出合理的分频系数:
PREDIV_A = 124;
PREDIV_S = 295;
而官方例程给出的分频系数为:
PREDIV_A = 127;
PREDIV_S = 290;
由(127 + 1) * (290 + 1) = 37248,可知,该数据不太合理;
❗️但是,无论是本文给出的参数还是官方给出的参数,亦或是默认参数,在实际测试时,频率都不准确❗️
(3)RTC时钟不准确问题
🔅针对这一问题,上文已经给出另外一种解决方案,即使用一个1ms定时器来模拟RTC,实测效果与手机时钟基本一致,能够满足赛题精准要求;
原文地址:https://blog.csdn.net/m0_63116406/article/details/144702987
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!