[STM32G0] STM32G030K6T6实现升级功能

[复制链接]
88|0
Haizangwang 发表于 2025-11-5 16:24 | 显示全部楼层 |阅读模式
前言
stm32g030k6t6开发板,32K,分为bootloader区+APP区。
bootloader分区,0x8000000 到 0x8001FFF,共4页,8K。其中升级标志位占其中单独1页,2K,0x8001800 到 0x8001FFF。
APP分区,0x8002000 到 0x8007FFF,占12页,24K。

一、bootloader分区功能
读取升级标志位,若为1则跳转到APP区。
升级标志位不为1,则每2s发送一次 C。
uart传输与解析。
传输完成跳转至APP。
二、步骤
1.内存写入与擦除
读、写升级标志位,擦除写入APP。

#define        FLASH_SYS_START_ADDR                0x8000000        /* stm32 flash起始地址 */
       
#define        FLASH_UPGRADE_START_ADDR        0x8001800        /* 升级标志位起始地址 */
#define        FLASH_UPGRADE_END_ADDR                0x8001FFF        /* 升级标志位结束地址 */       
       
#define        FLASH_APP_START_ADDR                0x8002000        /* APP起始地址 */
#define        FLASH_APP_END_ADDR                        0x8007FFF        /* APP结束地址 */

#define SYS_FLASH_PAGE_SIZE                 2048                /*flash 一页大小 */
#define SYS_FLASH_SIZE                                 32                          /* 所选STM32的FLASH容量大小(单位为K) */
#define SYS_SECTOR_SIZE                            2048            /* 2K字节 */
               



uint32_t app_addr = FLASH_APP_START_ADDR;

/*
*  fun     : 获取地址Addr在Flash中是第几页。根据每页大小2k
*  param   : Addr:开头地址
*  return  : FLASH页数
*  note    :
*/
static uint32_t GetPage(uint32_t Addr)
{
    uint32_t page = 0;
    page = (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;
    return page;
}

/*
*  fun     : 读地址
*  param   : R_Addr:开头地址
*  return  :
*  note    :
*/
static uint32_t ST_Flash_Read(uint32_t R_Addr)
{

     //直接读地址,可以读取其他类型。

    return *(volatile uint32_t *)R_Addr;

}


/*
*  fun     : 擦除FLASH
*  param   : s_addr:开头地址,end_addr:结束地址
*  return  :
*  note    :
*/
static void Erase_Flash(uint32_t s_addr, uint32_t end_addr)
{
    uint32_t SectorError = 0, FirstPage, NbOfPages;
    FLASH_EraseInitTypeDef E_FLASH;

    /* Get the 1st page to erase */
    FirstPage = GetPage(s_addr);

    /* Get the number of pages to erase from 1st page */
    NbOfPages = GetPage(end_addr) - FirstPage + 1;

    E_FLASH.TypeErase = FLASH_TYPEERASE_PAGES;
    E_FLASH.Page = FirstPage;
    E_FLASH.NbPages = NbOfPages;
    if (HAL_FLASHEx_Erase(&E_FLASH, &SectorError) != HAL_OK)
        {
    }
}

/*
*  fun     : 写Flash,写之前需要擦除整页flash,修改宏定义
*  param   : start_address:开头地址,end_address:结束地址,*buf:写入数据,length:写入字节数据个数
*  return  :
*  note    :
*/
void Write_Flash(uint32_t start_address,uint32_t end_address,uint64_t  *buf, uint16_t length)
{
    uint64_t data = 0;
    uint16_t i;
    if (start_address < FLASH_SYS_START_ADDR || (start_address >= (FLASH_SYS_START_ADDR + SYS_SECTOR_SIZE * SYS_FLASH_SIZE)))   
    {
         //长度不在范围
       return;
    }

    HAL_FLASH_Unlock();//解锁flash         
    Erase_Flash(start_address , end_address);   //擦除Flash

        for (i = 0; i < length; i++)
        {
        data = 0;
        data = buf;
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, start_address,data) != HAL_OK)
        {
            HAL_FLASH_Lock();
            return;
        }            
        start_address += 8;
        }  

    HAL_FLASH_Lock();//上锁
}

void FLASH_WriteAPP(uint8_t *data,uint16_t len)
{
        if (len == 128 || len == 1024)
        {
                HAL_FLASH_Unlock();//解锁flash       
               
                for (uint8_t i = 0; i < len/8; i++)
                {
                        uint64_t value[1] = {0};
                        value[0] = ((uint64_t)data[0+i*8]) + ((uint64_t)data[1+i*8]<<(1*8)) + ((uint64_t)data[2+i*8]<<(2*8)) + ((uint64_t)data[3+i*8]<<(3*8)) + \
                                                ((uint64_t)data[4+i*8]<<(4*8)) + ((uint64_t)data[5+i*8]<<(5*8)) + ((uint64_t)data[6+i*8]<<(6*8)) + ((uint64_t)data[7+i*8]<<(7*8));
                       
                        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, app_addr, value[0]) != HAL_OK)
                        {
                                HAL_FLASH_Lock();
                                return;
                        }         
                       
                        app_addr += 8;
                }

                HAL_FLASH_Lock();//上锁
        }
}

void Flash_EraseAPP(void)
{
    HAL_FLASH_Unlock();//解锁flash         
        Erase_Flash(FLASH_APP_START_ADDR , FLASH_APP_END_ADDR);
    HAL_FLASH_Lock();//上锁
       
        app_addr = FLASH_APP_START_ADDR;                //写入地址
}

/*
*  fun     : 读升级标志位
*  param   :
*  return  :
*  note    :
*/
ErrorStatus sys_flash_read_flag(uint32_t *Ram_Addr)
{
    *Ram_Addr = ST_Flash_Read(FLASH_UPGRADE_START_ADDR);
    return SUCCESS;
}

/*
*  fun     :
*  param   :
*  return  :
*  note    : 向指定bootloader分区地址写入升级标志位,写入1
*/
void sys_flash_write_sign(void)
{
        uint64_t upgrade_sign[1] = {0};
        upgrade_sign[0] = 1;
    Write_Flash(FLASH_UPGRADE_START_ADDR,FLASH_UPGRADE_END_ADDR,upgrade_sign,1);
}

/*
*  fun     : 清空升级标志位
*  param   :
*  return  :
*  note    : 向指定bootloader分区地址写入升级标志位,写入0
*/
void flash_clear_sign(void)
{
        uint64_t upgrade_sign[1] = {0};
        upgrade_sign[0] = 0;
    Write_Flash(FLASH_UPGRADE_START_ADDR,FLASH_UPGRADE_END_ADDR,upgrade_sign,1);
}






2.Ymodem协议,uart传输与解析
#define USART2_TX_BUF_SIZE                 1024
#define USART2_RX_BUF_SIZE                 1024

typedef struct {
    uint8_t   rx_buff[USART2_RX_BUF_SIZE];  // 接收缓冲区(存储实际数据)
    uint16_t  rx_len;                       // 当前接收数据长度
    uint8_t   rx_sta;                       // 接收状态标志(如:0=空闲,1=接收中,2=接收完成等)
} uart_param_t;
       
#define SOH                     (0x01)                          /* 128字节数据帧 */
#define STX                     (0x02)                          /* 1024字节 数据帧 */
#define EOT                     (0x04)                          /* 文件传输结束 */
#define ACK                     (0x06)                          /* 接收正确应答 */
#define NAK                     (0x15)                          /* 重发当前帧 */
#define CA                      (0x18)                          /* 取消传输命令 连续发送五次取消 */
#define CRC16                   (0x43)                          /* 握手信号 “C” */
#define HEAD                                        (0x00)                                /* 帧头 */
#define DATA                                        (0x01)                                 /* 数据帧 */
#define END                                                (0x02)                                /* 结束帧 */
#define ENDDATA                                        (0x03)                                /* 结束帧 */
#define NAK_MAX                             5                                        /* 重传次数上限超过该次数终止传输 */
#define        OTHER                                        (0x0f)
       
extern uint8_t filerxflag, jumpflag, endflag;       




uint8_t filerxflag = 0, jumpflag = 0, endflag = 0;
uart_param_t uart_param = {0};

uint8_t nakcount;
uint8_t *filename;
uint8_t *filesize;

#define CRC_CCITT 0x1021


/*函数名称:crc_cal_by_bit;按位计算CRC
  函数参数:uint8_t * ptr;指向发送缓冲区的首字节
            uint32_t  len;要发送的总字节数
  函数返回值:uint16_t
  多项式采用CRC-CCITT 0x1021
*/
static uint16_t crc_cal(uint8_t *ptr, uint32_t len)
{
    uint32_t crc = 0x0000;
    while(len-- != 0)
    {   
        for (uint8_t i = 0x80; i != 0; i /=2)
        {   
            crc *= 2;
            if((crc&0x10000) !=0)  //上一位CRC乘 2后,若首位是1,则除以 0x11021
                crc ^= 0x11021;

            if((*ptr&i) != 0)    //如果本位是1,那么CRC = 上一位的CRC + 本位/CRC_CCITT
                crc ^= CRC_CCITT;
        }
        ptr++;
    }
    uint16_t retCrc = (uint16_t)(crc & 0xffff);
       
    return retCrc ;
}

static void StopTransfer()
{
        uint8_t com[1] = {0};
        com[0] = CA;
        sys_uart_send(com, 1);
        LL_mDelay(10);
        sys_uart_send(com, 1);
        LL_mDelay(10);
        sys_uart_send(com, 1);
        LL_mDelay(10);
        sys_uart_send(com, 1);
        LL_mDelay(10);
        sys_uart_send(com, 1);
}

static uint16_t YmodemReceiveData(uint8_t *data, uint16_t length)
{
        uint8_t com[1] = {0};
        uint16_t datalen, crc16;
       
        if (*data == EOT)
        {
                return END;
        }
        if ((*(data+1)) == (~(*(data+2))&0xff))
        {
               
                if (*data == SOH)
                {
                        datalen = 128;
                }
                if (*data == STX)
                {
                        datalen = 1024;
                }
                crc16 = crc_cal(data+3, datalen);
                if (crc16 == (*(data+datalen+3)<<8|*(data+datalen+4)))
                {
                        if (*(data+1) == 0)
                        {
                                if (*(data+3) != 0)
                                {
                                        uint8_t *p, len = 0;
                                        p = data + 3;
                                        while(*p)
                                        {
                                                len++;
                                                p++;
                                        }
                                        memcpy(filename, data+3, len);
                                        p++;
                                        len = 0;
                                        while(*p)
                                        {
                                                len++;
                                                p++;
                                        }
                                        memcpy(filesize, p-len, len);
                                        return HEAD;
                                }
                                else
                                {
                                        return ENDDATA;
                                }
                        }
                        else
                        {
                                nakcount = 0;
                                com[0] = ACK;
                                sys_uart_send(com, 1);
                                FLASH_WriteAPP(data+3, datalen);
                                return DATA;
                        }
                }
                else
                {
                        com[0] = NAK;
                        sys_uart_send(com, 1);
                        nakcount++;
                }
        }
        else
        {
                com[0] = NAK;
                sys_uart_send(com, 1);
                nakcount++;
        }
        if (nakcount > NAK_MAX)
                StopTransfer();
       
        return OTHER;
}

//串口接收函数处理
static void receive_data_handle(uint8_t *data, uint16_t len)
{
        uint8_t com[1] = {0};
       
        switch(YmodemReceiveData(data, len))
        {
                case DATA:
                       
                break;
                case HEAD:
                        LL_mDelay(10);
                        filerxflag = 1;
                        Flash_EraseAPP();
                        com[0] = ACK;
                        sys_uart_send(com, 1);
                        com[0] = CRC16;
                        sys_uart_send(com, 1);
                break;
                case ENDDATA:
                        LL_mDelay(10);
                        com[0] = ACK;
                        sys_uart_send(com, 1);
                        LL_mDelay(10);
                        filerxflag = 0;
                        jumpflag = 1;
                break;
                case END:
                        if (endflag == 0)
                        {
                                endflag++;
                                com[0] = NAK;
                                sys_uart_send(com, 1);
                        }
                        else
                        {
                                endflag = 0;
                                com[0] = ACK;
                                sys_uart_send(com, 1);
                                LL_mDelay(10);
                                com[0] = CRC16;
                                sys_uart_send(com, 1);
                        }
                break;
               
                default:
                        break;
        }
       
}

//串口接收标志位判断
void sys_uart_receive_handle(void)
{
        if (uart_param.rx_sta)
        {
                if(uart_param.rx_len>0)
                {
                        receive_data_handle(uart_param.rx_buff, uart_param.rx_len);
                }
                uart_param.rx_sta=0;
                memset(uart_param.rx_buff, 0, uart_param.rx_len);
        }
}





3.主循环
添加
#include “stm32g0xx_hal.h”
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_FLASH);// AHB1总线组,FLASH外设

int main(void)
{
        HAL_Init();
        system_init();
        sys_uart_init();
       
        while(1)
        {
                //1.读升级标志位
                sys_flash_read_flag(updateflag);
                if (updateflag[0] == 1)
                {
                        printf("updateflag[0]=%d\n,jump to app.",updateflag[0]);
                        JumpToApp();
                }
               
                //2.发C
        if (filerxflag == 0)
                {
                        com[0] = CRC16;
                        LL_mDelay(2000);
                        sys_uart_send(com, 1);
                }
               
                //3.传输解析
                sys_uart_receive_handle();               
               
                //4.传输完成跳转
                if (jumpflag)
                {
                        jumpflag = 0;
                        updateflag[0] = 0;
                       
                        sys_flash_write_sign();
                        printf("jump to app.\r\n");
                        JumpToApp();
                }
                LL_mDelay(1);
        }
}


4.修改配置
点击魔术棒,Target,修改bootloader ROM对应Size 0x2000
点击Debug,选择右上角Settings,点击Flash Download,选择Erase Full Chip,全擦除,修改对应Address Range.

85846690b09897b49d.png

38539690b097b185c5.png


5.APP区
打开system_stm32g0xx.c,添加#define USER_VECT_TAB_ADDRESS,修改为APP起始地址#define VECT_TAB_OFFSET 0x00002000U
main函数里先__enable_irq();
点击魔术棒,修改APP对应起始地址和size。
Flash Download选择Erase Sectors.
添加#include “stm32g0xx_hal_flash.h”
#include “stm32g0xx_hal.h”
添加对应HAL文件
APP实现,收到升级命令后,向升级标志位起始地址写入0.

/*
*  fun     :
*  param   :
*  return  :
*  note    : 向指定bootloader分区地址写入升级标志位,0x8007000(待修改),写入0
*/
int32_t sysparam_flash_set_upgrade(void)
{
        uint64_t upgrade_sign[1] = {0};
        upgrade_sign[0] = 0;
    Write_Flash(FLASH_SIGN_START_ADDR,FLASH_SIGN_END_ADDR,upgrade_sign,1);//写8字节
       
    return 0;
}

234690b096ecec16.png

48022690b0968afbda.png

1109690b09648220f.png

54618690b095eab1ce.png

总结
Bootloader区与APP区不能覆盖。
————————————————
版权声明:本文为CSDN博主「转动地球」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/fighhti/article/details/154190954

您需要登录后才可以回帖 登录 | 注册

本版积分规则

95

主题

300

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部