我们上次学习了BKP备份寄存器的知识,这次我们来编写代码用程序实现。最后我会把程序全部放出来。
一.硬件接线图
1.接线图
按键用于控制,然后VB接在STlink的3.3V引出角。

2.编程准备

复制OLED显示电路,然后重命名,角读写备份寄存器BKP。

然后删除所有程序,之后编译,方便我们进行编程。
二.程序编写
1.了解函数

我们需要按照步骤,一步一步的进行BKP的访问。由于BKP程序很短,我们就暂时不进行封装。直接在主函数中编写,展示一下功能。
第一步开启BKP时钟,第二步使用PWR函数使能对BKP和RTC的访问。
(1)清空寄存器

恢复缺省配置,也就是对BKP进行复位操作。这个函数这次有用了,就是手动清空BKP所有的寄存器。如果有备用电池的话BKP数据主电源不清零,掉电不清零。上电复位还不清零。就没有清零的时候,那么我们怎么进行清零呢。此时调用这个函数,所有BKP的数据,就会全部清零。
(2)配置入侵检测功能

这两个函数是用来配置Tamper(也就是入侵)。

配置Tamper引脚的有效电平。就是高电平触发,还是低电平触发。

是否开启侵入检测功能。如果需要侵入检测的话,需要先配置tamper的有效电平,在使能侵入检测功能就行了。
(3)中断配置

中断配置,是否开启中断。
(4)时钟输出

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

配置RTC校准值。其实就是写入RTC校准寄存器。
(6)写备份寄存器

第一个参数指定要写在哪个DR里面。第二个参数就是要写入的数据了。
(7)读备份寄存器

读取我们的备份寄存器,参数指定要读取哪个DR,函数返回值就是DR里面存的值。
(8)老朋友

获取BKP标志位,清除标志位,获取中断标志位,清除中断标志位。
(9)备份寄存器访问使能

我们还需要关注一下备份寄存器访问使能的函数,需要开启BKP。因为BKP是在备份区域,备份区域一开始是写保护状态,需要调用这个函数,才能解除写保护。

看一下定义,这个函数就是设置PWR_CR寄存器中的DPB位

对应我们的要求。
2.BKP初始化


按照步骤,开启APB1时钟,因为BKP是APB1的外设。还需要我们开启PWR外设时钟。


找到刚刚学习的函数。

写入BKP的DR寄存器,转到定义看一下。

第一个参数BKP_DR这个参数可以是BKP_DRx,其中x可以是1-42的数字。当然只有大容量和互联型才有DR42,我们的中容量只有DR1-10。

我们指定DR1寄存器,写入0x1234,然后如何知道写入的数据是否正确,我们需要在读取出来看一下。

读取DR1寄存器的值。

在1行1列显示,数据长度为4。
3.测试

编译下载。

这里OLED显示的是1234,说明我们初步的检测是没有问题的。
那么这个数据是不是可以在主电源掉电的情况下,由备用电池维持呢。

我们把写入的代码注释掉。我们不在BKP写入数据,直接读取。

下载看一下。数据还是1234。因为我们目前芯片没有掉电过。所以BKP的数据是保持原因的。

按下复位键后,数据仍然是1234,说明我们复位数据是不会清空BKP的。

之后我们把主电源断电。然后再插上电源。

发现数据仍然是1234。这个数据就是由备用电源维持的了。

我们拔掉备用电源。

主电源再次断电。

然后上电,可以发现数据变为了0000了。这里BKP的数据就清零了。这说明如果没有备用电源,主电源断电后,BKP是不能维持工作的。
4.完善最终程序
首先定义两个数组,一个用于写入,一个用于读取。

这样子就可以了。

显示两个前缀。

引用按键头文件。

存放键值变量。

按键初始化。

读取返回值。

按键按下就让他们自增加一。

然后写入寄存器,就是我们这两个函数。把ArrayWrite[0]的数据写在DR1里,把ArrayWrite[1]的数据写在DR2里。我们同时写入两个,这样表示同时读写多个DR也是没有问题的。

然后我们看一下写入的数据。
读取数据就在if外面,主循环不断刷新,一旦数据有变化,我们就可以第一时间读取。

把读取的DR1和DR2的数据放在两个数组里。

显示一下我们读取的数据。
整理一下思路,我们程序一开始,是初始化两个用于指示的字符串显示,之后进行BKP初始化。主循环里,循环执行的逻辑是,先获取键码,如果按键按下,我们就变化一下测试数据。把测试数据分别写道BKP的DR1和DR2。再在OLED显示一下写入数据,然后再没有按键按下时,我们也不断的读取BKP的DR1和DR2的寄存器。刷新显示到OLED上。看一下当前BKP的数据。
5.最终结果展示

编译,下载。

目前W后面还没有显示,说明我们还没有进行写入程序。这时我们读取的数据就是0000 0000。我们按下按键。

数据变化,写入数据。可以看到写入的是1235 5679,因为第一次写入之前就已经自增了一次。所以第一次写入就是自增之后的数,所以不是1234 5678,而是加一之后1235 5679。如果想测试1234 5678可以先写入再自增。下面可以看到,读取的数据和写入的一样1235 5679。

然后继续变化数据,写入和读出都是一样的。

之后主电源掉电。重新上电。

目前上电后还没有写入,读取的值是掉电前,最后一次写入的值。这是这个程序现象。测试没有问题。有关BKP的实验到这里就结束了。
三.所有程序
1.main.c
cs
#include "stm32f10x.h" // Device header
#include "Delay.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 :");
//BKP初始化
//开启APB1时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
//使能BKP和RTC的访问。
PWR_BackupAccessCmd(ENABLE);
//写入BKP的DR寄存器
// BKP_WriteBackupRegister(BKP_DR1, 0x1234);
while(1)
{
KeyNum = Key_Getnum();
if(KeyNum == 1)
{
ArrayWrite[0] ++;
ArrayWrite[1] ++;
//写入BKP寄存器
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显示
OLED_ShowHexNum(2, 3, ArrayRead[0], 4);
OLED_ShowHexNum(2, 8, ArrayRead[1], 4);
}
}
2.key.c
cs
#include "stm32f10x.h" // Device header
#include "Delay.h"
void KEY_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//这里把端口设置为上拉模式因为我们要读取按键
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
uint8_t Key_Getnum(void)
{
uint8_t Keynum = 0;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)//读取GPIO_pin_1端口的值
{
Delay_ms(50);//按键按下会有抖动,先延时一段时间,进行消抖
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);//如果按键一直按下,就卡在这里,直到松手
Delay_ms(50);//松手后在延时,进行消抖
Keynum = 1;//这个按键被按下
}
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)//读取GPIO_pin_1端口的值
{
Delay_ms(50);//按键按下会有抖动,先延时一段时间,进行消抖
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);//如果按键一直按下,就卡在这里,直到松手
Delay_ms(50);//松手后在延时,进行消抖
Keynum = 2;//这个按键被按下
}
return Keynum;
}
3.key.h
cs
#include "stdint.h"
#ifndef __KEY_H //防止重定义
#define __KEY_H //那么就定义这个
void KEY_Init(void);
uint8_t Key_Getnum(void);
#endif
这就是我们所有的程序了。