STM32F103 HAL RTC介绍和使用

目录

概述

1 RTC介绍

1.1 RTC 核心特性

1.2 硬件配置要点

2 使用STM32 Cube配置项目

2.1 配置参数

2.2 生成项目

3 核心代码介绍

3.1 主要功能代码

3.2 附加功能代码

3.3 注意事项

4 实践和应用

4.1 初始化检查

4.2 精度校准:

4.3 时间戳功能:

4.4 Tamper 检测:

5 常见问题解决方案

6 解析编译时间的方法


概述

STM32F103 的实时时钟 (RTC) 是一个独立的定时器模块,即使在主电源关闭时(通过 VBAT 电池供电)也能保持时间计数。以下是使用 HAL 库操作 RTC 的完整的使用方法。

1 RTC介绍

1.1 RTC 核心特性

  • 独立供电:VBAT 引脚连接备份电池

  • 32位可编程计数器:可计数秒数(约 136 年)

  • 日历功能:自动计算年、月、日、星期、时、分、秒

  • 闹钟功能:可配置多个闹钟

  • 周期性唤醒:支持低功耗模式下的定时唤醒

  • 入侵检测:防止非法篡改时间

1.2 硬件配置要点

  1. VBAT 引脚:连接 3V 纽扣电池(CR2032)

  2. 时钟源选择

    • LSE(外部晶振):高精度(±20ppm)

    • LSI(内部 RC):较低精度(±500ppm)

RTC在MCU中的结构:

2 使用STM32 Cube配置项目

2.1 配置参数

1) 配置时钟频率为:32768Hz

2) 配置基本参数

3) 使能中断

2.2 生成项目

1) 配置项目参数

2))点击GENERAGE CODE,生成项目文件

3 核心代码介绍

3.1 主要功能代码

1) 初始化RTC

// 启用备份域访问
HAL_PWR_EnableBkUpAccess();

// 启用后备寄存器时钟
__HAL_RCC_BKP_CLK_ENABLE();

// 配置 RTC 时钟源
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
    Error_Handler();
}

// 选择 RTC 时钟源
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
    Error_Handler();
}

// 初始化 RTC
RTC_HandleTypeDef hrtc = {0};
hrtc.Instance = RTC;
hrtc.Init.AsynchPrediv = 127;    // LSE 32.768kHz 分频
hrtc.Init.SynchPrediv = 255;     // 最终时钟 = 32768/(128*256) = 1Hz
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;

if (HAL_RTC_Init(&hrtc) != HAL_OK) {
    Error_Handler();
}

2) 设置日期和时间

RTC_DateTypeDef sDate = {0};
RTC_TimeTypeDef sTime = {0};

// 设置日期:2023年10月15日,星期日
sDate.Year = 23;    // 2023 - 2000
sDate.Month = RTC_MONTH_OCTOBER;
sDate.Date = 15;
sDate.WeekDay = RTC_WEEKDAY_SUNDAY;

if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK) {
    Error_Handler();
}

// 设置时间:14:30:00
sTime.Hours = 14;
sTime.Minutes = 30;
sTime.Seconds = 0;
sTime.SubSeconds = 0;
sTime.TimeFormat = RTC_HOURFORMAT12_AM; // 24小时制下忽略
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;

if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) {
    Error_Handler();
}

3) 读取日期和时间

void Get_RTC_DateTime(RTC_DateTypeDef *date, RTC_TimeTypeDef *time)
{
    // 必须先读时间再读日期(原子操作)
    HAL_RTC_GetTime(&hrtc, time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, date, RTC_FORMAT_BIN);
    
    // 处理年份(2000-2099)
    date->Year += 2000;
}

4) 配置闹钟

// 配置闹钟A:每天14:45:30触发
RTC_AlarmTypeDef sAlarm = {0};
sAlarm.Alarm = RTC_ALARM_A;
sAlarm.AlarmTime.Hours = 14;
sAlarm.AlarmTime.Minutes = 45;
sAlarm.AlarmTime.Seconds = 30;
sAlarm.AlarmMask = RTC_ALARMMASK_NONE; // 所有字段匹配
sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
sAlarm.AlarmDateWeekDay = 0; // 忽略日期
sAlarm.Alarm = RTC_ALARMA_ENABLE;

if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK) {
    Error_Handler();
}

// 在stm32f1xx_it.c中实现中断处理
void RTC_Alarm_IRQHandler(void)
{
    HAL_RTC_AlarmIRQHandler(&hrtc);
}

// 实现回调函数
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
    // 闹钟触发时执行的操作
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); // 翻转LED
}

3.2 附加功能代码

1) 配置周期性唤醒

// 每60秒唤醒一次
if (HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 59, RTC_WAKEUPCLOCK_RTCCLK_DIV16) != HAL_OK) 
{
    Error_Handler();
}

// 在stm32f1xx_it.c中实现中断处理
void RTC_WKUP_IRQHandler(void)
{
    HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
}

// 实现回调函数
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    // 唤醒时执行的操作
    printf("System woken up by RTC\n");
}

2) 备份寄存器操作

// 写入备份寄存器
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0x1234);

// 读取备份寄存器
uint32_t data = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);

3.3 注意事项

1) 备份域保护

// 修改RTC配置前必须取消写保护
HAL_PWR_EnableBkUpAccess();
__HAL_RCC_BKP_CLK_ENABLE();

// 配置完成后重新启用保护
HAL_PWR_DisableBkUpAccess();

2) 时钟源切换

// 从LSE切换到LSI
HAL_RTC_DeInit(&hrtc);
RCC_PeriphCLKInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
HAL_RTC_Init(&hrtc);

3) 电池供电下的低功耗

// 进入STOP模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

// 唤醒后重新配置时钟
SystemClock_Config();

4 实践和应用

4.1 初始化检查

void set_rtc( void )
{
    RTC_TimeTypeDef sTime = {0};
    RTC_DateTypeDef DateToUpdate = {0};
    et_rtc_t _rtc;

    build_rtc(&_rtc);
    
    sTime.Hours = _rtc.hour;
    sTime.Minutes = _rtc.minute;
    sTime.Seconds = _rtc.second;

    if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
    {
        Error_Handler();
    }
    DateToUpdate.Month = _rtc.month;
    DateToUpdate.Date = _rtc.day;
    DateToUpdate.Year = _rtc.year;

    if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
    {
        Error_Handler();
    }
}


void User_init_rtc( void )
{
    // 修改RTC配置前必须取消写保护
    HAL_PWR_EnableBkUpAccess();
    __HAL_RCC_BKP_CLK_ENABLE();
    
    if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0xA5A1) 
    {
        // 首次上电或电池耗尽,初始化RTC
        set_rtc();
        HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0xA5A1);
    }
    
    // 配置完成后重新启用保护
    HAL_PWR_DisableBkUpAccess();
}

4.2 精度校准

// 通过调整异步预分频器进行校准
uint32_t sync_prediv = 255;
uint32_t async_prediv = 127 + calibration_value; // ±30ppm 范围
HAL_RTC_Init(&hrtc);

4.3 时间戳功能

// 记录事件时间戳
HAL_RTCEx_TimeStampIRQHandler(&hrtc);

// 获取时间戳
RTC_TimeTypeDef timestamp_time;
RTC_DateTypeDef timestamp_date;
HAL_RTCEx_GetTimeStamp(&hrtc, &timestamp_time, &timestamp_date, RTC_FORMAT_BIN);

4.4 Tamper 检测

// 配置防篡改检测
RTC_TamperTypeDef sTamper = {0};
sTamper.Tamper = RTC_TAMPER_1;
sTamper.Trigger = RTC_TAMPERTRIGGER_RISINGEDGE;
sTamper.Filter = RTC_TAMPERFILTER_DISABLE;
sTamper.SamplingFrequency = RTC_TAMPERSAMPLINGFREQ_32768HZ;
sTamper.PreechargeDuration = RTC_TAMPERPRECHARGEDURATION_1RTCCLK;
sTamper.TamperPullUp = RTC_TAMPER_PULLUP_ENABLE;
sTamper.TimeStampOnTamperDetection = RTC_TIMESTAMPONTAMPERDETECTION_ENABLE;
HAL_RTCEx_SetTamper(&hrtc, &sTamper);

5 常见问题解决方案

1) RTC 不保持时间

  • 检查 VBAT 电池连接(2.0-3.6V)

  • 验证备份域访问是否启用

  • 检查 LSE 是否正常起振

2) 日历读取错误

  • 确保读取顺序:先时间后日期

  • 检查时间格式(BIN/BCD)

  • 验证 RTC 时钟配置(1Hz 输出)

3)闹钟不触发

  • 确认闹钟中断已启用

  • 检查 NVIC 中断优先级配置

  • 验证闹钟掩码设置

4) 唤醒功能异常

  • 检查唤醒定时器配置

  • 验证低功耗模式配置

  • 确保系统时钟在唤醒后正确初始化

6 解析编译时间的方法

void build_datatime( rtc_t *prtc )
{
    u8 u8_buf[12]; 

    memset( u8_buf, ' ', 12 );
    memcpy( u8_buf, __TIME__, 9 );
    prtc->second = (u8_buf[6] - 0x30)*10 + (u8_buf[7] - 0x30);
    prtc->minute = (u8_buf[3] - 0x30)*10 + (u8_buf[4] - 0x30);
    prtc->hour = (u8_buf[0] - 0x30)*10 + (u8_buf[1] - 0x30); 
    
    memcpy( u8_buf, __DATE__, 12 );
    if(u8_buf[4] > 0x30){
       prtc->day = (u8_buf[4] - 0x30)*10 + (u8_buf[5] - 0x30); 
    } 
    else{
       prtc->day =  (u8_buf[5] - 0x30); 
    }
  
    if( strncmp( (char*)u8_buf, "Jan", 3) == 0 ) {
        prtc->month = 1;
    }
    else if( strncmp( (char*)u8_buf, "Feb", 3) == 0 ) {
        prtc->month = 2;
    }
    else if( strncmp( (char*)u8_buf, "Mar", 3) == 0 ) {
        prtc->month = 3;
    }
    else if( strncmp( (char*)u8_buf, "Apr", 3) == 0 ) {
        prtc->month = 4;
    }
    else if( strncmp( (char*)u8_buf, "May", 3) == 0 ) {
        prtc->month = 5;
    }
    else if( strncmp( (char*)u8_buf, "Jun", 3) == 0 ) {
        prtc->month = 6;
    }
    else if( strncmp( (char*)u8_buf, "Jul", 3) == 0 ) {
        prtc->month = 7;
    }
    else if( strncmp( (char*)u8_buf, "Aug", 3) == 0 ) {
        prtc->month = 8;
    }
    else if( strncmp( (char*)u8_buf, "Sep", 3) == 0 ) {
        prtc->month = 9;
    }
    else if( strncmp( (char*)u8_buf, "Oct", 3) == 0 ) {
        prtc->month = 10;
    }
    else if( strncmp( (char*)u8_buf, "Nov", 3) == 0 ) {
        prtc->month = 11;
    }
    else if( strncmp( (char*)u8_buf, "Dec", 3) == 0 ) {
        prtc->month = 12;
    }   
    prtc->ex_year = (u8_buf[7] - 0x30)*1000 + (u8_buf[8] - 0x30)*100 + \
                    (u8_buf[9] - 0x30)*10 + (u8_buf[10] - 0x30);

    prtc->year = (u8_buf[9] - 0x30)*10 + (u8_buf[10] - 0x30);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值