BKP是“BACKUP”的缩写,stm32f103RCTE的内部配备了10个16位宽度的BKP寄存器。在主电源切断或系统产生复位时间时,BKP寄存器仍然可以在备用电源的支持下保持其内容。
BKP在实际应用中可以存入重要数据,防止被恶意查看,或用于断电回复等。
本例实现对BKP寄存器的读写操作,和入侵检测和处理。主程序中写入寄存器后,依次打印出10个BKP寄存器数据,然后触发GPIOC13的入侵中断(输入低电平),在中断中打印出入侵事件发生后的寄存器内容(复位为0 )。
直接操作寄存器
用到的寄存器描述如下:
备份数据寄存器x(BKP_DRx) (x = 1 … 10):低16位[15:0]有效,用来写入或读出备份数据。
备份控制寄存器(BKP_CR):
低两位有效。
TPAL[1]:侵入检测TAMPER引脚有效电平(TAMPER pin active level)
- 0:侵入检测TAMPER引脚上的高电平会清除所有数据备份寄存器(如果TPE位为1)
- 1:侵入检测TAMPER引脚上的低电平会清除所有数据备份寄存器(如果TPE位为1)
TPE[0]:启动侵入检测TAMPER引脚(TAMPER pin enable)
- 0:侵入检测TAMPER引脚作为通用IO口使用
- 1:开启侵入检测引脚作为侵入检测使用
备份控制/状态寄存器(BKP_CSR):
TIF[9]:侵入中断标志(Tamper interrupt flag) 0:无侵入中断 1:产生侵入中断
当检测到有侵入事件且TPIE位为1时,此位由硬件置1。
通过向CTI位写1来清除此标志位(同时也清除了中断)。如果TPIE位被清除,则此位也会被清除。
TEF[8]:侵入事件标志(Tamper event flag) 0:无侵入事件 1:检测到侵入事件
当检测到侵入事件时此位由硬件置1。通过向CTE位写1可清除此标志位
TPIE[2]:允许侵入TAMPER引脚中断(TAMPER pin interrupt enable)
0:禁止侵入检测中断 1:允许侵入检测中断(BKP_CR寄存器的TPE位也必须被置1)
注1:侵入中断无法将系统内核从低功耗模式唤醒。 注2:仅当系统复位或由待机模式唤醒后才复位该位。
CTI[1]:清除侵入检测中断(Clear tamper interrupt)
0:无效 1:清除侵入检测中断和TIF侵入检测中断标志
CTE[0]:清除侵入检测事件(Clear tamper event)
0:无效 1:清除TEF侵入检测事件标志(并复位侵入检测器)。
要写入BKP寄存器数据必须在 PWR->CR中取消备份区写保护,才可以写入BKP数据。stm32开启入侵检测也不需要设置GPIOC的时钟和输入输出模式。
代码如下:(system.h 和 stm32f10x_it.h 等相关代码参照
stm32 直接操作寄存器开发环境配置)
User/main.c
#include <stm32f10x_lib.h>
#include "system.h"
#include "usart.h"
#include "bkp.h"
#define LED1 PAout(4)
#define LED2 PAout(5)
void Gpio_Init(void);
int main(void)
{
u16 data,i=10;
Rcc_Init(9); //系统时钟设置
Usart1_Init(72,9600);
Bkp_Init();
Tamper_Init();
Nvic_Init(0,0,TAMPER_IRQChannel,0); //设置中断
Gpio_Init();
while(i){
Write_Bkp(i,i);
data = Read_Bkp(i);
printf("\n DR%u = 0x%04X\n",i,data);
delay(30000); //延时30ms
i--;
}
while(1);
}
void Gpio_Init(void)
{
RCC->APB2ENR|=1<<2; //使能PORTA时钟
GPIOA->CRL&=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
GPIOA->CRL|=0x33334444;
//USART1 串口I/O设置
GPIOA -> CRH&=0xFFFFF00F; //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
GPIOA -> CRH|=0x000008B0;
}
User/stm32f103x_it.c
#include "stm32f10x_it.h"
#include "system.h"
#include "stdio.h"
#define LED1 PAout(4)
#define LED2 PAout(5)
#define LED3 PAout(6)
#define LED4 PAout(7)
extern u16 Read_Bkp(u8 reg);
void TAMPER_IRQHandler(void)
{
u16 i=10,data;
LED4 =1 ;
printf("\r\n A Tamper is coming .\r\n");
while(i){
data = Read_Bkp(i);
printf("\n DR%u = 0x%04X\n",i,data);
delay(30000); //延时30ms
i--;
}
BKP->CSR |= 3<<0; //清除事件中断标志位
}
Library/src/bkp.c
#include <stm32f10x_lib.h>
#include "bkp.h"
void Bkp_Init(void)
{
RCC->APB1RSTR |= 1<<27; //复位BKP寄存器
RCC->APB1RSTR &= ~(1<<27);
RCC->APB1ENR|=1<<28; //使能电源时钟
RCC->APB1ENR|=1<<27; //使能BKP时钟
}
/**
*
*后备寄存器写入操作
*reg:寄存器编号
*data:要写入的数值
*
**/
void Write_Bkp(u8 reg,u16 data)
{
PWR->CR|=1<<8; //取消备份区写保护
switch(reg)
{
case 1:
BKP->DR1=data;
break;
case 2:
BKP->DR2=data;
break;
case 3:
BKP->DR3=data;
break;
case 4:
BKP->DR4=data;
break;
case 5:
BKP->DR5=data;
break;
case 6:
BKP->DR6=data;
break;
case 7:
BKP->DR7=data;
break;
case 8:
BKP->DR8=data;
break;
case 9:
BKP->DR9=data;
break;
case 10:
BKP->DR10=data;
break;
}
}
u16 Read_Bkp(u8 reg)
{
u16 data;
switch(reg)
{
case 1:
data = BKP->DR1;
break;
case 2:
data = BKP->DR2;
break;
case 3:
data = BKP->DR3;
break;
case 4:
data = BKP->DR4;
break;
case 5:
data = BKP->DR5;
break;
case 6:
data = BKP->DR6;
break;
case 7:
data = BKP->DR7;
break;
case 8:
data = BKP->DR8;
break;
case 9:
data = BKP->DR9;
break;
case 10:
data = BKP->DR10;
break;
}
return data;
}
//开启入侵检测,检测引脚为GPIOC13 但是不用打开其时钟和设置引脚模式
void Tamper_Init()
{
BKP->CSR |= 3<<0; //清除事件中断标志位
BKP->CR |= 1<<1; //设定为入侵电平为低电平
BKP->CSR |= 1<<2; //允许入侵中断
BKP->CR |= 1<<0; //开启入侵检测
}
Library/inc/bkp.h
#include <stm32f10x_lib.h>
void Bkp_Init(void);
void Write_Bkp(u8 reg,u16 data);
u16 Read_Bkp(u8 reg);
void Tamper_Init(void);
库函数操作
main.c
#include "stm32f10x.h"
#include "stdio.h"
#define PRINTF_ON 1
#define CHECK_CODE 0xAE86
void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void USART_Configuration(void);
void BKP_Configuration(void);
void PrintBKP(void);
void WriteBKP(u16 Data,u8 DRNumber);
u8 CheckBKP(void);
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
USART_Configuration();
BKP_Configuration();
if(CheckBKP())
{
printf("\r\n The datas are as their initial status. \r\n");
WriteBKP(0xA522,2);
PrintBKP();
}else{
printf("\r\n The datas have been changed . \r\n");
WriteBKP(0xA53C,1);
PrintBKP();
}
while(1);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA , &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA , &GPIO_InitStructure);
}
void BKP_Configuration(void)
{
PWR_BackupAccessCmd(ENABLE);
BKP_ClearFlag();
BKP_TamperPinLevelConfig(BKP_TamperPinLevel_Low);
BKP_ITConfig(ENABLE);
BKP_TamperPinCmd(ENABLE);
}
void RCC_Configuration(void)
{
/* 定义枚举类型变量 HSEStartUpStatus */
ErrorStatus HSEStartUpStatus;
/* 复位系统时钟设置*/
RCC_DeInit();
/* 开启HSE*/
RCC_HSEConfig(RCC_HSE_ON);
/* 等待HSE起振并稳定*/
HSEStartUpStatus = RCC_WaitForHSEStartUp();
/* 判断HSE起是否振成功,是则进入if()内部 */
if(HSEStartUpStatus == SUCCESS)
{
/* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
RCC_PCLK2Config(RCC_HCLK_Div1);
/* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
RCC_PCLK1Config(RCC_HCLK_Div2);
/* 设置FLASH延时周期数为2 */
FLASH_SetLatency(FLASH_Latency_2);
/* 使能FLASH预取缓存 */
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
/* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
/* 使能PLL */
RCC_PLLCmd(ENABLE);
/* 等待PLL输出稳定 */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
/* 选择SYSCLK时钟源为PLL */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/* 等待PLL成为SYSCLK时钟源 */
while(RCC_GetSYSCLKSource() != 0x08);
}
/* 打开APB2总线上的GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP, ENABLE);
}
void USART_Configuration(void)
{
USART_InitTypeDef USART_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStructure;
USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;
USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
USART_ClockInit(USART1 , &USART_ClockInitStructure);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1,ENABLE);
}
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TAMPER_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void WriteBKP(u16 Data,u8 DRNumber) // 还可加入一些加密算法;DRNumber (1-9)
{
switch(DRNumber)
{
case 0x01: BKP_WriteBackupRegister(BKP_DR1,Data); break;
case 0x02: BKP_WriteBackupRegister(BKP_DR2,Data); break;
case 0x03: BKP_WriteBackupRegister(BKP_DR3,Data); break;
case 0x04: BKP_WriteBackupRegister(BKP_DR4,Data); break;
case 0x05: BKP_WriteBackupRegister(BKP_DR5,Data); break;
case 0x06: BKP_WriteBackupRegister(BKP_DR6,Data); break;
case 0x07: BKP_WriteBackupRegister(BKP_DR7,Data); break;
case 0x08: BKP_WriteBackupRegister(BKP_DR8,Data); break;
case 0x09: BKP_WriteBackupRegister(BKP_DR9,Data); break;
default: BKP_WriteBackupRegister(BKP_DR1,Data);
}
BKP_WriteBackupRegister(BKP_DR10,CHECK_CODE);
}
u8 CheckBKP(void)
{
if( BKP_ReadBackupRegister(BKP_DR10) == 0xAE86 ) // 如果此位数据丢失,则BPK数据丢失
return 1;
else
return 0;
}
void PrintBKP(void)
{
printf("DR1 = 0x%04X\t",BKP_ReadBackupRegister(BKP_DR1));
printf("DR2 = 0x%04X\t",BKP_ReadBackupRegister(BKP_DR2));
printf("DR3 = 0x%04X\t",BKP_ReadBackupRegister(BKP_DR3));
printf("DR4 = 0x%04X\t",BKP_ReadBackupRegister(BKP_DR4));
printf("DR5 = 0x%04X\t",BKP_ReadBackupRegister(BKP_DR5));
printf("DR6 = 0x%04X\t",BKP_ReadBackupRegister(BKP_DR6));
printf("DR7 = 0x%04X\t",BKP_ReadBackupRegister(BKP_DR7));
printf("DR8 = 0x%04X\t",BKP_ReadBackupRegister(BKP_DR8));
printf("DR9 = 0x%04X\t",BKP_ReadBackupRegister(BKP_DR9));
printf("DR10 = 0x%04X\t",BKP_ReadBackupRegister(BKP_DR10));
}
#if PRINTF_ON
int fputc(int ch,FILE *f)
{
USART_SendData(USART1,(u8) ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
return ch;
}
#endif
stm12f10x_it.c
#include "stm32f10x_it.h"
#include "stdio.h"
extern void PrintBKP(void);
void TAMPER_IRQHandler(void)
{
printf("\r\n A Tamper is coming .\r\n");
PrintBKP();
BKP_ClearITPendingBit();
BKP_ClearFlag();
}