单片机 STM32F103 RTC(实时时钟)的配置和使用

在 STM32F103 单片机上基于 RT-Thread 使用 RTC(实时时钟)的配置介绍。

1. 配置 RTC 驱动

在 RT-Thread 中启用 RTC 主要分两步:打开驱动框架在板级支持包中使能

  • 第一步:通过 menuconfig 开启驱动
    • 进入图形化配置界面,配置开启RTC
  • 第二步:确保 STM32 的 HAL 库配置正确
    • 打开 stm32f1xx_hal_conf.h 文件,确保 HAL_RTC_MODULE_ENABLED 已经被定义,以启用 RTC 的 HAL 驱动。


2. 检查硬件初始化

RTC 属于 STM32 的备份域,需要特殊的电源管理和时钟配置。如果这部分没配好,RTC 会无法正常工作。

  • 备份域访问权限:在写入 RTC 寄存器前,必须使能电源时钟和备份时钟,并取消备份域写保护。
  • 时钟源选择 :F103 的 RTC 通常使用**外部低速晶振(LSE,32.768kHz)**来保证走时精度和低功耗。确保 stm32f1xx_hal_msp.c 中的 HAL_RTC_MspInit() 函数已经正确配置了 LSE 时钟。
  • 备用寄存器标志 :为了避免每次复位都重新初始化 RTC,驱动通常会使用备份寄存器(如 BKP_DR1)来存储初始化标志。检查 drv_rtc.c 中是否有类似读写备份寄存器的代码。
c 复制代码
//系统时钟配置
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_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  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|RCC_PERIPHCLK_ADC;
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

stm32f1xx_hal_msp.c 中的 HAL_RTC_MspInit() 函数如下,此函数可以屏蔽掉,不配置,在rt-thread 的 drv_rtc.c 驱动文件中,会有相关的配置,具体可查看rt_rtc_config 函数。函数如下:

c 复制代码
/**
* @brief RTC MSP Initialization
* This function configures the hardware resources used in this example
* @param hrtc: RTC handle pointer
* @retval None
*/
void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc)
{
 if(hrtc->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();
   /* Peripheral clock enable */
   __HAL_RCC_RTC_ENABLE();
 /* USER CODE BEGIN RTC_MspInit 1 */

 /* USER CODE END RTC_MspInit 1 */
 }

}

/**
* @brief RTC MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param hrtc: RTC handle pointer
* @retval None
*/
void HAL_RTC_MspDeInit(RTC_HandleTypeDef* hrtc)
{
 if(hrtc->Instance==RTC)
 {
 /* USER CODE BEGIN RTC_MspDeInit 0 */

 /* USER CODE END RTC_MspDeInit 0 */
   /* Peripheral clock disable */
   __HAL_RCC_RTC_DISABLE();
 /* USER CODE BEGIN RTC_MspDeInit 1 */

 /* USER CODE END RTC_MspDeInit 1 */
 }

}

3. 应用层使用 RTC

一旦驱动注册成功,RTC 设备(设备名为 "rtc")就会被系统找到。RT-Thread 提供了非常方便的调用方式。

3.1 通过 FinSH/MSH 命令调试

这是最直接的验证方法。连接上串口终端后:

  • 查看时间 :输入 date,系统会打印当前时间和日期。
  • 设置时间 :输入 date 2026 02 24 18 30 00(年 月 日 时 分 秒),即可设置系统时间。
3.2 参考代码

RT-Thread 提供的参考代码:

c 复制代码
/*
 * 程序清单:这是一个 RTC 设备使用例程
 * 例程导出了 rtc_sample 命令到控制终端
 * 命令调用格式:rtc_sample
 * 程序功能:设置RTC设备的日期和时间,延时一段时间后获取当前时间并打印显示。
*/

#include <rtthread.h>
#include <rtdevice.h>

#define RTC_NAME       "rtc"

static int rtc_sample(int argc, char *argv[])
{
    rt_err_t ret = RT_EOK;
    time_t now;
    rt_device_t device = RT_NULL;

    /*寻找设备*/
    device = rt_device_find(RTC_NAME);
    if (!device)
    {
      LOG_E("find %s failed!", RTC_NAME);
      return RT_ERROR;
    }

    /*初始化RTC设备*/
    if(rt_device_open(device, 0) != RT_EOK)
    {
      LOG_E("open %s failed!", RTC_NAME);
      return RT_ERROR;
    }

    /* 设置日期 */
    ret = set_date(2018, 12, 3);
    if (ret != RT_EOK)
    {
        rt_kprintf("set RTC date failed\n");
        return ret;
    }

    /* 设置时间 */
    ret = set_time(11, 15, 50);
    if (ret != RT_EOK)
    {
        rt_kprintf("set RTC time failed\n");
        return ret;
    }

    /* 延时3秒 */
    rt_thread_mdelay(3000);

    /* 获取时间 */
    now = time(RT_NULL);
    rt_kprintf("%s\n", ctime(&now));

    return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(rtc_sample, rtc sample);

4. 硬件掉电保存问题

  • VBAT 供电 :如果希望主电源断电后 RTC 依然运行,必须给 VBAT 引脚接上电池(如 CR2032)或大电容。否则,每次断电时间都会复位。
  • 备份寄存器 :如前所述,检查备份寄存器(如 BKP_DR1)的值是判断是否是首次上电 的通用方法。如果该值不是预设的魔术数(如 0xA5A5),说明是冷启动,需要重新配置分频器;否则直接读取计数器即可。

注意

使用rtc设备,需要先进行 rtc的初始化配置 - rt_rtc_config,系统自动注册设备时不会进行初始化配置,需要手动调用初始化配置函数。

参考

RTC 设备
ntp同步成功后date时间戳未更新

相关推荐
ACP广源盛139246256732 小时前
GSV6502与GSV2001应用场景相同点和差异点对比@ACP
单片机·计算机外设
易水寒陈2 小时前
单片机实现的观察者模式
单片机·观察者模式
姜太公钓鲸2332 小时前
STM32是ST公司基于ARM Cortex-M内核开发的32位微控制器。上述文字中的内核是什么意思?作用是什么?
arm开发·stm32·嵌入式硬件
EVERSPIN2 小时前
基于N32G401系列MCU的DALI电源应用方案
单片机·嵌入式硬件·mcu
2501_918126912 小时前
怎么接usb转杜邦线到stm32上
stm32·单片机·嵌入式硬件·学习·个人开发
二十画~书生2 小时前
ESP32-S3音频板
经验分享·单片机·音视频·硬件工程·pcb工艺
星马梦缘2 小时前
如何用VSCODE开发stm32 (日志输出打印)
ide·vscode·stm32·单片机·keil·keil assistant
2501_918126913 小时前
stm32四条线,红绿黑白分别对应什么
stm32·单片机·学习·个人开发
小龙报11 小时前
【51单片机】 给单片机加 “安全锁”!看门狗 WDT:原理 + 配置 + 复位验证全拆解,让程序稳定不跑飞
驱动开发·stm32·单片机·嵌入式硬件·物联网·51单片机·硬件工程