博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
STM32 备份域(备份寄存器、备份SRAM)详解及数据丢失问题处理
阅读量:2190 次
发布时间:2019-05-02

本文共 6689 字,大约阅读时间需要 22 分钟。

STM32F4提供4KB的备份SRAM,在开发程序时可以用于存储掉电不丢失的数据(需要RTC纽扣电池支持),特别是一些实时修改的,掉电不能丢失的数据,比如我用于存储雨量累计流量等实时变化的数据,定时存储到flash,实时存储到备份区(不能频繁的写flash),当备份区数据丢失了再从flash加载,否则每次都从备份区加载。

然而在使用过程中发现备份区域数据丢失!下面从STM32系列芯片提供的整个备份域来看看啥情况。

 

某些STM32芯片提供了备份SRAM,例如STM32F系列芯片有4K的备份SRAM。然而在使用过程中发现备份区域数据丢失!下面从STM32系列芯片提供的整个备份域来看看啥情况。

电池备份域

  首先,这部分在参考手册的电源(PWR)章节有详细的介绍。器件的工作电压 (VDD) 要求介于 1.8 V 到 3.6 V 之间。嵌入式线性调压器用于提供内部 1.2 V数字电源。当主电源 VDD 断电时,可通过 VBAT 电压为实时时钟 (RTC)RTC备份寄存器 和 备份 SRAM(BKP SRAM) 供电。具体如下图:

电源
手册中有许多对于使用芯片时对于电源部分设计的要求,例如引脚的使用、电流的要求等等,具体见手册!

备份域访问

  复位后,备份域(RTC 寄存器、RTC 备份寄存器和备份 SRAM)将受到保护,以防止意外的写访问。要使能对备份域的访问,请按以下步骤进行操作:

访问 RTC 和 RTC 备份寄存器

  1. 将 RCC_APB1ENR 寄存器中的 PWREN 位置 1,使能电源接口时钟(分别参见手册第 6.3.15 节和第 6.3.16 节了解 STM32F405xx/07xx 和 STM32F415xx/17xx 和 STM32F42xxx 和 STM32F43xxx)
  2. 将用于 STM32F405xx/07xx 和 STM32F415xx/17xx 的 PWR 电源控制寄存器 (PWR_CR)和 用于STM32F42xxx 和 STM32F43xxx 的 PWR 电源控制寄存器 (PWR_CR) 中的 DBP 位置 1,使能对备份域的访问
  3. 选择 RTC 时钟源:参见手册第 6.2.8 节:RTC/AWU 时钟
  4. 通过对 RCC 备份域控制寄存器 (RCC_BDCR) 中的 RTCEN [15] 位进行编程,使能 RTC 时钟

访问备份 SRAM

  1. 将 RCC_APB1ENR 寄存器中的 PWREN 位置 1,使能电源接口时钟(分别参见手册第 6.3.15 节和第 6.3.16 节了解 STM32F405xx/07xx 和 STM32F415xx/17xx 和 STM32F42xxx 和 STM32F43xxx)。
  2. 将用于 STM32F405xx/07xx 和 STM32F415xx/17xx 的 PWR 电源控制寄存器 (PWR_CR) 和用于STM32F42xxx 和 STM32F43xxx 的 PWR 电源控制寄存器 (PWR_CR) 中的 DBP 位置 1,使能对备份域的访问。
  3. 通过将 RCC AHB1 外设时钟使能寄存器 (RCC_AHB1ENR) 中的 BKPSRAMEN 位置 1,使能备份 SRAM 时钟。

想要访问备份域还是非常简单的,下面以访问备份SRAM为例,从代码角度说明一下(具体见注释即可):

/** * @brief (使用标准外设库)备份SRAM初始化 * @param[in] void * @retval  NULL */static void vBkpSramInit(void){	/* 电源接口时钟使能 (Power interface clock enable) */	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);		/* DBP 位置 1,使能对备份域的访问 */	PWR_BackupAccessCmd(ENABLE);		/* 通过将 RCC AHB1 外设时钟使能寄存器 (RCC_AHB1ENR) 中的 BKPSRAMEN 位置 1, 使能备份 SRAM 时钟 */	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_BKPSRAM, ENABLE);		/* 应用程序必须等待备份调压器就绪标志 (BRR) 置 1,指示在待机模式和 VBAT 模式下会保持写入 RAM 中的数据。 */	while(PWR_GetFlagStatus(PWR_FLAG_BRR) != SET);}/** (使用HAL库)备份SRAM初始化 *  * @param[in]   NULL * @retval      Null**/void BKP_SRAM_Init(void){	/* 电源接口时钟使能 (Power interface clock enable) */	__HAL_RCC_PWR_CLK_ENABLE();	/* DBP 位置 1,使能对备份域的访问 */	HAL_PWR_EnableBkUpAccess();	/* 通过将 RCC AHB1 外设时钟使能寄存器 (RCC_AHB1ENR) 中的 BKPSRAMEN 位置 1, 使能备份 SRAM 时钟 */	__HAL_RCC_BKPSRAM_CLK_ENABLE();	/* 应用程序必须等待备份调压器就绪标志 (BRR) 置 1,指示在待机模式和 VBAT 模式下会保持写入 RAM 中的数据。 */	HAL_PWREx_EnableBkUpReg();}

经过以上初始化之后,就可以使用备份域中的各部分功能了(RTC和备份SRAM的初始化有些区别)。

备份域的使用

初始化后对于备份域中各功能(RTC、RTC备份寄存器、备份SRAM)的使用就比较灵活了。

  • RTC: 使用相对来说比较复杂,后面独立介绍
  • RTC备份寄存器: 读写非常简单,标准外设库和HAL库都提供了函数直接进行读写。
    /*----------------------------标准外设库----------------------------*//**  * @brief  Writes a data in a specified RTC Backup data register.  * @param  RTC_BKP_DR: RTC Backup data Register number.  *          This parameter can be: RTC_BKP_DRx where x can be from 0 to 19 to   *                                 specify the register.  * @param  Data: Data to be written in the specified RTC Backup data register.                       * @retval None  */void RTC_WriteBackupRegister(uint32_t RTC_BKP_DR, uint32_t Data){  __IO uint32_t tmp = 0;    /* Check the parameters */  assert_param(IS_RTC_BKP(RTC_BKP_DR));  tmp = RTC_BASE + 0x50;  tmp += (RTC_BKP_DR * 4);  /* Write the specified register */  *(__IO uint32_t *)tmp = (uint32_t)Data;}/**  * @brief  Reads data from the specified RTC Backup data Register.  * @param  RTC_BKP_DR: RTC Backup data Register number.  *          This parameter can be: RTC_BKP_DRx where x can be from 0 to 19 to   *                          specify the register.                     * @retval None  */uint32_t RTC_ReadBackupRegister(uint32_t RTC_BKP_DR){  __IO uint32_t tmp = 0;    /* Check the parameters */  assert_param(IS_RTC_BKP(RTC_BKP_DR));  tmp = RTC_BASE + 0x50;  tmp += (RTC_BKP_DR * 4);    /* Read the specified register */  return (*(__IO uint32_t *)tmp);}/*----------------------------HAL库----------------------------*//**  * @brief  Writes a data in a specified RTC Backup data register.  * @param  hrtc: pointer to a RTC_HandleTypeDef structure that contains  *                the configuration information for RTC.   * @param  BackupRegister: RTC Backup data Register number.  *          This parameter can be: RTC_BKP_DRx where x can be from 0 to 19 to   *                                 specify the register.  * @param  Data: Data to be written in the specified RTC Backup data register.                       * @retval None  */void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister, uint32_t Data){  uint32_t tmp = 0U;    /* Check the parameters */  assert_param(IS_RTC_BKP(BackupRegister));    tmp = (uint32_t)&(hrtc->Instance->BKP0R);  tmp += (BackupRegister * 4U);    /* Write the specified register */  *(__IO uint32_t *)tmp = (uint32_t)Data;}/**  * @brief  Reads data from the specified RTC Backup data Register.  * @param  hrtc: pointer to a RTC_HandleTypeDef structure that contains  *                the configuration information for RTC.   * @param  BackupRegister: RTC Backup data Register number.  *          This parameter can be: RTC_BKP_DRx where x can be from 0 to 19 to   *                                 specify the register.                     * @retval Read value  */uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister){  uint32_t tmp = 0U;    /* Check the parameters */  assert_param(IS_RTC_BKP(BackupRegister));  tmp = (uint32_t)&(hrtc->Instance->BKP0R);  tmp += (BackupRegister * 4U);    /* Read the specified register */  return (*(__IO uint32_t *)tmp);}

     

  • 备份SRAM: 这部分的使用就更加灵活了,可以直接当内存去访问。推荐一种使用分散加载文件进行访问的方式。具体为定义自己的结构体,使用结构体定义变量BKP_SRAM myContent __attribute__((section("BKP_SRAM_SECTION")));,最后使用分散加载文件,将以上定义的变量直接映射到备份SRAM即可。
; *************************************************************; *** Scatter-Loading Description File generated by uVision ***; *************************************************************LR_IROM1 0x08000000 0x0000C000  {    ; load region size_region  ER_IROM1 0x08000000 0x0000C000  {  ; load address = execution address   *.o (RESET, +First)   *(InRoot$$Sections)   .ANY (+RO)  }  RW_IRAM1 0x20000000 0x00020000  {  ; RW data   .ANY (+RW +ZI)  }  RW_BkSRAM 0x40024000 0x1000 {    *.o (BKP_SRAM_SECTION, +First)          ; 备份SRAM  }}

备份SRAM问题

在实际产品中使用时,发现备份SRAM中的数据丢失!检查在硬件上并没有出现任何问题,于是从软件一步步分析如下:

  1. 产品( 使用STM32F407VG )中实现了IAP、APP在线升级,备份域在这两部程序中均有使用(两部分程序中均对备份域进行了初始化)。
  2. 在由IAP跳转到APP后,发现在APP中初始化的备份SRAM中原有数据全部丢失( 准确的说应该是时钟不起作用,导致数据全是 0,看似数据丢失 )
  3. 分析原因,STM32芯片在上电后默认以内部低速时钟源(HSI运行),如果用户配置了使用外部时钟源,则再配置外部时钟源,然后将时钟切换为外部。程序在APP中配置时钟前是正常的,一旦时钟源出现切换则导致备份域再次初始化之后就无效了!感觉应该是 备份域的各种初始化必须在时钟初始化之后再进行配置才可以,颠倒顺序将导致备份域时钟初始化后不可用! 但是,其他外设(例如GPIO,同是挂在AHP总线上)却不受以上限制,比较奇怪!

解决

  在IAP跳转到APP前,将备份域的各时钟失能,这样APP中配置的备份SRAM才会有效。

后续

后续可以测试一下其他外设是否有此问题。最好测试一下同样是挂在同一总线下的外设(GPIO、DMA、备份域时钟全部是在AHB总线下的)。

 

转自链接:

你可能感兴趣的文章
算法导论阅读顺序
查看>>
Windows程序设计:直线绘制
查看>>
linux之CentOS下文件解压方式
查看>>
Django字段的创建并连接MYSQL
查看>>
div标签布局的使用
查看>>
HTML中表格的使用
查看>>
(模板 重要)Tarjan算法解决LCA问题(PAT 1151 LCA in a Binary Tree)
查看>>
(PAT 1154) Vertex Coloring (图的广度优先遍历)
查看>>
(PAT 1115) Counting Nodes in a BST (二叉查找树-统计指定层元素个数)
查看>>
(PAT 1143) Lowest Common Ancestor (二叉查找树的LCA)
查看>>
(PAT 1061) Dating (字符串处理)
查看>>
(PAT 1118) Birds in Forest (并查集)
查看>>
数据结构 拓扑排序
查看>>
(PAT 1040) Longest Symmetric String (DP-最长回文子串)
查看>>
(PAT 1145) Hashing - Average Search Time (哈希表冲突处理)
查看>>
(1129) Recommendation System 排序
查看>>
PAT1090 Highest Price in Supply Chain 树DFS
查看>>
(PAT 1096) Consecutive Factors (质因子分解)
查看>>
(PAT 1019) General Palindromic Number (进制转换)
查看>>
(PAT 1073) Scientific Notation (字符串模拟题)
查看>>