简介
产品开发中经常会有需要对产品进行程序固件升级的需求,通常为了避免升级程序对原有固件的损坏,会设计成A/B分区的模式,将FLASH 分成两块保存两份程序,从而避免升级失败造成无法启动的问题,通常A/B分区的升级方式,需要将两份程序放在不同的位置,编译应用程序时需要根据程序运行的地址调整linker 地址信息,在boot 程序中选择合适的app 程序进行启动。
STM32H533 Flash 特性上支持了硬件bank交换功能(bank swap)功能,更新完flash 后将更新后的flash swap 到运行地址,这样就不需要对编译程序时调整link 地址,swap 后程序运行的地址信息为固定的地址不需要调整。
bank swap 流程
从下面的描述可以看出当SWAP_BANK = 0时,BANK1 映射到0x08000000-0x0803ffff 地址空间 BANK2 映射到0x08040000-0x0807ffff 地址空间,SWAP_BANK = 1时 BANK2 映射到0x08000000-0x0803ffff 地址空间 BANK1 映射到0x08040000-0x0807ffff 地址空间
芯片用户手册中有bank swap 功能使用的流程,对应流程图如下:
bank_swap 的更新流程文档中有说明按照流程即可更新 ,主要步骤如下:
通过OBK key register (FLASH_NSOBKKEYR) 寄存器连续写入key1(0x192A 083B),key2(0x6E7F 4C5D),可以解锁OPTLOCK寄存器从而更新SWAP_BANK寄存器,一旦错误的解锁序列将触发解锁失败知道下次syetemreset才可以继续解锁。
按照上面的更新流程,本地通过串口使用ymodem协议,发送升级包至MCU,MCU 接收到升级包后按照上述流程写入flash ,后更新bank_swap 寄存器,然后重启系统完成升级功能。
Ymodem测试代码如下
/**********************************************************
*
* File :
* YModem.c
*
* Description:
* YModem protocal (use statemachine)
*
*/
#include "ymodem.h"
#include <stdio.h>
#include <stdlib.h>
#include "littleshell.h"
#include "stm32h5xx_hal.h"
typedef enum
{
WAIT_FILE_DESC,
WAIT_FIRST_DATA,
WAIT_DATA,
WAIT_LAST_STX_DATA,
WAIT_LAST_SOH_DATA,
WAIT_EOT,
WAIT_END
} ReceiveState;
/*
* Frame type
*/
typedef uint8_t FrameType;
#define FRAME_SOH 0x01 /* start of 128-byte data packet */
#define FRAME_STX 0x02 /* start of 1024-byte data packet */
#define FRAME_EOT 0x04 /* end of transmission */
#define FRAME_ACK 0x06 /* acknowledge */
#define FRAME_NAK 0x15 /* negative acknowledge */
#define FRAME_CAN 0x18 /* aborts transfer */
#define FRAME_C 0x43 /* 'C' == 0x43, start transmission */
#define FRAME_ABORT1 0x41
#define FRAME_ABORT2 0x61
/*
* Receive & Send Timeout
*/
#define RECEIVE_TIMEOUT 8000
#define SEND_TIMEOUT 1000
/*
* Frame size
*/
#define STX_DATA_SIZE 1024
#define SOH_DATA_SIZE 128
#define FRAME_DATA_SIZE 1024
#define FRAME_HEADER_SIZE 3
#define FRAME_TAILOR_SIZE 2
/*
* File Description
*/
#define FILE_NAME_LENGTH 50
#define FILE_SIZE_STR_LENGTH 9
static uint8_t* gFrameBuf = NULL;//[FRAME_DATA_SIZE + FRAME_HEADER_SIZE+ FRAME_TAILOR_SIZE];
static uint8_t* YmodemAlloc(void)
{
if( NULL == gFrameBuf )
gFrameBuf = (uint8_t*)malloc(FRAME_DATA_SIZE + FRAME_HEADER_SIZE+ FRAME_TAILOR_SIZE);
return gFrameBuf;
}
static void YmodemFree(void)
{
if(gFrameBuf)
free(gFrameBuf);
gFrameBuf = NULL;
}
/*
*
*/
extern uint8_t uartgetchar(uint8_t * pdata);
static YmodemRetVal YmodemRecvByte(uint8_t *data, uint32_t timeout)
{
uint32_t time_out = HAL_GetTick() + timeout;
while(!uartgetchar(data))
{
if(HAL_GetTick() > time_out)
return YMODEM_TIMEOUT;
}
return YMODEM_OK;
}
extern int iar_fputc(int ch);
static YmodemRetVal YmodemSendByte(uint8_t data)
{
iar_fputc((int)data);
return YMODEM_OK;
}
static int32_t YmodemAllocReceiveAddr(uint32_t recvAddr, uint32_t size)
{
(void) recvAddr;
(void) size;
return 0;
}
static YmodemRetVal YmodemSaveData(uint32_t addr, uint8_t *data, uint32_t dataLen)
{
if(data == NULL)
return YMODEM_PARAM_ERR;
ymode_flash_write(addr,data,dataLen);
return YMODEM_OK;
}
static YmodemRetVal YmodemReceiveFrame(FrameType * frameType, uint8_t * buf, uint32_t timeout)
{
YmodemRetVal retVal;
uint8_t oneByte;
uint32_t i;
/* Check parameters invalid */
if(frameType == NULL || buf == NULL)
{
return YMODEM_PARAM_ERR;
}
retVal = YmodemRecvByte(&oneByte, timeout);
if(retVal != YMODEM_OK)
{
return retVal;
}
*frameType = oneByte;
switch(oneByte)
{
case FRAME_SOH:
{
*buf = FRAME_SOH;
for(i = 1; i < (SOH_DATA_SIZE + FRAME_HEADER_SIZE + FRAME_TAILOR_SIZE); i++)
{
retVal = YmodemRecvByte(&oneByte, timeout);
if(retVal != YMODEM_OK)
{
return retVal;
}
*(buf + i) = oneByte;
}
break;
}
case FRAME_STX:
{
*buf = FRAME_SOH;
for(i = 1; i < (STX_DATA_SIZE + FRAME_HEADER_SIZE + FRAME_TAILOR_SIZE); i++)
{
retVal = YmodemRecvByte(&oneByte, timeout);
if(retVal != YMODEM_OK)
{
return retVal;
}
*(buf + i) = oneByte;
}
break;
}
case FRAME_EOT:
{
break;
}
case FRAME_ABORT1:
case FRAME_ABORT2:
{
return YMODEM_ABORT;
}
}
return retVal;
}
/**
* Convert character to uint8
*/
static uint8_t Char2Uint(char hexChar)
{
uint8_t val = 0;
if(hexChar >= '0' && hexChar <= '9')
{
val = hexChar - '0';
}
return val;
}
/**
*
*/
static YmodemRetVal ParseFileDescription(uint8_t * content, uint8_t contentLen, char * fileName, uint32_t * fileSize)
{
uint8_t strFileSize[FILE_SIZE_STR_LENGTH] = {0};
uint8_t *pTemp;
uint32_t num;
/* Check Parameters */
if(content == NULL || fileName == NULL || fileSize == NULL)
return YMODEM_PARAM_ERR;
/* Init Parameteres if needed */
*fileSize = 0;
/* Init local varibles */
pTemp = strFileSize;
num = 0;
/* Get file name */
while(1)
{
*fileName = *content;
if(*fileName == '\0')
break;
fileName++;
content++;
}
content++; /* skip '\0'*/
/* Get file size */
while(1)
{
*pTemp = *content;
if(*pTemp == ' ')
break;
num *= 10;
num += Char2Uint(*pTemp);
pTemp++;
content++;
}
*fileSize = num;
return YMODEM_OK;
}
YmodemRetVal YmodemReceive(uint32_t addrSaveData, uint32_t * savedSize)
{
ReceiveState recvState;
YmodemRetVal recvRetVal;
FrameType frameType;
uint8_t *dataContent; /* pointer to frame content */
uint32_t dataContentLen; /* frame content length */
char fileName[FILE_NAME_LENGTH];
uint32_t fileSize;
uint32_t recveivedFileSize;
uint8_t bRecvFinish;
recvState = WAIT_FILE_DESC;
bRecvFinish = 0;
while(!bRecvFinish)
{
recvRetVal = YmodemReceiveFrame(&frameType, gFrameBuf, RECEIVE_TIMEOUT);
if(recvRetVal == YMODEM_ABORT)
{
return YMODEM_ABORT;
}
switch(recvState)
{
case WAIT_FILE_DESC:
{
if(recvRetVal == YMODEM_TIMEOUT)
{
YmodemSendByte(FRAME_C);
}
else if(recvRetVal == YMODEM_OK)
{
if(frameType == FRAME_SOH)
{
YmodemRetVal parseRetVal;
dataContent = gFrameBuf + FRAME_HEADER_SIZE;
dataContentLen = SOH_DATA_SIZE;
parseRetVal = ParseFileDescription(dataContent, dataContentLen, fileName, &fileSize);
if(parseRetVal == YMODEM_OK)
{
YmodemSendByte(FRAME_ACK);
YmodemSendByte(FRAME_C);
YmodemAllocReceiveAddr(addrSaveData, fileSize);
recvState = WAIT_DATA;
recveivedFileSize = 0;
}
}
}
break;
}
case WAIT_DATA:
{
if(recvRetVal == YMODEM_TIMEOUT)
{
YmodemSendByte(FRAME_NAK);
}
else if(recvRetVal == YMODEM_OK)
{
uint8_t saveData = 0; /* Save data if the frame type is correct */
uint32_t remainDataSize = 0;
remainDataSize = fileSize - recveivedFileSize;
if(frameType == FRAME_SOH)
{
dataContentLen = remainDataSize > SOH_DATA_SIZE ? SOH_DATA_SIZE : remainDataSize;
saveData = 1;
}
else if(frameType == FRAME_STX)
{
dataContentLen = remainDataSize > STX_DATA_SIZE ? STX_DATA_SIZE : remainDataSize;
saveData = 1;
}
if(saveData)
{
YmodemRetVal saveRetVal;
dataContent = gFrameBuf + FRAME_HEADER_SIZE;
saveRetVal = YmodemSaveData(addrSaveData, dataContent, dataContentLen);
if(saveRetVal == YMODEM_OK)
{
YmodemSendByte(FRAME_ACK);
recveivedFileSize += dataContentLen;
addrSaveData += dataContentLen;
if(recveivedFileSize >= fileSize)
{
*savedSize = recveivedFileSize;
recvState = WAIT_EOT;
}
}
else
{
/*
* Send NAK ?
*/
}
}
}
break;
}
case WAIT_EOT:
{
if(recvRetVal == YMODEM_TIMEOUT)
{
YmodemSendByte(FRAME_NAK);
}
else if(recvRetVal == YMODEM_OK)
{
if(frameType == FRAME_EOT)
{
YmodemSendByte(FRAME_ACK);
YmodemSendByte(FRAME_C);
recvState = WAIT_END;
}
}
break;
}
case WAIT_END:
{
if(recvRetVal == YMODEM_TIMEOUT)
{
YmodemSendByte(FRAME_NAK);
}
else if(recvRetVal == YMODEM_OK)
{
if(frameType == FRAME_SOH)
{
YmodemSendByte(FRAME_ACK);
bRecvFinish = 1;
}
}
break;
}
default:
break;
}
}
return YMODEM_OK;
}
extern uint8_t puc_flag;
unsigned int update(char argc,char ** argv)
{
int32_t ret;
uint32_t size;
if(YmodemAlloc() == NULL)
{
printf("alloc ng \r\n");
return 1;
}
printf("Erase SPI falsh need about 1 min \r\n");
/* step 1: erase spi flash*/
ymode_flash_start();
printf("Erase flash ok select image file by ymodem.\r\n");
/* step 2: disable printf */
//logctrl_instance()->enable = 0;
puc_flag = 0;
/* step 3: start ymodem reciver */
ret = YmodemReceive(YMODE_FLASH_BASE_START,&size);
ymode_flash_end();
/* step 4: enable printf */
//logctrl_instance()->enable = 1;
YmodemFree();
puc_flag = 1;
printf("update finish ret %d size %d \r\n",ret,size);
/* reset the system */
HAL_NVIC_SystemReset();
return 0;
}
LTSH_FUNCTION_EXPORT(update,"update spi flash image");
flash更新配置代码
#include <stdint.h>
#include <string.h>
#include <stm32h5xx_hal.h>
#define YMODE_FLASH_PG_SIZE 16U
static uint8_t pg_array[YMODE_FLASH_PG_SIZE] = {0xff};
static uint8_t index = 0;
static uint32_t last_addr;
static uint32_t ymode_flash_write_byte(uint32_t addr,uint8_t data)
{
pg_array[index++] = data;
if(index == YMODE_FLASH_PG_SIZE)
{
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, addr - YMODE_FLASH_PG_SIZE + 1, ((uint32_t)pg_array));
/* reset index & cache buffer */
index = 0;
memset((void *)pg_array,0xff,YMODE_FLASH_PG_SIZE);
last_addr = addr - YMODE_FLASH_PG_SIZE;
}
return data;
}
uint32_t ymode_flash_write(uint32_t addr, uint8_t *data, uint32_t datalen)
{
for(int i = 0;i < datalen;i++)
{
ymode_flash_write_byte(addr,data[i]);
addr++;
}
return 0;
}
uint32_t ymode_flash_start(void)
{
FLASH_EraseInitTypeDef EraseInitStruct;
uint32_t sectorerr;
HAL_ICACHE_Disable();
/* Unlock the Flash to enable the flash control register access *************/
HAL_FLASH_Unlock();
if (READ_BIT(FLASH->OPTSR_CUR, FLASH_OPTSR_SWAP_BANK) == 0)
{
EraseInitStruct.Banks = FLASH_BANK_2;
}
else
{
EraseInitStruct.Banks = FLASH_BANK_1;
}
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
EraseInitStruct.Sector = 0;
EraseInitStruct.NbSectors = 32;
return HAL_FLASHEx_Erase(&EraseInitStruct, §orerr);
}
uint32_t ymode_flash_end(void)
{
if(index != 0)
{
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, last_addr + YMODE_FLASH_PG_SIZE, ((uint32_t)pg_array));
}
HAL_FLASH_OB_Unlock();
uint32_t bank_swap = READ_REG(FLASH->OPTSR_PRG);
if(READ_BIT(FLASH->OPTSR_CUR, FLASH_OPTSR_SWAP_BANK) == 0)
{
bank_swap |= FLASH_OPTSR_SWAP_BANK_Msk;
}
else
{
bank_swap &= ~FLASH_OPTSR_SWAP_BANK_Msk;
}
WRITE_REG(FLASH->OPTSR_PRG,bank_swap);
MODIFY_REG(FLASH->OPTCR,FLASH_OPTCR_OPTSTART_Msk,FLASH_OPTCR_OPTSTART_Msk);
/* Lock the Flash to disable the flash control register access (recommended
to protect the FLASH memory against possible unwanted operation) *********/
HAL_FLASH_Lock();
HAL_FLASH_OB_Lock();
HAL_ICACHE_Enable();
return 0;
}
验证
添加update 命令通过串口触发升级请求。
unsigned int update(char argc,char ** argv)
{
int32_t ret;
uint32_t size;
if(YmodemAlloc() == NULL)
{
printf("alloc ng \r\n");
return 1;
}
printf("Erase SPI falsh need about 1 min \r\n");
/* step 1: erase spi flash*/
ymode_flash_start();
printf("Erase flash ok select image file by ymodem.\r\n");
/* step 2: disable printf */
//logctrl_instance()->enable = 0;
puc_flag = 0;
/* step 3: start ymodem reciver */
ret = YmodemReceive(YMODE_FLASH_BASE_START,&size);
ymode_flash_end();
/* step 4: enable printf */
//logctrl_instance()->enable = 1;
YmodemFree();
puc_flag = 1;
printf("update finish ret %d size %d \r\n",ret,size);
/* reset the system */
HAL_NVIC_SystemReset();
return 0;
}
LTSH_FUNCTION_EXPORT(update,"update spi flash image");
本地生成编译升级程序升级后的程序在入口打印输出printf("ota test version old.\r\n");
从以下验证打印可以看出升级后已经输出对应log 说明flash 的bank_swap 升级功能验证OK.
升级完成后debug 产看swap_bank 状态位也已经按照预期的更新了。
感谢分享