STM32-笔记41-RTC(实时时钟)

一、什么是RTC?

实时时钟的缩写是RTC(Real_Time Clock)。RTC 是集成电路,通常称为时钟芯片。

实时时钟是一个独立的定时器。 RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, RTC的设置和时间维持不变。
复位后,对备份寄存器和RTC的访问被禁止,并且备份域被保护以防止可能存在的意外的写操作。执行以下操作可以使能对备份寄存器和RTC的访问:

  • 通过设置寄存器RCC_APB1ENR的PWREN和BKPEN位来打开电源和后备接口的时钟
  • 电源控制寄存器(PWR_CR)的DBP位来使能对后备寄存器和RTC的访问。
  • 32位的可编程计数器,可对应Unix时间戳的秒计数器。

    Unix时间戳是从120位的可编程预分频器,可适配不同频率的输入时钟。 可选择三种RTC时钟源: HSE时钟除以128(通常为8MHz/128) LSE振荡器时钟(通常为32.768KHz) LSI振荡器时钟(40KHz) 970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。

    20位的可编程预分频器,可适配不同频率的输入时钟。

    可选择三种RTC时钟源:

    HSE时钟除以128(通常为8MHz/128)

    LSE振荡器时钟(通常为32.768KHz)

    LSI振荡器时钟(40KHz)

    二、RTC框图

    三、RTC寄存器及库函数

    3.1 备份域控制寄存器(RCC_BDCR)

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

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

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

    20位寄存器

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

    20位寄存器

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

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

    库函数

    HAL_RTC_Init(); //初始化函数

    HAL_RTC_GetTime(); //获取时间

    HAL_RTC_GetDate(); //获取日期

    HAL_RTC_SetTime(); //设置时间

    HAL_RTC_SetData(); //设置日期

    HAL_RTC_GetAlarm();//获取设置的闹钟是多少

    HAL_RTC_SetAlarm_IT(); //设置闹钟是多少,无IT就是设置一个闹钟,但是闹钟到时间不打开,有IT的就是,设置一个闹钟,到时间就会打开,会响

    _HAL_RTC_ALARM_GET_FLAG(); //这个查看的标志位是配置标志(如下图所示)

    四、读写RTC时间实验

  • 注意事项: 必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、 RTC_ALR寄存器
  • 对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的 RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器
  • 复制项目文件53-BKP读写

    重命名为54-读写RTC时间实验

    打开项目文件

    main.c

    #include "sys.h"
    #include "delay.h"
    #include "led.h"
    #include "uart1.h"
    #include "rtc.h"
    
    int main(void)
    {
        HAL_Init();                         /* 初始化HAL库 */
        stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
        led_init();                         /* 初始化LED灯 */
        uart1_init(115200);
        rtc_init();
        printf("hello world!\r\n");
        
        if(rtc_read_bkr(1) != 0xA5A5)//防止复位影响:假如我们前面已经设定了bkr的值了,就不用在重新设定时间了
        {
            rtc_write_bkr(1, 0xA5A5);
            printf("读出来的值为:%X\r\n", rtc_read_bkr(1));
            
            struct tm time_data;
            time_data.tm_year = 2025;
            time_data.tm_mon = 1;
            time_data.tm_mday = 13;
            time_data.tm_hour = 12;
            time_data.tm_min = 28;
            time_data.tm_sec = 30;
            rtc_set_time(time_data);
        }
        while(1)
        { 
            rtc_get_time();
            delay_ms(1000);
        }
    }
    

    rtc.c

    #include "rtc.h"
    #include "stdio.h"
    
    RTC_HandleTypeDef rtc_handle = {0};
    //初始化rtc,因为bkp依赖于rtc 
    void rtc_init(void)
    {
        __HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟
        __HAL_RCC_BKP_CLK_ENABLE();//使能bkp时钟
        HAL_PWR_EnableBkUpAccess();//允许访问备份域
        
        rtc_handle.Instance = RTC;
        rtc_handle.Init.AsynchPrediv = 32767;//分频系数32768-1 = 32767
        rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE; //侵入脚:不使用侵入检测相关内容
        HAL_RTC_Init(&rtc_handle);
    }
    //在msp函数中设置使能RTC、设置时钟源、中断配置(这里没用)
    void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
    {
        __HAL_RCC_RTC_ENABLE();
        
        RCC_OscInitTypeDef osc_initstruct = {0};
        RCC_PeriphCLKInitTypeDef periphclk_initstruct = {0};
        
        osc_initstruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;//LSE
        osc_initstruct.LSEState = RCC_LSE_ON;//LSE的状态:打开
        osc_initstruct.PLL.PLLState = RCC_PLL_NONE;//锁向环:none
        HAL_RCC_OscConfig(&osc_initstruct);//配置振荡器
    
        periphclk_initstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;//配置RTC
        periphclk_initstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;//RTC时钟选择LSE
        HAL_RCCEx_PeriphCLKConfig(&periphclk_initstruct);//配置RTC时钟源
        
    }
    //读rtc,读出来是2个字符16个字节,十六位寄存器,bkrx指定读的是哪个寄存器
    uint16_t rtc_read_bkr(uint8_t bkrx)
    {
        uint32_t data = 0;
        //读bkp寄存器
        data = HAL_RTCEx_BKUPRead(&rtc_handle, bkrx);
        return (uint16_t)data; 
    }
    //写rtc,写进哪个寄存器?bkrx,写进去什么?data
    void rtc_write_bkr(uint8_t bkrx,uint16_t data)
    {
        //写bkp寄存器
        HAL_RTCEx_BKUPWrite(&rtc_handle, bkrx, data);
    }
    void rtc_get_time(void)//获取时间
    {
        RTC_TimeTypeDef rtc_time = {0};//获取时间
        RTC_DateTypeDef rtc_date = {0};//获取日期
        
        HAL_RTC_GetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BIN);
        HAL_RTC_GetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BIN);
        
        printf("rtc time: %d-%02d-%02d %02d:%02d:%02d\r\n", rtc_date.Year + 2000, rtc_date.Month, rtc_date.Date,
            rtc_time.Hours, rtc_time.Minutes, rtc_time.Seconds);
    }
    
    void rtc_set_time(struct tm time_data)//设置时间(年月日时分秒)
    {
        RTC_TimeTypeDef rtc_time = {0};
        RTC_DateTypeDef rtc_date = {0};
        
        rtc_time.Hours = time_data.tm_hour;
        rtc_time.Minutes = time_data.tm_min;
        rtc_time.Seconds = time_data.tm_sec;
        HAL_RTC_SetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BIN);
        
        rtc_date.Year = time_data.tm_year - 2000;
        rtc_date.Month = time_data.tm_mon;
        rtc_date.Date = time_data.tm_mday;
        HAL_RTC_SetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BIN);
        
        while(!__HAL_RTC_ALARM_GET_FLAG(&rtc_handle, RTC_FLAG_RTOFF));
    }
    

    rtc.h

    #ifndef __RTC_H__
    #define __RTC_H__
    
    
    #include "sys.h"
    #include "time.h"
    
    void rtc_init(void);
    uint16_t rtc_read_bkr(uint8_t bkrx);
    void rtc_write_bkr(uint8_t bkrx, uint16_t data);
    void rtc_get_time(void);
    void rtc_set_time(struct tm time_data);
    
    #endif
    

    五、RTC闹钟实验

    复制项目文件夹54-读写RTC时间实验

    重命名项目文件为55-RTC闹钟实验

    打开项目文件

    main.c

    #include "sys.h"
    #include "delay.h"
    #include "led.h"
    #include "uart1.h"
    #include "rtc.h"
    
    int main(void)
    {
        HAL_Init();                         /* 初始化HAL库 */
        stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
        led_init();                         /* 初始化LED灯 */
        uart1_init(115200);
        rtc_init();
        printf("hello world!\r\n");
        
        if(rtc_read_bkr(1) != 0xA5A5)//防止复位影响:假如我们前面已经设定了bkr的值了,就不用在重新设定时间了
        {
            rtc_write_bkr(1, 0xA5A5);
            printf("读出来的值为:%X\r\n", rtc_read_bkr(1));
            
            struct tm time_data,alarm_data;
            time_data.tm_year = 2025;
            time_data.tm_mon = 1;
            time_data.tm_mday = 13;
            time_data.tm_hour = 13;
            time_data.tm_min = 29;
            time_data.tm_sec = 30;
            rtc_set_time(time_data);
            /*设置闹钟的时间*/
            alarm_data.tm_hour = 13;
            alarm_data.tm_min = 29;
            alarm_data.tm_sec = 45;
            rtc_set_alarm(alarm_data);
        }
        
        while(1)
        { 
            rtc_get_time();
            delay_ms(1000);
        }
    }
    

    rtc.c

    #include "rtc.h"
    #include "stdio.h"
    
    RTC_HandleTypeDef rtc_handle = {0};
    //初始化rtc,因为bkp依赖于rtc 
    void rtc_init(void)
    {
        __HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟
        __HAL_RCC_BKP_CLK_ENABLE();//使能bkp时钟
        HAL_PWR_EnableBkUpAccess();//允许访问备份域
        
        rtc_handle.Instance = RTC;
        rtc_handle.Init.AsynchPrediv = 32767;//分频系数32768-1 = 32767
        rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE; //侵入脚:不使用侵入检测相关内容
        HAL_RTC_Init(&rtc_handle);
    }
    //在msp函数中设置使能RTC、设置时钟源、中断配置
    void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
    {
        __HAL_RCC_RTC_ENABLE();
        
        RCC_OscInitTypeDef osc_initstruct = {0};
        RCC_PeriphCLKInitTypeDef periphclk_initstruct = {0};
        
        osc_initstruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;//LSE
        osc_initstruct.LSEState = RCC_LSE_ON;//LSE的状态:打开
        osc_initstruct.PLL.PLLState = RCC_PLL_NONE;//锁向环:none
        HAL_RCC_OscConfig(&osc_initstruct);//配置振荡器
    
        periphclk_initstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;//配置RTC
        periphclk_initstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;//RTC时钟选择LSE
        HAL_RCCEx_PeriphCLKConfig(&periphclk_initstruct);//配置RTC时钟源
        
        HAL_NVIC_SetPriority(RTC_Alarm_IRQn,2,2);//中断
        HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
        
    }
    void RTC_Alarm_IRQHandler(void)//中断处理函数
    {
        HAL_RTC_AlarmIRQHandler(&rtc_handle);
    }
    void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
    {
        printf("闹钟响了。。。\r\n");
    }
    
    //读rtc,读出来是2个字符16个字节,十六位寄存器,bkrx指定读的是哪个寄存器
    uint16_t rtc_read_bkr(uint8_t bkrx)
    {
        uint32_t data = 0;
        //读bkp寄存器
        data = HAL_RTCEx_BKUPRead(&rtc_handle, bkrx);
        return (uint16_t)data; 
    }
    //写rtc,写进哪个寄存器?bkrx,写进去什么?data
    void rtc_write_bkr(uint8_t bkrx,uint16_t data)
    {
        //写bkp寄存器
        HAL_RTCEx_BKUPWrite(&rtc_handle, bkrx, data);
    }
    void rtc_get_time(void)//获取时间
    {
        RTC_TimeTypeDef rtc_time = {0};//获取时间
        RTC_DateTypeDef rtc_date = {0};//获取日期
        
        HAL_RTC_GetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BIN);
        HAL_RTC_GetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BIN);
        
        printf("rtc time: %d-%02d-%02d %02d:%02d:%02d\r\n", rtc_date.Year + 2000, rtc_date.Month, rtc_date.Date,
            rtc_time.Hours, rtc_time.Minutes, rtc_time.Seconds);
    }
    
    void rtc_set_time(struct tm time_data)//设置时间(年月日时分秒)
    {
        RTC_TimeTypeDef rtc_time = {0};
        RTC_DateTypeDef rtc_date = {0};
        
        rtc_time.Hours = time_data.tm_hour;
        rtc_time.Minutes = time_data.tm_min;
        rtc_time.Seconds = time_data.tm_sec;
        HAL_RTC_SetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BIN);
        
        rtc_date.Year = time_data.tm_year - 2000;
        rtc_date.Month = time_data.tm_mon;
        rtc_date.Date = time_data.tm_mday;
        HAL_RTC_SetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BIN);
        
        while(!__HAL_RTC_ALARM_GET_FLAG(&rtc_handle, RTC_FLAG_RTOFF));
    }
    void rtc_set_alarm(struct tm alarm_data)//设置闹钟
    {
        RTC_AlarmTypeDef alarm = {0};
        
        alarm.Alarm = RTC_ALARM_A;//闹钟
        alarm.AlarmTime.Hours = alarm_data.tm_hour;//时间的设置:时分秒
        alarm.AlarmTime.Minutes = alarm_data.tm_min;
        alarm.AlarmTime.Seconds = alarm_data.tm_sec;
        
        HAL_RTC_SetAlarm_IT(&rtc_handle, &alarm, RTC_FORMAT_BIN);
    }
    

    rtc.h

    #ifndef __RTC_H__
    #define __RTC_H__
    
    
    #include "sys.h"
    #include "time.h"
    
    void rtc_init(void);
    uint16_t rtc_read_bkr(uint8_t bkrx);
    void rtc_write_bkr(uint8_t bkrx, uint16_t data);
    void rtc_get_time(void);
    void rtc_set_time(struct tm time_data);
    void rtc_set_alarm(struct tm alarm_data);
    
    #endif
    

    执行结果如下

    作者:1101 1101

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32-笔记41-RTC(实时时钟)

    发表回复