STM32F4 BOOT引导APP升级

一、APP制作

1、APP的制作可以基于STM32CubeMX,具体可以参考网上的教程,只需配置基本的时钟树、SWD、调试串口功能即可(需要其他功能的可自行配置)。需要将工程编译成bin文件,这里可以准备多份bin文件(比如串口打印不同的信息),以便后续的BOOT跳转成功与否的观察。

2、修改link.sct文件以及VECT_TAB_OFFSET(这里是为了偏移APP起始位置)

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08010000 0x00080000  {    ; load region size_region
  ER_IROM1 0x08010000 0x00080000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x20000000 0x00020000  {  ; RW data
   .ANY (+RW +ZI)
  }
}
/*!< Uncomment the following line if you need to relocate your vector Table in
     Internal SRAM. */
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET  0x10000 /*!< Vector Table base offset field. 
                                   This value must be a multiple of 0x200. */
/******************************************************************************/

生成APP工程后,link.sct以及VECT_TAB_OFFSET的偏移都是基于片上flash基地址的,也就是0x8000000,直观的可以在keil魔术棒里看到。

由于我们需要把BOOT这一段小程序加载到0x8000000这段地址,所以就需要适当的更改APP的偏移地址(基于BOOT bin文件的大小),这里我们可以偏移0x10000(如前两张图)。红色框内的原始值可以不做更改,但为了不误导,尽量可以更改为0x8010000(APP在flash的起始加载位置)。到这里APP的制作就完成了。

二、BOOT制作

1、工程制作同APP。

2、需要移植一下drv_flash_f4.c里的一些flash操作函数(关于drv_flash_f4.c可以网上自行搜索怎样生成)。这里我们需要把该文件里的所有宏,GetSector、stm32_flash_read、stm32_flash_write、stm32_flash_erase这三个函数移植好。

/* Base address of the Flash sectors Bank 1 */
#define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) /* Base @ of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) /* Base @ of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) /* Base @ of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) /* Base @ of Sector 11, 128 Kbytes */

/* Base address of the Flash sectors Bank 2 */
#define ADDR_FLASH_SECTOR_12     ((uint32_t)0x08100000) /* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_13     ((uint32_t)0x08104000) /* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_14     ((uint32_t)0x08108000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_15     ((uint32_t)0x0810C000) /* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_16     ((uint32_t)0x08110000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_17     ((uint32_t)0x08120000) /* Base @ of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_18     ((uint32_t)0x08140000) /* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_19     ((uint32_t)0x08160000) /* Base @ of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_20     ((uint32_t)0x08180000) /* Base @ of Sector 8, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_21     ((uint32_t)0x081A0000) /* Base @ of Sector 9, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_22     ((uint32_t)0x081C0000) /* Base @ of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_23     ((uint32_t)0x081E0000) /* Base @ of Sector 11, 128 Kbytes */

/**
  * @brief  Gets the sector of a given address
  * @param  None
  * @retval The sector of a given address
  */
static uint8_t GetSector(uint32_t Address)
{
    uint32_t sector = 0;

    if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0))
    {
        sector = FLASH_SECTOR_0;
    }
    else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1))
    {
        sector = FLASH_SECTOR_1;
    }
    else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2))
    {
        sector = FLASH_SECTOR_2;
    }
    else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3))
    {
        sector = FLASH_SECTOR_3;
    }
    else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4))
    {
        sector = FLASH_SECTOR_4;
    }
    else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5))
    {
        sector = FLASH_SECTOR_5;
    }
    else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6))
    {
        sector = FLASH_SECTOR_6;
    }
    else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7))
    {
        sector = FLASH_SECTOR_7;
    }
#if defined(FLASH_SECTOR_8)
    else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8))
    {
        sector = FLASH_SECTOR_8;
    }
#endif
#if defined(FLASH_SECTOR_9)
    else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9))
    {
        sector = FLASH_SECTOR_9;
    }
#endif
#if defined(FLASH_SECTOR_10)
    else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10))
    {
        sector = FLASH_SECTOR_10;
    }
#endif
#if defined(FLASH_SECTOR_11)
    else if((Address < ADDR_FLASH_SECTOR_12) && (Address >= ADDR_FLASH_SECTOR_11))
    {
        sector = FLASH_SECTOR_11;
    }
#endif
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx)|| defined(STM32F439xx) || defined(STM32F469xx) || defined(STM32F479xx)
    else if((Address < ADDR_FLASH_SECTOR_13) && (Address >= ADDR_FLASH_SECTOR_12))
    {
        sector = FLASH_SECTOR_12;
    }
    else if((Address < ADDR_FLASH_SECTOR_14) && (Address >= ADDR_FLASH_SECTOR_13))
    {
        sector = FLASH_SECTOR_13;
    }
    else if((Address < ADDR_FLASH_SECTOR_15) && (Address >= ADDR_FLASH_SECTOR_14))
    {
        sector = FLASH_SECTOR_14;
    }
    else if((Address < ADDR_FLASH_SECTOR_16) && (Address >= ADDR_FLASH_SECTOR_15))
    {
        sector = FLASH_SECTOR_15;
    }
    else if((Address < ADDR_FLASH_SECTOR_17) && (Address >= ADDR_FLASH_SECTOR_16))
    {
        sector = FLASH_SECTOR_16;
    }
    else if((Address < ADDR_FLASH_SECTOR_18) && (Address >= ADDR_FLASH_SECTOR_17))
    {
        sector = FLASH_SECTOR_17;
    }
    else if((Address < ADDR_FLASH_SECTOR_19) && (Address >= ADDR_FLASH_SECTOR_18))
    {
        sector = FLASH_SECTOR_18;
    }
    else if((Address < ADDR_FLASH_SECTOR_20) && (Address >= ADDR_FLASH_SECTOR_19))
    {
        sector = FLASH_SECTOR_19;
    }
    else if((Address < ADDR_FLASH_SECTOR_21) && (Address >= ADDR_FLASH_SECTOR_20))
    {
        sector = FLASH_SECTOR_20;
    }
    else if((Address < ADDR_FLASH_SECTOR_22) && (Address >= ADDR_FLASH_SECTOR_21))
    {
        sector = FLASH_SECTOR_21;
    }
    else if((Address < ADDR_FLASH_SECTOR_23) && (Address >= ADDR_FLASH_SECTOR_22))
    {
        sector = FLASH_SECTOR_22;
    }
    else /* (Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_23) */
    {
        sector = FLASH_SECTOR_23;
    }
#endif
    return sector;
}

/**
 * Read data from flash.
 * @note This operation's units is word.
 *
 * @param addr flash address
 * @param buf buffer to store read data
 * @param size read bytes size
 *
 * @return result
 */
int stm32_flash_read(uint32_t addr, uint8_t *buf, size_t size)
{
    size_t i;
    
    printf("flash size addr is (0x%p)", (void*)(addr + size));
    if ((addr + size) > STM32_FLASH_END_ADDRESS)
    {
        printf("read outrange flash size! addr is (0x%p)", (void*)(addr + size));
        return -1;
    }
    
    for (i = 0; i < size; i++, buf++, addr++)
    {
        *buf = *(uint8_t *) addr;
    }

    return size;
}

/**
 * Write data to flash.
 * @note This operation's units is word.
 * @note This operation must after erase. @see flash_erase.
 *
 * @param addr flash address
 * @param buf the write data buffer
 * @param size write bytes size
 *
 * @return result
 */
int stm32_flash_write(uint32_t addr, const uint8_t *buf, size_t size)
{
    int8_t result     = 0;
    uint32_t end_addr = addr + size;
    
    if ((end_addr) > STM32_FLASH_END_ADDRESS)
    {
        printf("write outrange flash size! addr is (0x%p)", (void*)(addr + size));
        return -1;
    }

    if (size < 1)
    {
        return -1;
    }

    HAL_FLASH_Unlock();

    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);

    for (size_t i = 0; i < size; i++, addr++, buf++)
    {
        //printf("1[%02x] [%02x] [%d]\n", *(uint8_t *)addr, *buf, i);
        /* write data to flash */
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, addr, (uint64_t)(*buf)) == HAL_OK)
        {
            if (*(uint8_t *)addr != *buf)
            {
                result = -1;
                //printf("2[%02x] [%02x] [%d]\n", *(uint8_t *)addr, *buf, i);
                break;
            }
        }
        else
        {
            result = -1;
            //printf("3[%02x] [%02x] [%d]\n", *(uint8_t *)addr, *buf, i);
            break;
        }
    }

    HAL_FLASH_Lock();

    if (result != 0)
    {
        return result;
    }

    return size;
}

/**
 * Erase data on flash.
 * @note This operation is irreversible.
 * @note This operation's units is different which on many chips.
 *
 * @param addr flash address
 * @param size erase bytes size
 *
 * @return result
 */
int stm32_flash_erase(uint32_t addr, size_t size)
{
    int8_t result = 0;
    uint32_t FirstSector = 0, NbOfSectors = 0;
    uint32_t SECTORError = 0;

    if ((addr + size) > STM32_FLASH_END_ADDRESS)
    {
        printf("ERROR: erase outrange flash size! addr is (0x%p)\n", (void*)(addr + size));
        return -1;
    }

    /*Variable used for Erase procedure*/
    FLASH_EraseInitTypeDef EraseInitStruct;

    /* Unlock the Flash to enable the flash control register access */
    HAL_FLASH_Unlock();

    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);

    /* Get the 1st sector to erase */
    FirstSector = GetSector(addr);
    /* Get the number of sector to erase from 1st sector*/
    NbOfSectors = GetSector(addr + size - 1) - FirstSector + 1;
    /* Fill EraseInit structure*/
    EraseInitStruct.TypeErase     = FLASH_TYPEERASE_SECTORS;
    EraseInitStruct.VoltageRange  = FLASH_VOLTAGE_RANGE_3;
    EraseInitStruct.Sector        = FirstSector;
    EraseInitStruct.NbSectors     = NbOfSectors;

    if (HAL_FLASHEx_Erase(&EraseInitStruct, (uint32_t *)&SECTORError) != HAL_OK)
    {
        result = -1;
        goto __exit;
    }

__exit:
    HAL_FLASH_Lock();

    if (result != 0)
    {
        return result;
    }

    printf("erase done: addr (0x%p), size %d", (void*)addr, size);
    return size;
}

3、编写跳转APP函数。网上例程较多,可自行参考。

typedef void (*pFunction)(void);

uint8_t jump_to_app(uint32_t app_addr)
{
    uint32_t JumpAddress;
    pFunction Jump_To_Application;
  
    /* 检查栈顶地址是否合法 */
    if(((*(__IO uint32_t *)APP_FLASH_ADDR) & 0x2FFE0000) == 0x20000000)
    {
        /* 屏蔽所有中断,防止在跳转过程中,中断干扰出现异常 */
        __disable_irq();

        /* 用户代码区第二个 字 为程序开始地址(复位地址) */
        JumpAddress = *(__IO uint32_t *) (app_addr + 4);

        /* Initialize user application's Stack Pointer */
        /* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */
        __set_MSP(*(__IO uint32_t *) app_addr);
        
        /* 类型转换 */
        Jump_To_Application = (pFunction) JumpAddress;
        
        /* 跳转到 APP */
        Jump_To_Application();
        
        return 1;
    }
    return 0;
}

三、BOOT引导APP跳转

1、不借助上位机的文件发送功能,直接使用flash进行APP程序的擦写(这里需要JFlash烧写工具,网上自行下载即可,关于JFlash的使用也很简单,只需简单配置芯片型号,然后便可以进行烧写程序)。

1.1、将其中一份APP1 bin文件烧写至0x8040000(这里的地址根据APP bin文件自行判断大小,比如你的bin文件大小为64k,即0x10000,那么至少要将该bin文件烧写至0x8020000(0x8010000+0x10000)),然后可以将APP2 bin文件烧写至0x8060000的地址处。

1.2、编写一个简易的BOOT程序

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    
    printf("\r\n======================================\r\n");
    stm32_flash_erase(APP_FLASH_ADDR, 0x30000); //APP_FLASH_ADDR = 0x8010000
    
    /* 每次从FLASH APP区读取1024字节数据,并写入BOOT区 */ //592
    int cnt = 0;
    int read_start_addr = 0x8040000;
    int write_start_addr = APP_FLASH_ADDR;
    while (cnt < 97)
    {
        read_flash_test(read_start_addr, my_buf, 1024);
        stm32_flash_write(write_start_addr, my_buf, 1024);
        
        read_start_addr += 1024;
        write_start_addr += 1024;
        
        printf("\r\n=================== [%d] ===================\r\n", cnt);
        
        cnt++;
    }
    
    read_flash_test(0x8078400, my_buf, 592);
    stm32_flash_write(0x8028400, my_buf, 592);
    
    jump_to_app(APP_FLASH_ADDR);

程序很简单,由于只是为了测试少了很多错误判断(各位客官自行加上),大体流程是板子上电后,从0x8000000加载并启动这段BOOT程序,程序按顺序先进行了一些初始化,然后擦除0x8010000处的flash数据(这也是我们要将APP加载并启动的地址),然后按照1K的数据大小进行flash数据搬运,具体搬运多少次看客官的APP bin文件大小了,搬运完成后调用jump_to_app进行跳转APP1。这里int read_start_addr = 0x8040000可以改成0x8060000,BOOT搬运并跳转APP2。

2、可以借助Xcom这个小软件作为上位机发送APP bin文件,此时BOOT需要实现数据的接收,这里采用串口中断接收:

uint8_t single_buff[1];                    // 按字节保存APP程序
uint8_t app_buff[128 * 1024] = {0};        // 保存接收到的APP程序
volatile uint32_t app_buff_len = 0;        // APP程序的长度(字节)

void start_uart_rx(void)
{
    while(HAL_UART_Receive_IT(&huart1, single_buff, 1) != HAL_OK);
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1)
    {
        // 把接收到的数据,放到缓冲区中
        app_buff[app_buff_len] = single_buff[0];
        
        app_buff_len++;
    }
    
    // 重新开启中断接收
    start_uart_rx();
}

main函数:

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    start_uart_rx();
    printf("\r\n======================================\r\n");
    HAL_Delay(15000);//延时需要足够长,此时上位机发送bin文件,中断接收
    
    stm32_flash_erase(APP_FLASH_ADDR, 0x30000);
    stm32_flash_write(APP_FLASH_ADDR, app_buff, 0x18650);
    jump_to_app(APP_FLASH_ADDR);

是不是很简单!当然这样难免出现数据丢包等问题,这里做个简易测试可不作考虑。BOOT程序启动后,通过Xcom发送APP bin文件即可。

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
stm32f4通过ymodem升级程序的过程如下: 首先,需要在PC机上安装一个支持ymodem协议的终端软件,例如Tera Term、SecureCRT等,用于与stm32f4进行通信。 接下来,stm32f4需要配置串口功能,以便与PC机进行通信。我们可以通过stm32cubemx软件进行简便的配置,设置串口的波特率、数据位、停止位和奇偶校验位等参数。然后生成相应的代码,并将其导入到stm32f4的工程项目中。 在stm32f4的工程项目中,首先需要自行实现ymodem协议相关的函数,例如发送和接收数据帧的函数。同时,需要事先准备好要升级的新程序固件文件。 接下来,在stm32f4的应用程序中,我们可以通过按下某个按键、发送指令或者其他方式触发升级程序的启动。启动后,stm32f4会进入升级模式,等待PC机发送升级指令。 在PC机上,打开终端软件,连接到stm32f4的串口。然后,在终端软件的相关设置中,选择ymodem协议,并选择要升级的新程序固件文件。之后,可以发送升级指令到stm32f4stm32f4接收到升级指令后,会开始通过串口接收PC机发送的新程序固件文件数据。在接收过程中,stm32f4会逐步解析并存储接收到的数据,直至接收完整个固件文件。 当接收完成后,stm32f4会对接收到的固件文件进行验证,确保文件完整性。如果验证通过,即可将固件文件写入到特定的存储器区域,以覆盖旧的程序。 最后,升级完成后,stm32f4会自动重启,并开始运行新的程序。 总结来说,stm32f4通过ymodem升级程序的过程涉及串口配置、协议函数实现升级指令发送和固件文件接收验证等步骤。通过这些步骤,我们可以实现方便、快速而可靠的程序升级

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值