软件STM32cubeIDE下STM32F1xx和STM32F4xx使用:备份寄存器+复位标志位-基础样例
- 1、前言
- 2 、 实验环境
- 3、自我总结
- (1)对于备份寄存器(BKP):
- (2)对于复位标志位(RCC_CSR):
- (3)==实验阶段和实际应用有区别==
- 4、先行了解
- 5、在STM32F1和STM32F4上进行实验
- 实验说明
- 1)新建工程配置
- 2)串口打印uart1
- 3)看门狗配置,用来造成复位
- 4)配置备份寄存器和标志位
- 5)配置系统时钟
- 6)生成代码后,加入的代码段
- 6、实验结果展示与结论
- 1)结果情况1:
- 2)结果情况2:
- 3)结果情况3:
- 7、实际应用遇到问题-实际应用
- (1)没有纽扣电池情况下,备份寄存器的值被保存下来
- (2)写入备份寄存器会引发其它异常
- (3)带有boot的可能会清掉复位标志位
- 8、代码链接
- 9、细节部分
- (1)通过hal库来看都有什么复位标志位
- (2)通过hal库来看有多少个备份寄存器可以使用
- (3)要保证外部供电完全断电
- (4)了解原理图也是不可少的
- 10、总结
1、前言
最近在项目上使用备份寄存器和复位标志,于是学习了一下,并使用。
用STM32也好几年了,很多像pwm和串口,也调试过很多了,但仍然认为很多东西,依旧要当自己为新手。像这次调试的这两个,之前没有调试过,也没有想到过会要用上这两个。
2 、 实验环境
- 软件环境:STM32cubeIDE 1.8.0
- 硬件芯片:STM32F103VET6(野火:指南者)
- 硬件芯片:STM32F407ZET6(正点原子:探索者)
- 其它硬件:串口转换器,下载器,有电的纽扣电池等。
3、自我总结
(1)对于备份寄存器(BKP):
你可以理解在STM32芯片中,有个地方(备份寄存器BKP)或者一块区域(备份SRAM)帮忙保存数据,当然这是有条件的:
第一个是,不能完全断电,物理意义上的不能完全断电。
第二个是,写入时是有限制的,写入时,要开使能,才允许写入,这么做是为保护,不让随意写入。
他们三个:RTC时钟、备份寄存器和备份SRAM,是有关系的,都在备份区域,所以在查BKP备份寄存器时,总能看到RTC时钟相关的内容。
(2)对于复位标志位(RCC_CSR):
复位标志位,你可以理解是软件自己检测的一种工具,打个比方,当代码出现异常,不再喂狗的时候,看门狗就会使得整个系统复位,起来后,你去读取复位标志,复位标志位,就会告诉软件,上次是因为什么而复位的。
一些复位信号的产生,会引起整个系统的产生,然后有个地方会存下是什么复位了,记录这个复位,让你好读取。软件做的只有读取和将那个地方清空一下,方便下次起来,好判定。如果不清空,那么因为本次已经置位了,下次,你可能不知道是什么复位了。
(3)实验阶段和实际应用有区别
自己使用过程中,其实分为实验验证阶段和实际应用阶段的,这点还是要注意的,我们试验阶段是要证明它可以使用,并且熟悉相应的特性,了解其原理。
而实际情况可能跟测试时会有不同:
第一个是:本次使用备份寄存器就是这样,在测试时,发现完全断电,备份寄存器确实也被复位为0了,但是实际融合项目上,发现因为整个设备比较复杂,虽然设备关机了,但是因为有其他部分存在,给备份寄存器供电了,没有电池,但相当一个电池了。而且还时灵时不灵,只有将外围设备拔除完全,才会被复位为0。
4、先行了解
对于更专业的解释,可以直接找手册,发现网上大部分知识点,在参考手册里都有,或者说,都是从手册里扒的,手册直接在网上找就可以了。
参考手册F1:STM32F10x中文参考手册.pdf
参考手册F4:STM32F4xx中文参考手册.pdf
这里放一些自己认为重要的吧。
(1)如下链接,全面简绍了一下,可以收获的两个点是:
第一个:备份寄存器等,需要很低的电,就可以将数据保存下来。
第二个:BKP和RTC之间是有关系的。
博文链接:https://blog.csdn.net/ZCShouCSDN/article/details/82896924
(2)看过一些博文后,回来看参考手册,发手册里面有些可以更好帮我们理解,这里建议多看手册吧。
这里告诉我们两个点是:
第一个:备份区域也是可以复位的,一种方式是通过软件的方式复位。
第二个:第二个复位方式是,完全断电,同时我们也可以理解,为啥纽扣电池要接V-BAT引脚,因为V-BAT专门连接备份区域,以保证数据存在。
(3)对于复位标志位,我截图以下几张,也自己认为比较重要的图,它告诉你复位标志位有哪些,分别都是什么复位。
(4)需要了解的库函数,实际使用过程中,要使能的定时器和一些函数这里先列举。在看资料过程中,一些博文都会提到要使能一些定时器开关的,这里因为咱们使用了IDE软件,自动生成,反而不需要关心,但是要知道有这些东西存在,移植的时候要带上。
在这里插入代码片
/* 以下都是在需要自己编写的代码 */
/* 获取某个复位标志位函数 */
if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) != RESET)
{
}
/* 复位 复位标志位,就是清空标志位,让其为0 */
__HAL_RCC_CLEAR_RESET_FLAGS();
/* 读取某个备份寄存器函数 */
g_nBackup_reg_value=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) ;
/* 写入某个备份寄存器函数,要开使能 */
HAL_PWR_EnableBkUpAccess();
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x02);// Writes a data in a RTC Backup data Register 1
HAL_PWR_DisableBkUpAccess();
/* 以下都是在生成好的代码中,之间复制过来的,知道有这些函数就可以了,软件会自动生成 */
__HAL_RCC_AFIO_CLK_ENABLE();
__HAL_RCC_PWR_CLK_ENABLE(); //RCC电源时钟使能
__HAL_AFIO_REMAP_SWJ_NOJTAG();
HAL_PWR_EnableBkUpAccess();
__HAL_RCC_BKP_CLK_ENABLE(); /* Enable BKP CLK enable for backup registers */
__HAL_RCC_RTC_ENABLE(); /* Peripheral clock enable */
HAL_NVIC_SetPriority(RTC_IRQn, 0, 0);/* RTC interrupt Init */
HAL_NVIC_EnableIRQ(RTC_IRQn);
5、在STM32F1和STM32F4上进行实验
实验说明
(1)因为要进行复位,我们之前进行过看门狗实验,正好可以让它进行复位,看看复位标志位能不能记录。
看门狗相关文章:# 软件stm32cubeIDE下配置STM32F103的独立看门狗iwdg-学习笔记-基础样例
(2)而备份寄存器,我们需要需要完全断电和使用开发板上纽扣电池,看看断到外部电源后,会有什么情况。
(3)因为配置工程过程差不多,所以合并在一起说明了,如果对代码有疑问,可以去看代码。
1)新建工程配置
(1)基本配置:外部RCC和下载
2)串口打印uart1
配置串口,就是为显示用的,没啥特殊要求,就直接默认了。
3)看门狗配置,用来造成复位
如果下图,是看门狗配置,具体为啥是8秒左右,看之前文章吧,有详细说明。
如下图,是计算的方式
4)配置备份寄存器和标志位
复位标志位其实是不用配置什么的,而备份寄存器需要配置下,通过配置RTC,配置使能,没什么特别要求,默认既可以了。
5)配置系统时钟
STM32F1基本是72Mhz,而STM32F4基本是168M,这块配置下就行,自己使用的时候忘记配置系统时钟了,不过也不影响。
6)生成代码后,加入的代码段
这里主要分为三个部分的代码段,一段是读取复位标志的,一段是备份寄存器读写的,一段是看门狗的,他们都在main.c函数里。这里为了方便,我也不分开了,直接将主要的部分放在下面。
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
//第二�??
#include "string.h"
#include "stdint.h"
uint8_t u_buf[64];
#define printf(...) HAL_UART_Transmit((UART_HandleTypeDef * )&huart1, (uint8_t *)u_buf,\
sprintf((char *)u_buf,__VA_ARGS__),0x200);
/
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
IWDG_HandleTypeDef hiwdg;
RTC_HandleTypeDef hrtc;
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_IWDG_Init(void);
static void MX_RTC_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
//第三步加喂狗函数
void iwdg_feeddog(void)
{
HAL_IWDG_Refresh(&hiwdg); // feed dog
}
int main(void)
{
/* USER CODE BEGIN 1 */
int sum=0;
uint32_t g_nBackup_reg_value=0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_IWDG_Init();
MX_RTC_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
复位标志/
// //printf("RCC_FLAG_LSIRDY:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY));HAL_Delay(100);
// printf("RCC_FLAG_PINRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST));HAL_Delay(100);
// printf("RCC_FLAG_PORRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST));HAL_Delay(100);
// printf("RCC_FLAG_SFTRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST));HAL_Delay(100);
// printf("RCC_FLAG_IWDGRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST));HAL_Delay(100);
// printf("RCC_FLAG_WWDGRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST));HAL_Delay(100);
// printf("RCC_FLAG_LPWRRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST));HAL_Delay(100);
//
//
// __HAL_RCC_CLEAR_RESET_FLAGS();
// printf("=========================================================");
// printf("RCC_FLAG_PINRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST));HAL_Delay(100);
// printf("RCC_FLAG_PORRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST));HAL_Delay(100);
// printf("RCC_FLAG_SFTRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST));HAL_Delay(100);
// printf("RCC_FLAG_IWDGRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST));HAL_Delay(100);
// printf("RCC_FLAG_WWDGRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST));HAL_Delay(100);
// printf("RCC_FLAG_LPWRRST:%d \n\t",__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST));HAL_Delay(100);
/* 按位与在一个里
#define RCC_FLAG_LSIRDY 6
#define RCC_FLAG_PINRST 5
#define RCC_FLAG_PORRST 4
#define RCC_FLAG_SFTRST 3
#define RCC_FLAG_IWDGRST 2
#define RCC_FLAG_WWDGRST 1
#define RCC_FLAG_LPWRRST 0
*/
//LSIRDY:内部低速振荡器就绪 (Internal low-speed oscillator ready)
if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) != RESET)
{
printf(" RCC_FLAG_LSIRDY_flag=1 ");
}
else
{
printf(" RCC_FLAG_LSIRDY_flag=0 ");
}
HAL_Delay(100);
//PINRSTF:NRST引脚复位标志 (PIN reset flag)
if (__HAL_RCC_GET_FLAG(RCC_FLAG_PINRST) != RESET)
{
printf(" RCC_FLAG_PINRST=1 ");
}
else
{
printf(" RCC_FLAG_PINRST=0 ");
}
HAL_Delay(100);
//PORRSTF:POR/PDR 复位标志 (POR/PDR reset flag)
if (__HAL_RCC_GET_FLAG(RCC_FLAG_PORRST) != RESET)
{
printf(" RCC_FLAG_PORRST=1 ");
}
else
{
printf(" RCC_FLAG_PORRST=0 ");
}
HAL_Delay(100);
//SFTRSTF:软件复位标�? (Software reset flag)
if (__HAL_RCC_GET_FLAG(RCC_FLAG_SFTRST) != RESET)
{
printf(" RCC_FLAG_SFTRST=1 ");
}
else
{
printf(" RCC_FLAG_SFTRST=0 ");
}
HAL_Delay(100);
//IWDGRSTF:独立看门狗复位标志 (Independent watchdog reset flag)
if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET) //看门狗复�?
{
printf(" RCC_FLAG_IWDGRST=1 ");
}
else
{
printf(" RCC_FLAG_IWDGRST=0 ");
}
HAL_Delay(100);
//WWDGRSTF:窗口看门狗复位标志 (Window watchdog reset flag)
if (__HAL_RCC_GET_FLAG(RCC_FLAG_WWDGRST) != RESET) //看门狗复�?
{
printf(" RCC_FLAG_WWDGRST=1 ");
}
else
{
printf(" RCC_FLAG_WWDGRST=0 ");
}
HAL_Delay(100);
// LPWRRSTF:低功�?�复位标�? (Low-power reset flag)
if (__HAL_RCC_GET_FLAG(RCC_FLAG_LPWRRST) != RESET) //看门狗复�?
{
printf(" RCC_FLAG_LPWRRST=1 ");
}
else
{
printf(" RCC_FLAG_LPWRRST=0 ");
}
HAL_Delay(500);
// __HAL_RCC_CLEAR_RESET_FLAGS();
// printf(" =========__HAL_RCC_CLEAR_RESET_FLAGS()====== ");
备份寄存�?/
g_nBackup_reg_value=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) ;
printf("(1)g_nBackup_reg_value:%d ",g_nBackup_reg_value);
HAL_PWR_EnableBkUpAccess();
// Writes a data in a RTC Backup data Register 1
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x02);
HAL_PWR_DisableBkUpAccess();
g_nBackup_reg_value=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) ;
printf("(2)g_nBackup_reg_value:%d ",g_nBackup_reg_value);
printf("main start!! \t");
HAL_Delay(1000);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0xBEBE)
// {
// // Write Back Up Register 1 Data
// HAL_PWR_EnableBkUpAccess();
// // Writes a data in a RTC Backup data Register 1
// HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0xBEBE);
// HAL_PWR_DisableBkUpAccess();
//
//
// }
// else
// {
// // data register already written so turn LED3
//
//
// }
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
sum++;
// printf("123456");
printf("sum_value:%d",sum);
if(sum>=3)
{
}else
{
iwdg_feeddog();
}
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
6、实验结果展示与结论
1)结果情况1:
STM32F1和STM32F4,表现基本一致,有供电情况下,备份寄存器内写入值,可以被保存起来,完全断电后,会置为为0。如下图所示
2)结果情况2:
复位标志位,默认上电后,每一个复位标志位,都有自己的值,不一定一定是0,需要根据情况分析,但是F1和F4两块开发板,复位情况保持一致,如下图所示。
我们可以看到第一次默认上电,标志位 RCC_FLAG_PINRST 和 RCC_FLAG_PORRST 都是1
,通过手册我们可以知道,一个是复位引脚的,一个是上下电的。
3)结果情况3:
STM32F1和STM32F4,通过相同函数__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY)读取复位标志位的值是不一样的,所以采用两种形式进行打印输出。
如下图所示,F1中读取反馈的值是非常大的一个数,所以使用和RESET进行比较的形式输出,只要比较是不是0,我们看REST的值,发现就是判定是0和非0.
而在STM32F4,这个值读回来就是1或者0,就直接打印就好。
7、实际应用遇到问题-实际应用
(1)没有纽扣电池情况下,备份寄存器的值被保存下来
这块之前也说些了,实际应用过程中,因为外部连了很多设备,虽然他们都没有电池,也不给目标芯片供电,但在整个系统加持下,形成了一个类似电池的设备,让值被保持下来,而这个电池还有时灵,有时不灵,干扰测试结果。
(2)写入备份寄存器会引发其它异常
在实际使用过程中,因为带有boot,发现写入备份寄存器,导致boot跳转满,还无法升级,这块实际使用时,多测试和注意吧。
(3)带有boot的可能会清掉复位标志位
在实际使用过程中,也用了boot,所以boot在跳转前,会清除所有标志位,包括复位标志,所以实际使用时,主要不要清除复位标志位。
8、代码链接
(1)STM32F1样例代码
代码链接:https://download.csdn.net/download/qq_22146161/87744129
(1)STM32F4样例代码
代码链接:https://download.csdn.net/download/qq_22146161/87744131
9、细节部分
(1)通过hal库来看都有什么复位标志位
我们在hal库中通过一个复位标志,就能找到其它复位标志,这样也可以知道有多少个复位标志,再去查手册,知道了每个复位都会什么情况下复位。
(2)通过hal库来看有多少个备份寄存器可以使用
同样道理,我们可以通过一个备份寄存器,知道本系列芯片下,可以使用多少个备份寄存器,因为不同系列这个可以的数量可能是不一样的。
(3)要保证外部供电完全断电
如下图,所示,在调试过程中,发现备份寄存器值被保存下拉,一查才发现,j-link插着呢,电池电源和j-link都要关闭。
在这里插入图片描述
(4)了解原理图也是不可少的
我们可以通过原理图,来查看,硬件有没有接电池,也就是VBAT引脚外部硬件连接情况,有没有电池给其供电。
10、总结
一个再小的东西,也要保持自己谦虚,谨慎对待,小心实验。