当前位置: 代码迷 >> 综合 >> 【STM32CUBE+IAR+IAP升级】
  详细解决方案

【STM32CUBE+IAR+IAP升级】

热度:16   发布时间:2023-12-11 19:38:41.0

STM32CUBE+IAR+IAP升级

案例应用:利用IAR+串口调试助手对STM32F411VET6进行简单的IAP实现,(代码分为两部分:BootLoader+APP翻转LED)

1.工具:

  • IAR
  • STM32CUBEMX
  • 串口调试助手
  • IAP预备知识参考:STM32CubeIDE IAP原理讲解,及UART双APP迭代升级IAP实现
  • 声明下:本文章参考[sudaroot]的文章STM32CubeIDE IAP原理讲解,及UART双APP迭代升级IAP实现

2.工程配置

2.1.1简要说下,本案例IAP,使用uart进行轮询方式接收数据(bin文件),没有任何校验,因此在后续接受过程中可能会出现丢包(丢失bin文件数据)的问题,建议串口波特率设置小些(本案例为9600

FLASH划分:STM32F411vet6片内FLASH512K,RAM128K,本案例将分配32KByte给BootLoader,480KByte给应用程序(本案例为LED_APP,功能0.5s翻转电平一次),如下图所示
在这里插入图片描述
2.1.2 LED_APP

? ——>STM32CUBEMX中 RCC,SYS,GPIO(PD12),UART,时钟树配置如下,LED应用程序挺简单的
在这里插入图片描述
对printf()函数的重定义具体见:【STM32】STM32CUBEMX+UART串口调试,循环接受发送数据

——>main.c

#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"void SystemClock_Config(void);
#define APPLICATION_ADDRESS (uint32_t)0x08008000//LED_APP起始位置int main(void)
{
    __enable_irq();//开中断SCB->VTOR = APPLICATION_ADDRESS;//设置程序烧写起始位置HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART2_UART_Init();printf("LED_APP TEST, This is IAP Application\r\n");while (1){
    printf("This is IAP Application running\r\n");HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);HAL_Delay(500);}}

——>IAR设置程序起始位置,(对应用程序调试时,不用修改起始位置,调试无误后再修改向量表起始地址和其ROM,RAM,并生成bin文件)

? 1.在第2步中.cif文件要选选stm32f411xe_flash.icf,因为我们这里是将程序烧写进flash,如果是烧写到sarm,那就选sarm.icf

? 2.第3步中设置向量表起始地址这里是0x08008000(可以相应修改自定义的,根据你分配区域的大小设置)

? 3.第4步中Memory Regions中ROM: 0x08008000~0x0807FFFF(总共480KB),RAM:栈是在RAM上分配 的,大小0x2000 0000 ~ 0x2001 FFFF(对应F411ve 128K),一般起始地址为0x2000 0000 ,这里设置为0x2000 00c0,留给参数变量c0大小的区域,也可不用修改
在这里插入图片描述
——>IAR生成bin文件设置,设置完后保存,编译之后会在你的工程exe文件中生成BIN文件,注意bin文件的大小要在设置的范围内
在这里插入图片描述
2.1.3 BootLoader

? ——>STM32CUBEMX配置和LED_APP配置一样,UART串口波特率设置成9600,

? ——>同样需要对printf()函数进行重定义,具体见:【STM32】STM32CUBEMX+UART串口调试,循环接受发送数据

main.c

#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include <string.h>
#include "flash_if.h"typedef  void (*pFunction)(void);#define UART_BUF_SIZE 256uint32_t JumpAddress;
pFunction JumpToApplication;
uint8_t uart_buf[UART_BUF_SIZE] = {
    0};void SystemClock_Config(void);int main(void)
{
    uint8_t key = 0;uint32_t temp = 0;uint32_t timeout = 0;uint32_t userapplen = 0;HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_USART2_UART_Init();printf("\r\n This is Bootloader\r\n");while (1){
    printf("\r\n=================== Main Menu ============================\r\n\n");printf(" Download user application to the internal Flash ------ 1\r\n\n");printf(" Execute the loaded application ----------------------- 2\r\n\n");/* Clean the input path */__HAL_UART_FLUSH_DRREGISTER(&huart2);/* Receive key */HAL_UART_Receive(&huart2, &key, 1, HAL_MAX_DELAY);switch(key){
    case '1':/* Download user application in the Flash *//* 1. erase user application area */printf("Wait for the internal Flash erase to complete\r\n");if(FLASH_If_Erase(APPLICATION_ADDRESS, 6) == 1){
    printf("Erase the internal Flash is fail\r\n");Error_Handler();}printf("Erase the internal Flash is complete\r\n");/* 2. download a file via serial port */printf("Waiting for the file to be sent ... \r\n");userapplen = 0;timeout = HAL_MAX_DELAY;/* Clean the input path */__HAL_UART_FLUSH_DRREGISTER(&huart2);while(1){
    if(HAL_UART_Receive(&huart2, uart_buf, UART_BUF_SIZE, timeout) != HAL_OK){
    temp = UART_BUF_SIZE - (huart2.RxXferCount + 1);if(FLASH_If_Write(APPLICATION_ADDRESS + userapplen, (uint32_t*)uart_buf, temp / 4) != FLASHIF_OK){
    printf("Write the internal Flash is fail\r\n");Error_Handler();}userapplen = userapplen + temp;break;}timeout = 1000;//等待接收完成if(FLASH_If_Write(APPLICATION_ADDRESS + userapplen, (uint32_t*)uart_buf, UART_BUF_SIZE / 4))//4字节对齐{
    printf("Write the internal Flash is fail\r\n");Error_Handler();}userapplen = userapplen + UART_BUF_SIZE;}printf("Programming Completed Successfully! %ldBtye\r\n", userapplen);break;case '2':printf("Start program execution......\r\n\n");/** 关闭或反初始化前面用到的外设和中断* 1、反初始化UART* 2、关闭系统滴答定时器中断* */HAL_UART_DeInit(&huart2);// HAL_SuspendTick();// __disable_irq();/* execute the new program */JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);/* Jump to user application */JumpToApplication = (pFunction) JumpAddress;/* Initialize user application's Stack Pointer */__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);JumpToApplication();break;default:printf("Invalid Number ! ==> The number should be either 1 or 2\r");break;}}
}

编译调试无误后就可以开始了,生成bin文件注意大小,在32K之内,不需要修改起始地址。

1、上电后打印功能菜单。

2、发送‘1’就会进入IAP模式;首先会擦除APP空间,然后等待用串口助手发送LED_APP的bin文件写入flash,直至完成。

3、发送‘2’就会直接跳转到LED_APP运行,结果如下。
在这里插入图片描述
——>BootLoader程序简单说下(个人看法,如有不同见解,欢迎留言讨论)

  • FLASH_If_Erase(APPLICATION_ADDRESS, 6)函数,第一个参数擦除的sector的起始地址,第二个参数根据自己的flash中sector数进行相应设置(判断下有没有擦除成功)

  • FLASH_If_Write(APPLICATION_ADDRESS + userapplen, (uint32_t * )uart_buf, temp / 4)写flash函数,第一个参数起始地址,第二个要写的数据的缓存地址,第三个要写的数据长度。在该函数之前uart_buf类型为uint8_t,(1个字节)用于接收BIN文件数据,所以轮询方式的HAL_UART_Receive()接收了几十次,FLASH_If_Write()也写了几十次,不过FLASH_If_Write()是以4字节对齐的方式写入,对应于(uint32_t * )uart_buf,

  • case ‘1’:中while()循环:当通过串口助手发送bin文件之后,执行HAL_UART_Receive()后(有可能满buffer有可能没有)—>执行1次FLASH_If_Write(),写完之后更新下次要写的flash起始位置—>HAL_UART_Receive()—>执行1次FLASH_If_Write()…,最后一次进入if(HAL_UART_Receive()!= HAL_OK),写完剩余数据,跳出循环,那么问题来了HAL_UART_Receive()接收数据和FLASH_If_Write()会存在快慢的问题,如果本次接收到的数据还没有写完flash,下一次又来数据又去写那么会存在丢包的问题,为避免该问题,这里将波特率设置的很小9600,可以尝试看看115200时的现象。

  • case ‘2’:在跳转到新的LED_APP之前关掉前面用到的外设和中断,然后在新的应用程序重新开启中断,避免干扰

具体代码见:STM32CUBEMX+IAR+IAP应用程序升级(STM32F411VET6)