Dual Bank Flash on STM32L476
前言
因Firmwave更新時我們期望原先運行的程式不會因此停止,所以這邊就要開創Dual Bank Flash的功能,一邊再下載更新Firmwave另一邊執行原先運行程式,最後再比對F/W version選擇最新的,就跳到該Bank開機
Flash
快閃記憶體,係一種EEPROM嘅形式,可以喺操作中多次擦寫嘅記憶體。呢種科技主要用喺一般性資料儲存,同埋喺電腦同其他數碼產品之間交換傳輸資料,例如記憶卡同USB手指。快閃記憶體係一種特殊嘅、以大區塊抹寫嘅EEPROM。早期嘅快閃記憶體進行一次抹除,就會清嗮全部喺晶片上嘅資料。
快閃記憶體嘅成本遠比可以位元組為單位寫入嘅EEPROM低[1],亦因為咁成為咗非揮發性固態儲存最重要亦最廣為採納嘅技術。PDA、手提電腦、數碼隨身聽、數碼相機同埋手機上都有用到快閃記憶體。另外,快閃記憶體喺遊戲主機上嘅採用亦都越嚟越多,取代咗儲存遊戲資料用嘅EEPROM或者帶有電池嘅SRAM。
雙bank IAP原理
STM32部分型號有提供雙BANK功能,簡單說就是把Flash切割成2區塊BANK1與BANK2。
。當在bank1中運行app時就可以把新更新的固件寫入到bank2,寫完以後就切換到從bank2啟動運行新的app。如果當前在bank2運行就把新固件寫入bank1,寫完以後切換從bank1啟動。
如何判別與開啟Dual Bank Flash
首先在Datasheet裡面查看第一頁Memories是否存在2 bank(這邊以STM32L476/STM32L073為例),如果沒有會寫single bank Flash
開啟2 bank首先開啟STM32CubeProgrammer把下圖BFB2打勾(L476為例,L073基本上就是daul bank不用特別開啟)
STM32F4 / G4 / G0 / L4 / L0 在SystemInit() 沒有重設Vector table , 當你使用Dual bank , 並轉跳Bank之後, 產生中斷會導致系統異常 . 建議要將” USER_VECT_TAB_ADDRESS” 打開 (default 是disable) , 如此進到SystemInit()之後才會重置Vector table .
what is UFB?
這是存在於SYSCFG寄存器中的一個位,它有兩個值:0或者1,功能是
- 0:FLASH bank1 被映射在0x8000000地址上
- 1:FLASH bank2 被映射在0x8000000地址上
這個為主要是去切換BANK的主要判斷,下2張圖為不同BANK啟動流程
Bank圖解
BANK1啟動流程
BANK2啟動流程
由上圖可以看到Bank2會映射到0x8000000起始地址上,因此如果Bank2寫Flash在地址0x08090000上實際上用CudePrg看是在0x08010000地址上,而Bank1有無映射都是相同的因此讀Flah與寫入Flash都是跟一般模式相同。知道此原理後Bank2可以去讀取Bank1數值而Bank1可以去讀取Bank2存取數值了~
範例coding參考
下面是L476給的範例程式所初始化部分
void toggleBankAndReset() {
FLASH_OBProgramInitTypeDef OBInit;
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
HAL_FLASH_OB_Unlock();
<mark style="background-color:rgba(0, 0, 0, 0);color:#09ed1b" class="has-inline-color"> /* Get the Dual boot configuration status */</mark>
<mark style="background-color:rgba(0, 0, 0, 0);color:#eb0c0c" class="has-inline-color"> HAL_FLASHEx_OBGetConfig(&OBInit);
</mark><mark style="background-color:rgba(0, 0, 0, 0);color:#0ceb16" class="has-inline-color"> /* Enable/Disable dual boot feature */</mark><mark style="background-color:rgba(0, 0, 0, 0);color:#eb0c0c" class="has-inline-color">
OBInit.OptionType = OPTIONBYTE_USER;
OBInit.USERType = OB_USER_BFB2;</mark>
if (((OBInit.USERConfig) & (OB_BFB2_ENABLE)) == OB_BFB2_ENABLE) {
OBInit.USERConfig = OB_BFB2_DISABLE;
} else {
OBInit.USERConfig = OB_BFB2_ENABLE;
}
if (HAL_FLASHEx_OBProgram(&OBInit) != HAL_OK) {
// uint32_t errorCode = HAL_FLASH_GetError();
while (1) {
BSP_LED_On(LED2);
HAL_Delay(500);
BSP_LED_Off(LED2);
HAL_Delay(500);
}
}
if (HAL_FLASH_OB_Launch() != HAL_OK) {
//uint32_t errorCode = HAL_FLASH_GetError();
while (1) {
BSP_LED_On(LED2);
HAL_Delay(500);
BSP_LED_Off(LED2);
HAL_Delay(500);
}
}
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
}
下面是確認是在哪個bank部分
uint8_t getActiveBank() {
volatile uint32_t remap = READ_BIT(SYSCFG->MEMRMP, 0x1 << 8);
return remap == 0 ? 1 : 2;
}
燒入方式
這邊燒入方式需要注意因為CubeIDE目前來看無法直接從0x08080000開始燒入,因此會借助cubeProgrammer使用bin檔燒入方法如下
- 先清除全部Flash內存
- 紅框處為燒入點選按鈕
- 籃框為選擇要燒入的Bin檔(這邊要先燒Bank2,因此先從0x08080000開始燒入)
- 綠框為燒入啟始位置
- 檢查0x08080000是否燒入成功
- 檢查0x08000000是否還是FF
- 修正燒入起始位置重覆步驟2~4燒入Bank1
- 檢查全部內容是否燒入成功