你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32F429的IAP升级(HAL库/RS485)

[复制链接]
aimejia 发布时间:2018-5-22 09:44
本帖最后由 aimejia 于 2018-5-22 11:19 编辑

最近一周一直在基于STM32F429项目的IAP工程,耗时4天才完成,得空记录下来。

文章主要涵盖了以下几点:
1. IAP是什么?
2. bin文件和hex文件的差别
3. ymodem协议介绍及其缺陷
4. RS485通讯
5. IAP的main()函数代码片段

项目的框架如下:

  1. ymodem协议
  2.     PC机_超级终端 -----------------> STM32产品的串口
  3.                    RS485通讯线
复制代码

从ST官网下载的IAP的SDK,其中包含了经典的ymodem协议和基于STM32F429的HAL库,工程就是基于该SDK开发的。

1. IAP的概念

  IAP的具体概念解析网上搜索一大堆,在这里简单描述。IAP(In Application Programming)即在应用编程,常听说的还有ISP(In System Programming)即在系统编程。ISP指的是将通过JTAG等接口将单片机程序烧录进单片机的FLASH(当然也可以是其他存储介质,如SRAM),而IAP指的是采用引导程序(Boot) + 应用程序(App)的方式烧写单片机程序,App是真正实现业务逻辑功能的代码。

  一般产品的调试口,也就是JTAG口是被置于机壳里面的,烧写需要打开机壳,也需要专业工具和电脑桌面软件。IAP的Boot程序通过ISP的方式烧录到单片机的低地址的FLASH处,每次单片机复位后会先执行Boot程序,在Boot程序中进行判断,用户是否要升级,若是则从串口(或者网口/CAN通信口)读取App程序写到高地址的FLASH,读写完毕后再跳转到FLASH上App的起始地址,执行业务逻辑功能代码,若否则直接跳转到App的代码处理:
  1. |-- 要升级 --> 读取读取串口发来的APP程序,写入FLASAH目标地址 --|
  2.                              |                                                              |
  3.     Boot: 判断是否要升级App -|                                                             |
  4.                              |-- 不升级 ---------------------------------------------------------> 跳转到APP程序起始地址处理
复制代码

Boot和App都是单片机程序,只是实现的功能不同,前者是为了引导App,后者是为了实现业务逻辑功能。这里有一个关键的动作,就是跳转,即从Boot跳转到App起始地址处。

  需要清晰2个概念:
  (1) 程序的起始地址
  程序的起始地址默认是被放在FLASH的起始地址处,即0x08000000:

1.jpg
Boot是放在这个默认起始地址的,App则要往后移动,这里设置为0x08008000:

2.jpg
需要注意,这是在App没有采用分散加载时设置的程序存放起始地址,若采用了分散加载,则需要修改工程中的.sct文件。详细内容可参照杜春雷的《ARM体系结构及编程》。

  (2) 中断向量表的地址
  对于STM32来说,每个单片机程序都有一张中断向量表,也就是说,在存有Boot和App的FLASH上就有两张中断向量表,Boot根据Boot程序中的中断向量表发生中断跳转,同理,App就要根据App程序的中断向量表发生中断跳转。中断向量表的摆放位置正是程序的开始地址。所以需要将App的中断向量表的摆放位置放在0x08004000。在system_stm32f4xx.c中:

  1. #ifdef VECT_TAB_SRAM
  2.   SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
  3. #else
  4.   SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
  5. #endif
复制代码

程序运行时候发生中断/异常/系统调度时就会去读取SCB->VTOR以获取中断向量表的地址;FLASH_BASE即STM32F429的(内置)FLASH的起始地址:

  1. #define FLASH_BASE  ((uint32_t)0x08000000)
  2. VECT_TAB_OFFSET是偏移量,所以我们可以通过设置此值指定中断向量表的存放地址。

  3.   讲到这里,顺便介绍STM32的中断向量表。表的形态如下:(摘自startup_stm32f429xx.s)

  4. __Vectors       DCD     __initial_sp               ; Top of Stack
  5.                 DCD     Reset_Handler              ; Reset Handler
  6.                 DCD     NMI_Handler                ; NMI Handler
  7.                 ; ...
  8.                 ; External Interrupts
  9.                 DCD     WWDG_IRQHandler                   ; Window WatchDog                                       
  10.                 DCD     PVD_IRQHandler                    ; PVD through EXTI Line detection                        
  11.                 ; ...
  12.                 DCD     LTDC_ER_IRQHandler                ; LTDC error
  13.                 DCD     DMA2D_IRQHandler                  ; DMA2D

  14. __Vectors_End
  15. __Vectors_Size  EQU  __Vectors_End - __Vectors
复制代码

显然,Boot程序结束后程序运行指针跳转到App的__initial_sp处,__initial_sp处存放的是App的栈的起始地址。中断向量表的作用是当程序发生异常/中断的时候会根据表中的标号而跳转到具体对应的中断处理函数(ISR)。

2. ymodem协议介绍

  关于ymodem协议的接收网上资料甚多,不做过多的介绍了。简单分析ymodem的数据包格式:

  1. /* /-------- Packet in IAP memory ------------------------------------------\
  2. * | 0      |  1    |  2     |  3   |  4      | ... | n+4     | n+5  | n+6  |
  3. * |------------------------------------------------------------------------|
  4. * | unused | start | number | !num | data[0] | ... | data[n] | crc0 | crc1 |
  5. * \------------------------------------------------------------------------/
  6. * the first byte is left unused for memory alignment reasons                 */
复制代码

      (1) unused:无意义,用于字节对齐
  (2) start:起始信号,”SOH”表本数据包的数据区有128字节,”STX”则表示有1024字节
  (3) number:数据包的编号,编号为0x00-0xFF。到数据包编号到达255后,将会从0开始计数。
  (4) !num:数据包编号的反码
  (5) data[0]…data[n]:数据区。对于第一个数据包(编号为0),存放的是文件名
  (6) CRC0、CRC1:校验码(只有数据区参与校验)
  下面是通讯流程:
  (1) 接收方发送一个字符’C’,也就是十六进制0x43。代表接收方已经处于接收数据的状态
  (2) 发送方接收到’C’之后,发送头帧数据包,数据包格式如上所述,此时数据区的数据是文件名和文件大小
  (3) 接收方收到数据包后发送ACK应答正确,然后发送一个字符’C’,发送方收到’C’后开始发送第二帧数据,第二帧数据即使第一个数据包
  (4) 接收方收好数据包后,发送ACK正确应答,然后等待下一包数据传送完毕,继续ACK应答,如此循环
  (5) 数据传输完毕后,发送方第一次发EOT,第一次接收方以NAK应答,进行二次确认
  (6) 发送方收到NAK后,第二次发EOT。接收方第二次收到结束符,依次以ACK和C做应答
  (7) 发送方收到ACK和C之后,发送结束符
  (8) 接收方收到结束符之后,以ACK做应答,然后通信正式结束

  ymodem协议除了接收数据包外还会将数据包按照指定地址写入FLASH。本SDK使用的是HAL库。
  这部分的实现在ymodem.c的Ymodem_Receive()函数中:

  1. #define PACKET_HEADER_SIZE      ((uint32_t)3)
  2. #define PACKET_DATA_INDEX       ((uint32_t)4)
  3. #define PACKET_START_INDEX      ((uint32_t)1)
  4. #define PACKET_NUMBER_INDEX     ((uint32_t)2)
  5. #define PACKET_CNUMBER_INDEX    ((uint32_t)3)
  6. #define PACKET_TRAILER_SIZE     ((uint32_t)2)
  7. #define PACKET_OVERHEAD_SIZE    (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE - 1)
  8. #define PACKET_SIZE             ((uint32_t)128)
  9. #define PACKET_1K_SIZE          ((uint32_t)1024)

  10. uint32_t flashdestination;
  11. uint8_t aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE];

  12. static HAL_StatusTypeDef ReceivePacket(uint8_t *p_data, uint32_t *p_length, uint32_t timeout)
  13. {
  14.   uint32_t crc;
  15.   uint32_t packet_size = 0;
  16.   HAL_StatusTypeDef status;
  17.   uint8_t char1;

  18.   *p_length = 0;
  19.   //调用HAL库函数接收字符,UartHandle是数据包的句柄
  20.   status = HAL_UART_Receive(&UartHandle, &char1, 1, timeout);

  21.   if (status == HAL_OK)
  22.   {
  23.     switch (char1)
  24.     {
  25.       case SOH:
  26.         packet_size = PACKET_SIZE;      //PACKET_SIZE为128
  27.         break;
  28.       case STX:
  29.         packet_size = PACKET_1K_SIZE;   //PACKET_1K_SIZE为1024
  30.         break;
  31.       case EOT:                         //数据传输完毕
  32.         break;
  33.       case CA:
  34.         if ((HAL_UART_Receive(&UartHandle, &char1, 1, timeout) == HAL_OK) && (char1 == CA))
  35.         {
  36.           packet_size = 2;
  37.         }
  38.         else
  39.         {
  40.           status = HAL_ERROR;
  41.         }
  42.         break;
  43.       case ABORT1:
  44.       case ABORT2:
  45.         status = HAL_BUSY;
  46.         break;
  47.       default:
  48.         status = HAL_ERROR;
  49.         break;
  50.     }
  51.     *p_data = char1;

  52.     if (packet_size >= PACKET_SIZE )
  53.     {
  54.       //接收真正的数据包
  55.       status = HAL_UART_Receive(&UartHandle, &p_data[PACKET_NUMBER_INDEX], packet_size + PACKET_OVERHEAD_SIZE, timeout);

  56.       //检验数据包
  57.       if (status == HAL_OK )
  58.       {
  59.         if (p_data[PACKET_NUMBER_INDEX] != ((p_data[PACKET_CNUMBER_INDEX]) ^ NEGATIVE_BYTE))
  60.         {
  61.           packet_size = 0;
  62.           status = HAL_ERROR;
  63.         }
  64.         else
  65.         {
  66.           /* Check packet CRC */
  67.           crc = p_data[ packet_size + PACKET_DATA_INDEX ] << 8;
  68.           crc += p_data[ packet_size + PACKET_DATA_INDEX + 1 ];
  69.           if (Cal_CRC16(&p_data[PACKET_DATA_INDEX], packet_size) != crc )
  70.           {
  71.             packet_size = 0;
  72.             status = HAL_ERROR;
  73.           }
  74.         }
  75.       }
  76.       else
  77.       {
  78.         packet_size = 0;
  79.       }
  80.     }
  81.   }
  82.   *p_length = packet_size;
  83.   return status;
  84. }

  85. COM_StatusTypeDef Ymodem_Receive ( uint32_t *p_size )  //p_size为输出型参数,用于存放从PC端发来的文件的大小
  86. {
  87.   uint32_t i, packet_length, session_done = 0, file_done, errors = 0, session_begin = 0;

  88.   uint32_t ramsource, filesize;
  89.   uint8_t *file_ptr;
  90.   uint8_t file_size[FILE_SIZE_LENGTH], tmp, packets_received;
  91.   COM_StatusTypeDef result = COM_OK;

  92.   /* APPLICATION_ADDRESS是App的起始地址 */
  93.   flashdestination = APPLICATION_ADDRESS;

  94.   while ((session_done == 0) && (result == COM_OK))
  95.   {
  96.     packets_received = 0;       //记录接收到的数据包的个数
  97.     file_done = 0;
  98.     while ((file_done == 0) && (result == COM_OK))
  99.     {
  100.       switch (ReceivePacket(aPacketData, &packet_length, DOWNLOAD_TIMEOUT))
  101.       {
  102.         case HAL_OK:
  103.           errors = 0;
  104.           switch (packet_length)
  105.           {
  106.             case 2:
  107.               /* 发送方终止发送 */
  108.               Serial_PutByte(ACK);
  109.               result = COM_ABORT;
  110.               break;
  111.             case 0:
  112.               /* 正常结束传输 */
  113.               Serial_PutByte(ACK);
  114.               file_done = 1;
  115.               break;
  116.             default:
  117.               /* 数据包编号出错 */
  118.               if (aPacketData[PACKET_NUMBER_INDEX] != packets_received)
  119.               {
  120.                 Serial_PutByte(NAK);
  121.               }
  122.               else
  123.               {
  124.                 if (packets_received == 0)
  125.                 {
  126.                   /* 数据包编号为0,证明这是数据区存放文件名的帧数据,事实上这个判断是有误的 */
  127.                   if (aPacketData[PACKET_DATA_INDEX] != 0)
  128.                   {
  129.                     //读取文件名
  130.                     i = 0;
  131.                     file_ptr = aPacketData + PACKET_DATA_INDEX;
  132.                     while ( (*file_ptr != 0) && (i < FILE_NAME_LENGTH))
  133.                     {
  134.                       aFileName[i++] = *file_ptr++;
  135.                     }
  136.                     aFileName[i++] = '\0';

  137.                     //读取文件大小
  138.                     i = 0;
  139.                     file_ptr ++;
  140.                     while ( (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH))
  141.                     {
  142.                       file_size[i++] = *file_ptr++;
  143.                     }
  144.                     file_size[i++] = '\0';
  145.                     Str2Int(file_size, &filesize);

  146.                     if (*p_size > (USER_FLASH_SIZE + 1))  //文件大过于可供存储的FLASH的空间
  147.                     {
  148.                       tmp = CA;
  149.                       HAL_UART_Transmit(&UartHandle, &tmp, 1, NAK_TIMEOUT);
  150.                       HAL_UART_Transmit(&UartHandle, &tmp, 1, NAK_TIMEOUT);
  151.                       result = COM_LIMIT;
  152.                     }

  153.                     /* 擦除扇区 */
  154.                     FLASH_If_Erase(APPLICATION_ADDRESS);
  155.                     *p_size = filesize;

  156.                     Serial_PutByte(ACK);
  157.                     Serial_PutByte(CRC16);
  158.                   }
  159.                   /* 文件头为空,传输结束 */
  160.                   else
  161.                   {
  162.                     Serial_PutByte(ACK);
  163.                     file_done = 1;
  164.                     session_done = 1;
  165.                     break;
  166.                   }
  167.                 }
  168.                 else /* 真正的数据包 */
  169.                 {
  170.                   //将收到的数据存放到FLASH
  171.                   ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];
  172.                   if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)
  173.                   {
  174.                     flashdestination += packet_length;
  175.                     Serial_PutByte(ACK);
  176.                   }
  177.                   else  //写入失败
  178.                   {
  179.                     /* End session */
  180.                     Serial_PutByte(CA);
  181.                     Serial_PutByte(CA);
  182.                     result = COM_DATA;
  183.                   }
  184.                 }
  185.                 packets_received ++;
  186.                 session_begin = 1;
  187.               }
  188.               break;
  189.           }
  190.           break;
  191.         case HAL_BUSY:
  192.           Serial_PutByte(CA);
  193.           Serial_PutByte(CA);
  194.           result = COM_ABORT;
  195.           break;
  196.         default:
  197.           if (session_begin > 0)
  198.           {
  199.             errors ++;  
  200.           }
  201.           if (errors > MAX_ERRORS) errors大于MAX_ERRORS,PC端将收到"接收端未响应的提示",终止传输
  202.           {
  203.             /* Abort communication */
  204.             Serial_PutByte(CA);
  205.             Serial_PutByte(CA);
  206.           }
  207.           else
  208.           {
  209.             Serial_PutByte(CRC16);  //返回'C'字符,PC端提示接收端未响应并记录次数,超过次数PC端也将提出
  210.           }
  211.           break;
  212.       }
  213.     }
  214.   }
  215.   return result;
  216. }
复制代码

事实上,对数据包的接收处理操作是有问题的:packets_received用于记录数据包的个数,它是uint8_t类型,取值是0-255这确实是符合ymodem协议的,但是超过255的数据包呢,观察上面代码可以发现并没有对第超过255个数据包,也就是第256个数据包的处理。第256个数据包的编号也是为0,会进入:

  1. if (packets_received == 0)
  2. {
  3.     if (aPacketData[PACKET_DATA_INDEX] != 0)
  4.     {
  5.         //进行读取文件名、文件大小、擦除FLASH操作
  6.     }
  7. }
复制代码

但是编号为0的第256个数据包的数据区事实上是数据,收到该数据包还是要以真正的数据包的写FLASH等操作。

  一开始我利用IAP传输小于256K的APP的时候是正常运行的,后来传输400+k的APP就会出现问题:无法跳转至APP。经过多番调试才定位于此,所以简单修改上面的代码,代码片段为:

  1. volatile int8_t is_first_pack = 1;
  2. switch (RPreturn)
  3. {
  4.     case HAL_OK:
  5.     errors = 0;
  6.     after_isp = 1;
  7.     switch (packet_length)
  8.     {
  9.         case 2:
  10.         Serial_PutByte(ACK);
  11.         result = COM_ABORT;
  12.         break;
  13.     case 0:
  14.         Serial_PutByte(ACK);        
  15.         file_done = 1;
  16.         break;

  17.     default:
  18.     if (aPacketData[PACKET_NUMBER_INDEX] != packets_received)   //ÅжÏÊý¾Ý°üµÄ±àºÅÊÇ·ñÕýÈ·
  19.     {
  20.         Serial_PutByte(NAK);
  21.     }
  22.     else
  23.     {
  24.         if (packets_received == 0 )
  25.         {                           
  26.           if (aPacketData[PACKET_DATA_INDEX] != 0 )
  27.           {
  28.             if (is_first_pack)  //第一个编号为0的数据包
  29.             {
  30.                 //...               
  31.                 is_first_pack = 0;              
  32.             }
  33.             else    //即使数据编号为0但不是第一个数据包,采取存储操作         
  34.             {
  35.                 ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];        
  36.                 if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)
  37.                 {
  38.                     flashdestination += packet_length;
  39.                     Serial_PutByte(ACK);
  40.                 }
  41.                 else
  42.                 {
  43.                     Serial_PutByte(CA);
  44.                     Serial_PutByte(CA);
  45.                     result = COM_DATA;
  46.                 }               
  47.             }
  48.           }
  49.           else
  50.           {
  51.             Serial_PutByte(ACK);
  52.             file_done = 1;
  53.             session_done = 1;
  54.             break;
  55.           }
  56.         }
  57.         else
  58.         {

  59.             ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];                        
  60.             if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)
  61.             {
  62.                 flashdestination += packet_length;
  63.                 Serial_PutByte(ACK);
  64.             }
  65.             else
  66.             {
  67.                 Serial_PutByte(CA);
  68.                 Serial_PutByte(CA);
  69.                 result = COM_DATA;
  70.             }        
  71.         }
  72.         packets_received ++;
  73.         packets_received = packets_received % 256;
  74.         session_begin = 1;
  75.       }
  76.       break;
  77.     }
  78.     break;
  79.     //..
  80.     break;
  81. }
复制代码

3. Bin和Hex文件的差别

       第一次接触Hex文件一般是在学习51内核单片机的时候,通过烧录器烧录到开发板上使用的就是Hex格式的文件。Hex文件是以ASCII文本形式保存编译后的二进制文件信息,注意这里强调的是文本文件(而非数据文件,即二进制文件)。文本文件是人们可以看得懂的文件,但是计算机/MCU只认识二进制数据文件(Bin文件),Bin文件才是MCU固件烧写的最终形式,也就是说MCU的ROM中烧写的内容完全是Bin文件。由此可得,我们通过烧录器烧录Hex文件到单片机的ROM时,烧录器其实会将Hex文件的数据转为Bin文件的数据,最后才烧录到ROM。

  其实明白这一点我们就知道在IAP工程中,APP文件需要是Bin格式的文件而不能是Hex文件。既然Bin文件是MCU/计算机最终想要的,为什么我们不直接生成Bin文件,而却要生成Hex文件?其实Hex文件保存的不仅是Bin文件的内容,还有一些附属配置信息,随便拿个项目Hex文件分析:


  1. : 10 9AB0 00 6841298459D0420F9AC4641EFBC10408 2E
  2. : 10 9AC0 00 E7604BC5CC64011029B85A6980413C55 08
  3. : 08 9AD0 00 55557C2964291C81 15
  4. : 04 0000 05 080081AD C1
  5. : 00 0000 01 FF
复制代码

Hex文件中的数据是ASCII编码,所以是人们能看懂的。上面3行内容,每行都是以’:’开始的,之后是数据长度、地址域、数据类型、数据域、校验和。

  1. /* /-------- Hex Data format -------------------------------------------------------------\
  2. * |   0     |  1     |  3、4  |    5     |    6   |    7   |  ...   |   n     |   n + 1  |
  3. * |--------------------------------------------------------------------------------------|
  4. * | : 开始 | 数据长度 |地址域 | 记录类型 | 数据域 | 数据域 | 数据域 | 数据域n |  校验和  |
  5. * \--------------------------------------------------------------------------------------/
  6. * 每行数据都是以冒号开始的                 */
复制代码

注:记录类型的意义
  (1) 00: 数据记录
  (2) 01: 文件结束记录
  (3) 02: 扩展段地址记录
  (4) 03: 段开始地址记录
  (5) 04: 扩展线性地址记录
  (6) 05: 线性地址开始记录

  由此可见,生成Hex文件的意义在于:
  (1) Hex文件使用ASCII文本保存固件信息,方便查看固件内容
  (2) 文件内容每行的校验和与最后一行的文件结束标志,在文件的传输与保存过程中能够检验固件是否完整

  因此hex文件更适用于保存与传输。相比之下,Bin文件纯二进制文件,内部只包含程序编译后的机器码和变量数据。当文件损坏时,我们也无法知道文件已损坏。不过在IAP中,Bin文件仍旧是不可替代的。

4. RS485通讯

  从ST官网下载的的IAP SDK是基于RS232通讯的,即ymodem默认是从串口接收在APP数据的。但是我这个实际项目中用到的是采用RS485的通讯。RS232转为RS485,在软件上只是多了一步方向控制操作。因为RS485是半双工通讯,所以在发送单片机需要发送数据时需要将RS485总线设置为发送状态,接收数据则需要设置为接受状态。关于收发控制,软件上实现只是拉高/拉低对应控制RS485控制芯片的接收状态的GPIO即可。我的做法是默认是接收状态,当要发送时在发送函数内切换为发送状态,函数退出之前又切回接收状态。

5. IAP的main代码片段分析

       IAP升级代码工程,一般需要基于一个能跑起来的通讯(这里是指RS485)和LED灯(用于指示状态,当然也可以用LCD等其他提示状态)运行正常的工程代码。下面是main()函数:

  1. void SerialDownload(void);

  2. //定义函数指针,用于跳转到APP
  3. typedef  void (*pFunction)(void);   
  4. extern pFunction JumpToApplication;
  5. extern uint32_t JumpAddress;
  6. UART_HandleTypeDef UartHandle;

  7. int main(void)
  8. {
  9.     HAL_Init();
  10.     SystemClock_Config();

  11.     LED_BSP_Init();
  12.     FLASH_If_Init();

  13.     //初始化RS485
  14.     UART_Init();
  15.     RS485_RX_ENABLE();      //默认设置为接收状态
  16.     HAL_Delay(10);

  17. IAP:   
  18.     Serial_PutString((uint8_t *)"\r\n====================================================================");
  19.     Serial_PutString((uint8_t *)"\r\n=                       IAP For STM32F429xx                        =");
  20.     Serial_PutString((uint8_t *)"\r\n====================================================================\r\n");

  21.     SerialDownload();   
  22.     HAL_Delay(10);

  23.     //APPLICATION_ADDRESS是在FLASH中存放APP的起始地址
  24.     //此判断是为了保证APP的栈地址是在SRAM中。其实结果并不一定是0x20000000。有些APP可能定义的全局变量较多,那么栈的起始地址会偏移
  25.     if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)   
  26.     {

  27.         Serial_PutString((uint8_t *)"\r\n=======================  Run application  ======================= \r\n\n");
  28.         HAL_Delay(10);

  29.         /* Jump to user application */
  30.         JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
  31.         JumpToApplication = (pFunction) JumpAddress;
  32.         /* Initialize user application's Stack Pointer */
  33.         __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
  34.         JumpToApplication();
  35.     }

  36.     Serial_PutString((uint8_t *)"\r\n ======================= Download error, once again ======================= \r\n\n");
  37. goto IAP;
  38. }

复制代码

IAP的实现还是十分简单,但是中间走了N多弯路,特别是在调试ymodem接收大于256kb的APP上。关键要注意上述几点内容。当然,上述内容属于个人见解。



转载自hsade





收藏 2 评论0 发布时间:2018-5-22 09:44

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版