当前位置: 首页 > news >正文

STM32单片机入门学习——第43节: [12-3] 读写备份寄存器实时时钟

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!

本文写于:2025.04.19

STM32开发板学习——第43节: [12-3] 读写备份寄存器&实时时钟

  • 前言
  • 开发板说明
  • 引用
  • 解答和科普
  • 一、读写BKP备份寄存器
  • 二、RTC实时时钟
  • 问题
  • 总结

前言

   本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始32单片机的学习之路。
   欢迎大家给我提意见,能给我的嵌入式之旅提供方向和路线,现在作为小白,我就先学习32单片机了,就跟着B站上的江协科技开始学习了.
   在这里会记录下江协科技32单片机开发板的配套视频教程所作的实验和学习笔记内容,因为我之前有一个开发板,我大概率会用我的板子模仿着来做.让我们一起加油!
   另外为了增强我的学习效果:每次笔记把我不知道或者问题在后面提出来,再下一篇开头作为解答!

开发板说明

   本人采用的是慧净的开发板,因为这个板子是我N年前就买的板子,索性就拿来用了。另外我也购买了江科大的学习套间。
   原理图如下
1、开发板原理图
在这里插入图片描述
2、STM32F103C6和51对比
在这里插入图片描述
3、STM32F103C6核心板
在这里插入图片描述

视频中的都用这个开发板来实现,如果有资源就利用起来。另外也计划实现江协科技的套件。

下图是实物图
在这里插入图片描述

引用

【STM32入门教程-2023版 细致讲解 中文字幕】
还参考了下图中的书籍:
STM32库开发实战指南:基于STM32F103(第2版)
在这里插入图片描述
数据手册
在这里插入图片描述

解答和科普

一、读写BKP备份寄存器

1、 读写BKP程序
PB1接一个按键,用于控制,VBAT引脚接STLINK的3.3V.
在这里插入图片描述
在这里插入图片描述
第一步,开启PWR和BKP的时钟,第二步使用PWR的一个函数,使能对BKP和RTC的访问,然后写入数据的话,BKP有个写入的函数,读取数据,BKP也有个读取的函数。
在这里插入图片描述
手动清空BKP所有的数据寄存器,这样BKP的数据,都会变为0;

void BKP_RTCOutputConfig(uint16_t BKP_RTCOutputSource);

这时时钟输出功能的配置,可以选择在RTC引脚上输出时钟信号,输出RTC校准时钟、RTC闹钟脉冲或者秒脉冲。

void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);
uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);
读写寄存器DR;
备份寄存器使能
void PWR_BackupAccessCmd(FunctionalState NewState);
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
#include "OLED.h"uint8_t KeyNum;uint16_t ArrayWrite[] = {0x1234, 0x5678};
uint16_t ArrayRead[2];int main(void)
{OLED_Init();Key_Init();OLED_ShowString(1, 1, "W:");OLED_ShowString(2, 1, "R:");RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);PWR_BackupAccessCmd(ENABLE);while (1){KeyNum = Key_GetNum();if (KeyNum == 1){ArrayWrite[0] ++;ArrayWrite[1] ++;BKP_WriteBackupRegister(BKP_DR1, ArrayWrite[0]);BKP_WriteBackupRegister(BKP_DR2, ArrayWrite[1]);OLED_ShowHexNum(1, 3, ArrayWrite[0], 4);OLED_ShowHexNum(1, 8, ArrayWrite[1], 4);}ArrayRead[0] = BKP_ReadBackupRegister(BKP_DR1);ArrayRead[1] = BKP_ReadBackupRegister(BKP_DR2);OLED_ShowHexNum(2, 3, ArrayRead[0], 4);OLED_ShowHexNum(2, 8, ArrayRead[1], 4);}
}

二、RTC实时时钟

2、RTC线路
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
初始化。第一步,执行注意事项:开启PWR和BKP时钟,使能BKP和RTC的访问。
第二步,启动RTC的时钟,计划使用LSE作为系统时钟,所以要使用RCC模块里的函数,开启LSE时钟,为了省电,默认是关闭的,所以要手动开启;
第三步,配置RTCCLK这个数据选择器,指定LSE为RTCCLK,这一步函数也是在RCC模块里的;
第四步,先不着急,要完成两个等待函数,一个是等待函数,另一个是这里的,等待上一次操作完成;
第五步,配置预分频器,给PRL重装器一个合适的分频值,确保输出给计数器的频率是1Hz;
第六步,配置CNT的值,给这个RTC一个初始时间,如果需要闹钟的话就配置闹钟,需要中断,可以配置中断;
并没有库函数配置RTC,没有Cmd函数,不需要启动一下。

void RCC_LSEConfig(uint8_t RCC_LSE);
启动LSE时钟就调用这个函数。
void RCC_LSICmd(FunctionalState NewState);
配置LSI内部低速时钟,如果出现外部时钟不起振,可以用这个内部时钟来进行实验; 
void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource);
这个函数用来选择RTCCLK的时钟源,实际上就是配置数据选择器
void RCC_RTCCLKCmd(FunctionalState NewState);
启动RTCCLK,调用上面函数选择时钟之后,还需要调用这个Cmd函数,使能一下;
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);
获取标志位,因为这个LSE时钟,不是说你让它启动,他就能立刻启动的,调用时钟启动后,还需要等待一下标志位,等RCC有个标志位LSERDY置1后,这个时钟才是启动完成,工作稳定。

在这里插入图片描述

void RTC_EnterConfigMode(void);
进入配置模式,置CRL的CNF为1,进入配置模式;必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR寄存器。
void RTC_ExitConfigMode(void);
退出配置模式,就是把CNF清零;
uint32_t  RTC_GetCounter(voi
获取,CNT计数器的值;读取时钟,就靠这个函数。
void RTC_SetCounter(uint32_t CounterValue);
写入计数器CNT的值;设置时间,就靠这个函数;
void RTC_SetPrescaler(uint32_t PrescalerValue);
写入预分频器,这个值会写入到预分频器的PRL重装寄存器中,用来配置预分频器的分频系数;
void RTC_SetAlarm(uint32_t AlarmValue);
写入闹钟值;
uint32_t  RTC_GetDivider(void);
读取预分频器中的DIV余数寄存器;为了得到更细的时间;
void RTC_WaitForLastTask(void);
等待上次操作完成;对应对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器。
void RTC_WaitForSynchro(void);
等待同步,若在读取RTC寄存器时,RTC的APB1接口曾经处于禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1。
FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);
void RTC_ClearFlag(uint16_t RTC_FLAG);
ITStatus RTC_GetITStatus(uint16_t RTC_IT);
void RTC_ClearITPendingBit(uint16_t RTC_IT);
标志位相关函数。

写入预分频器也要进入配置模式。只是不用我们写,库函数中包含了。
RTC晶振确实起振不了,为了观察到实验现象,可以备选内部低速时钟LSI,

在这里插入图片描述

void MyRTC_SetTime(void)	//数组的时间转换为秒数到CNT{time_t  time_cnt;struct tm time_data;time_data.tm_year =MyRTC_Time[0]-1900;time_data.tm_mon  =MyRTC_Time[1]-1;time_data.tm_mday=MyRTC_Time[2];time_data.tm_hour=MyRTC_Time[3];time_data.tm_min=MyRTC_Time[4];time_data.tm_sec=MyRTC_Time[5];time_cnt= mktime(&time_data);	//日期时间到秒数的计数RTC_SetCounter( time_cnt);		//写入计数器CNTRTC_WaitForLastTask();	}

第一步,把数组指定时间,填充到struct tm结构体,第二步,使用mktime函数,得到秒数,第三步将得到的秒数写入到RTC的CNT中
extern uint16_t MyRTC_Time[];
全局变量传参,数组不加也行,单个变量必须加;

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
#include "OLED.h"
#include "MyRTC.h"int main(void)
{OLED_Init();MyRTC_Init();OLED_ShowString(1,1,"Date:XXXX-XX-XX");OLED_ShowString(2,1,"Time:XX:XX:XX");OLED_ShowString(3,1,"CNT :");OLED_ShowString(4,1,"DIV :");while(1){MyRTC_ReadTime();OLED_ShowNum(1,6,MyRTC_Time[0],4);OLED_ShowNum(1,11,MyRTC_Time[1],2);OLED_ShowNum(1,14,MyRTC_Time[2],2);OLED_ShowNum(2,6,MyRTC_Time[3],2);OLED_ShowNum(2,9,MyRTC_Time[4],2);OLED_ShowNum(2,12,MyRTC_Time[5],2);OLED_ShowNum(3,6,RTC_GetCounter(),10);OLED_ShowNum(4,6,RTC_GetDivider(),10);}
}

C

#include "stm32f10x.h"                  // Device header
#include <time.h>uint16_t MyRTC_Time[]={2023,1 ,1 ,23 ,59,55};
void MyRTC_SetTime(void);void MyRTC_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);PWR_BackupAccessCmd(ENABLE);if(BKP_ReadBackupRegister(BKP_DR1)!= 0xA5A5){RCC_LSEConfig(RCC_LSE_ON);							//	启动外边LSE晶振while (RCC_GetFlagStatus(RCC_FLAG_LSERDY)!=SET);	//低速外部时钟源准备就绪(LSE)RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//数据选择器选择LSERCC_RTCCLKCmd(ENABLE);						//使能时钟//RTCCLK配置完成RTC_WaitForSynchro();	//等待同步RTC_WaitForLastTask();	//等待上次操作完成RTC_SetPrescaler(32768-1);    	//	配置分频器,写操作不是立即生效,等待写操作完成RTC_WaitForLastTask();			//等待上次操作完成//	RTC_SetCounter(1672588795);		//设定初始时间MyRTC_SetTime();//	RTC_WaitForLastTask();			//等待上次操作完成BKP_WriteBackupRegister(BKP_DR1,0xA5A5);}else{RTC_WaitForSynchro();	//等待同步RTC_WaitForLastTask();	//等待上次操作完成}}void MyRTC_SetTime(void)	//数组的时间转换为秒数到CNT{time_t  time_cnt;struct tm time_data;time_data.tm_year =MyRTC_Time[0]-1900;time_data.tm_mon  =MyRTC_Time[1]-1;time_data.tm_mday=MyRTC_Time[2];time_data.tm_hour=MyRTC_Time[3];time_data.tm_min=MyRTC_Time[4];time_data.tm_sec=MyRTC_Time[5];time_cnt= mktime(&time_data)-8*60*60;	//日期时间到秒数的计数RTC_SetCounter( time_cnt);		//写入计数器CNTRTC_WaitForLastTask();	}void MyRTC_ReadTime(void){time_t  time_cnt;struct tm time_data;time_cnt= RTC_GetCounter()+8*60*60;time_data = *localtime(&time_cnt);			//结构体赋值MyRTC_Time[0]=time_data.tm_year+1900;MyRTC_Time[1]=time_data.tm_mon+1;MyRTC_Time[2]=time_data.tm_mday;MyRTC_Time[3]=time_data.tm_hour;MyRTC_Time[4]=time_data.tm_min;MyRTC_Time[5]=time_data.tm_sec;}
#ifndef __MYRTC_H
#define __MYRTC_Hextern uint16_t MyRTC_Time[];void MyRTC_SetTime(void);
void MyRTC_Init(void);void MyRTC_ReadTime(void);#endif

问题

总结

本节课主要是了解这个BKP备份寄存的硬件在代码下的实现,RTC实时时钟的读取与设置,这用到了上节课时间戳讲的函数,由于STM32不能识别当地时间,所以在这里就是这里获取的时间都是伦敦时间,需要加一个偏移量。

相关文章:

  • 无需训练的具身导航探索!TRAVEL:零样本视觉语言导航中的检索与对齐
  • 山东科技大学人工智能原理考试回忆复习资料
  • python基础知识点(1)
  • 猫咪如厕检测与分类识别系统系列【十二】猫咪进出事件逻辑及日志优化
  • 【Datawhale AI春训营】Java选手初探数据竞赛
  • 【对Linux文件权限的深入理解】
  • 有源低通滤波器 sallen-key低通滤波器原理与计算
  • 《2025最新Java面试题全解析:从基础到高并发架构设计》
  • 速查手册:TA-Lib 超过150种量化技术指标计算全解 - 2. Momentum Indicators(动量指标)
  • 超大文件处理——文件强制切割:突破存储传输限制,提升数据处理效能—星辰大文化术——未来之窗超算中心
  • PKI 公钥基础设施
  • STM32学习笔记汇总
  • JavaWeb 课堂笔记 —— 13 MySQL 事务
  • 解决win10执行批处理报编码错误
  • Nodejs数据库单一连接模式和连接池模式的概述及写法
  • Meteonorm8-免费使用教程(详细教程-免费)
  • RK3506-rtlinux
  • Linux系统之部署TestNet资产管理系统
  • 豆瓣图书数据采集与可视化分析(一)- 豆瓣图书数据爬取
  • 【DT】USB通讯失败记录
  • 又有多地推进产科整合
  • 3月赴美外国游客数量加速下滑
  • 申花迎来中超三连胜,这一次终于零封对手了
  • “中国电三之都”江苏丰县成功举办第十五届电动车展览会
  • 澳门世界杯“中日对决”,蒯曼击败伊藤美诚晋级女单决赛
  • 创纪录!南向资金今年净流入已超6000亿港元,港股缘何被爆买?