第十三章 RTC 实时时钟

第十三章 RTC 实时时钟

目录

[第十三章 RTC 实时时钟](#第十三章 RTC 实时时钟)

[1 RTC简介](#1 RTC简介)

[1.1 主要特性](#1.1 主要特性)

[2 功能描述](#2 功能描述)

[2.1 概述](#2.1 概述)

[2.2 复位过程](#2.2 复位过程)

[2.3 读RTC寄存器](#2.3 读RTC寄存器)

[2.4 配置RTC寄存器](#2.4 配置RTC寄存器)

[2.5 RTC标志的设置](#2.5 RTC标志的设置)

[3 RTC寄存器描述](#3 RTC寄存器描述)

[3.1 RTC控制寄存器高位(RTC_CRH)](#3.1 RTC控制寄存器高位(RTC_CRH))

[3.2 RTC控制寄存器低位(RTC_CRL)](#3.2 RTC控制寄存器低位(RTC_CRL))

[3.3 RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL)](#3.3 RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL))

[3.4 RTC预分频器余数寄存器(RTC_DIVH/RTC_DIVL)](#3.4 RTC预分频器余数寄存器(RTC_DIVH/RTC_DIVL))

[3.5 RTC计数器寄存器(RTC_CNTH/RTC_CNTL)](#3.5 RTC计数器寄存器(RTC_CNTH/RTC_CNTL))

[3.6 RTC闹钟寄存器(RTC_ALRH/RTC_ALRL)](#3.6 RTC闹钟寄存器(RTC_ALRH/RTC_ALRL))

[3.7 RTC寄存器映像](#3.7 RTC寄存器映像)

[4 程序设计](#4 程序设计)

[4.1 RTC_Calendar例程](#4.1 RTC_Calendar例程)

[4.2 RTC_LSICalib例程](#4.2 RTC_LSICalib例程)

[5 下载验证](#5 下载验证)

[5.1 RTC_Calendar例程](#5.1 RTC_Calendar例程)

[5.2 RTC_LSICalib例程](#5.2 RTC_LSICalib例程)


本章分为如下几个小节:

1 RTC简介

2 功能描述

3 RTC寄存器描述

4 程序设计

5 下载验证

1 RTC简介

实时时钟是一个独立的定时器。RTC 模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。

RTC 模块和时钟配置系统(RCC_BDCR 寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC 的设置和时间维持不变。系统复位后,对后备寄存器和 RTC 的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。

执行以下操作将使能对后备寄存器和 RTC 的访问:

  • 设置寄存器 RCC_APB1ENR 的 PWREN 和 BKPEN 位,使能电源和后备接口时钟
  • 设置寄存器 PWR_CR 的 DBP 位,使能对后备寄存器和 RTC 的访问。

1.1 主要特性

W55MH32的RTC的主要特性如下:

  • 可编程的预分频系数:分频系数最高为 220。
  • 32 位的可编程计数器,可用于较长时间段的测量。
  • 2 个分离的时钟:用于 APB1 接口的 PCLK1 和 RTC 时钟(RTC 时钟的频率必须小于 PCLK1 时钟频率的四分之一以上)。
  • 可以选择以下三种 RTC 的时钟源:
    • HSE 时钟除以 128;
    • LSE 振荡器时钟;
    • LSI 振荡器时钟(详见 6.2.8 节 RTC 时钟)。
  • 2 个独立的复位类型:
    • APB1 接口由系统复位;
    • RTC 核心(预分频器、闹钟、计数器和分频器)只能由后备域复位(详见 6.1.3 节)。
  • 3 个专门的可屏蔽中断:
    • 闹钟中断,用来产生一个软件可编程的闹钟中断。
    • 秒中断,用来产生一个可编程的周期性中断信号(最长可达 1 秒)。
    • 溢出中断,指示内部可编程计数器溢出并回转为 0 的状态。

2 功能描述

2.1 概述

RTC 由两个主要部分组成(参见下图)。第一部分(APB1 接口)用来和 APB1 总线相连。此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。APB1 接口由 APB1 总线时钟驱动,用来与 APB1 总线接口。

另一部分(RTC 核心)由一组可编程计数器组成,分成两个主要模块。第一个模块是 RTC 的预分频模块,它可编程产生最长为 1 秒的 RTC 时间基准 TR_CLK。RTC 的预分频模块包含了一个 20 位的可编程分频器(RTC 预分频器)。如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个TR_CLK 周期中 RTC 产生一个中断(秒中断)。第二个模块是一个 32 位的可编程计数器,可被初始化为当前的系统时间。系统时间按 TR_CLK 周期累加并与存储在 RTC_ALR 寄存器中的可编程时间相比较,如果 RTC_CR 控制寄存器中设置了相应允许位,比较匹配时将产生一个闹钟中断。

简化的 RTC 框图

2.2 复位过程

除了 RTC_PRL、RTC_ALR、RTC_CNT 和 RTC_DIV 寄存器外,所有的系统寄存器都由系统复位或电源复位进行异步复位。

2.3 读RTC寄存器

RTC 核完全独立于 RTCAPB1 接口。

软件通过 APB1 接口访问 RTC 的预分频值、计数器值和闹钟值。但是,相关的可读寄存器只在与RTCAPB1 时钟进行重新同步的 RTC 时钟的上升沿被更新。RTC 标志也是如此的。这意味着,如果 APB1 接口曾经被关闭,而读操作又是在刚刚重新开启 APB1 之后,则在第一次的内部寄存器更新之前,从 APB1 上读出的 RTC 寄存器数值可能被破坏了(通常读到 0)。下述几种情况下能够发生这种情形:

发生系统复位或电源复位

系统刚从待机模式唤醒(参见第 18.3节:低功耗模式)。

系统刚从停机模式唤醒(参见第 18.3节:低功耗模式)。

所有以上情况中,APB1 接口被禁止时(复位、无时钟或断电)RTC 核仍保持运行状态。

因此,若在读取 RTC 寄存器时,RTC 的 APB1 接口曾经处于禁止状态,则软件首先必须等待RTC_CRL 寄存器中的 RSF 位(寄存器同步标志)被硬件置'1')。

注: RTC 的 APB1 接口不受 WFI 和 WFE 等低功耗模式的影响。

2.4 配置RTC寄存器

必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入 RTC_PRL、RTC_CNT、RTC_ALR 寄存器。

另外,对 RTC 任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询 RTC_CR寄存器中的 RTOFF 状态位,判断 RTC 寄存器是否处于更新中。仅当 RTOFF 状态位是'1'时,才可以写入 RTC 寄存器。

配置过程:

  1. 查询 RTOFF 位,直到 RTOFF 的值变为'1'

  2. 置 CNF 值为 1,进入配置模式

  3. 对一个或多个 RTC 寄存器进行写操作

  4. 清除 CNF 标志位,退出配置模式

  5. 查询 RTOFF,直至 RTOFF 位变为'1'以确认写操作已经完成。仅当 CNF 标志位被清除时,写操作才能进行,这个过程至少需要 3 个 RTCCLK 周期。

2.5 RTC标志的设置

在每一个 RTC 核心的时钟周期中,更改 RTC 计数器之前设置 RTC 秒标志(SECF)。在计数器到达0x0000 之前的最后一个 RTC 时钟周期中,设置 RTC 溢出标志(OWF)。

在计数器的值到达闹钟寄存器的值加 1(RTC_ALR+1)之前的 RTC 时钟周期中,设置 RTC_Alarm 和RTC 闹钟标志(ALRF)。对 RTC 闹钟的写操作必须使用下述过程之一与 RTC 秒标志同步:

使用 RTC 闹钟中断,并在中断处理程序中修改 RTC 闹钟和/或 RTC 计数器。

等待 RTC 控制寄存器中的 SECF 位被设置,再更改 RTC 闹钟和/或 RTC 计数器。

RTC 秒和闹钟波形图示例,PR=0003,ALARM=00004

RTC 溢出波形图示例,PR=0003

3 RTC寄存器描述

3.1 RTC控制寄存器高位(RTC_CRH)

偏移地址:0x00

复位值:0x0000

这些位用来屏蔽中断请求。注意:系统复位后所有的中断被屏蔽,因此可通过写 RTC 寄存器来确保在初始化后没有挂起的中断请求。当外设正在完成前一次写操作时(标志位 RTOFF=0),不能对RTC_CRH 寄存器进行写操作。RTC 功能由这个控制寄存器控制。

3.2 RTC控制寄存器低位(RTC_CRL)

偏移地址:0x00

复位值:0x0000

注:1.任何标志位都将保持挂起状态,直到适当的 RTC_CR 请求位被软件复位,表示所请求的中断已经被接受。

2.在复位时禁止所有中断,无挂起的中断请求,可以对 RTC 寄存器进行写操作。

3.当 APB1 时钟不运行时,OWF、ALRF、SECF 和 RSF 位不被更新。

4.OWF、ALRF、SECF 和 RSF 位只能由硬件置位,由软件来清零。

5.若 ALRF=1 且 ALRIE=1,则允许产生 RTC 全局中断。如果在 EXTI 控制器中允许产生 EXTI 线 17 中断,则允许产生 RTC 全局中断和 RTC 闹钟中断。

6.若 ALRF=1,如果在 EXTI 控制器中设置了 EXTI 线 17 的中断模式,则允许产生 RTC 闹钟中断;如果在 EXTI 控制器中设置了 EXTI 线 17 的事件模式,则这条线上会产生一个脉冲(不会产生 RTC闹钟中断)。

3.3 RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL)

预分频装载寄存器用来保存 RTC 预分频器的周期计数值。它们受 RTC_CR 寄存器的 RTOFF 位保护,仅当 RTOFF 值为'1'时允许进行写操作。

RTC 预分频装载寄存器高位(RTC_PRLH)

偏移地址:0x08

复位值:0x0000

RTC 预分频装载寄存器低位(RTC_PRLL)

偏移地址:0x0C

复位值:0x8000

注: 如果输入时钟频率(fRTCCLK)为 32.768 千赫,则在此寄存器中写入 7FFFh 以获得 1 秒的信号周期

3.4 RTC预分频器余数寄存器(RTC_DIVH/RTC_DIVL)

在 TR_CLK 的每个周期里,RTC 预分频器中计数器的值都会被重新设置为 RTC_PRL 寄存器的值。用户可通过读取 RTC_DIV 寄存器,以获得预分频计数器的当前值,而不停止分频计数器的工作,从而获得精确的时间测量。此寄存器是只读寄存器,其值在 RTC_PRL 或 RTC_CNT 寄存器中的值发生改变后,由硬件重新装载。

RTC 预分频器余数寄存器高位(RTC_DIVH)

偏移地址:0x10

复位值:0x0000

RTC 预分频器余数寄存器低位(RTC_DIVL)

偏移地址:0x14

复位值:0x8000

3.5 RTC计数器寄存器(RTC_CNTH/RTC_CNTL)

RTC 核有一个 32 位可编程的计数器,可通过两个 16 位的寄存器访问。计数器以预分频器产生的TR_CLK时间基准为参考进行计数。RTC_CNT寄存器用来存放计数器的计数值。他们受 RTC_CR的位RTOFF写保护,仅当RTOFF值为'1'时,允许写操作。在高或低寄存器(RTC_CNTH或RTC_CNTL)上的写操作,能够直接装载到相应的可编程计数器,并且重新装载 RTC 预分频器。当进行读操作时,直接返回计数器内的计数值(系统时间)。

RTC 计数器寄存器高位(RTC_CNTH)

偏移地址:0x18

复位值:0x0000

RTC 计数器寄存器低位(RTC_CNTL)

偏移地址:0x1C

复位值:0x0000

3.6 RTC闹钟寄存器(RTC_ALRH/RTC_ALRL)

当可编程计数器的值与 RTC_ALR 中的 32 位值相等时,即触发一个闹钟事件,并且产生 RTC 闹钟中断。此寄存器受 RTC_CR 寄存器里的 RTOFF 位写保护,仅当 RTOFF 值为'1'时,允许写操作。

RTC 闹钟寄存器高位(RTC_ALRH)

偏移地址:0x20

复位值:0xFFFF

RTC 闹钟寄存器低位(RTC_ALRL)

偏移地址:0x24

复位值:0xFFFF

3.7 RTC寄存器映像

RTC-寄存器映像和复位值

4 程序设计

4.1 RTC_Calendar例程

1.初始化部分:延时初始化:调用delay_init()函数,虽未展示其具体实现,但为程序提供时间相关基础功能。

  • 时钟配置:RCC_ClkConfiguration()函数对系统时钟进行配置。先复位 RCC,开启 HSE 并等待其就绪,配置 PLL 为 HSE 经 1 分频后输入,9 倍频输出,使能 PLL 并等待其就绪,选择 PLLCLK 作为系统时钟源,同时设置 HCLK、PCLK1 和 PCLK2 的分频因子,还开启 LSI 和 HSI。
  • 串口初始化:UART_Configuration()函数配置 USART1 串口。使能 USART1 和 GPIOA 时钟,将 PA9 配置为复用推挽输出(TX),PA10 配置为浮空输入(RX),设置波特率为 115200,数据位 8 位,停止位 1 位,无校验,无硬件流控制,工作模式为收发模式,最后使能串口。
  • NVIC 配置:NVIC_Configuration()函数设置 NVIC 优先级分组为 1,使能 RTC 中断,设置其抢占优先级为 1,子优先级为 0。
  • RTC 配置:RTC_Configuration()函数配置 RTC。使能 PWR 和 BKP 时钟,允许访问备份区域,对 BKP 进行初始化,开启 LSI 并等待其就绪,选择 LSI 作为 RTC 时钟源,使能 RTC 时钟并等待同步,设置 RTC 秒中断,设置预分频器为 32767,使 RTC 周期为 1 秒。

2.主循环部分:在main()函数中,先进行延时、时钟配置,接着配置串口,输出 "RTC Calendar Test." 及系统时钟频率信息。检查 BKP 寄存器值判断 RTC 是否已配置,若未配置则进行 RTC 配置并设置时间,若已配置则判断复位类型并根据情况处理。最后调用Time_Show()函数进入一个循环,当TimeDisplay为 1 时显示当前时间。

3.时间调整与显示部分:时间调整:Time_Adjust()函数调用Time_Regulate()函数获取用户通过串口输入的时分秒,然后设置 RTC 计数器。

复制代码
// 时间调整函数(通过串口输入设置时间)
void Time_Adjust(void)
{
    RTC_WaitForLastTask();
    RTC_SetCounter(Time_Regulate()); // 调用输入函数获取时间并设置RTC计数器
    RTC_WaitForLastTask();
}
// 时间输入函数(串口交互获取时分秒)
uint32_t Time_Regulate(void)
{
    uint32_t Tmp_HH, Tmp_MM, Tmp_SS;
    
    // 输入小时(0-23)
    printf("\r\n  Please Set Hours");
    while ((Tmp_HH = USART_Scanf(23)) == 0xFF);
    
    // 输入分钟(0-59)
    printf("\r\n  Please Set Minutes");
    while ((Tmp_MM = USART_Scanf(59)) == 0xFF);
    
    // 输入秒(0-59)
    printf("\r\n  Please Set Seconds");
    while ((Tmp_SS = USART_Scanf(59)) == 0xFF);
    
    return (Tmp_HH * 3600 + Tmp_MM * 60 + Tmp_SS); // 返回总秒数
}
// 时间显示函数(实时打印时间)
void Time_Show(void)
{
    printf("\n\r");
    while (1)
    {
        if (TimeDisplay == 1) // TimeDisplay由RTC中断触发置1
        {
            Time_Display(RTC_GetCounter()); // 转换并显示时间
            TimeDisplay = 0;
        }
    }
}
// 时间格式化显示
void Time_Display(uint32_t TimeVar)
{
    uint32_t THH = TimeVar / 3600;
    uint32_t TMM = (TimeVar % 3600) / 60;
    uint32_t TSS = TimeVar % 60;
    
    // 计数器溢出处理(约24小时归零)
    if (RTC_GetCounter() == 0x0001517F) 
    {
        RTC_SetCounter(0);
        RTC_WaitForLastTask();
    }
    
    printf("Time: %02d:%02d:%02d\n", THH, TMM, TSS);
}

时间显示:Time_Show()函数进入一个循环,当TimeDisplay为 1 时,调用Time_Display()函数获取 RTC 计数器的值并转换为时、分、秒格式进行显示。同时,Time_Display()函数还会在 RTC 计数器达到特定值(0x0001517F)时将其重置为 0。

4.串口输入处理部分:USART_Scanf()函数用于从串口接收用户输入的数字,检查输入是否为有效数字,若无效则提示用户重新输入,直到输入有效数字。

4.2 RTC_LSICalib例程

1.初始化部分:延时初始化:调用delay_init()函数,虽未给出其具体实现,但为程序提供时间相关的基础功能。

复制代码
// 1. 延时初始化(依赖delay.h实现)
delay_init();

// 1.a 串口初始化
void UART_Configuration(uint32_t bound)
{
    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // 配置TX/RX引脚
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;  // PA9-TX
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10;  // PA10-RX
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 配置串口参数
    USART_InitStructure.USART_BaudRate = bound;  // 115200bps
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    
    USART_Init(USART1, &USART_InitStructure);
    USART_Cmd(USART1, ENABLE);
}

// 1.b RTC配置
void RTC_Configuration(void)
{
    // 使能电源和备份域时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);
    
    BKP_DeInit();  // 复位备份域
    
    // 配置RTC时钟源(LSI)
    RCC_LSICmd(ENABLE);
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
    RCC_RTCCLKCmd(ENABLE);
    
    RTC_WaitForSynchro();  // 等待RTC寄存器同步
    RTC_ITConfig(RTC_IT_SEC, ENABLE);  // 使能秒中断
    RTC_SetPrescaler(40000);  // 预设预分频值(假设LSI=40kHz)
    
    // 配置RTC输出
    BKP_TamperPinCmd(DISABLE);
    BKP_RTCOutputConfig(BKP_RTCOutputSource_Second);
}

// 1.c 定时器配置(TIM5用于测量LSI频率)
void TIM_Configuration(void)
{
    // 使能时钟和引脚重映射
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
    GPIO_PinRemapConfig(GPIO_Remap_TIM5CH4_LSI, ENABLE);
    
    // 配置TIM5时基
    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
    
    // 配置TIM5通道4为输入捕获模式
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_ICInitStructure.TIM_ICFilter = 0;
    TIM_ICInit(TIM5, &TIM_ICInitStructure);
    
    // 使能TIM5和捕获中断
    TIM_Cmd(TIM5, ENABLE);
    TIM5->SR = 0;  // 清除中断标志
    TIM_ITConfig(TIM5, TIM_IT_CC4, ENABLE);
}

// 1.d NVIC中断配置
void NVIC_Configuration(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);  // 优先级分组1
    
    // 配置RTC中断(最高优先级)
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 配置TIM5中断(次高优先级)
    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

串口初始化:UART_Configuration()函数配置 USART1 串口。使能 USART1 和 GPIOA 时钟,将 PA9 配置为复用推挽输出(用于 TX 发送),PA10 配置为浮空输入(用于 RX 接收)。设置串口波特率为 115200,数据位 8 位,停止位 1 位,无校验位,无硬件流控制,工作模式为同时接收和发送,并最终使能串口。

RTC 配置:RTC_Configuration()函数对 RTC 进行配置。使能 PWR 和 BKP 时钟,允许访问备份区域,对 BKP 进行初始化。开启 LSI,选择 LSI 作为 RTC 时钟源,使能 RTC 时钟并等待同步,设置 RTC 秒中断,设置预分频器为 40000,禁用 BKP 篡改引脚,配置 RTC 输出为秒信号。

定时器配置:TIM_Configuration()函数对 TIM5 进行配置。使能 AFIO 和 TIM5 时钟,重映射 TIM5 通道 4 到 LSI。设置 TIM5 的预分频器为 0,计数模式为向上计数,周期为 0xFFFF,时钟分频为 1。配置 TIM5 通道 4 为输入捕获模式,上升沿触发,直接映射到 TI,不分频,无滤波。使能 TIM5,清除状态寄存器,使能 TIM5 通道 4 捕获中断。

NVIC 配置:NVIC_Configuration()函数设置 NVIC 优先级分组为 1。使能 RTC 中断,设置其抢占优先级为 0,子优先级为 0;使能 TIM5 中断,设置子优先级为 2。

2.主循环部分:在main()函数中,先进行延时初始化,接着配置串口,输出 "RTC LSI Calib Test." 及系统时钟频率信息。然后进行 RTC、TIM5 配置和 NVIC 配置。等待OperationComplete变为 2,这意味着 TIM5 测量操作完成。当OperationComplete为 2 时,计算 LSI 的实际频率,并根据该频率设置 RTC 的预分频器,最后进入无限循环while(1)。

复制代码
int main(void)
{
    // 系统初始化
    delay_init();
    UART_Configuration(115200);
    printf("RTC LSI Calib Test.\n");
    RCC_GetClocksFreq(&clocks);
    printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, PCLK2: %3.1fMhz, ADCCLK: %3.1fMhz\n",
           (float)clocks.SYSCLK_Frequency / 1000000, 
           (float)clocks.HCLK_Frequency / 1000000,
           (float)clocks.PCLK1_Frequency / 1000000, 
           (float)clocks.PCLK2_Frequency / 1000000, 
           (float)clocks.ADCCLK_Frequency / 1000000);
    
    // 外设初始化
    RTC_Configuration();
    TIM_Configuration();
    NVIC_Configuration();
    
    // 2. 等待TIM5测量完成(OperationComplete=2)
    while (OperationComplete != 2);
    
    // 计算LSI实际频率并校准RTC
    if (PeriodValue != 0)
    {
        // LSI频率 = (2*PCLK1)/PeriodValue
        LsiFreq = (uint32_t)((uint32_t)(clocks.PCLK1_Frequency * 2) / (uint32_t)PeriodValue);
    }
    printf("LsiFreq: %d Hz\n", LsiFreq);
    
    // 根据测量结果校准RTC预分频器
    RTC_SetPrescaler(LsiFreq - 1);
    RTC_WaitForLastTask();
    
    // 无限循环
    while (1);
}

3.辅助函数部分:IncrementVar_OperationComplete()函数使OperationComplete变量自增,并返回自增前的值。

复制代码
// 3.a 递增OperationComplete并返回旧值
uint32_t IncrementVar_OperationComplete(void)
{
    OperationComplete++;
    return (uint32_t)(OperationComplete - 1);
}

// 3.b 获取OperationComplete值
uint32_t GetVar_OperationComplete(void)
{
    return (uint32_t)OperationComplete;
}

// 3.c 设置PeriodValue值
void SetVar_PeriodValue(uint32_t Value)
{
    PeriodValue = (uint32_t)(Value);
}

GetVar_OperationComplete()函数返回OperationComplete变量的值。

SetVar_PeriodValue()函数设置PeriodValue变量的值。

5 下载验证

5.1 RTC_Calendar例程

5.2 RTC_LSICalib例程

相关推荐
小慧10241 天前
STM32多串口应用
stm32·单片机·嵌入式硬件
小龙报1 天前
【SOLIDWORKS 练习题】草图专题:3.机械臂
笔记·单片机·嵌入式硬件·物联网·开源·硬件工程·材料工程
Moonquakes5401 天前
嵌入式开发基础学习笔记(RGB LCD 驱动开发)
arm开发·驱动开发·嵌入式硬件
youcans_1 天前
【动手学STM32G4】(16)PWM 触发 ADC 精确同步采样
stm32·单片机·嵌入式硬件·pwm·adc
松涛和鸣2 天前
DAY63 IMX6ULL ADC Driver Development
linux·运维·arm开发·单片机·嵌入式硬件·ubuntu
想放学的刺客2 天前
单片机嵌入式试题(第23期)嵌入式系统电源管理策略设计、嵌入式系统通信协议栈实现要点两个全新主题。
c语言·stm32·单片机·嵌入式硬件·物联网
猫猫的小茶馆2 天前
【Linux 驱动开发】五. 设备树
linux·arm开发·驱动开发·stm32·嵌入式硬件·mcu·硬件工程
jghhh012 天前
基于上海钜泉科技HT7017单相计量芯片的参考例程实现
科技·单片机·嵌入式硬件
恶魔泡泡糖2 天前
51单片机外部中断
c语言·单片机·嵌入式硬件·51单片机
意法半导体STM322 天前
【官方原创】如何基于DevelopPackage开启安全启动(MP15x) LAT6036
javascript·stm32·单片机·嵌入式硬件·mcu·安全·stm32开发