【蓝桥杯——物联网设计与开发】基础模块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,实测效果与手机时钟基本一致,能够满足赛题精准要求;

相关推荐
smartpi_ai几秒前
离线语音模块误识别问题全面解决方案:命令词设计、防误触与灵敏度调优指南
人工智能·单片机·语音识别
LCG元4 分钟前
STM32F103 驱动 HX711 称重实战:DOUT 就绪时序、增益切换陷阱与两点标定精度实测
stm32·单片机·嵌入式硬件
染予19 分钟前
代码中相关C语言知识点总结
stm32
优信电子41 分钟前
STM32 驱动 DHT30 测量环境温湿度(兼容SHT30):从时序解析到完整代码实战
stm32·嵌入式·传感器·sht30·温湿度·环境测量·dht30
小李不想当小白2 小时前
DMA直接存储器存取(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·分享
朝发如雪3 小时前
快速掌握单片机基础(6)(数据处理与传送指令)(跳转与分支指令)(栈操作与混合编程)(程序状态寄存器操作指令)(内存映射)
单片机·arm
blue_ice .3 小时前
Verilog DDS 数字信号发生器:DAC 输出模拟波形与 ADC 回采观测
开发语言·单片机·嵌入式硬件·fpga开发
深圳市恒锐丰科技杨生3 小时前
EG4318 / EG4319 / EG4320 铅酸电池三段式充电副边管理芯片|屹晶 EGmicro
嵌入式硬件·硬件工程
深圳市恒锐丰科技杨生3 小时前
EG1253 电流模式 PWM 控制器|屹晶 EGmicro
嵌入式硬件·硬件工程
远翔调光芯片^138287988723 小时前
ECP5702能芯科技PD取电芯片在市场上的优势有哪些?
开发语言·人工智能·单片机·嵌入式硬件·智能家居