你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【NUCLEO-H533RE评测】串口Ymodem协议升级程序

[复制链接]
andey 发布时间:2024-7-25 08:03

简介

产品开发中经常会有需要对产品进行程序固件升级的需求,通常为了避免升级程序对原有固件的损坏,会设计成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 地址空间

14610.jpg

芯片用户手册中有bank swap 功能使用的流程,对应流程图如下:

10414.jpg

bank_swap 的更新流程文档中有说明按照流程即可更新 ,主要步骤如下:

微信截图_20240725092311.png

通过OBK key register (FLASH_NSOBKKEYR) 寄存器连续写入key1(0x192A 083B),key2(0x6E7F 4C5D),可以解锁OPTLOCK寄存器从而更新SWAP_BANK寄存器,一旦错误的解锁序列将触发解锁失败知道下次syetemreset才可以继续解锁。

OBKKEYR.png

按照上面的更新流程,本地通过串口使用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.

20240724-234134.gif

升级完成后debug 产看swap_bank 状态位也已经按照预期的更新了。

微信截图_20240726141959.png

收藏 评论1 发布时间:2024-7-25 08:03

举报

1个回答
y369369 回答时间:2024-8-6 09:02:44

感谢分享

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版