BKP(备份寄存器)和 RTC(实时时钟)

什么是BKP?

备份寄存器(BackupRegister)42****个16 位的寄存器 (不同设备存在差异:20字节(中容量和小容量)/84字节(大容量和互联型)),可用来存储 最多****84个字节 的用户应用程序数据。他们处在备份域里。(当VDD电 源被切断,他们仍然由VBAT维持供电。)

特点:

  • 系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位
  • 当断电后 ,后备寄存器中写入的数据会丢失

作用:

管理侵入检测RTC 校准功能

复位后,对 备份寄存器 和 RTC的访问 被禁止,并且 备份域 被保护 以防止可能存在的意外的写操作。执行以下操作可以使能对备份寄存器和RTC****的访问

  • 通过设置寄存器****RCC_APB1ENR 的 PWREN 和 BKPEN 位来 打开 电源 和 后备接口的时钟
  • 电源控制寄存器**(PWR_CR)** 的 DBP位 来使能对 后备寄存器和 RTC的访问。

BKP框图

小实验:读写BKP后备寄存器

bkp.c文件代码:

cs 复制代码
#include "rtc.h"

RTC_HandleTypeDef rtc_handle = {0};
void rtc_init(void){
    //要想读写BKP  
    //1.要开始电源和后背寄存器的时钟
    __HAL_RCC_PWR_CLK_ENABLE();   
    __HAL_RCC_BKP_CLK_ENABLE();
    //使能后备寄存器
    HAL_PWR_EnableBkUpAccess();
    
    rtc_handle.Instance = RTC;
    rtc_handle.Init.AsynchPrediv = 32767;
    rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;    
    HAL_RTC_Init(&rtc_handle);
}

/*读后备寄存器中的数据,并返回*/
uint16_t rtc_read_bkr(uint8_t bkrx){
    
    uint32_t data = 0;
    
    data = HAL_RTCEx_BKUPRead(&rtc_handle, bkrx);
    
    return (uint16_t)data;
}

/*对后备寄存器中写数据,指定参数:哪个寄存器,写入的数据*/
void rtc_write_bkr(uint8_t bkrx,uint16_t data){
    
    HAL_RTCEx_BKUPWrite(&rtc_handle,bkrx,data);
}
  • bkp.h文件代码
cs 复制代码
#ifndef __RTC_H__
#define __RTC_H__
#include "stm32f1xx.h"

void rtc_init(void);
uint16_t rtc_read_bkr(uint8_t bkrx);
void rtc_write_bkr(uint8_t bkrx,uint16_t data);

#endif
  • mian.c文件代码
cs 复制代码
#include "sys.h"
#include "led.h"
#include "delay.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);
    printf("hello,world\r\n");
    rtc_init();
    
    rtc_write_bkr(1,0xABCD);
    printf("读出来的十六进制的数是:%X\r\n",rtc_read_bkr(1));
    
    while(1)
    { 
    }
}

实验现象:

  • 现象1:当系统复位后,之前写入的的数据依旧被打印出来。
  • 现象2:断电后,后备寄存器中的数据会丢失。

什么是RTC?

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

和上面的BKP原理相同:

RTC模块 和 时钟配置系统**(RCC_BDCR寄存器)** 处于后备区域 ,即在系统复位或从待机模式唤醒后, RTC的设置和时间维持不变。

**复位后,对备份寄存器和RTC的访问被禁止,**并且备份域被保护以防止可能存在的意外的写操作。执行以下操作可以使能对备份寄存器和RTC的访问:

  • 通过设置寄存器RCC_APB1ENR的PWREN和BKPEN位来 打开电源 和 后备接口 的时钟
  • 电源控制寄存器(PWR_CR)的DBP位来使能对 后备寄存器和 RTC的访问。

RTC框图

简图:

可选择三种RTC时钟源:

  • HSE时钟除以128(通常为8MHz/128)
  • LSE振荡器时钟(通常为32.768KHz)
  • LSI振荡器时钟(40KHz)

32位的可编程计数器(CNT),可对应Unix时间戳的秒计数器。

Unix **时间戳(格林威治 -- 北京时间)**是从1970年1月1日的 00:00:00(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。

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

(参考手册)图:

RTC寄存器

  • 备份域控制寄存器**(RCC_BDCR)**
  • RTC控制寄存器高位(RTC_CRH)
  • RTC控制寄存器低位(RTC_CRL)
  • RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL)
  • RTC预分频器余数寄存器(RTC_DIVH / RTC_DIVL)

RTC库函数

  • 获取时间、日期、闹钟的函数:
  • 设置时间、日期、闹钟的函数:
  • 获取 RTC控制寄存器中的RTOFF位是否置1:可以进行写操作

RTC驱动步骤

小实验1:读写RTC时间实验

实验目的

配置RTC来实现,设置时钟和日期

硬件清单

stm32f103c8t6、USB转TTL、ST-Link

实验代码

  • rtc.c文件代码:
cs 复制代码
#include "rtc.h"
#include "stdio.h"

RTC_HandleTypeDef rtc_handle = {0};

void rtc_init(void){
    //要想读写BKP  
    //1.要开始电源和后背寄存器的时钟
    __HAL_RCC_PWR_CLK_ENABLE();   
    __HAL_RCC_BKP_CLK_ENABLE();
    //使能后备寄存器
    HAL_PWR_EnableBkUpAccess();
    
    rtc_handle.Instance = RTC;
    rtc_handle.Init.AsynchPrediv = 32767;
    rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE;    
    HAL_RTC_Init(&rtc_handle);
}

void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc){
    if(hrtc->Instance == RTC){
       __HAL_RCC_RTC_ENABLE();
        //配置外部时钟源(3种):LSE,HSE,LSI
        RCC_OscInitTypeDef osc_initstruct = {0};
        
        osc_initstruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
        osc_initstruct.LSEState = RCC_LSE_ON;
        osc_initstruct.PLL.PLLState = RCC_PLL_NONE;
        HAL_RCC_OscConfig(&osc_initstruct);
        
        //配置RTC选择的外部时钟源
        RCC_PeriphCLKInitTypeDef periph_initstruct = {0};
        periph_initstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
        periph_initstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
        
        HAL_RCCEx_PeriphCLKConfig(&periph_initstruct);
        
        HAL_NVIC_SetPriority(RTC_Alarm_IRQn,2,2);
        HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
    }
}

/*读后备寄存器中的数据,并返回*/
uint16_t rtc_read_bkr(uint8_t bkrx){
    
    uint32_t data = 0;
    data = HAL_RTCEx_BKUPRead(&rtc_handle, bkrx);
    return (uint16_t)data;
}

/*对后备寄存器中写数据,指定参数:哪个寄存器,写入的数据*/
void rtc_write_bkr(uint8_t bkrx,uint16_t data){
    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));
}

注意事项: 在MspInit()函数中,初始化:三个时钟源和RTC的时钟源:

  • HAL_RCC_OscConfig( ); //三个时钟源的选择
  • HAL_RCCEx_PeriphCLKConfig( ); //RTC时钟源的选择

在设置时间的函数中,要判断RCC控制寄存器中的RTOFF的标志位是否置1:可以读写。

上面代码的意思是:若RTOFF位没有置1,进行等待。

  • rtc.h文件代码:
cs 复制代码
#ifndef __RTC_H__
#define __RTC_H__
#include "stm32f1xx.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 time_date);
#endif
  • mian.c文件代码
cs 复制代码
#include "sys.h"
#include "led.h"
#include "delay.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);
    printf("hello,world\r\n");
    rtc_init();
    
    if(rtc_read_bkr(1) != 0xABED){
        rtc_write_bkr(1,0xABED);
        printf("读出来的十六进制的数是:%X\r\n",rtc_read_bkr(1));
        struct tm time_data;
    
        time_data.tm_year = 2025;
        time_data.tm_mon = 5;
        time_data.tm_mday = 30;
        time_data.tm_hour = 11;
        time_data.tm_min  = 31;
        time_data.tm_sec = 0;
        rtc_set_time(time_data);  
    }
    while(1)
    { 
        rtc_get_time();
        delay_ms(1000);
    }
}

**总结:**要复习函数中的形参传入结构体知识点。

小实验2:RTC闹钟实验

实验目的

设置闹钟

实验代码

  • MspInit()函数中添加:
  • 中断服务函数
  • 设置闹钟的实验
  • rtc.h文件代码
cs 复制代码
#ifndef __RTC_H__
#define __RTC_H__
#include "stm32f1xx.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 time_date);
#endif
  • mian.c文件代码
cs 复制代码
#include "sys.h"
#include "led.h"
#include "delay.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);
    printf("hello,world\r\n");
    rtc_init();
    
    if(rtc_read_bkr(1) != 0xABED){
        rtc_write_bkr(1,0xABED);
        printf("读出来的十六进制的数是:%X\r\n",rtc_read_bkr(1));
        struct tm time_data;
    
        time_data.tm_year = 2025;
        time_data.tm_mon = 5;
        time_data.tm_mday = 30;
        time_data.tm_hour = 11;
        time_data.tm_min  = 31;
        time_data.tm_sec = 0;
        rtc_set_time(time_data);  
        
        struct tm alarm_date;
        
        alarm_date.tm_hour = 11;
        alarm_date.tm_min = 31;
        alarm_date.tm_sec = 20;
        
        rtc_set_alarm(alarm_date);
    }
    while(1)
    { 
        rtc_get_time();
        delay_ms(1000);
    }
}
相关推荐
竹照煜_ysn1 小时前
蓝桥杯51单片机设计
单片机·嵌入式硬件·51单片机
Electron-er2 小时前
汽车LIN总线通讯:从物理层到协议栈的深度解析
单片机·汽车电子·lin总线·lin总线通讯
Do vis8242 小时前
STM32第十六天蓝牙模块
stm32·单片机·嵌入式硬件
学不动CV了2 小时前
ARM单片机启动流程(二)(详细解析)
c语言·arm开发·stm32·单片机·51单片机
猫猫的小茶馆4 小时前
【STM32】通用定时器基本原理
c语言·stm32·单片机·嵌入式硬件·mcu·51单片机
jingshaoqi_ccc5 小时前
stm32的USART使用DMA配置成循环模式时发送和接收有着本质区别
stm32·单片机·嵌入式硬件
MingYue_SSS8 小时前
开关电源抄板学习
经验分享·笔记·嵌入式硬件·学习
玉树临风江流儿8 小时前
炸鸡派-定时器基础例程
单片机·嵌入式硬件
小宋同学在不断学习9 小时前
stm32-掌握SPI原理(一)
stm32·单片机·spi
is08159 小时前
STM32的 syscalls.c 和 sysmem.c
c语言·stm32·嵌入式硬件