版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/weixin_42653531/article/details/99744158
前言:很多的嵌入式設備使用過程中,當系統掉電時,往往需要把一些用戶設置的參數保存起來,或者是將掉電前的一些狀態信息保存,或者是統計系統重啓次數。保存這些動態信息其實有很多種方法,第一種:在系統掉電前保存到片內flash,但是對flash的讀寫是按頁操作,對於保存一些少量的數據來說並不合算。第二種:在系統掉電前保存在片外的EEPROM,可以根據需要選用合適的EEPROM存儲的大小,但這樣需要增加額外電路,增加成本。第三種:利用片內的備份寄存器裏的後備數據寄存器存儲。對於一些中、小型容量產品來說,有10個16位的數據後備寄存器。與前兩種不同,它需要將引腳接上電池,否則數據會丟失。下面對備份寄存器深入瞭解。
1.備份寄存器的特性
● 20字節數據後備寄存器(中容量和小容量產品),或84字節數據後備寄存器(大容量和互聯型產品)
● 用來管理防侵入檢測並具有中斷功能的狀態/控制寄存器
● 用來存儲RTC校驗值的校驗寄存器。
● 在PC13引腳(當該引腳不用於侵入檢測時)上輸出RTC校準時鐘,RTC鬧鐘脈衝或者秒脈衝
備份寄存器在後備供電區域裏,當電源被切斷,他們仍然由維持供電。當系統在待機模式下被喚醒,或系統復位或電源復位時,他們也不會被複位。下面主要介紹入侵檢測和數據後備寄存器的應用,關於RTC部分單獨一篇介紹。備份寄存器之所以與RTC有關,是因爲RTC在電源切斷後也是需要保持計數。
2.侵入檢測功能
當TAMPER引腳(即PC.13)上的信號從0變成1或者從1變成0(取決於備份控制寄存器BKP_CR的TPAL位),會產生一個侵入檢測事件(即使切斷)。侵入檢測事件將所有數據備份寄存器內容清除。
然而爲了避免丟失侵入事件,侵入檢測信號是邊沿檢測的信號與侵入檢測允許位的邏輯與,從而在侵入檢測引腳被允許前發生的侵入事件也可以被檢測到。
● 當TPAL=0時(高電平有效):如果在啓動侵入檢測TAMPER引腳前(通過設置TPE位)該引腳已經爲高電平,一旦啓動侵入檢測功能,則會產生一個額外的侵入事件(儘管在TPE位置’1’後並沒有出現上升沿)。
● 當TPAL=1時(低電平有效):如果在啓動侵入檢測引腳TAMPER前(通過設置TPE位)該引腳已經爲低電平,一旦啓動侵入檢測功能,則會產生一個額外的侵入事件(儘管在TPE位置’1’後並沒有出現下降沿)。
注意:對TAMPER引腳的檢測,可以是邊沿觸發(上升沿、下降沿),也可以是電平觸發,後者需要啓用中斷配合,下面會講解。產生侵入事件會將備份寄存器復位,產生事件的同時也可以通過軟件使能中斷,進入一個侵入檢測中斷TAMPER_IRQHandler。當然中斷不使能,事件仍然會發生。
3.代碼設計
涉及到的寄存器不逐一介紹,下面通過標準庫裏的函數進行開發。如果需要直接操作寄存器可以打開相應庫函數的定義,函數裏面也是執行寄存器的操作,將其內容複製出來即可。
#include "stm32f10x.h"
#include "stdio.h"
void TAMPER_ITConfig(void);
static void NVIC_Configuration(void);
static void USART1_Config(void);
int main(void)
{
unsigned short i;
char ch;
USART1_Config();//串口1輸出調試信息
NVIC_Configuration();//配置串口接收中斷的優先級
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);//使能電源管理單元的時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
PWR_BackupAccessCmd(ENABLE);//使能後備寄存器訪問
BKP_TamperPinCmd(DISABLE); //先關閉侵入檢測引腳
BKP_ITConfig(DISABLE); //關閉侵入中斷
BKP_TamperPinLevelConfig(BKP_TamperPinLevel_Low); //設置檢測引腳低電平有效
BKP_ClearFlag(); //清除侵入檢測事件
TAMPER_ITConfig();//配置中斷優先級並打開侵入中斷,不需要進入中斷可以註釋掉這句,並不影響侵入事件的發生
BKP_TamperPinCmd(ENABLE);//開啓侵入檢測引腳
#if 0 //調試方法一
printf("上電讀取BKP數據:\r\n");
for(i=0x0004;i<=0x0028;i+=4){ // baseaddr:0x0004~0x0028 共10個16位的數據後備寄存器
printf("%c ",BKP_ReadBackupRegister(i));
}
printf("\r\n");
printf("往BKP寫入數據:\r\n");
ch='a';
for(i=0x0004;i<=0x0028;i+=4){
BKP_WriteBackupRegister(i,ch++);
printf("%c ",BKP_ReadBackupRegister(i));
}
printf("\r\n");
#else //調試方法二
i=BKP_ReadBackupRegister(BKP_DR1);
printf("上電次數%d \r\n",i);
i++;
BKP_WriteBackupRegister(BKP_DR1,i);
#endif
while(1)
{
}
}
void TAMPER_ITConfig(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);//初始化侵入中斷的優先級
BKP_ITConfig(ENABLE);//使能侵入中斷
}
void USART1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//配置串口1(USART1)時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
//配置串口1(USART1 Tx (PA.09))
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置串口1 USART1 Rx (PA.10)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口1模式(USART1 mode)配置
USART_InitStructure.USART_BaudRate = 9600;//一般設置爲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_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啓中斷
USART_Cmd(USART1, ENABLE); //使能串口
USART_ClearFlag(USART1,USART_FLAG_TC);
}
int fputc(int ch, FILE *f)//重寫標準庫的fputc函數
{
//將Printf內容發往串口
USART_SendData(USART1, (unsigned char) ch);
while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);
return (ch);
}
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_Init(&NVIC_InitStructure);
}
在stm32f10x_it.c文件加入:
void TAMPER_IRQHandler(void)
{
if(BKP_GetITStatus()!=RESET){
printf("觸發侵入中斷\r\n");
BKP_ClearITPendingBit();//清除侵入檢測中斷
BKP_ClearFlag();//清除侵入檢測事件
//如果將下面兩句執行,那麼就變成電平觸發,導致的現象:若PC.13引腳保持有效電平,則系統會反覆進入中斷
//BKP_TamperPinCmd(DISABLE);
//BKP_TamperPinCmd(ENABLE);
}
}
首先需要準備兩個獨立的電源,將板子的和PC.13(侵入檢測引腳)接到一個電源(因爲我的板子沒有電池),將接到另一個電源,並且將兩個電源共地。並串口1接到電腦,利用電腦上位機顯示調試打印信息。
其次,編譯下載程序,打開串口助手,按下板子的復位鍵(我這裏按了四次),也可以切斷再上電反覆四次,如下圖:
可看出,不管系統復位還是掉電,上電次數得到了記錄。下面將PC.13引腳從高電平且換到低電平(產生一個侵入信號),再進行系統復位或重新上電(我這裏按了兩次復位):
可看出,侵入事件發生後,備份寄存器裏的數據被複位。當掉電時,PC.13引腳仍然在檢測,若出現下降沿,備份寄存器也會進行復位,這個可以自行驗證。還有上面提到的電平觸發,也可以自行驗證。我自己驗證過,這裏不做贅述。
<<書中有路勤爲徑,學海無涯苦作舟。———韓愈>>