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

news2025/4/20 18:53:00

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

本文写于: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);		//写入计数器CNT
	 RTC_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);		//数据选择器选择LSE
	RCC_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);		//写入计数器CNT
	 RTC_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_H

extern uint16_t MyRTC_Time[];

void MyRTC_SetTime(void);
void MyRTC_Init(void);

void MyRTC_ReadTime(void);
	 
#endif

问题

总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2338921.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零基础上手Python数据分析 (18):Matplotlib 基础绘图 - 让数据“开口说话”

写在前面 —— 告别枯燥数字,拥抱可视化力量,掌握 Matplotlib 绘图基础 欢迎来到 “高效数据分析实战指南:Python零基础入门” 专栏! 经过前面 Pandas 模块的学习和实战演练,我们已经掌握了使用 Python 和 Pandas 进行数据处理、清洗、整合、分析的核心技能。 我们能够从…

【网络原理】UDP协议

目录 一. UDP 报文格式 &#xff08;1&#xff09;端口号 &#xff08;2&#xff09;UDP长度 &#xff08;3&#xff09;校验和 UDP协议属于传输层协议&#xff0c;由操作系统内核内置 一. UDP 报文格式 UDP数据报&#xff1a;无连接&#xff0c;不可靠传输&#xff0c;面…

HCIP OSPF综合实验

1.网络拓扑图 实验要求&#xff1a; 2.需求分析 IP规划&#xff1a; 对每个路由器配置ospf并用172.16.0.0/16网段进行划分&#xff0c;项目中一共有area0 - area4五个ospf区域加一个rip网段&#xff0c;所以我们在172.16.0.0/16选出6个网段 ISP 对r5只能配ip可以把他看成外…

实现批量图片文字识别(python+flask+EasyOCR)

话不多说,向上效果图 1)先说框架版本 为什么要先说框架版本呢,因为我在各种版本中尝试了两天,总算确定了如下版本适合我,至于其他的版本,各位自己去尝试 python 3.9.7 EasyOCR 1.7.2 flask 3.0.3 2)执行操作效果图 2.1)多选文件 2.2)图片预览 2.3)提取选中文件 2.4)提取所有文…

【java实现+4种变体完整例子】排序算法中【堆排序】的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格

以下是堆排序的详细解析&#xff0c;包含基础实现、常见变体的完整代码示例&#xff0c;以及各变体的对比表格&#xff1a; 一、堆排序基础实现 原理 基于二叉堆结构&#xff08;最大堆&#xff09;&#xff0c;通过以下步骤实现排序&#xff1a; 构建最大堆&#xff1a;将…

WhatTheDuck:一个基于浏览器的CSV查询工具

今天给大家介绍一个不错的小工具&#xff1a;WhatTheDuck。它是一个免费开源的 Web 应用程序&#xff0c;允许用户上传 CSV 文件并针对其内容执行 SQL 查询分析。 WhatTheDuck 支持 SQL 代码自动完成以及语法高亮。 WhatTheDuck 将上传的数据存储为 DuckDB 内存表&#xff0c;继…

工控系统前端设计(pyqt)

题目源自&#xff1a;白月黑羽的项目实战四-[工控系统前端] 代码已上传至gitcode https://gitcode.com/m0_37662818/Industrial_Control_System_Front_End 心得体会&#xff1a;直接用组态软件或者js吧 项目亮点 tablemodel的使用&#xff0c;绑定了表格和数据风机自定义ite…

剑指Offer(数据结构与算法面试题精讲)C++版——day15

剑指Offer&#xff08;数据结构与算法面试题精讲&#xff09;C版——day15 题目一&#xff1a;二叉树最低层最左边的值题目二&#xff1a;二叉树的右侧视图题目三&#xff1a;二叉树剪枝附录&#xff1a;源码gitee仓库 题目一&#xff1a;二叉树最低层最左边的值 题目&#xff…

打靶日记 zico2: 1

一、探测靶机IP&#xff08;进行信息收集&#xff09; 主机发现 arp-scan -lnmap -sS -sV -T5 -p- 192.168.10.20 -A二、进行目录枚举 发现dbadmin目录下有个test_db.php 进入后发现是一个登录界面&#xff0c;尝试弱口令&#xff0c;结果是admin&#xff0c;一试就出 得到加…

程序性能(1)嵌入式基准测试工具

程序性能(1)嵌入式基准测试工具 Author&#xff1a;Once Day date: 2025年4月19日 漫漫长路&#xff0c;才刚刚开始… 全系列文档查看&#xff1a;Perf性能分析_Once-Day的博客-CSDN博客 参考文档: CPU Benchmark – MCU Benchmark – CoreMark – EEMBC Embedded Micropr…

LLM MCP模型上下文协议快速入门(for Java)

什么是MCP Model Control Protocol&#xff08;MCP&#xff09;是由AI研究机构Anthropic在2023年第二季度首次提出的新型协议规范&#xff0c;旨在解决大语言模型LLM应用中的上下文管理难题。作为LLM交互领域的创新标准&#xff0c;MCP协议在发布后短短一年内已进行了多次更新…

支持向量机(SVM):原理、应用与深入解析

内容摘要 本文深入探讨支持向量机&#xff08;SVM&#xff09;。阐述其作为分类算法在小样本、非线性及高维模式识别中的优势&#xff0c;详细介绍SVM基本概念、能解决的问题、核函数作用、对偶问题引入及常见核函数等内容&#xff0c;同时分析其优缺点&#xff0c;并与逻辑回…

chapter32_SpringMVC与DispatcherServlet

一、简介 从本章节开始进入SpringMVC的学习&#xff0c;SpringMVC最重要的类就是DispatcherServlet DispatcherServlet的本质是一个Servlet&#xff0c;回顾一下Servlet JavaWeb就是基于Servlet的Servlet接口有5个方法Servlet实现类是HttpServlet&#xff0c;自定义的Servle…

spring security解析

Spring Security 中文文档 :: Spring Security Reference 1. 密码存储 最早是明文存储&#xff0c;但是攻击者获得数据库的数据后就能得到用户密码。 于是将密码单向hash后存储&#xff0c;然后攻击者利用彩虹表&#xff08;算法高级&#xff08;23&#xff09;-彩虹表&…

STM32单片机C语言

1、stdint.h简介 stdint.h 是从 C99 中引进的一个标准 C 库的文件 路径&#xff1a;D:\MDK5.34\ARM\ARMCC\include 大家都统一使用一样的标准&#xff0c;这样方便移植 配置MDK支持C99 位操作 如何给寄存器某个值赋值 举个例子&#xff1a;uint32_t temp 0; 宏定义 带参…

多模态融合(十一): SwinFusion——武汉大学马佳义团队(二)

目录 一.摘要 二. Introduction 三. Related Work A. 特定任务图像融合方法 B. 通用图像融合方法 C. 视觉 Transformer 四.方法 A. 整体框架 B. 损失函数 C.解析 1. 整体框架 2. 特征提取 3. 注意力引导的跨域融合 五. 实验结果与讨论 A. 实验配置 B. 实现…

IDEA202403常用快捷键【持续更新】

文章目录 一、全局搜索二、美化格式三、替换四、Git提交五、代码移动六、调试运行 在使用IDEA进行程序开发&#xff0c;快捷键会让这个过程更加酸爽&#xff0c;下面记录各种快捷键的功能。 一、全局搜索 快捷键功能说明Shift Shift全局搜索Ctrl N搜索Java类 二、美化格式 …

从 LabelImg 到 Label Studio!AI 数据标注神器升级,Web 版真香

视频讲解&#xff1a; 从 LabelImg 到 Label Studio&#xff01;AI 数据标注神器升级&#xff0c;Web 版真香 Label Studio 支持图像、文本、音频、视频、时间序列等多类型数据标注&#xff0c;覆盖计算机视觉&#xff08;目标检测、语义分割&#xff09;、自然语言处理&#x…

【ESP32】ESP-IDF开发 | 低功耗蓝牙开发 | GAP协议 + 设备扫描例程

1. 简介 1.1 GAP协议 GAP&#xff08;General Access Protocol&#xff09;&#xff0c;全称通用访问协议&#xff0c;它定义了低功耗蓝牙设备的发现流程&#xff0c;设备管理和设备连接的建立。 低功耗蓝牙设备定义了4种角色&#xff1a; 广播者&#xff08;Broadcaster&…

网络开发基础(游戏)之 Socket API

Socket简介 Socket (套接字)是网络编程的基础&#xff0c;在 C# 中通过 System.Net.Sockets 命名空间提供了一套完整的 API 来实现网络通信。 网络上的两个程序通过一个双向的通信连接实现数据交换&#xff0c; 这个连接的一端称为一个Socket。 一个Socket包含了进行网络通信必…