【正点原子STM32】RTC实时时钟(RTC方案、BCD码、时间戳、RTC相关寄存器和HAL库驱动、RTC基本配置步骤、RTC基本驱动步骤、时间设置和读取、RTC闹钟配置和RTC周期性自动唤醒配置)

这篇具有很好参考价值的文章主要介绍了【正点原子STM32】RTC实时时钟(RTC方案、BCD码、时间戳、RTC相关寄存器和HAL库驱动、RTC基本配置步骤、RTC基本驱动步骤、时间设置和读取、RTC闹钟配置和RTC周期性自动唤醒配置)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、RTC简介
二、STM32 RTC框图介绍
2.1、STM32 F1 RTC结构框图
2.2、STM32 F4 / F7 / H7 RTC结构框图

三、RTC相关寄存器介绍
3.1、RTC基本配置步骤
3.2、RTC相关寄存器(F1)
3.3、RTC相关寄存器(F4 / F7 / H7)

四、RTC相关HAL库驱动介绍
4.1、RTC相关HAL库驱动(F1)
4.2、RTC相关HAL库驱动(F4 / F7 / H7)

五、RTC基本驱动步骤
5.1、RTC基本驱动步骤(F1)
5.2、RTC基本驱动步骤(F4 / F7 / H7)

六、时间设置和读取
6.1、时间设置和读取(F1)
6.2、RTC闹钟配置 和 RTC周期性自动唤醒配置(F4 / F7 / H7)

七、编程实战
7.1、F1-RTC
7.2、H750-RTC

八、总结

一、RTC简介

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
RTC(Real Time Clock,实时时钟)是一种专门用于记录时间的设备或模块,通常作为计算机系统中的一部分存在。其本质是一个计数器,以秒为单位进行计数,可以提供精确的时间信息,并且具有以下特性:

  1. 提供时间信息: RTC能够提供当前的时间,通常以秒钟数的形式表示,但也可以提供更精细的时间分辨率,如毫秒或微秒级别。

  2. 持久性: RTC具有持久性,即在MCU(Microcontroller Unit,微控制器单元)掉电后仍然能够继续运行,因此能够确保时间信息的连续性和准确性。

  3. 低功耗: RTC通常具有低功耗特性,以确保在长时间内使用时消耗的能量较少,这对于依靠电池供电的应用尤为重要,因为它可以延长电池的使用寿命。

总的来说,RTC在许多应用中扮演着关键的角色,特别是在需要准确记录时间并且需要在掉电后继续运行的场景下,如数据记录、日志记录、定时任务等。

常用的RTC方案?

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
常用的RTC方案包括片上RTC外设方案和独立RTC芯片方案。下面是它们的对比:

片上RTC外设方案:

  • 信息差异: 提供秒/亚秒信号,通常只提供基本的时间信息,如秒、亚秒等。
  • 功耗: 功耗较高,因为RTC外设通常与MCU的其他功能集成在一起,共享同一个电源。
  • 体积: 不占用额外体积,因为RTC外设已经集成在MCU芯片内部。
  • 成本: 成本较低,因为不需要额外购买RTC芯片,只需在选择MCU时考虑是否需要RTC外设。

独立RTC芯片方案:

  • 信息差异: 提供秒信号和日历功能,能够记录日期、月份、年份等日历信息。
  • 功耗: 功耗较低,因为独立RTC芯片通常设计为低功耗模式,能够长时间运行。
  • 体积: 体积较大,因为独立RTC芯片需要额外的空间来安装。
  • 成本: 成本较高,因为需要购买额外的RTC芯片,并且可能需要设计额外的外围电路。

综合来看,选择片上RTC外设方案还是独立RTC芯片方案取决于具体的应用需求和设计考虑:

  • 如果需要基本的时间信息记录,对功耗和成本有较低要求,并且希望节省空间,可以选择片上RTC外设方案。
  • 如果需要更丰富的时间信息记录,对功耗和成本有更高要求,并且可以接受额外的空间占用,可以选择独立RTC芯片方案。

BCD码

BCD码(Binary-Coded Decimal‎)也称二进码十进数,BCD码可分为有权码和无权码两类。
BCD码(Binary-Coded Decimal)是一种用二进制数表示十进制数的编码方式。在BCD码中,每个十进制数位用4位二进制数表示,其取值范围为0000到1001(即0到9的二进制表示),每个十进制数位占据一个字节(8位),方便直接转换为十进制数。

BCD码的优点在于它可以直观地表示十进制数,并且每个十进制数位都可以独立处理,便于进行数字的加减运算和其他数学运算。因此,在实时时钟(RTC)等需要精确记录时间的应用中,常常使用BCD码来存储时间信息。

举例来说,如果要表示十进制数 25,它的BCD码表示为 0010 0101。其中,0010表示十位数 2,0101表示个位数 5。

BCD码的主要缺点是它对数字的表示范围有限,无法直接表示大于9的十进制数,因此在一些高性能、大数据量的应用中可能不够灵活。
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置

时间戳

时间戳(Timestamp)是指一种表示日期和时间的方式,通常是一个数字或字符串,表示自某个特定起点(通常是某个固定的起始时间,如1970年1月1日午夜格林尼治时间)经过了多长的时间间隔,单位可以是秒、毫秒、微秒等。

常见的时间戳是指 Unix 时间戳,它表示自1970年1月1日午夜格林尼治时间(也称为 Unix 纪元时间)以来经过的秒数。Unix 时间戳是计算机领域中最常用的时间表示方式之一,因为它简单、清晰,并且在不同系统之间具有良好的可移植性。

时间戳的优点在于它不受时区的影响,能够统一地表示全球范围内的时间,便于进行时间的比较和计算。在软件开发、数据库管理、日志记录等领域广泛应用。

举例来说,当前时间戳(以秒为单位)可以通过编程语言或操作系统提供的相关函数获取,例如在 Unix/Linux 系统中可以使用 time() 函数获取当前时间戳。

二、STM32 RTC框图介绍

2.1、STM32 F1 RTC结构框图

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
STM32 F1系列的RTC(Real-Time Clock,实时时钟)模块是一个功能强大的硬件模块,用于提供精确的时间和日期信息,并在设备掉电后保持时间信息。下面是对其结构的详细解释:

  1. RTC预分频器

    • RTC模块包含一个预分频器,用于将外部时钟源(HSE、LSI、LSE)的频率分频为RTC模块所需的时钟频率。预分频器允许通过配置来调整RTC模块的时钟频率,以满足不同的应用需求。
  2. 32位可编程计数器

    • RTC模块内部包含一个32位的可编程计数器,用于计算秒数、分钟数、小时数等。这个计数器可以通过配置进行初始化,并在每个时钟周期更新。它允许RTC模块跟踪时间的流逝,以及提供精确的日期和时间信息。
  3. 待机唤醒

    • RTC模块具有待机唤醒功能,可以在设备进入低功耗待机模式时继续运行,以提供持续的时间跟踪和唤醒功能。这使得设备可以在低功耗模式下保持时间信息,并在需要时快速唤醒。
  4. RTC控制寄存器与APB1接口

    • RTC模块与APB1总线相连,通过RTC控制寄存器可以对RTC模块进行配置和控制。这些寄存器允许软件对RTC模块的各个功能进行设置,包括时钟源选择、预分频器设置、计数器初始化等。
  5. 三个时钟源

    • RTC模块可以使用三种不同的时钟源来提供时钟信号:外部高速时钟(HSE)/ 128、低速内部时钟(LSI,频率为40kHz)和低速外部时钟(LSE,频率为32.768kHz)。这些时钟源中的任何一个都可以被选择,以提供RTC模块所需的时钟信号。
  6. RTC工作在后备区域

    • RTC模块通常工作在设备的后备区域,这意味着即使设备的主要电源VDD掉电,RTC模块仍然可以正常工作,并且可以继续提供时间跟踪和唤醒功能。这种特性对于需要持续跟踪时间的应用非常有用,如实时时钟、定时器和报警系统等。

2.2、STM32 F4 / F7 / H7 RTC结构框图

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
STM32 F4、F7和H7系列的RTC(Real-Time Clock,实时时钟)模块提供了更多的功能和灵活性,用于提供精确的时间和日期信息,并在设备掉电后保持时间信息。以下是对其结构的详细解释:

  1. RTC时钟源

    • RTC模块可以使用多种时钟源来提供时钟信号,包括外部低速时钟(LSE,频率为32.768kHz)、外部高速时钟(HSE_RTC)和低速内部时钟(LSI)。这些时钟源中的任何一个都可以被选择,以提供RTC模块所需的时钟信号。
  2. 预分频器

    • RTC模块包含异步预分频器和同步预分频器,用于将时钟源的频率分频为RTC模块所需的时钟频率。预分频器允许通过配置来调整RTC模块的时钟频率,以满足不同的应用需求。
  3. 亚秒寄存器

    • RTC模块包含亚秒寄存器,用于提供更精确的时间跟踪。亚秒寄存器可以在每秒钟产生多个时钟周期,以提供更高的时间分辨率。
  4. 唤醒预分频器和自动重载寄存器

    • RTC模块具有唤醒预分频器和唤醒自动重载寄存器,用于配置唤醒功能并在设备进入低功耗模式时继续运行。唤醒预分频器和自动重载寄存器可以设置唤醒周期,并在达到指定的唤醒时间时产生中断。
  5. RTC和日历

    • RTC模块不仅提供了时间跟踪功能,还提供了日历功能,可以跟踪年、月、日、小时、分钟和秒等日期信息。这些信息可以在RTC模块的寄存器中存储,并可以通过读取和写入操作进行访问和更新。
  6. 时间戳

    • RTC模块还支持时间戳功能,可以用来记录事件发生的时间。时间戳可以记录时间和日期信息,并在需要时读取和处理。
  7. 闹钟和入侵检测

    • RTC模块具有闹钟功能,可以设置闹钟触发时间,并在达到指定的闹钟时间时产生中断。此外,RTC模块还支持入侵检测功能,可以检测外部事件的发生,并在检测到入侵事件时产生中断。
  8. 备份和RTC入侵控制寄存器

    • RTC模块具有备份寄存器和RTC入侵控制寄存器,用于存储和配置RTC模块的备份数据和入侵检测功能。这些寄存器可以用来存储设备的配置信息、用户数据等,并在需要时进行读取和更新。

三、RTC相关寄存器介绍

3.1、RTC基本配置步骤

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
正确配置STM32的RTC模块是确保其正常运行的关键步骤之一。以下是基本的RTC配置步骤:

  1. 使能对RTC的访问

    • 首先,需要通过RCC寄存器使能对PWR(Power Control,电源控制)和BKP(Backup Registers,后备寄存器)的时钟访问权限。在PWR_CR寄存器中设置特定位来使能对后备寄存器和RTC的访问权限。
  2. 设置RTC时钟源

    • 接下来,需要选择RTC的时钟源。通常情况下,选择外部低速晶体振荡器(LSE,32.768kHz)作为RTC的时钟源。要启用LSE,需要在RCC_BDCR寄存器中设置相应的位来激活LSE,并将RTC的计数时钟源设置为LSE。
  3. 进入配置模式

    • 在对RTC进行配置之前,需要将RTC模块设置为配置模式。这可以通过设置RTC_CRL寄存器中的特定位来实现。等待RTOFF位变为1,并将CNF位设置为1,以进入配置模式。
  4. 设置RTC寄存器

    • 一旦进入配置模式,可以设置RTC寄存器以配置RTC的各种参数。例如,设置RTC的预分频器寄存器(RTC_PRL)来设置分频值,设置RTC的计数器寄存器(RTC_CNT)来设置初始计数值等。
  5. 退出配置模式

    • 配置完成后,需要退出配置模式以使RTC模块正常工作。这可以通过清除RTC_CRL寄存器中的CNF位来实现。然后等待RTOFF位变为1,表明RTC退出配置模式,配置完成。

总的来说,以上步骤描述了如何使能和配置STM32的RTC模块,确保其正常运行并提供准确的时间跟踪和日期信息。在配置完成后,RTC模块将能够持续跟踪时间并在需要时提供准确的时间和日期信息。

3.2、RTC相关寄存器(F1)

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置

3.3、RTC相关寄存器(F4 / F7 / H7)

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置

四、RTC相关HAL库驱动介绍

4.1、RTC相关HAL库驱动(F1)

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
针对STM32F1系列的RTC相关HAL库驱动函数和相关寄存器功能描述如下:

  1. HAL_RTC_Init(…)

    • 关联寄存器:CRL/CRH/PRLH/PRLL
    • 功能描述:该函数用于初始化RTC模块,包括配置RTC的控制寄存器(CRL/CRH)以及设置RTC的预分频寄存器(PRLH/PRLL)。
  2. HAL_RTC_MspInit(…)

    • 功能描述:这是RTC的初始化回调函数,在其中需要使能RTC所需的时钟和外设。
    • 相关操作:通常在该函数中需要调用HAL_RCC_OscConfig(…)函数开启LSE时钟源,并调用HAL_RCCEx_PeriphCLKConfig(…)函数设置RTC的时钟源为LSE。
  3. HAL_RCC_OscConfig(…)

    • 关联寄存器:RCC_CR/PWR_CR
    • 功能描述:该函数用于配置时钟源,一般用于开启LSE时钟源。
  4. HAL_RCCEx_PeriphCLKConfig(…)

    • 关联寄存器:RCC_BDCR
    • 功能描述:该函数用于配置外设时钟,用于设置RTC的时钟源为LSE。
  5. HAL_PWR_EnableBkUpAccess(…)

    • 关联寄存器:PWR_CR
    • 功能描述:该函数用于使能备份域的访问权限,以便可以读写备份寄存器。
  6. HAL_RTCEx_BKUPWrite/Read()

    • 关联寄存器:BKP_DRx
    • 功能描述:这些函数用于读写RTC的备份寄存器,用于存储一些特定的数据或参数。

在使用RTC之前,需要确保已经开启了相应的时钟源。通常情况下,需要开启RTC的时钟源(LSE)以及相应的电源时钟,可以通过调用相关的宏或函数来实现:

  • __HAL_RCC_RTC_ENABLE():使能RTC时钟
  • __HAL_RCC_PWR_CLK_ENABLE():使能PWR模块时钟
  • __HAL_RCC_BKP_CLK_ENABLE():使能BKP模块时钟

通过以上的函数和操作,可以实现对STM32F1系列的RTC模块进行初始化和配置,确保其正常运行并提供准确的时间跟踪和日期信息。

4.2、RTC相关HAL库驱动(F4 / F7 / H7)

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
针对STM32F4/F7/H7系列的RTC日历功能,以下是相关的HAL库驱动函数及其功能描述:

  1. HAL_RTC_Init(…)

    • 关联寄存器:RTC_CR、RTC_PRER、RTC_WPR
    • 功能描述:初始化RTC模块,包括配置RTC控制寄存器(RTC_CR)、预分频寄存器(RTC_PRER)以及写保护寄存器(RTC_WPR)。
  2. HAL_RTC_MspInit(…)

    • 功能描述:RTC的初始化回调函数,在其中需要使能RTC所需的时钟和外设。
    • 相关操作:通常需要调用HAL_RCC_OscConfig(…)函数开启LSE时钟源,并调用HAL_RCCEx_PeriphCLKConfig(…)函数设置RTC的时钟源为LSE。
  3. HAL_RCC_OscConfig(…)

    • 关联寄存器:RCC_CR
    • 功能描述:配置时钟源,用于开启LSE时钟源。
  4. HAL_RCCEx_PeriphCLKConfig(…)

    • 关联寄存器:RCC_BDCR
    • 功能描述:配置外设时钟,用于设置RTC的时钟源为LSE。
  5. HAL_RTC_Set/GetTime(…)

    • 关联寄存器:RTC_TR
    • 功能描述:设置/读取时间,包括小时、分钟和秒。
  6. HAL_RTC_Set/GetDate(…)

    • 关联寄存器:RTC_DR
    • 功能描述:设置/读取日期,包括年、月、日和星期。
  7. HAL_PWR_EnableBkUpAccess(…)

    • 关联寄存器:PWR_CR
    • 功能描述:使能备份域的访问权限,以便可以读写备份寄存器。
  8. HAL_RTCEx_BKUPWrite/Read()

    • 关联寄存器:RTC_BKPxR
    • 功能描述:这些函数用于读写RTC的备份寄存器,用于存储一些特定的数据或参数。

在使用RTC之前,需要确保已经开启了相应的时钟源。可以通过调用以下宏或函数来实现:

  • __HAL_RCC_RTC_CLK_ENABLE():使能RTC时钟
  • __HAL_RCC_RTC_ENABLE():使能RTC模块

通过以上的函数和操作,可以实现对STM32F4/F7/H7系列的RTC模块进行初始化和配置,以及设置和读取时间日期信息,从而实现日历功能。
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置

五、RTC基本驱动步骤

5.1、RTC基本驱动步骤(F1)

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
针对STM32F1系列的RTC基本驱动步骤,以下是一般性的流程:

  1. 使能电源时钟并使能后备域访问

    • 使用宏或函数使能电源时钟和备份时钟,确保后续可以对RTC的后备域进行访问:
      __HAL_RCC_PWR_CLK_ENABLE();       // 使能电源时钟
      __HAL_RCC_BKP_CLK_ENABLE();       // 使能备份时钟
      HAL_PWR_EnableBkUpAccess();       // 使能备份访问
      
  2. 开启LSE/选择RTC时钟源/使能RTC时钟

    • 配置RTC的时钟源为外部低速时钟(LSE),并使能RTC时钟:
      HAL_RCC_OscConfig(...);                  // 开启LSE
      HAL_RCCEx_PeriphCLKConfig(...);  // 选择RTC时钟源
      __HAL_RCC_RTC_ENABLE();               // 使能RTC时钟
      
  3. 初始化RTC,设置分频值以及工作参数

    • 调用HAL_RTC_Init()函数初始化RTC模块,并设置分频值、时钟输出使能等参数:
      HAL_RTC_Init(...);            // 初始化RTC
      HAL_RTC_MspInit(...);     // 完成RTC底层初始化工作
      
  4. 设置RTC的日期和时间

    • 使用相应的寄存器操作函数,设置RTC的日期和时间信息:
      rtc_set_time(...);           // 操作寄存器方式设置RTC的日期和时间
      
  5. 获取RTC当前日期和时间

    • 定义相应的函数,使用寄存器读取当前RTC的日期和时间信息:
      rtc_get_time(...);           // 定义函数读取RTC的当前日期和时间
      

以上是一个简要的RTC基本驱动步骤示例,具体实现时需要根据实际需求和硬件平台进行相应的配置和调整。

5.2、RTC基本驱动步骤(F4 / F7 / H7)

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
针对STM32F4/F7/H7系列的RTC基本驱动步骤,以下是一般性的流程:

  1. 使能电源时钟并使能后备域访问

    • 使用宏或函数使能PWR时钟,并使能备份域访问权限:
      __HAL_RCC_PWR_CLK_ENABLE();        // 使能PWR时钟
      HAL_PWR_EnableBkUpAccess();        // 使能备份访问
      
  2. 开启LSE/选择RTC时钟源/使能RTC

    • 配置RTC的时钟源为外部低速时钟(LSE),并使能RTC时钟:
      HAL_RCC_OscConfig(...);                   // 开启LSE
      HAL_RCCEx_PeriphCLKConfig(...);   // 选择RTC时钟源
      __HAL_RCC_RTC_CLK_ENABLE();          // 使能RTC时钟
      
  3. 初始化RTC(同/异步分频系数和时钟格式)

    • 调用HAL_RTC_Init()函数初始化RTC模块,设置同步/异步分频系数和时钟格式等参数:
      HAL_RTC_Init(...);              // 初始化RTC
      HAL_RTC_MspInit(...);       // 完成RTC底层初始化工作
      
  4. 设置RTC的日期和时间

    • 使用HAL_RTC_SetTime()和HAL_RTC_SetDate()函数设置RTC的时间和日期:
      HAL_RTC_SetTime(...);      // 设置RTC时间
      HAL_RTC_SetDate(...);      // 设置RTC日期
      
  5. 获取RTC当前日期和时间

    • 使用HAL_RTC_GetTime()和HAL_RTC_GetDate()函数获取当前RTC的时间和日期:
      HAL_RTC_GetTime(...);      // 获取当前RTC时间
      HAL_RTC_GetDate(...);      // 获取当前RTC日期
      

以上是一个简要的RTC基本驱动步骤示例,具体实现时需要根据实际需求和硬件平台进行相应的配置和调整。

六、时间设置和读取

6.1、时间设置和读取(F1)

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
针对STM32F1系列的RTC,由于其没有日历寄存器,只能存储总秒数,因此需要进行一些额外的操作才能将总秒数转换为日历时间,并提供一些功能函数来处理日历时间。下面是一些相关的函数和结构体说明:

  1. 全局结构体变量 calendar 存储时间信息

    • 在代码中通常会定义一个结构体变量用于存储日历时间的各个参数,比如年、月、日、时、分、秒等信息。
  2. rtc_date2sec() 函数

    • 这是一个静态函数,用于将日历时间转换为对应的总秒数。该函数会根据输入的年、月、日、时、分、秒等参数计算出对应的总秒数。
  3. rtc_get_time() 函数

    • 这个函数的功能是将总秒数转换为日历时间。它会根据给定的总秒数,计算出对应的年、月、日、时、分、秒等时间信息,并将结果存储到结构体变量 calendar 中。
  4. rtc_is_leap_year() 函数

    • 这个函数用于判断给定的年份是否是闰年。
  5. rtc_get_week() 函数

    • 这个函数用于计算给定的公历日历日期对应的星期几。

通过这些功能函数,您可以方便地进行时间的设置和读取,以及一些相关的时间计算操作,使得在STM32F1系列芯片上使用RTC更加便捷有效。

6.2、RTC闹钟配置 和 RTC周期性自动唤醒配置(F4 / F7 / H7)

RTC闹钟配置一般步骤

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
RTC闹钟配置步骤,以下是更详细的说明:

  1. RTC已初始化

    • 在配置RTC闹钟之前,确保RTC已经初始化并且相关参数已经设置好。
  2. 配置闹钟参数并开启中断

    • 使用HAL_RTC_SetAlarm_IT()函数配置闹钟参数并开启中断:
      HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);
      
  3. 设置闹钟中断优先级

    • 使用HAL_NVIC_SetPriority()函数设置闹钟中断的优先级:
      HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
      
  4. 使能闹钟中断

    • 使用HAL_NVIC_EnableIRQ()函数使能闹钟中断:
      HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
      
  5. 编写中断服务函数

    • 定义一个中断服务函数,用于处理RTC闹钟中断事件:
      void RTC_Alarm_IRQHandler(void)
      {
          HAL_RTC_AlarmIRQHandler(&hrtc);
      }
      
  6. HAL库RTC中断共用处理函数

    • HAL库提供了RTC中断共用处理函数HAL_RTC_AlarmIRQHandler(),在中断服务函数中调用该函数:
      HAL_RTC_AlarmIRQHandler(&hrtc);
      
  7. 重定义中断回调函数

    • 如果需要在闹钟中断发生时执行特定操作,可以重定义HAL库提供的中断回调函数HAL_RTC_AlarmAEventCallback():
      void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
      {
          // 在此处编写闹钟中断发生时的操作
      }
      

以上是RTC闹钟配置的一般步骤和相应的操作说明。通过这些步骤,您可以实现RTC闹钟的配置和中断处理功能。

RTC周期性自动唤醒配置一般步骤

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
RTC周期性自动唤醒配置步骤,以下是更详细的说明:

  1. RTC已初始化

    • 在配置RTC周期性自动唤醒功能之前,确保RTC已经初始化并且相关参数已经设置好。
  2. 清除RTC WKUP标志位

    • 使用__HAL_RTC_WAKEUPTIMER_CLEAR_FLAG()函数清除RTC的WKUP标志位:
      __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&hrtc, RTC_FLAG_WUTF);
      
  3. 设置闹钟参数并开启中断

    • 使用HAL_RTCEx_SetWakeUpTimer_IT()函数设置周期性自动唤醒的时间间隔并开启中断:
      HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, wakeup_time, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
      
  4. 设置中断优先级

    • 使用HAL_NVIC_SetPriority()函数设置RTC周期性自动唤醒中断的优先级:
      HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 0, 0);
      
  5. 使能中断

    • 使用HAL_NVIC_EnableIRQ()函数使能RTC周期性自动唤醒中断:
      HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
      
  6. 编写中断处理逻辑

    • 定义一个中断服务函数,用于处理RTC周期性自动唤醒中断事件:
      void RTC_WKUP_IRQHandler(void)
      {
          HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
      }
      
  7. HAL库RTC中断共用处理函数

    • HAL库提供了RTC周期性自动唤醒中断共用处理函数HAL_RTCEx_WakeUpTimerIRQHandler(),在中断服务函数中调用该函数:
      HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
      
  8. 重定义中断回调函数

    • 如果需要在周期性自动唤醒中断发生时执行特定操作,可以重定义HAL库提供的中断回调函数HAL_RTCEx_WakeUpTimerEventCallback():
      void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
      {
          // 在此处编写周期性自动唤醒中断发生时的操作
      }
      

以上是RTC周期性自动唤醒配置的一般步骤和相应的操作说明。通过这些步骤,您可以实现RTC周期性自动唤醒功能,并在中断发生时执行相应的操作。

七、编程实战

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置

7.1、F1-RTC

rtc.c

#include "./BSP/RTC/rtc.h"
#include "./BSP/LED/led.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"

RTC_HandleTypeDef g_rtc_handle; /* RTC控制句柄 */
_calendar_obj calendar;         /* 时间结构体 */

/**
 * @brief       RTC写入后备区域SRAM
 * @param       bkrx : 后备区寄存器编号,范围:0~41,对应 RTC_BKP_DR1~RTC_BKP_DR42
 * @param       data : 要写入的数据,16位长度
 * @retval      无
 */
void rtc_write_bkr(uint32_t bkrx, uint16_t data)
{
    // 取消备份区写保护
    HAL_PWR_EnableBkUpAccess();
    // 使用HAL函数写入后备寄存器
    HAL_RTCEx_BKUPWrite(&g_rtc_handle, bkrx + 1, data);
}

/**
 * @brief       RTC读取后备区域SRAM
 * @param       bkrx : 后备区寄存器编号,范围:0~41,对应 RTC_BKP_DR1~RTC_BKP_DR42
 * @retval      读取到的值
 */
uint16_t rtc_read_bkr(uint32_t bkrx)
{
    uint32_t temp = 0;
    // 使用HAL函数读取后备寄存器
    temp = HAL_RTCEx_BKUPRead(&g_rtc_handle, bkrx + 1);
    return (uint16_t)temp; /* 返回读取到的值 */
}

/**
 * @brief       设置RTC时间
 * @param       syear : 年份
 * @param       smon  : 月份
 * @param       sday  : 日期
 * @param       hour  : 小时
 * @param       min   : 分钟
 * @param       sec   : 秒钟
 * @retval      0 表示成功
 */
uint8_t rtc_set_time(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec)
{
    uint32_t seccount = 0;
    
    // 将日期转换成秒钟数
    seccount = rtc_date2sec(syear, smon, sday, hour, min, sec);

    // 使能RTC后备寄存器和电源时钟
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_RCC_BKP_CLK_ENABLE();
    HAL_PWR_EnableBkUpAccess();
    
    // 设置RTC CRL寄存器,写入时间前先设置写保护位
    RTC->CRL |= 1 << 4;
    
    // 分别写入低16位和高16位
    RTC->CNTL = seccount & 0xFFFF;
    RTC->CNTH = seccount >> 16;
    
    // 清除写保护位
    RTC->CRL &= ~(1 << 4);
    
    // 等待RTC寄存器的操作完成
    while(!__HAL_RTC_ALARM_GET_FLAG(&g_rtc_handle, RTC_FLAG_RTOFF));
    
    return 0;
}

/**
 * @brief       获取RTC时间
 */
void rtc_get_time(void)
{
    static uint16_t daycnt = 0;
    uint32_t seccount = 0;
    uint32_t temp = 0;
    uint16_t temp1 = 0;
    const uint8_t month_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};   /* 平年的月份日期表 */
    
    // 读取RTC寄存器的时间
    seccount = RTC->CNTH;
    seccount <<= 16;
    seccount += RTC->CNTL;
    
    // 计算天数
    temp = seccount / 86400;

    // 如果超过一天,更新日期
    if (daycnt != temp)
    {
        daycnt = temp;
        temp1 = 1970;           /* 从1970年开始 */

        // 计算年份
        while (temp >= 365)
        {
            if (rtc_is_leap_year(temp1))    /* 是闰年 */
            {
                if (temp >= 366)
                {
                    temp -= 366;    /* 闰年的秒钟数 */
                }
                else
                {
                    break;
                }
            }
            else
            {
                temp -= 365;    /* 平年 */
            }
            
            temp1++;
        }

        calendar.year = temp1;  /* 得到年份 */
        temp1 = 0;

        // 计算月份
        while (temp >= 28)
        {
            if (rtc_is_leap_year(calendar.year) && temp1 == 1)  /* 当年是不是闰年/2月份 */
            {
                if (temp >= 29)
                {
                    temp -= 29; /* 闰年的秒钟数 */
                }
                else
                {
                    break;
                }
            }
            else
            {
                if (temp >= month_table[temp1])
                {
                    temp -= month_table[temp1]; /* 平年 */
                }
                else
                {
                    break;
                }
            }

            temp

1++;
        }

        calendar.month = temp1 + 1;     /* 得到月份 */
        calendar.date = temp + 1;       /* 得到日期 */
    }

    // 计算小时、分钟、秒钟
    temp = seccount % 86400;
    calendar.hour = temp / 3600;        /* 小时 */
    calendar.min = (temp % 3600) / 60;  /* 分钟 */
    calendar.sec = (temp % 3600) % 60;  /* 秒钟 */
    // 获取星期
    calendar.week = rtc_get_week(calendar.year, calendar.month, calendar.date);
}

/**
 * @brief       RTC初始化
 * @retval      0 表示成功
 */
uint8_t rtc_init(void)
{
    // 使能RTC时钟和后备寄存器
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_RCC_BKP_CLK_ENABLE();
    HAL_PWR_EnableBkUpAccess();
    
    // 配置RTC参数并初始化
    g_rtc_handle.Instance = RTC;
    g_rtc_handle.Init.AsynchPrediv = 32767;
    g_rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
    HAL_RTC_Init(&g_rtc_handle);
    
    // 如果后备寄存器未被初始化过,则设置默认时间并写入标记
    if (rtc_read_bkr(0) != 0x8888)
    {
        rtc_set_time(2088, 8, 8, 8, 8, 8);
        rtc_write_bkr(0, 0x8888);
    }
    
    return 0;
}

/**
 * @brief       RTC MSP初始化
 * @param       hrtc : RTC句柄
 */
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
    // 使能RTC外设时钟
    __HAL_RCC_RTC_ENABLE();
    
    RCC_OscInitTypeDef rcc_oscinitstruct;
    RCC_PeriphCLKInitTypeDef rcc_periphclkinitstruct;
    
    // 配置LSE时钟源
    rcc_oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
    rcc_oscinitstruct.LSEState = RCC_LSE_ON;
    rcc_oscinitstruct.PLL.PLLState = RCC_PLL_NONE;
    HAL_RCC_OscConfig(&rcc_oscinitstruct);
    
    // 配置RTC时钟源
    rcc_periphclkinitstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
    rcc_periphclkinitstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
    HAL_RCCEx_PeriphCLKConfig(&rcc_periphclkinitstruct);
}

/**
 * @brief       将年月日时分秒转换成秒钟数
 * @param       syear : 年份
 * @param       smon  : 月份
 * @param       sday  : 日期
 * @param       hour  : 小时
 * @param       min   : 分钟
 * @param       sec   : 秒钟
 * @retval      转换后的秒钟数
 */
static long rtc_date2sec(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec)
{
    uint32_t Y, M, D, X, T;
    signed char monx = smon; /* 将月份转换成带符号的值, 方便后面运算 */

    if (0 >= (monx -= 2))    /* 1..12 -> 11,12,1..10 */
    {
        monx += 12;          /* Puts Feb last since it has leap day */
        syear -= 1;
    }

    Y = (syear - 1) * 365 + syear / 4 - syear / 100 + syear / 400; /* 公元元年1到现在的闰年数 */
    M = 367 * monx / 12 - 30 + 59;
    D = sday - 1;
    X = Y + M + D - 719162;                      /* 减去公元元年到1970年的天数 */
    T = ((X * 24 + hour) * 60 + min) * 60 + sec; /* 总秒钟数 */
    return T;
}

/**
 * @brief       判断年份是否是闰年
 * @param       year : 年份
 * @retval      0, 非闰年; 1, 是闰年;
 */
static uint8_t rtc_is_leap_year(uint16_t year)
{
    /* 闰年规则: 四年闰百年不闰,四百年又闰 */
    if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/**
 * @brief       将年月日时分秒转换成秒钟数
 * @param       year : 年份
 * @param       month  : 月份
 * @param       day  : 日期
 * @retval      0, 星期天; 1 ~ 6: 星期一 ~ 星期六
 */
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day)
{
    uint8_t week = 0;

    if (month < 3)
    {
        month += 12;
        --year;
    }

    week = (day + 1 + 2 * month + 3 * (month + 1) / 5 + year + (year >> 2) - year / 100 + year / 400) % 7;
    return week;
}

rtc.h

#ifndef __RTC_H
#define __RTC_H

#include "./SYSTEM/sys/sys.h"

/* 时间结构体, 包括年月日周时分秒等信息 */
typedef struct
{
    uint8_t hour;       /* 时 */
    uint8_t min;        /* 分 */
    uint8_t sec;        /* 秒 */
    /* 公历年月日周 */
    uint16_t year;      /* 年 */
    uint8_t  month;     /* 月 */
    uint8_t  date;      /* 日 */
    uint8_t  week;      /* 周 */
} _calendar_obj;

extern _calendar_obj calendar;  /* 时间结构体 */

uint8_t rtc_init(void);  /* 初始化RTC */
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day);  /* 获取星期 */
static uint8_t rtc_is_leap_year(uint16_t year);  /* 判断是否闰年 */
static long rtc_date2sec(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec);  /* 将年月日时分秒转换成秒钟数 */
void rtc_get_time(void);  /* 获取RTC当前时间 */
uint8_t rtc_set_time(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec);  /* 设置RTC的时间 */

#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/RTC/rtc.h"

int main(void)
{
    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    delay_init(72);                         /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    usmart_dev.init(72);                    /* 初始化USMART */
    led_init();                             /* 初始化LED */
    rtc_init();                             /* 初始化RTC */

    while (1)
    {
        rtc_get_time();                     /* 获取RTC时间 */
        printf("Date:%04d-%02d-%02d ", calendar.year, calendar.month, calendar.date);
        printf("Time:%02d:%02d:%02d \r\n", calendar.hour, calendar.min, calendar.sec);
        delay_ms(1000);                     /* 延时1秒 */
    }
}

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置

HAL库源码

rtc.c

#include "./BSP/RTC/rtc.h"
#include "./BSP/LED/led.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"


RTC_HandleTypeDef g_rtc_handle; /* RTC控制句柄 */
_calendar_obj calendar;         /* 时间结构体 */

/**
 * @brief       RTC写入后备区域SRAM
 * @param       bkrx : 后备区寄存器编号,范围:0~41
                        对应 RTC_BKP_DR1~RTC_BKP_DR42
 * @param       data : 要写入的数据,16位长度
 * @retval      无
 */
void rtc_write_bkr(uint32_t bkrx, uint16_t data)
{
    HAL_PWR_EnableBkUpAccess(); /* 取消备份区写保护 */
    HAL_RTCEx_BKUPWrite(&g_rtc_handle, bkrx + 1, data);
}

/**
 * @brief       RTC读取后备区域SRAM
 * @param       bkrx : 后备区寄存器编号,范围:0~41
                对应 RTC_BKP_DR1~RTC_BKP_DR42
 * @retval      读取到的值
 */
uint16_t rtc_read_bkr(uint32_t bkrx)
{
    uint32_t temp = 0;
    temp = HAL_RTCEx_BKUPRead(&g_rtc_handle, bkrx + 1);
    return (uint16_t)temp; /* 返回读取到的值 */
}

/**
 * @brief       RTC初始化
 *   @note
 *              默认尝试使用LSE,当LSE启动失败后,切换为LSI.
 *              通过BKP寄存器0的值,可以判断RTC使用的是LSE/LSI:
 *              当BKP0==0X5050时,使用的是LSE
 *              当BKP0==0X5051时,使用的是LSI
 *              注意:切换LSI/LSE将导致时间/日期丢失,切换后需重新设置.
 *
 * @param       无
 * @retval      0,成功
 *              1,进入初始化模式失败
 */
uint8_t rtc_init(void)
{
    /* 检查是不是第一次配置时钟 */
    uint16_t bkpflag = 0;

    __HAL_RCC_PWR_CLK_ENABLE(); /* 使能PWR电源时钟 */
    __HAL_RCC_BKP_CLK_ENABLE(); /* 使能BKP备份时钟 */
    HAL_PWR_EnableBkUpAccess(); /* 取消备份区写保护 */
    
    g_rtc_handle.Instance = RTC;
    g_rtc_handle.Init.AsynchPrediv = 32767;     /* 时钟周期设置,理论值:32767, 这里也可以用 RTC_AUTO_1_SECOND */
    g_rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
    if (HAL_RTC_Init(&g_rtc_handle) != HAL_OK)  /* 初始化RTC */
    {
        return 1;
    }
    
    bkpflag = rtc_read_bkr(0);  /* 读取BKP0的值 */
    if ((bkpflag != 0X5050) && (bkpflag != 0x5051))         /* 之前未初始化过,重新配置 */
    {
        rtc_set_time(2020, 4, 25, 20, 25, 35);              /* 设置时间 */
    }

    __HAL_RTC_ALARM_ENABLE_IT(&g_rtc_handle, RTC_IT_SEC);   /* 允许秒中断 */
    __HAL_RTC_ALARM_ENABLE_IT(&g_rtc_handle, RTC_IT_ALRA);  /* 允许闹钟中断 */
    
    HAL_NVIC_SetPriority(RTC_IRQn, 0x2, 0);                 /* 设置RTC中断 */
    HAL_NVIC_EnableIRQ(RTC_IRQn);                           /* 使能中断 */
    
    rtc_get_time(); /* 更新时间 */
    
    return 0;
}

/**
 * @brief       RTC初始化
 *   @note
 *              RTC底层驱动,时钟配置,此函数会被HAL_RTC_Init()调用
 * @param       hrtc:RTC句柄
 * @retval      无
 */
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
    uint16_t retry = 200;
    
    __HAL_RCC_RTC_ENABLE();     /* RTC时钟使能 */

    RCC_OscInitTypeDef rcc_oscinitstruct;
    RCC_PeriphCLKInitTypeDef rcc_periphclkinitstruct;
    
    /* 使用寄存器的方式去检测LSE是否可以正常工作 */
    RCC->BDCR |= 1 << 0;    /* 开启外部低速振荡器LSE */
    
    while (retry && ((RCC->BDCR & 0X02) == 0))  /* 等待LSE准备好 */
    {
        retry--;
        delay_ms(5);
    }

    if (retry == 0)     /* LSE起振失败 使用LSI */
    {
        rcc_oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;  /* 选择要配置的振荡器 */
        rcc_oscinitstruct.LSIState = RCC_LSI_ON;                    /* LSI状态:开启 */
        rcc_oscinitstruct.PLL.PLLState = RCC_PLL_NONE;              /* PLL无配置 */
        HAL_RCC_OscConfig(&rcc_oscinitstruct);                      /* 配置设置的rcc_oscinitstruct */

        rcc_periphclkinitstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;   /* 选择要配置的外设 RTC */
        rcc_periphclkinitstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;   /* RTC时钟源选择 LSI */
        HAL_RCCEx_PeriphCLKConfig(&rcc_periphclkinitstruct);                /* 配置设置的rcc_periphClkInitStruct */
        rtc_write_bkr(0, 0X5051);
    }
    else
    {
        rcc_oscinitstruct.OscillatorType = RCC_OSCILLATORTYPE_LSE ; /* 选择要配置的振荡器 */
        rcc_oscinitstruct.LSEState = RCC_LSE_ON;                    /* LSE状态:开启 */
        rcc_oscinitstruct.PLL.PLLState = RCC_PLL_NONE;              /* PLL不配置 */
        HAL_RCC_OscConfig(&rcc_oscinitstruct);                      /* 配置设置的rcc_oscinitstruct */

        rcc_periphclkinitstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;   /* 选择要配置外设 RTC */
        rcc_periphclkinitstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;   /* RTC时钟源选择LSE */
        HAL_RCCEx_PeriphCLKConfig(&rcc_periphclkinitstruct);                /* 配置设置的rcc_periphclkinitstruct */
        rtc_write_bkr(0, 0X5055);
    }
}

/**
 * @brief       RTC时钟中断
 *   @note      秒钟中断 / 闹钟中断 共用同一个中断服务函数
 *              根据RTC_CRL寄存器的 SECF 和 ALRF 位区分是哪个中断
 * @param       无
 * @retval      无
 */
void RTC_IRQHandler(void)
{
    if (__HAL_RTC_ALARM_GET_FLAG(&g_rtc_handle, RTC_FLAG_SEC) != RESET)     /* 秒中断 */
    {
        rtc_get_time();     /* 更新时间 */
        __HAL_RTC_ALARM_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_SEC);            /* 清除秒中断 */
        //printf("sec:%d\r\n", calendar.sec);   /* 打印秒钟 */
    }

    if (__HAL_RTC_ALARM_GET_FLAG(&g_rtc_handle, RTC_FLAG_ALRAF) != RESET)   /* 闹钟中断 */
    {
        __HAL_RTC_ALARM_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_ALRAF);          /* 清除闹钟中断 */
        printf("Alarm Time:%d-%d-%d %d:%d:%d\n", calendar.year, calendar.month, calendar.date, calendar.hour, calendar.min, calendar.sec);
    }

    __HAL_RTC_ALARM_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_OW);                 /* 清除溢出中断标志 */
    
    while (!__HAL_RTC_ALARM_GET_FLAG(&g_rtc_handle, RTC_FLAG_RTOFF));       /* 等待RTC寄存器操作完成, 即等待RTOFF == 1 */
}

/**
 * @brief       判断年份是否是闰年
 *   @note      月份天数表:
 *              月份   1  2  3  4  5  6  7  8  9  10 11 12
 *              闰年   31 29 31 30 31 30 31 31 30 31 30 31
 *              非闰年 31 28 31 30 31 30 31 31 30 31 30 31
 * @param       year : 年份
 * @retval      0, 非闰年; 1, 是闰年;
 */
static uint8_t rtc_is_leap_year(uint16_t year)
{
    /* 闰年规则: 四年闰百年不闰,四百年又闰 */
    if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

/**
 * @brief       设置时间, 包括年月日时分秒
 *   @note      以1970年1月1日为基准, 往后累加时间
 *              合法年份范围为: 1970 ~ 2105年
                HAL默认为年份起点为2000年
 * @param       syear : 年份
 * @param       smon  : 月份
 * @param       sday  : 日期
 * @param       hour  : 小时
 * @param       min   : 分钟
 * @param       sec   : 秒钟
 * @retval      0, 成功; 1, 失败;
 */
uint8_t rtc_set_time(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec)
{
    uint32_t seccount = 0;

    seccount = rtc_date2sec(syear, smon, sday, hour, min, sec); /* 将年月日时分秒转换成总秒钟数 */

    __HAL_RCC_PWR_CLK_ENABLE(); /* 使能电源时钟 */
    __HAL_RCC_BKP_CLK_ENABLE(); /* 使能备份域时钟 */
    HAL_PWR_EnableBkUpAccess(); /* 取消备份域写保护 */
    /* 上面三步是必须的! */
    
    RTC->CRL |= 1 << 4;         /* 进入配置模式 */
    
    RTC->CNTL = seccount & 0xffff;
    RTC->CNTH = seccount >> 16;
    
    RTC->CRL &= ~(1 << 4);      /* 退出配置模式 */

    while (!__HAL_RTC_ALARM_GET_FLAG(&g_rtc_handle, RTC_FLAG_RTOFF));       /* 等待RTC寄存器操作完成, 即等待RTOFF == 1 */

    return 0;
}

/**
 * @brief       设置闹钟, 具体到年月日时分秒
 *   @note      以1970年1月1日为基准, 往后累加时间
 *              合法年份范围为: 1970 ~ 2105年
 * @param       syear : 年份
 * @param       smon  : 月份
 * @param       sday  : 日期
 * @param       hour  : 小时
 * @param       min   : 分钟
 * @param       sec   : 秒钟
 * @retval      0, 成功; 1, 失败;
 */
uint8_t rtc_set_alarm(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec)
{
    uint32_t seccount = 0;

    seccount = rtc_date2sec(syear, smon, sday, hour, min, sec); /* 将年月日时分秒转换成总秒钟数 */

    __HAL_RCC_PWR_CLK_ENABLE(); /* 使能电源时钟 */
    __HAL_RCC_BKP_CLK_ENABLE(); /* 使能备份域时钟 */
    HAL_PWR_EnableBkUpAccess(); /* 取消备份域写保护 */
    /* 上面三步是必须的! */
    
    RTC->CRL |= 1 << 4;         /* 进入配置模式 */
    
    RTC->ALRL = seccount & 0xffff;
    RTC->ALRH = seccount >> 16;
    
    RTC->CRL &= ~(1 << 4);      /* 退出配置模式 */

    while (!__HAL_RTC_ALARM_GET_FLAG(&g_rtc_handle, RTC_FLAG_RTOFF));       /* 等待RTC寄存器操作完成, 即等待RTOFF == 1 */

    return 0;
}

/**
 * @brief       得到当前的时间
 *   @note      该函数不直接返回时间, 时间数据保存在calendar结构体里面
 * @param       无
 * @retval      无
 */
void rtc_get_time(void)
{
    static uint16_t daycnt = 0;
    uint32_t seccount = 0;
    uint32_t temp = 0;
    uint16_t temp1 = 0;
    const uint8_t month_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* 平年的月份日期表 */

    seccount = RTC->CNTH; /* 得到计数器中的值(秒钟数) */
    seccount <<= 16;
    seccount += RTC->CNTL;

    temp = seccount / 86400; /* 得到天数(秒钟数对应的) */

    if (daycnt != temp) /* 超过一天了 */
    {
        daycnt = temp;
        temp1 = 1970;   /* 从1970年开始 */

        while (temp >= 365)
        {
            if (rtc_is_leap_year(temp1)) /* 是闰年 */
            {
                if (temp >= 366)
                {
                    temp -= 366;    /* 闰年的秒钟数 */
                }
                else
                {
                    break;
                }
            }
            else
            {
                temp -= 365;        /* 平年 */
            }

            temp1++;
        }

        calendar.year = temp1;      /* 得到年份 */
        temp1 = 0;

        while (temp >= 28)      /* 超过了一个月 */
        {
            if (rtc_is_leap_year(calendar.year) && temp1 == 1) /* 当年是不是闰年/2月份 */
            {
                if (temp >= 29)
                {
                    temp -= 29; /* 闰年的秒钟数 */
                }
                else
                {
                    break;
                }
            }
            else
            {
                if (temp >= month_table[temp1])
                {
                    temp -= month_table[temp1]; /* 平年 */
                }
                else
                {
                    break;
                }
            }

            temp1++;
        }

        calendar.month = temp1 + 1; /* 得到月份 */
        calendar.date = temp + 1;   /* 得到日期 */
    }

    temp = seccount % 86400;                                                    /* 得到秒钟数 */
    calendar.hour = temp / 3600;                                                /* 小时 */
    calendar.min = (temp % 3600) / 60;                                          /* 分钟 */
    calendar.sec = (temp % 3600) % 60;                                          /* 秒钟 */
    calendar.week = rtc_get_week(calendar.year, calendar.month, calendar.date); /* 获取星期 */
}

/**
 * @brief       将年月日时分秒转换成秒钟数
 *   @note      输入公历日期得到星期(起始时间为: 公元0年3月1日开始, 输入往后的任何日期, 都可以获取正确的星期)
 *              使用 基姆拉尔森计算公式 计算, 原理说明见此贴:
 *              https://www.cnblogs.com/fengbohello/p/3264300.html
 * @param       syear : 年份
 * @param       smon  : 月份
 * @param       sday  : 日期
 * @retval      0, 星期天; 1 ~ 6: 星期一 ~ 星期六
 */
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day)
{
    uint8_t week = 0;

    if (month < 3)
    {
        month += 12;
        --year;
    }

    week = (day + 1 + 2 * month + 3 * (month + 1) / 5 + year + (year >> 2) - year / 100 + year / 400) % 7;
    return week;
}

/**
 * @brief       将年月日时分秒转换成秒钟数
 *   @note      以1970年1月1日为基准, 1970年1月1日, 0时0分0秒, 表示第0秒钟
 *              最大表示到2105年, 因为uint32_t最大表示136年的秒钟数(不包括闰年)!
 *              本代码参考只linux mktime函数, 原理说明见此贴:
 *              http://www.openedv.com/thread-63389-1-1.html
 * @param       syear : 年份
 * @param       smon  : 月份
 * @param       sday  : 日期
 * @param       hour  : 小时
 * @param       min   : 分钟
 * @param       sec   : 秒钟
 * @retval      转换后的秒钟数
 */
static long rtc_date2sec(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec)
{
    uint32_t Y, M, D, X, T;
    signed char monx = smon;    /* 将月份转换成带符号的值, 方便后面运算 */

    if (0 >= (monx -= 2))       /* 1..12 -> 11,12,1..10 */
    {
        monx += 12; /* Puts Feb last since it has leap day */
        syear -= 1;
    }

    Y = (syear - 1) * 365 + syear / 4 - syear / 100 + syear / 400; /* 公元元年1到现在的闰年数 */
    M = 367 * monx / 12 - 30 + 59;
    D = sday - 1;
    X = Y + M + D - 719162;                      /* 减去公元元年到1970年的天数 */
    T = ((X * 24 + hour) * 60 + min) * 60 + sec; /* 总秒钟数 */
    return T;
}

rtc.h

#ifndef __RTC_H
#define __RTC_H

#include "./SYSTEM/sys/sys.h"


/* 时间结构体, 包括年月日周时分秒等信息 */
typedef struct
{
    uint8_t hour;       /* 时 */
    uint8_t min;        /* 分 */
    uint8_t sec;        /* 秒 */
    /* 公历年月日周 */
    uint16_t year;      /* 年 */
    uint8_t  month;     /* 月 */
    uint8_t  date;      /* 日 */
    uint8_t  week;      /* 周 */
} _calendar_obj;

extern _calendar_obj calendar;                      /* 时间结构体 */

/* 静态函数 */
static uint8_t rtc_is_leap_year(uint16_t year);     /* 判断当前年份是不是闰年 */
static long rtc_date2sec(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec);   /* 将年月日时分秒转换成秒钟数 */

/* 接口函数 */
uint8_t rtc_init(void);                             /* 初始化RTC */
void rtc_get_time(void);                            /* 获取RTC时间信息 */
uint16_t rtc_read_bkr(uint32_t bkrx);               /* 读取后备寄存器 */
void rtc_write_bkr(uint32_t bkrx, uint16_t data);   /* 写后备寄存器 */ 
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day);    /* 根据年月日获取星期几 */
uint8_t rtc_set_time(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec);   /* 设置时间 */
uint8_t rtc_set_alarm(uint16_t syear, uint8_t smon, uint8_t sday, uint8_t hour, uint8_t min, uint8_t sec);  /* 设置闹钟时间 */

#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/RTC/rtc.h"

/* 定义字符数组用于显示周 */
char* weekdays[]={"Sunday","Monday","Tuesday","Wednesday",
                  "Thursday","Friday","Saterday"};

int main(void)
{
    uint8_t tbuf[40];
    uint8_t t = 0;

    HAL_Init();                             /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    delay_init(72);                         /* 延时初始化 */
    usart_init(115200);                     /* 串口初始化为115200 */
    usmart_dev.init(72);                    /* 初始化USMART */
    led_init();                             /* 初始化LED */
    lcd_init();                             /* 初始化LCD */
    rtc_init();                             /* 初始化RTC */

    rtc_set_alarm(2020, 4, 26, 9, 23, 45);  /* 设置一次闹钟 */

    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "RTC TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);

    while (1)
    {
        t++;

        if ((t % 10) == 0)                  /* 每100ms更新一次显示数据 */
        {
            rtc_get_time();
            sprintf((char *)tbuf, "Time:%02d:%02d:%02d", calendar.hour, calendar.min, calendar.sec);
            lcd_show_string(30, 120, 210, 16, 16, (char *)tbuf, RED);
            sprintf((char *)tbuf, "Date:%04d-%02d-%02d", calendar.year, calendar.month, calendar.date);
            lcd_show_string(30, 140, 210, 16, 16, (char *)tbuf, RED);
            sprintf((char *)tbuf, "Week:%s", weekdays[calendar.week]);
            lcd_show_string(30, 160, 210, 16, 16, (char *)tbuf, RED);
        }

        if ((t % 20) == 0)
        {
            LED0_TOGGLE();              /* 每200ms,翻转一次LED0 */
        }

        delay_ms(10);
    }
}

7.2、H750-RTC

rtc.c

#include "./BSP/RTC/rtc.h"
#include "./BSP/LED/led.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"

RTC_HandleTypeDef g_rtc_handle;  /* RTC句柄 */

/**
 * @brief   RTC初始化函数
 * @retval  无
 */
uint8_t rtc_init(void)
{
    RTC_TimeTypeDef rtc_time_init_struct;    /* RTC时间结构体 */
    RTC_DateTypeDef rtc_date_init_struct;    /* RTC日期结构体 */
    
    /* RTC句柄实例化 */
    g_rtc_handle.Instance = RTC;
    g_rtc_handle.Init.HourFormat = RTC_HOURFORMAT_24;  /* 设置小时格式为24小时制 */
    g_rtc_handle.Init.AsynchPrediv = 0x7F;            /* 设置RTC异步预分频值 */
    g_rtc_handle.Init.SynchPrediv = 0xFF;             /* 设置RTC同步预分频值 */
    g_rtc_handle.Init.OutPut = RTC_OUTPUT_DISABLE;     /* 关闭RTC输出 */
    g_rtc_handle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; /* RTC输出极性 */
    g_rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;    /* RTC输出类型 */
    HAL_RTC_Init(&g_rtc_handle);    /* 初始化RTC */
    
    /* 设置RTC时间 */
    rtc_time_init_struct.Hours = 20;        /* 设置小时 */
    rtc_time_init_struct.Minutes = 20;      /* 设置分钟 */
    rtc_time_init_struct.Seconds = 20;      /* 设置秒钟 */
    rtc_time_init_struct.TimeFormat = RTC_HOURFORMAT12_AM; /* 设置小时格式为12小时制 */
    rtc_time_init_struct.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; /* 设置夏令时 */
    rtc_time_init_struct.StoreOperation = RTC_STOREOPERATION_RESET; /* 设置RTC的存储操作 */
    HAL_RTC_SetTime(&g_rtc_handle, &rtc_time_init_struct, RTC_FORMAT_BIN); /* 将时间设置到RTC */
    
    /* 设置RTC日期 */
    rtc_date_init_struct.Year = 8;         /* 设置年份 */
    rtc_date_init_struct.Month = 8;        /* 设置月份 */
    rtc_date_init_struct.Date = 8;         /* 设置日期 */
    rtc_date_init_struct.WeekDay = 5;      /* 设置星期几 */
    HAL_RTC_SetDate(&g_rtc_handle, &rtc_date_init_struct, RTC_FORMAT_BIN); /* 将日期设置到RTC */
}

/**
 * @brief   RTC外设初始化函数
 * @param   hrtc: RTC句柄
 * @retval  无
 */
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
    RCC_OscInitTypeDef rcc_osc_init_handle;        /* RCC振荡器初始化结构体 */
    RCC_PeriphCLKInitTypeDef rcc_periphclk_init_handle; /* RCC外设时钟初始化结构体 */
    
    /* RTC时钟使能 */
    __HAL_RCC_RTC_CLK_ENABLE();                    /* 使能RTC时钟 */
    HAL_PWR_EnableBkUpAccess();                    /* 使能后备区域访问 */
    
    /* 配置LSE振荡器 */
    rcc_osc_init_handle.OscillatorType = RCC_OSCILLATORTYPE_LSE;    /* 振荡器类型为LSE */
    rcc_osc_init_handle.LSEState = RCC_LSE_ON;                       /* 打开LSE振荡器 */
    rcc_osc_init_handle.PLL.PLLState = RCC_PLL_NONE;                 /* 关闭PLL */
    HAL_RCC_OscConfig(&rcc_osc_init_handle);                         /* 配置振荡器 */
    
    /* 配置RTC时钟源 */
    rcc_periphclk_init_handle.PeriphClockSelection = RCC_PERIPHCLK_RTC;    /* 选择RTC外设时钟 */
    rcc_periphclk_init_handle.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;    /* 设置RTC时钟源为LSE */
    HAL_RCCEx_PeriphCLKConfig(&rcc_periphclk_init_handle);                 /* 配置RTC时钟源 */
    
    /* 使能RTC时钟 */
    __HAL_RCC_RTC_ENABLE();                                     /* 使能RTC时钟 */
}

rtc.h

#ifndef __RTC_H
#define __RTC_H

#include "./SYSTEM/sys/sys.h"

uint8_t rtc_init(void);


#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/MPU/mpu.h"
#include "./BSP/LED/led.h"
#include "./BSP/RTC/rtc.h"

extern RTC_HandleTypeDef g_rtc_handle;  /* RTC句柄 */

int main(void)
{
    RTC_TimeTypeDef rtc_t;    /* RTC时间结构体 */
    RTC_DateTypeDef rtc_d;    /* RTC日期结构体 */
    
    sys_cache_enable();                 /* 打开L1-Cache */
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(240, 2, 2, 4); /* 设置时钟, 480Mhz */
    delay_init(480);                    /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    usmart_dev.init(240);               /* 初始化USMART */
    led_init();                         /* 初始化LED */
    mpu_memory_protection();            /* 保护相关存储区域 */
    rtc_init();                         /* 初始化RTC */
    
    while (1)
    {
        HAL_RTC_GetTime(&g_rtc_handle, &rtc_t, RTC_FORMAT_BIN);  /* 获取RTC时间 */
        HAL_RTC_GetDate(&g_rtc_handle, &rtc_d, RTC_FORMAT_BIN);  /* 获取RTC日期 */
        
        printf("Date:20%02d-%02d-%02d ",rtc_d.Year, rtc_d.Month, rtc_d.Date);  /* 打印日期 */
        printf("Time:%02d:%02d:%02d \r\n",rtc_t.Hours, rtc_t.Minutes, rtc_t.Seconds); /* 打印时间 */
        
        delay_ms(1000);  /* 延时1秒 */
    }
}

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置

HAL库源码

rtc.c

#include "./BSP/RTC/rtc.h"
#include "./BSP/LED/led.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"


RTC_HandleTypeDef g_rtc_handle;  /* RTC句柄 */

/**
 * @brief       RTC写入后备区域SRAM
 * @param       bkrx : 后备区寄存器编号,范围:0~31
                对应 RTC_BKP_DR0~RTC_BKP_DR31
 * @param       data : 要写入的数据,32位长度
 * @retval      无
 */
void rtc_write_bkr(uint32_t bkrx, uint32_t data)
{
    HAL_PWR_EnableBkUpAccess();     /* 取消备份区写保护 */
    HAL_RTCEx_BKUPWrite(&g_rtc_handle, bkrx, data);
}

/**
 * @brief       RTC读取后备区域SRAM
 * @param       bkrx : 后备区寄存器编号,范围:0~31 
                对应 RTC_BKP_DR0~RTC_BKP_DR31
 * @retval      读取到的值
 */
uint32_t rtc_read_bkr(uint32_t bkrx)
{
    uint32_t temp = 0;
    temp = HAL_RTCEx_BKUPRead(&g_rtc_handle, bkrx);
    return (uint16_t)temp;      /* 返回读取到的值 */
}

/**
 * @brief       RTC时间设置
 * @param       hour,min,sec: 小时,分钟,秒钟 
 * @param       ampm        : AM/PM, 0=AM/24H; 1=PM/12H;
 * @retval      0,成功
 *              !0,异常状态
 */
HAL_StatusTypeDef rtc_set_time(uint8_t hour, uint8_t min, uint8_t sec, uint8_t ampm)
{
    RTC_TimeTypeDef rtc_time_handle;

    rtc_time_handle.Hours = hour;
    rtc_time_handle.Minutes = min;
    rtc_time_handle.Seconds = sec;
    rtc_time_handle.TimeFormat = ampm;
    rtc_time_handle.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    rtc_time_handle.StoreOperation = RTC_STOREOPERATION_RESET;
    return HAL_RTC_SetTime(&g_rtc_handle, &rtc_time_handle, RTC_FORMAT_BIN);
}

/**
 * @brief       RTC日期设置
 * @param       year,month,date : 年(0~99),月(1~12),日(0~31)
 * @param       week            : 星期(1~7,0,非法!)
 * @retval      0,成功
 *              !0,异常状态
 */
HAL_StatusTypeDef rtc_set_date(uint8_t year, uint8_t month, uint8_t date, uint8_t week)
{
    RTC_DateTypeDef rtc_date_handle;

    rtc_date_handle.Date = date;
    rtc_date_handle.Month = month;
    rtc_date_handle.WeekDay = week;
    rtc_date_handle.Year = year;
    return HAL_RTC_SetDate(&g_rtc_handle, &rtc_date_handle, RTC_FORMAT_BIN);
}

/**
 * @brief       获取RTC时间
 * @param       *hour,*min,*sec : 小时,分钟,秒钟
 * @param       *ampm           : AM/PM,0=AM/24H,1=PM.
 * @retval      无
 */
void rtc_get_time(uint8_t *hour, uint8_t *min, uint8_t *sec, uint8_t *ampm)
{
    RTC_TimeTypeDef rtc_time_handle;

    HAL_RTC_GetTime(&g_rtc_handle, &rtc_time_handle, RTC_FORMAT_BIN);

    *hour = rtc_time_handle.Hours;
    *min = rtc_time_handle.Minutes;
    *sec = rtc_time_handle.Seconds;
    *ampm = rtc_time_handle.TimeFormat;
}

/**
 * @brief       获取RTC日期
 * @param       *year,*mon,*date: 年,月,日
 * @param       *week           : 星期
 * @retval      无
 */
void rtc_get_date(uint8_t *year, uint8_t *month, uint8_t *date, uint8_t *week)
{
    RTC_DateTypeDef rtc_date_handle;

    HAL_RTC_GetDate(&g_rtc_handle, &rtc_date_handle, RTC_FORMAT_BIN);

    *year = rtc_date_handle.Year;
    *month = rtc_date_handle.Month;
    *date = rtc_date_handle.Date;
    *week = rtc_date_handle.WeekDay;
}

/* 月修正数据表 */
uint8_t const table_week[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};

/**
 * @breif       获得现在是星期几, 输入公历日期得到星期(只允许1901-2099年)
 * @param       year,month,day:公历年月日
 * @retval      星期号(1~7,代表周1~周日)
 */
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day)
{
    uint16_t temp2;
    uint8_t yearH, yearL;
    yearH = year / 100;
    yearL = year % 100;

    /*  如果为21世纪,年份数加100 */
    if (yearH > 19)yearL += 100;

    /*  所过闰年数只算1900年之后的 */
    temp2 = yearL + yearL / 4;
    temp2 = temp2 % 7;
    temp2 = temp2 + day + table_week[month - 1];

    if (yearL % 4 == 0 && month < 3) temp2--;

    temp2 %= 7;

    if (temp2 == 0) temp2 = 7;

    return temp2;
}

/**
 * @brief       RTC初始化
 *   @note
 *              默认尝试使用LSE,当LSE启动失败后,切换为LSI.
 *              通过BKP寄存器0的值,可以判断RTC使用的是LSE/LSI:
 *              当BKP0==0X5050时,使用的是LSE
 *              当BKP0==0X5051时,使用的是LSI
 *              注意:切换LSI/LSE将导致时间/日期丢失,切换后需重新设置.
 *
 * @param       无
 * @retval      0,成功
 *              1,进入初始化模式失败
 */
uint8_t rtc_init(void)
{
    /* 检查是不是第一次配置时钟 */
    uint16_t bkpflag = 0;
    
    g_rtc_handle.Instance = RTC;
    g_rtc_handle.Init.HourFormat = RTC_HOURFORMAT_24;/* RTC设置为24小时格式 */
    g_rtc_handle.Init.AsynchPrediv = 0X7F;           /* RTC异步分频系数(1~0X7F) */
    g_rtc_handle.Init.SynchPrediv = 0XFF;            /* RTC同步分频系数(0~7FFF) */
    g_rtc_handle.Init.OutPut = RTC_OUTPUT_DISABLE;     
    g_rtc_handle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    g_rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
    if (HAL_RTC_Init(&g_rtc_handle) != HAL_OK)
    {
        return 1;
    }

    bkpflag = rtc_read_bkr(0);  /* 读取BKP0的值 */
    if ((bkpflag != 0X5050) && (bkpflag != 0x5051))         /* 之前未初始化过,重新配置 */
    {
        rtc_set_time(23, 59, 56, RTC_HOURFORMAT12_AM);      /* 设置时间 ,根据实际时间修改 */
        rtc_set_date(20, 1, 13, 7);                         /* 设置日期 */
    }

    return 0;
}

/**
 * @brief       RTC底层驱动,时钟配置
 * @param       hrtc:RTC句柄
 * @note        此函数会被HAL_RTC_Init()调用
 * @retval      无
 */
void HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc)
{
    uint16_t retry = 200;
    
    RCC_OscInitTypeDef rcc_osc_init_handle;
    RCC_PeriphCLKInitTypeDef rcc_periphclk_init_handle;

    __HAL_RCC_RTC_CLK_ENABLE();                                          /* 使能RTC时钟 */
    HAL_PWR_EnableBkUpAccess();                                          /* 取消备份区域写保护 */
    
    /* 使用寄存器的方式去检测LSE是否可以正常工作 */
    RCC->BDCR |= 1 << 0;    /* 尝试开启LSE */

    while (retry && ((RCC->BDCR & 0X02) == 0))  /* 等待LSE准备好 */
    {
        retry--;
        delay_ms(5);
    }
    
    if (retry == 0)     /* LSE起振失败 使用LSI */
    {
        rcc_osc_init_handle.OscillatorType = RCC_OSCILLATORTYPE_LSI;        /* 选择要配置的振荡器 */
        rcc_osc_init_handle.PLL.PLLState = RCC_PLL_NONE;                    /* PLL不配置 */
        rcc_osc_init_handle.LSIState = RCC_LSI_ON;                          /* LSI状态:开启 */
        HAL_RCC_OscConfig(&rcc_osc_init_handle);                            /* 配置设置的rcc_oscinitstruct */

        rcc_periphclk_init_handle.PeriphClockSelection = RCC_PERIPHCLK_RTC; /* 选择要配置外设 RTC */
        rcc_periphclk_init_handle.RTCClockSelection = RCC_RTCCLKSOURCE_LSI; /* RTC时钟源选择LSI */
        HAL_RCCEx_PeriphCLKConfig(&rcc_periphclk_init_handle);              /* 配置设置的rcc_periphclkinitstruct */
        rtc_write_bkr(0, 0X5051);
    }
    else
    {
        rcc_osc_init_handle.OscillatorType = RCC_OSCILLATORTYPE_LSE;        /* 选择要配置的振荡器 */
        rcc_osc_init_handle.PLL.PLLState = RCC_PLL_NONE;                    /* PLL不配置 */
        rcc_osc_init_handle.LSEState = RCC_LSE_ON;                          /* LSE状态:开启 */
        HAL_RCC_OscConfig(&rcc_osc_init_handle);                            /* 配置设置的rcc_oscinitstruct */

        rcc_periphclk_init_handle.PeriphClockSelection = RCC_PERIPHCLK_RTC; /* 选择要配置外设 RTC */
        rcc_periphclk_init_handle.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; /* RTC时钟源选择LSE */
        HAL_RCCEx_PeriphCLKConfig(&rcc_periphclk_init_handle);              /* 配置设置的rcc_periphclkinitstruct */
        rtc_write_bkr(0, 0X5053);
    }
    
    __HAL_RCC_RTC_ENABLE();                                                 /* RTC使能 */
}

/**
 * @breif       设置闹钟时间(按星期闹铃,24小时制)
 * @param       week        : 星期几(1~7) 
 * @param       hour,min,sec: 小时,分钟,秒钟
 * @retval      无
 */
void rtc_set_alarma(uint8_t week, uint8_t hour, uint8_t min, uint8_t sec)
{
    RTC_AlarmTypeDef rtc_alarm_handle;
    
    rtc_alarm_handle.AlarmTime.Hours = hour;                                /* 小时 */
    rtc_alarm_handle.AlarmTime.Minutes = min;                               /* 分钟 */
    rtc_alarm_handle.AlarmTime.Seconds = sec;                               /* 秒 */
    rtc_alarm_handle.AlarmTime.SubSeconds = 0;
    rtc_alarm_handle.AlarmTime.TimeFormat = RTC_HOURFORMAT12_AM;
    
    rtc_alarm_handle.AlarmMask = RTC_ALARMMASK_NONE;                        /* 精确匹配星期,时分秒 */
    rtc_alarm_handle.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
    rtc_alarm_handle.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY; /* 按星期 */
    rtc_alarm_handle.AlarmDateWeekDay = week;                               /* 星期 */
    rtc_alarm_handle.Alarm = RTC_ALARM_A;                                   /* 闹钟A */
    HAL_RTC_SetAlarm_IT(&g_rtc_handle, &rtc_alarm_handle, RTC_FORMAT_BIN);
    
    HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 1, 2);   /* 抢占优先级1,子优先级2 */
    HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}
/**
 * @breif       周期性唤醒定时器设置
 * @param       wksel
 *   @arg       RTC_WAKEUPCLOCK_RTCCLK_DIV16        ((uint32_t)0x00000000)
 *   @arg       RTC_WAKEUPCLOCK_RTCCLK_DIV8         ((uint32_t)0x00000001)
 *   @arg       RTC_WAKEUPCLOCK_RTCCLK_DIV4         ((uint32_t)0x00000002)
 *   @arg       RTC_WAKEUPCLOCK_RTCCLK_DIV2         ((uint32_t)0x00000003)
 *   @arg       RTC_WAKEUPCLOCK_CK_SPRE_16BITS      ((uint32_t)0x00000004)
 *   @arg       RTC_WAKEUPCLOCK_CK_SPRE_17BITS      ((uint32_t)0x00000006)
 * @note        000,RTC/16;001,RTC/8;010,RTC/4;011,RTC/2;
 * @note        注意:RTC就是RTC的时钟频率,即RTCCLK!
 * @param       cnt: 自动重装载值.减到0,产生中断.
 * @retval      无
 */
void rtc_set_wakeup(uint8_t wksel, uint16_t cnt)
{ 
    __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&g_rtc_handle, RTC_FLAG_WUTF);  /* 清除RTC WAKE UP的标志 */

    HAL_RTCEx_SetWakeUpTimer_IT(&g_rtc_handle, cnt, wksel);          /* 设置重装载值和时钟 */

    HAL_NVIC_SetPriority(RTC_WKUP_IRQn, 2, 2);                       /* 抢占优先级2,子优先级2 */
    HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);
}

/**
 * @breif       RTC闹钟中断服务函数
 * @param       无
 * @retval      无
 */
void RTC_Alarm_IRQHandler(void)
{
    HAL_RTC_AlarmIRQHandler(&g_rtc_handle);
}

/**
 * @breif       RTC闹钟A中断处理回调函数
 * @param       hrtc:RTC句柄
 * @retval      无
 */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
    printf("ALARM A!\r\n");
}

/**
 * @breif       RTC WAKE UP中断服务函数
 * @param       无
 * @retval      无
 */
void RTC_WKUP_IRQHandler(void)
{
    HAL_RTCEx_WakeUpTimerIRQHandler(&g_rtc_handle); 
}

/**
 * @breif       RTC WAKE UP中断处理处理回调函数
 * @param       hrtc:RTC句柄
 * @retval      无
 */
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    LED1_TOGGLE();
}

rtc.h

#ifndef __RTC_H
#define __RTC_H

#include "./SYSTEM/sys/sys.h"

extern RTC_HandleTypeDef g_rtc_handle;

uint8_t rtc_init(void);     /* 初始化RTC */
uint32_t rtc_read_bkr(uint32_t bkrx);               /* 读后备寄存器 */
void rtc_write_bkr(uint32_t bkrx, uint32_t data);   /* 写后备寄存器 */
void rtc_get_time(uint8_t *hour, uint8_t *min, uint8_t *sec, uint8_t *ampm);    /* 获取时间 */
HAL_StatusTypeDef rtc_set_time(uint8_t hour, uint8_t min, uint8_t sec, uint8_t ampm); /* 设置时间 */
void rtc_get_date(uint8_t *year, uint8_t *month, uint8_t *date, uint8_t *week); /* 获取日期 */
HAL_StatusTypeDef rtc_set_date(uint8_t year, uint8_t month, uint8_t date, uint8_t week);  /* 设置日期 */

void rtc_set_wakeup(uint8_t wksel, uint16_t cnt);   /* 设置周期性唤醒 */
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day);    /* 获取星期 */
void rtc_set_alarma(uint8_t week, uint8_t hour, uint8_t min, uint8_t sec);  /* 设置闹钟 */

#endif

main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/MPU/mpu.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/RTC/rtc.h"


int main(void)
{
    uint8_t hour,min,sec,ampm;
    uint8_t year,month,date,week;
    uint8_t tbuf[40];
    uint8_t t=0;
    
    sys_cache_enable();                 /* 打开L1-Cache */
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(240, 2, 2, 4); /* 设置时钟, 480Mhz */
    delay_init(480);                    /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    usmart_dev.init(240);               /* 初始化USMART */
    led_init();                         /* 初始化LED */
    mpu_memory_protection();            /* 保护相关存储区域 */
    lcd_init();                         /* 初始化LCD */
    rtc_init();                         /* 初始化RTC */
    rtc_set_wakeup(RTC_WAKEUPCLOCK_CK_SPRE_16BITS, 0);  /* 配置WAKE UP中断,1秒钟中断一次 */

    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30, 70, 200, 16, 16, "RTC TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);

    while (1)
    {
        t++;

        if ((t % 10) == 0)  /* 每100ms更新一次显示数据 */
        {
            rtc_get_time(&hour, &min, &sec, &ampm);
            sprintf((char *)tbuf, "Time:%02d:%02d:%02d", hour, min, sec);
            lcd_show_string(30, 130, 210, 16, 16, (char*)tbuf, RED);
            rtc_get_date(&year, &month, &date, &week);
            sprintf((char *)tbuf, "Date:20%02d-%02d-%02d", year, month, date);
            lcd_show_string(30, 150, 210, 16, 16, (char*)tbuf, RED);
            sprintf((char *)tbuf, "Week:%d", week);
            lcd_show_string(30, 170, 210, 16, 16, (char*)tbuf, RED);
        }

        if ((t % 20) == 0)
        {
            LED0_TOGGLE();  /* 每200ms,翻转一次LED0 */
        }

        delay_ms(10);
    }
}

八、总结

stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置
stm32 ll库 rtc 获取时间戳,STM32,RTC方案、BCD码、时间戳,RTC相关寄存器和HAL库驱动,RTC基本配置步骤,RTC基本驱动步骤,时间设置和读取,闹钟配置和周期性自动唤醒配置文章来源地址https://www.toymoban.com/news/detail-843745.html

到了这里,关于【正点原子STM32】RTC实时时钟(RTC方案、BCD码、时间戳、RTC相关寄存器和HAL库驱动、RTC基本配置步骤、RTC基本驱动步骤、时间设置和读取、RTC闹钟配置和RTC周期性自动唤醒配置)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32——RTC实时时钟

    Unix 时间戳(Unix Timestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数,不考虑闰秒 时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量 世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间 底层使用秒计数器可以节省硬件设计电路,

    2024年01月23日
    浏览(42)
  • STM32-RTC实时时钟

    目录 RTC实时时钟 功能框图 UNIX时间戳 初始化结构体 RTC时间结构体 RTC日期结构体 RTC闹钟结构体 进入和退出配置函数 实验环节1:显示日历 常规配置 RTC配置 测试环节 实验现象 实验环节2:闹钟 常规配置 RTC配置 测试环节 实验现象 STM32的RTC外设,实质上是一个 掉电后还继续运

    2024年02月06日
    浏览(49)
  • STM32--RTC实时时钟

    Unix 时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒 。 时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量。 世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间。 GMT : GMT(Greenwich Mean Time), 格林威治平时(也称格林

    2024年02月10日
    浏览(49)
  • 【STM32】RTC(实时时钟)

    本质:计数器 RTC中断是外部中断(EXTI) 当VDD掉电的时候,Vbat可以通过电源---实时计时 STM32的RTC外设(Real Time Clock),实质是一个   掉电   后还继续运行的定时器。从定时器的角度来说,相对于通用定时器TIM外设,它十分简单, 只有很纯粹的计时和触发中断的功能 ;但从

    2024年02月03日
    浏览(49)
  • STM32——STM32F103时钟解析(正点原子资料+HAL库代码分析)

    上次写系统时钟解析的时候说出一篇103的时钟解析,我就整理HAL库开发的正点的资料,给小白梳理,我也是小白,不做权威使用。 在 STM32 中,有五个时钟源,为 HSI、HSE、LSI、LSE、PLL。从时钟频率来分可以分为高速时钟源和低速时钟源,在这 5 个中 HIS,HSE 以及 PLL 是高速时钟

    2024年02月19日
    浏览(49)
  • 【STM32学习】实时时钟 —— RTC

    STM32RTC实时时钟实验讲解,从入门到放弃 【STM32】RTC休眠唤醒(停机模式)、独立看门狗开启状态下 关于STM32使用RTC唤醒停止模式的设置 RTC(Real Time Clock):实时时钟,是指可以像时钟一样输出实际时间的电子设备,一般会是集成电路,因此也称为时钟芯片。总之,RTC只是个能靠电

    2024年02月01日
    浏览(48)
  • STM32-实时时钟RTC-2

                                     

    2024年01月20日
    浏览(47)
  • STM32-RTC实时时钟详解

    RTC的本质很简单,就是一个时钟经过精确分频最后得到的一个1Hz的时钟,也可以说是计数器,其他大部分功能都是基于这个计数器设计的数字逻辑。 本文讲的RTC是基于STM32F030来讲的,相比与F1系列的RTC来说,M0的将很多原本需要软件实现的功能硬件化了,使用起来更加便利。

    2024年02月04日
    浏览(47)
  • STM32基础10--实时时钟(RTC)

     目录 前言 RTC框图 STM32实时时钟电路 功能需要 STM32CubeMx配置RTC 配置RCC 配置RTC 配置时间,闹钟,唤醒 开启中断 设置中断优先级 功能代码实现 STM32Cude生成RTC初始化 自定义触发闹钟次数变量  重写周期唤醒回调函数 重写闹钟中断函数         在做51单片机项目时,如果需

    2023年04月11日
    浏览(39)
  • STM32学习笔记(十二)丨RTC实时时钟

    ​  本次课程采用单片机型号为STM32F103C8T6。 ​  课程链接:江协科技 STM32入门教程   往期笔记链接:   STM32学习笔记(一)丨建立工程丨GPIO 通用输入输出   STM32学习笔记(二)丨STM32程序调试丨OLED的使用   STM32学习笔记(三)丨中断系统丨EXTI外部中断   

    2024年02月16日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包