【STM32CUBE+IAR+IAP升级】

STM32CUBE+IAR+IAP升级

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

1.工具:

2.工程配置

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

FLASH划分:STM32F411vet6片内FLASH512K,RAM128K,本案例将分配32KByte给BootLoader,480KByte给应用程序(本案例为LED_APP,功能0.5s翻转电平一次),如下图所示
【STM32CUBE+IAR+IAP升级】
2.1.2 LED_APP

​ ——>STM32CUBEMX中 RCC,SYS,GPIO(PD12),UART,时钟树配置如下,LED应用程序挺简单的
【STM32CUBE+IAR+IAP升级】
对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大小的区域。
【STM32CUBE+IAR+IAP升级】
——>IAR生成bin文件设置,设置完后保存,编译之后会在你的工程exe文件中生成BIN文件,注意bin文件的大小要在设置的范围内
【STM32CUBE+IAR+IAP升级】
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		256

uint32_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运行,结果如下。
【STM32CUBE+IAR+IAP升级】
——>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之前关掉前面用到的外设和中断,然后在新的应用程序重新开启中断,避免干扰

上一篇:Robotic Grasp之三维视觉


下一篇:STM32的两个hex文件拼接(转)