bootloader+IAP+APP+生成烧写文件.hex+升级文件.bin

目录

好记性不如烂笔头:

设计要点:

升级文件的生成:

生产烧写文件的生成:

结束语:


好记性不如烂笔头:

        本文主要用来记录 bootloader+IAP+APP+生成烧写文件.hex+升级文件.bin 的流程,用于以后类似项目的参考。

        ​​由于公司产品需要量产,虑到后续程序升级的问题就添加了bootloader。本想着前几年搞过了以为很简单,没得想到,忘记了好多,搞得花了一周时间才搞定。看来以后还是需要养成记笔记的习惯。

        本文主要记录bootloader的编写,APP的程序的设计,上位机升级程序的设计,如何生成hex文件和bin文件,以及编写脚本自动生成带bootloader的烧写.hex文件


设计要点:

        本文采用的升级协议是Ymodem协议,上位机是使用qt来编写,界面如下,浏览选择升级文件.bin,然后发送即可升级。

bootloader和APP程序的设计如下图:

         

bootloader程序参考ST给的官方IAP资料en.x-cube-iap-usart。核心代码如下:

COM_StatusTypeDef Ymodem_Receive ( uint32_t *p_size )
{
  uint32_t i, packet_length, session_done = 0, file_done, errors = 0, session_begin = 0;
  uint32_t flashdestination, ramsource, filesize;
  uint8_t *file_ptr;
  uint8_t file_size[FILE_SIZE_LENGTH], tmp, packets_received;
  COM_StatusTypeDef result = COM_OK;

  /* Initialize flashdestination variable */
  flashdestination = APPLICATION_ADDRESS;

  while ((session_done == 0) && (result == COM_OK))
  {
    packets_received = 0;
    file_done = 0;
    while ((file_done == 0) && (result == COM_OK))
    {
      switch (ReceivePacket(aPacketData, &packet_length, DOWNLOAD_TIMEOUT))
      {
        case HAL_OK:
          errors = 0;
          switch (packet_length)
          {
            case 2:
              /* Abort by sender */
              Serial_PutByte(ACK);
              result = COM_ABORT;
              break;
            case 0:
              /* End of transmission */
              Serial_PutByte(ACK);
              file_done = 1;
              break;
            default:
              /* Normal packet */
              if (aPacketData[PACKET_NUMBER_INDEX] != packets_received)
              {
                Serial_PutByte(NAK);
              }
              else
              {
                if (packets_received == 0)
                {
                  /* File name packet */
                  if (aPacketData[PACKET_DATA_INDEX] != 0)
                  {
                    /* File name extraction */
                    i = 0;
                    file_ptr = aPacketData + PACKET_DATA_INDEX;
                    while ( (*file_ptr != 0) && (i < FILE_NAME_LENGTH))
                    {
                      aFileName[i++] = *file_ptr++;
                    }

                    /* File size extraction */
                    aFileName[i++] = '\0';
                    i = 0;
                    file_ptr ++;
                    while ( (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH))
                    {
                      file_size[i++] = *file_ptr++;
                    }
                    file_size[i++] = '\0';
                    Str2Int(file_size, &filesize);

                    /* Test the size of the image to be sent */
                    /* Image size is greater than Flash size */
                    if (*p_size > (USER_FLASH_SIZE + 1))
                    {
                      /* End session */
                      tmp = CA;
                      HAL_UART_Transmit(&UartHandle, &tmp, 1, NAK_TIMEOUT);
                      HAL_UART_Transmit(&UartHandle, &tmp, 1, NAK_TIMEOUT);
                      result = COM_LIMIT;
                    }
                    /* erase user application area */
                    FLASH_If_Erase(APPLICATION_ADDRESS);
                    *p_size = filesize;

                    Serial_PutByte(ACK);
                    Serial_PutByte(CRC16);
                  }
                  /* File header packet is empty, end session */
                  else
                  {
                    Serial_PutByte(ACK);
                    file_done = 1;
                    session_done = 1;
                    break;
                  }
                }
                else /* Data packet */
                {
                  ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];

                  /* Write received data in Flash */
                  if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)                   
                  {
                    flashdestination += packet_length;
                    Serial_PutByte(ACK);
                  }
                  else /* An error occurred while writing to Flash memory */
                  {
                    /* End session */
                    Serial_PutByte(CA);
                    Serial_PutByte(CA);
                    result = COM_DATA;
                  }
                }
                packets_received ++;
                session_begin = 1;
              }
              break;
          }
          break;
        case HAL_BUSY: /* Abort actually */
          Serial_PutByte(CA);
          Serial_PutByte(CA);
          result = COM_ABORT;
          break;
        default:
          if (session_begin > 0)
          {
            errors ++;
          }
          if (errors > MAX_ERRORS)
          {
            /* Abort communication */
            Serial_PutByte(CA);
            Serial_PutByte(CA);
          }
          else
          {
            Serial_PutByte(CRC16); /* Ask for a packet */
          }
          break;
      }
    }
  }
  return result;
}

        本项目使用的不是串口,所以需要移植一下,适合自己的项目,主要修改了数据的获取以及数据的发送函数,以及接收处理函数,由于是项目上在使用的,不方便贴核心代码。中心思想是围绕Ymodem进行数据解析。 主函数代码如下,其中Ymodem_Receive2为核心代码,参考上述代码:

int main(void)
{
	int32_t Size = 0;
//	uint16_t  offset = 0;
//	uint16_t  Value = 0;
//	uint8_t   sensorBuffer[20];
	unsigned int Start_Mode_Flag = 0;
	systick_config();
	tongxin_init();
	
	//Set_Update_Flag();
	
	//Set_Update_Over_Flag();
	
	Start_Mode_Flag = Read_Start_Mode();
	if(Start_Mode_Flag != 0xADACABAA)
	{
		delay_1ms(500);
		//跳转到APP程序中
		APP_Start();
		while (1) 
		{

		}
	}
	while (1) 
	{
		Size = Ymodem_Receive2(&otabuf[0]);
	}
}

 踩坑点1:

        本项目使用的不是串口,是其他的通信口,需要建立缓存机制,接收到的所有数据都需要进入缓存,在解析数据时都要出缓存。导致了有的数据获取和发送会出现失败的情况,导致通信异常。需要对缓存的数据长度以及进出指针做出处理。

 踩坑点2:

         本项目使用的是GD32F207的芯片,在APP的中需要跳转向量表,在main函数开头已经设置了跳转向量表,没得想到调用的对外设进行初始化的库函数中还有跳转向量表的设置。这个点浪费了我一天的时间。所有以后在编写带bootloader的APP时,一定要全局搜索一下nvic_vector_table_set这个函数的调用情况。

升级文件的生成:

        使用说明:DebugInFlash文件夹需要和keil的工程文件.uvprojx在同一个目录下,在此文件夹中需要包含如下文件:

        bootloader.hex

        merge.cmd

        boot_app.sh

 .bin文件的生成需要在KEIL中做对应的设置,如下图所示:

        #K\ARM\ARMCC\bin\fromelf.exe --bincombined --output=.\DebugInFlash\@L.bin !L

生产烧写文件的生成:

        生成烧写文件为hex格式文件,主要包含了bootloader.hex和app.hex,在上图生成.bin中的设置下面调用了一个merge.cmd文件,调用了此文件可以自动生成烧写文件bootloader_app.hex。脚本代码如下:

set "cmdDir=%~dp0"
copy %cmdDir%bootloader.hex %cmdDir%bootloader_app.hex

@echo off
set "file_name=%cmdDir%bootloader_app.hex"
set back_line=1


for /f %%a in ('type "%file_name%"^|find /c /v ""') do (echo %%a&set num_line=%%a)
set/a last_line=num_line-back_line

cd.>"%temp%\tmp.hex"

for /f "tokens=1* delims=:" %%a in ('findstr/n .* "%file_name%"') do (
if %%a leq %last_line% (echo.^:%%b) else (goto:end)
)>>"%temp%\tmp.hex"

:end ==============================================================
type "%temp%\tmp.hex">%file_name%

type %cmdDir%..\.\GD32F20X_OBJ\app.hex >> %cmdDir%bootloader_app.hex

另外一种方式是需要安装git bash,具体方式如下:

使用如下命令替换上图中的Run#2命令:

#"C:\Program Files\Git\bin\sh.exe" --login -i -c "./DebugInFlash/boot_app.sh"

具体的命令需要根据自己安装的git路径进行修改。

此方式主要参考了鱼鹰Osprey大佬的博客文章

 如下为脚本的源代码:

#!/bin/bash
#"C:\Program Files\Git\bin\sh.exe" --login -i -c "./DebugInFlash/boot_app.sh"  # MDK 中执行的命令
 
 
boot_name_file_hex=./DebugInFlash/bootloader.hex          #  BOOT 文件所在位置和名称
app_name_file_hex=./GD32F20X_OBJ/app.hex           #dir_file_name_hex=`find ${app_dir_file_hex} -name "*.hex"`
boot_app_name_file_hex=./DebugInFlash/bootloader_app.hex 
 
 
cp ${boot_name_file_hex}  ${boot_app_name_file_hex}     # 复制新文件 boot 文件
sed -i '$d' ${boot_app_name_file_hex}               # 删除最后一行
cat ${app_name_file_hex} >>  ${boot_app_name_file_hex} # 追加文件





 设计好之后,以后在build应用程序后会直接生成烧写文件。不需要在对文件进行二次加工。

结束语:

        本文参考了其他博主的文章,由于时间太久了不记得具体哪几位了,就不一一链接了

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: IAP是In-App Purchases(应用内购买)的缩写,是一种让用户在应用内购买虚拟物品、功能或者服务的功能。通过IAP,用户可以在应用程序内直接购买并支付虚拟商品,而不需要离开应用程序去购买。 Bootloader(引导程序)是计算机系统中的一个特殊程序,它在系统启动时最先被加载执行,用于初始化硬件、引导操作系统以及其他系统初始化工作。 AppApplication(应用程序)的缩写,是一种在移动设备上安装和运行的应用软件。MFC是Microsoft Foundation Classes(微软基础类库)的缩写,是一个用于开发Windows应用程序的C++类库框架。 因此,"IAP bootloader app MFC" 的意思是,在使用MFC类库框架开发的Windows应用程序中,通过IAP功能实现引导程序的更新或者应用程序的更新。 一个典型的应用场景是,如果用户在使用某个应用程序时,开发者可以使用IAP功能提供新的引导程序或者应用程序更新的提醒,并提供购买选项供用户选择。一旦用户购买了更新,应用程序会通过IAP功能将更新的引导程序或者应用程序直接下载并进行安装,而不需要用户离开应用程序或者手动进行更新的安装操作。 这种方式可以让用户方便地在应用程序内更新或购买新的功能或虚拟物品,提升用户体验,也给开发者提供了一种增加收益和提供新功能的方式。 ### 回答2: IAP(In-App Purchases)是指在移动应用程序中进行购买和付款的功能。通过IAP,用户可以使用应用内购买来解锁额外的功能、获取虚拟物品或订阅服务等。在移动设备上,IAP是一种常见的商业模式,为开发者提供了一种方式来获取收入。 Bootloader是启动引导程序的意思,它负责在计算机或移动设备启动时加载操作系统。在移动设备上,Bootloader负责验证和加载操作系统,确保设备能够正确启动。Bootloader通常由设备制造商提供,且不可修改。 AppApplication的缩写,指移动设备上的应用程序。MFC(Microsoft Foundation Classes)是一种C++应用程序框架,通常用于开发Microsoft Windows操作系统上的应用程序。 综合来看,IAP bootloader app mfc可以理解为在移动设备上使用MFC框架开发的应用程序,具备IAP功能,并且由设备制造商提供的启动引导程序负责加载该应用程序。这种应用程序可以通过IAP进行付款和购买,提供额外的功能、虚拟物品或订阅服务等。这样的应用程序可以为开发者提供收入,同时由于使用了MFC框架,开发者可以更快地开发出功能丰富的应用程序。总之,IAP bootloader app mfc结合了应用内购买功能、启动引导程序和MFC框架,为用户提供更好的应用体验,同时为开发者带来商业机会。 ### 回答3: IAP(In-App Purchase)是应用内购买的简写,指的是在移动应用程序中实现购买和下载额外功能或内容的机制。通过IAP,用户可以使用应用内部的货币或在线支付购买额外的功能、解锁附加内容或去除广告等。 Bootloader(引导加载程序)是指在计算机硬件上最先运行的程序,用于启动操作系统。它位于计算机的ROM芯片中,负责调用操作系统的内核,并加载操作系统至内存中,以便开始系统的正常运行。 App(应用程序)是指在移动设备上使用的软件程序,可以用于各种不同的用途,例如社交媒体、游戏、生产力工具等。移动应用程序通常是通过应用商店(如App Store、Google Play)进行下载和安装的。 MFC(Microsoft Foundation Classes)是微软提供的一套C++类库,用于开发基于Windows平台的图形用户界面(Graphical User Interface, GUI)应用程序。通过MFC,开发人员可以使用更高层次的抽象来简化Windows编程的过程,使开发更加高效和便捷。 综上所述,IAP是一种用于在移动应用中进行购买和下载额外功能或内容的机制。Bootloader是计算机硬件上最先运行的程序,用于启动操作系统。App是移动设备上使用的各种不同用途的软件程序。而MFC是微软提供的一套用于开发Windows平台GUI应用程序的类库。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值