IAP升级

相关资料:https://blog.csdn.net/elikang/article/details/86082960
IAP升级的原理这里就不介绍了。

1. 修改代码实现IAP+Ymodem

基于stm32官方提供的demo工程 STM32F10x_AN2557_FW_V3.3.0

提取出IAP和Ymodem文件,并修改Ymodem代码,从标准库到HAL库。

1.1 首先,从demo中找到执行框架,并将其封装在新建的文件IAP.c中。

#include "main.h"
#include "Define.h"

/* Flash user program offset */

uint32_t BlockNbr       = 0;
uint32_t UserMemoryMask = 0;        //掩码,用于检测FLASH是否有写保护
__IO uint32_t FlashProtection = 0;  //读写+不被编译器优化

pFunction Jump_To_Application;
uint32_t JumpAddress;

extern uint8_t file_name[FILE_NAME_LENGTH];
extern uint32_t FlashDestination;

extern int32_t Ymodem_Receive (uint8_t *);
extern uint8_t Ymodem_Transmit (uint8_t *,const  uint8_t* , uint32_t );
extern void Int2Str(uint8_t* str, int32_t intnum);
 
uint8_t tab_1024[1024] =
  {
    0
  };

/**
  * @brief  Download a file via serial port
  * @param  None
  * @retval None
  */
void SerialDownload(void)
{
  uint8_t Number[10] = "          ";
  int32_t Size = 0;

  printf("Waiting for the file to be sent ... (press 'a' to abort)\n\r");
  Size = Ymodem_Receive(&tab_1024[0]);
  if (Size > 0)
  {
    printf("\n\n\r Programming Completed Successfully!\n\r--------------------------------\r\n Name: %s",file_name);
    Int2Str(Number, Size);
    printf("\n\r Size: %s Bytes\r\n",Number);
    printf("-------------------\n");
  }
  else if (Size == -1)
  {
    printf("\n\n\rThe image size is higher than the allowed space memory!\n\r");
  }
  else if (Size == -2)
  {
    printf("\n\n\rVerification failed!\n\r");
  }
  else if (Size == -3) //收到取消
  {
    printf("\r\n\nAborted by user.\n\r");
  }
  else
  {
    printf("\n\rFailed to receive the file!\n\r");
  }
}


/**
  * @brief  Display the Main Menu on to HyperTerminal
    在超级终端上显示主菜单
  * @param  None
  * @retval None
  */
void Main_Menu(void)
{
  uint8_t key = 0;
  
  /* Get the number of block (4 or 2 pages) from where the user program will be loaded 
     获取块(4或2页)的数目 ,用户程序将从中加载的
  (0x8003000-0x8000000)>>12 = 3
  */
  BlockNbr = (FlashDestination - 0x08000000) >> 12;//8

  /* Compute the mask to test if the Flash memory, where the user program will be
     loaded, is write protected 
    计算掩码以测试加载用户程序的闪存是否有写保护
  */
#if defined (STM32F10X_MD) || defined (STM32F10X_MD_VL)
  UserMemoryMask = ((uint32_t)~((1 << BlockNbr) - 1));
#else /* USE_STM3210E_EVAL */
  if (BlockNbr < 62)
  {
    UserMemoryMask = ((uint32_t)~((1 << BlockNbr) - 1));
  }
  else
  {
    UserMemoryMask = ((uint32_t)0x80000000);
  }
#endif /* (STM32F10X_MD) || (STM32F10X_MD_VL) */


  /* Test if any page of Flash memory where program user will be loaded is write protected 
    测试将加载程序用户的闪存页是否有写保护
  */

  if (((uint32_t)(READ_REG(FLASH->WRPR)) & UserMemoryMask) != UserMemoryMask)
  {
    FlashProtection = 1;
  }
  else
  {
    FlashProtection = 0;
  }
    printf("\r\n================== Main Menu ============================\r\n\n");
    printf("  Download Image To the STM32F10x Internal Flash ------- 1\r\n\n");
    printf("  Upload Image From the STM32F10x Internal Flash ------- 2\r\n\n");
    printf("  Execute The New Program ------------------------------ 3\r\n\n");
    if(FlashProtection != 0)
    {
      printf("  Disable the write protection ------------------------- 4\r\n\n");
			
    }
		printf("==========================================================\r\n\n");
  while (1)
  {
    key = getchar();
		//功能选项
    if (key == 0x31)
    {
      /* Download user application in the Flash */
			printf("--> Download user application in the Flash\n\r");
      SerialDownload();
			
    }
    else if (key == 0x32)
    {
      /* Upload user application from the Flash */
      //SerialUpload();
			printf("上传\n\r");
    }
    else if (key == 0x33)
    {
			printf("跳转\n\r");
      JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);

      /* Jump to user application */
      Jump_To_Application = (pFunction) JumpAddress;
      /* Initialize user application's Stack Pointer */
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);
      Jump_To_Application();
			
			
    }
    else if ((key == 0x34) && (FlashProtection == 1))
    {
      /* Disable the write protection of desired pages */
      HAL_FLASH_Unlock();
			printf("解锁FLASH\n\r");
    }
    else
    {
      if (FlashProtection == 0)
      {
        printf("Invalid Number ! ==> The number should be either 1, 2 or 3\r");
      }
      else
      {
        printf("Invalid Number ! ==> The number should be either 1, 2, 3 or 4\r");
      } 
    }
  }
}


1.2 其次,提取出Ymodem.c文件,并对其中的一些函数进行修改。

下面是Ymodem.c文件修改前后的对比。最开始是要修改包含的头文件。main.h文件中包括HAL的Flash函数。Define.h文件中则是定义了Flash操作相关的宏定义常量。
Ymodem1

下面一段是自己加的注释,实则并没有改变代码语句。在使用串口终端和设备进行交互时,用户键盘输入的命令字等会在这段代码处被识别。
Ymodem2

下面是传输过程中,将数据包写入到Flash的操作。这里将标准库的函数用HAL库函数来替代。修改前最好理解一下这里的前后文。
Ymodem3

下面是注释信息,Ymodem协议,等待过程中,一直打印‘C’。
Ymodem4

1.3 最后,添加一个宏定义头文件,用于放置地址信息等定义。

应用程序的起始地址和boot代码的设定大小有关。这里给boot代码划分32K的代码空间。

#define ApplicationAddress    0x8008000         //boot 32K
#define FLASH_IMAGE_SIZE                   (uint32_t) (FLASH_SIZE - (ApplicationAddress - 0x08000000))

//这里实际上是RCT6 256KB的flash
 #define PAGE_SIZE                         (0x800)    /* 2 Kbytes */
 #define FLASH_SIZE                        (0x40000) /* 256K Byte */

/* Exported macro ------------------------------------------------------------*/
/* Common routines */
#define IS_AF(c)  ((c >= 'A') && (c <= 'F'))
#define IS_af(c)  ((c >= 'a') && (c <= 'f'))
#define IS_09(c)  ((c >= '0') && (c <= '9'))
#define ISVALIDHEX(c)  IS_AF(c) || IS_af(c) || IS_09(c)
#define ISVALIDDEC(c)  IS_09(c)
#define CONVERTDEC(c)  (c - '0')

#define CONVERTHEX_alpha(c)  (IS_AF(c) ? (c - 'A'+10) : (c - 'a'+10))
#define CONVERTHEX(c)   (IS_09(c) ? (c - '0') : CONVERTHEX_alpha(c))

#define SerialPutString(x) Serial_PutString((uint8_t*)(x))


#define FILE_NAME_LENGTH        (256)
#define FILE_SIZE_LENGTH        (16)

typedef  void (*pFunction)(void);

1.4 在将上述三个文件准备好之后,就是如何使用。下面是boot工程的main函数片段。

(1)初始化外设。
(2)打印boot信息
(3)判断是否需要进入boot模式,(按键被按下,则进入boot,未被按下,则直接跳转到app)
(4)如果按键被按下,则进入IAP升级框架。(Main_Menu函数)

MX_GPIO_Init();
  MX_I2C1_Init();
  MX_IWDG_Init();
  MX_USART1_UART_Init();

    printf("\n\r=== 南京某某呵呵啥啥有限公司 ===\n\r");
    printf("--> BOOT\n\r");
    printf("--> 编译时间:%s %s\n\r",__DATE__,__TIME__);
	
	//解锁FLASH
	HAL_FLASH_Unlock();

	//检测按键(后期可以考虑检测标志位)
	if(HAL_GPIO_ReadPin(USER_KEY_GPIO_Port,USER_KEY_Pin)==0)
	{
	    printf("\r\n======================================================================");
	    printf("\r\n=              (C) COPYRIGHT 2010 STMicroelectronics                 =");
	    printf("\r\n=                                                                    =");
	    printf("\r\n=     In-Application Programming Application  (Version 3.3.0)        =");
	    printf("\r\n=                                                                    =");
	    printf("\r\n=                                   By MCD Application Team          =");
	    printf("\r\n======================================================================");
	    printf("\r\n\r\n");
       extern void Main_Menu(void);
       Main_Menu();
	}
	else
	{
		
      uint32_t JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
      pFunction Jump_To_Application = (pFunction) JumpAddress;
      printf("\n\rJump to APP!\n\r");
      __set_MSP(*(__IO uint32_t*) ApplicationAddress);
      Jump_To_Application();
	}
  while (1)
  {
      clearIWDG();

2. 代码框架分析

2.1 IAP框架

略(稍后补充)。

2.2 Ymodem协议介绍

略(稍后补充)。

3. 关于使用。

3.1 Bootloader

boot工程配置如下图所示。
boot配置
另外,keil编译生成的是hex文件,需要添加指令才可以生成bin文件。如下图所示。编译完成后会生成Boot.bin文件。
boot.bin
上面用到的Hexbin.bat是一个脚本文件,内容如下。因为项目需求,有两个版本的代码同时存在。该脚本会判断两个版本代码的生成目录是否存在。如果不存在,则创建HexBinE1和HexBinE2文件夹。
通过fromelf.exe工具将hex文件转成bin文件,再移动到创建的文件夹中。
用户可根据自己的需求修改该脚本。

@echo off
if not exist ..\..\HexBinE1 (
   mkdir ..\..\HexBinE1
)


set exePath=%1ARM\ARMCC\bin
set outName=%2
set binName=%3

%exePath%\fromelf.exe --bin %outName% --output %binName%

move /y .\%binName% ..\..\HexBinE1 >nul

if not exist ..\..\HexBinE2 (
   mkdir ..\..\HexBinE2
)

%exePath%\fromelf.exe --bin %outName% --output %binName%

move /y .\%binName% ..\..\HexBinE2 >nul

3.2 Application

app工程配置如下图所示。重要注意点是,工程配置的地址以及中断向量表的地址。这里的配置相当于让出前面32K的空间,给boot使用。
app配置
同样关于如何生成bin文件,也需要进行如下图所示的操作。以E2版本为例。
app.bin
这里的HexBin.bat脚本内容如下。判断文件夹是否存在,生成bin文件,移动到文件夹中。

@echo off
if not exist ..\..\HexBinE2 (
   mkdir ..\..\HexBinE2
)

set exePath=%1ARM\ARMCC\bin
set outName=%2
set binName=%3

%exePath%\fromelf.exe --bin %outName% --output %binName%
move /y .\%binName% ..\..\HexBinE2 >nul

这里的BuildAction.exe是使用Qt编写的合成工具,用于将Boot.bin和APP_E2.bin合成为一个bin文件。
合并工具
其中的配置文件ini内容如下。用户可根据自己的需求更改。

*以/或者*开头的行,认为整行都是注释,不解析
*配置文件只可以修改=之后的内容。

*boot文件的相对位置
boot_path=..\..\..\HexBinE2\Boot
*app文件的相对位置
app_path=..\..\..\HexBinE2\APP_E2
*syscfg文件相对位置
syscfg_path=..\..\Inc
*产品标识码
identification=GDCJB2020
*boot_size大小默认是以KBytes为单位,例如,实际boot大小是16K,下面只要写16就好
boot_size=32
*app区大小
app_size=96
*整个flash大小
flash_size=256
*本工具调试开关
DEBUG=0

还有一个注意点是BuildAction.exe工具需要一个头文件sys_cfg.h,用以定义版本号。内容如下。

#define DEBUG_ENABLE     0           // 调试使能(停用:0 /启用:1)
// 应用层软件版本号,4个ASCII字符,临时版本0.XX,正式版本E.XX 格式:45 2E 30 31
#define APPVIRSION_BYTE0    0x32
#define APPVIRSION_BYTE1    0x2E
#define APPVIRSION_BYTE2    0x30
#define APPVIRSION_BYTE3    0x37

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值