1. 程式人生 > >STM32 之 備份域(備份暫存器、備份SRAM)詳解及資料丟失問題處理

STM32 之 備份域(備份暫存器、備份SRAM)詳解及資料丟失問題處理

某些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匯流排下的)。