【蓝桥杯——物联网设计与开发】基础模块8 - RTC

目录

一、RTC

(1)资源介绍

🔅简介

🔅时钟与分频(十分重要‼️)

[(2)STM32CubeMX 软件配置](#(2)STM32CubeMX 软件配置)

(3)代码编写

(4)实验现象

二、RTC接口函数封装

三、踩坑日记

(1)RTC时间读取问题

(2)RTC分频系数修改问题

(3)RTC时钟不准确问题


一、RTC

(1)资源介绍

🔅简介

(以下资料来源于STM32L0_参考手册(L0x1))

  • RTC提供自动唤醒来管理所有低功耗模式。
  • 实时时钟(RTC)是一个独立的BCD定时器/计数器。RTC提供了一个带有可编程报警中断的时间时钟/日历。
  • RTC还包括一个具有中断能力的周期性可编程唤醒标志。
  • 两个32位寄存器包含秒、分、小时(12或24小时格式)、天(星期中的一天)、日期(月中的一天)、月和年,以二进制编码的十进制格式(BCD)表示。分秒值也可以以二进制格式提供。
  • 28天、29天(闰年)、30天和31天的补偿将自动执行。还可以执行夏令时补偿。
  • 额外的32位寄存器包含可编程报警子秒,秒,分钟,小时,日和日期。
  • 数字校准功能可用于补偿晶体振荡器精度的任何偏差。
  • 在RTC域重置后,所有RTC寄存器都受到保护,防止可能的寄生写访问。
  • 只要电源电压保持在工作范围内,无论设备状态(运行模式、低功耗模式或复位),RTC都不会停止。

图1 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 时钟树

(2)STM32CubeMX 软件配置

🔅"工程建立、时钟树配置、Debug 串行线配置、代码生成配置" 在下文中有讲解,这里不再赘述❗️

【蓝桥杯------物联网设计与开发】基础模块1- GPIO输出https://blog.csdn.net/m0_63116406/article/details/135604705?spm=1001.2014.3001.5502

1️⃣点击**"Timers"** → 点击**"RTC"** → 勾选**"Activate Clock Source"** 和 "Activate Calendar" → 修改异步预分配值为124 ,同步预分频值为295 → 修改日历时间23时59分55秒
图3 RTC配置

2️⃣使能OLED

3️⃣使能TIM2

4️⃣生成代码即可;

(3)代码编写

🟢️main 函数

cpp 复制代码
/* 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更新时钟函数

cpp 复制代码
void RTC_Get(void)
{
	HAL_RTC_GetTime(&hrtc, &now_time, RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc, &now_date, RTC_FORMAT_BIN);
}

🟡️定时器中断更新时钟函数

cpp 复制代码
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 RC37kHz)时钟源,计算出合理的分频系数:

PREDIV_A = 124;

PREDIV_S = 295;

而官方例程给出的分频系数为:

PREDIV_A = 127;

PREDIV_S = 290;

由**(127 + 1) * (290 + 1) = 37248**,可知,该数据不太合理;

❗️但是 ,无论是本文给出的参数 还是官方给出的参数 ,亦或是默认参数 ,++在实际测试时,频率都不准确++❗️

(3)RTC时钟不准确问题

🔅针对这一问题,上文已经给出另外一种解决方案,即使用一个1ms 定时器来++模拟++ RTC,实测效果与手机时钟基本一致,能够满足赛题精准要求;

相关推荐
点灯小铭1 分钟前
基于STM32单片机智能RFID刷卡汽车位锁桩设计
stm32·单片机·汽车·毕业设计·课程设计
TDengine (老段)1 小时前
TDengine IDMP 高级功能(4. 元素引用)
大数据·数据库·人工智能·物联网·数据分析·时序数据库·tdengine
bai5459361 小时前
STM32 软件I2C读写MPU6050
stm32·单片机·嵌入式硬件
逼子格3 小时前
AT89C52单片机介绍
单片机·嵌入式硬件·51单片机·硬件工程师·硬件工程师真题·at89c52·器件手册
生涯にわたる学び5 小时前
ARM 实操 流水灯 按键控制 day53
arm开发·嵌入式硬件
whaosoft-1435 小时前
w嵌入式分享合集68
嵌入式硬件
David WangYang6 小时前
基于 IOT 的安全系统,带有使用 ESP8266 的语音消息
物联网·安全·语音识别
竹照煜_ysn8 小时前
STM32——软硬件I2C
stm32·嵌入式硬件·mongodb
Ronin-Lotus9 小时前
嵌入式硬件篇---电感串并联
嵌入式硬件
Wallace Zhang9 小时前
STM32 - Embedded IDE - GCC - 显著减少固件的体积
stm32·单片机·嵌入式硬件