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

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

[复制链接]
aimejia 发布时间:2018-5-22 09:44
本帖最后由 aimejia 于 2018-5-22 11:19 编辑
+ ^5 G, l: G! v/ N
' r) P: L$ F: T1 v4 U+ J0 r最近一周一直在基于STM32F429项目的IAP工程,耗时4天才完成,得空记录下来。
/ _7 E: |( a* m2 ~" j
; ^3 ]5 U9 |& d; u. C文章主要涵盖了以下几点:3 N4 E9 s% T! K5 f" k
1. IAP是什么?' C/ j% \5 {+ r) e+ `
2. bin文件和hex文件的差别! q; i. W) }8 k& X) K5 x
3. ymodem协议介绍及其缺陷
3 Y3 V# q/ r8 x  _0 g! V# k4. RS485通讯* v$ ~' u( e$ U6 u
5. IAP的main()函数代码片段 6 A% n1 p' j' h" j5 u
2 j0 v$ G. X; J
项目的框架如下:
, L  Z; P, H4 G8 s6 l2 ~9 N5 _: s" U) T$ j" T
  1. ymodem协议
    3 t! r( ]6 _' D( M  }4 o; `+ G
  2.     PC机_超级终端 -----------------> STM32产品的串口1 X1 S: f- g% b, [7 M7 f7 t
  3.                    RS485通讯线
复制代码

6 O" z* P# j1 D. D/ p+ U从ST官网下载的IAP的SDK,其中包含了经典的ymodem协议和基于STM32F429的HAL库,工程就是基于该SDK开发的。
9 M1 T- L* v4 k2 Y" ~6 W
+ d' l$ z; D9 g/ W! l1. IAP的概念- G6 A% P2 e0 s0 a+ p# H
" V+ i' t9 N, w/ b+ Y. l4 [, }7 w
  IAP的具体概念解析网上搜索一大堆,在这里简单描述。IAP(In Application Programming)即在应用编程,常听说的还有ISP(In System Programming)即在系统编程。ISP指的是将通过JTAG等接口将单片机程序烧录进单片机的FLASH(当然也可以是其他存储介质,如SRAM),而IAP指的是采用引导程序(Boot) + 应用程序(App)的方式烧写单片机程序,App是真正实现业务逻辑功能的代码。
5 U" |5 x& s9 W
+ I3 l* k' t, w& w  一般产品的调试口,也就是JTAG口是被置于机壳里面的,烧写需要打开机壳,也需要专业工具和电脑桌面软件。IAP的Boot程序通过ISP的方式烧录到单片机的低地址的FLASH处,每次单片机复位后会先执行Boot程序,在Boot程序中进行判断,用户是否要升级,若是则从串口(或者网口/CAN通信口)读取App程序写到高地址的FLASH,读写完毕后再跳转到FLASH上App的起始地址,执行业务逻辑功能代码,若否则直接跳转到App的代码处理:; H4 r$ I9 y7 h% O
  1. |-- 要升级 --> 读取读取串口发来的APP程序,写入FLASAH目标地址 --|2 Z6 Q1 X9 @  w6 v9 ^6 x& E" ?
  2.                              |                                                              |
    9 X/ w! N' ?9 P6 i; e
  3.     Boot: 判断是否要升级App -|                                                             |
    ! @4 O1 o' Y. J# b# g
  4.                              |-- 不升级 ---------------------------------------------------------> 跳转到APP程序起始地址处理
复制代码
) e8 P$ x; |  V) K* t
Boot和App都是单片机程序,只是实现的功能不同,前者是为了引导App,后者是为了实现业务逻辑功能。这里有一个关键的动作,就是跳转,即从Boot跳转到App起始地址处。
$ L1 V- G. @  x/ D# m9 l* x) |$ ?9 M2 e; D  a# L
  需要清晰2个概念:
7 S8 W! M$ F  x' ^% _! r. [$ {  (1) 程序的起始地址
* d+ B  B) o1 c! M, L  程序的起始地址默认是被放在FLASH的起始地址处,即0x08000000:
# M  f' t4 u; j9 n5 h4 H+ X# w, K% F) H
1.jpg
: z8 D, t- q) p; a* t$ cBoot是放在这个默认起始地址的,App则要往后移动,这里设置为0x08008000:
% O: J! F/ h0 K" y, {$ S7 \2 q% C& b( e- o' K8 t) f) @
2.jpg * g0 w' I  k" @- G
需要注意,这是在App没有采用分散加载时设置的程序存放起始地址,若采用了分散加载,则需要修改工程中的.sct文件。详细内容可参照杜春雷的《ARM体系结构及编程》。9 N1 Q5 u5 A% W- d6 |
' L4 D6 m  w7 G- M4 q
  (2) 中断向量表的地址
* f; H( Q  C6 u+ `3 n" O  对于STM32来说,每个单片机程序都有一张中断向量表,也就是说,在存有Boot和App的FLASH上就有两张中断向量表,Boot根据Boot程序中的中断向量表发生中断跳转,同理,App就要根据App程序的中断向量表发生中断跳转。中断向量表的摆放位置正是程序的开始地址。所以需要将App的中断向量表的摆放位置放在0x08004000。在system_stm32f4xx.c中:( \3 L, u) N( v% l. Z

# R1 `8 P. D1 C; b7 t
  1. #ifdef VECT_TAB_SRAM
    . ^  H! R# k9 O, ~! Q# K5 b
  2.   SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */+ ^' J0 `; k  B0 |6 o- [7 j
  3. #else( q) z) ~! l9 M  K4 c9 a
  4.   SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
    - H, o- _: |7 i! a( j0 ]
  5. #endif
复制代码
/ \9 ~8 s6 o. Y( N
程序运行时候发生中断/异常/系统调度时就会去读取SCB->VTOR以获取中断向量表的地址;FLASH_BASE即STM32F429的(内置)FLASH的起始地址:# ]1 |8 c/ k7 [" c! k

$ L/ J! A( X+ `: o: U
  1. #define FLASH_BASE  ((uint32_t)0x08000000)" ^& a, H2 ^- T3 [7 Y) k
  2. VECT_TAB_OFFSET是偏移量,所以我们可以通过设置此值指定中断向量表的存放地址。
    + h6 l* l$ q# L0 w  M2 k4 Z

  3. / s; _: I3 V% j0 K& f- J9 a& B1 S
  4.   讲到这里,顺便介绍STM32的中断向量表。表的形态如下:(摘自startup_stm32f429xx.s)
    , a7 }4 j; ^* @) B7 Z7 |
  5. + ?0 u# W+ h% @) b$ R/ x
  6. __Vectors       DCD     __initial_sp               ; Top of Stack
    - L  z6 ~1 b8 h& X( z! _$ K) F
  7.                 DCD     Reset_Handler              ; Reset Handler" x1 X5 V1 u- ^5 C
  8.                 DCD     NMI_Handler                ; NMI Handler
    / f5 e9 ?! N) V9 t; F5 q
  9.                 ; ...* j' `' ?+ H2 }* K
  10.                 ; External Interrupts
    & K1 Z1 a$ G  u  }: e# `
  11.                 DCD     WWDG_IRQHandler                   ; Window WatchDog                                       
    # `! }+ @7 k5 U5 R; W
  12.                 DCD     PVD_IRQHandler                    ; PVD through EXTI Line detection                        
    $ `: _- V$ Q2 X: x3 q% B( j
  13.                 ; ...: u# S9 u+ g* [% _$ r' n
  14.                 DCD     LTDC_ER_IRQHandler                ; LTDC error! v0 {# @0 S6 k6 X
  15.                 DCD     DMA2D_IRQHandler                  ; DMA2D7 z% |# n4 P+ X7 C7 \0 {
  16. , T' A! R5 J  Z" y+ E7 r
  17. __Vectors_End  y, ?7 z" z1 W. X' P8 V
  18. __Vectors_Size  EQU  __Vectors_End - __Vectors
复制代码
' c; S% [# ?; h& x+ G! A3 S& m" M
显然,Boot程序结束后程序运行指针跳转到App的__initial_sp处,__initial_sp处存放的是App的栈的起始地址。中断向量表的作用是当程序发生异常/中断的时候会根据表中的标号而跳转到具体对应的中断处理函数(ISR)。
7 E3 _* B4 _3 j2 A1 r0 F; L2 O2 B" b6 J( x. i! \5 w- t' P' q
2. ymodem协议介绍
& y$ _# m+ U3 K9 v$ H
% G" r1 |" s6 `! G& J( C  关于ymodem协议的接收网上资料甚多,不做过多的介绍了。简单分析ymodem的数据包格式:4 f3 K$ t4 k. F9 j) M
# e# l, g3 m. ?9 {
  1. /* /-------- Packet in IAP memory ------------------------------------------\# {! p- m3 r0 H0 C3 X- E( C
  2. * | 0      |  1    |  2     |  3   |  4      | ... | n+4     | n+5  | n+6  | " Q& s! Y4 w6 F; p  g
  3. * |------------------------------------------------------------------------|
    3 V% `. Z' U- R% x, X/ z" d
  4. * | unused | start | number | !num | data[0] | ... | data[n] | crc0 | crc1 |
    1 e, @2 d7 Y/ [" x
  5. * \------------------------------------------------------------------------/* {6 K% _, X9 O+ J0 |& p5 l& u
  6. * the first byte is left unused for memory alignment reasons                 */
复制代码

" f) L  m; z; s, O1 n( D" L# M. x      (1) unused:无意义,用于字节对齐
" q, ~- B, k. h, k# a2 u6 D  (2) start:起始信号,”SOH”表本数据包的数据区有128字节,”STX”则表示有1024字节
+ E" a+ ~4 E9 n+ j6 G  (3) number:数据包的编号,编号为0x00-0xFF。到数据包编号到达255后,将会从0开始计数。 1 E" W" [9 I* `  v% f
  (4) !num:数据包编号的反码 . y  |/ I! n" {) a% [7 R0 ]
  (5) data[0]…data[n]:数据区。对于第一个数据包(编号为0),存放的是文件名 1 |! b- W0 Z( d  Q( p$ M
  (6) CRC0、CRC1:校验码(只有数据区参与校验) + S4 V1 v4 c! X( d1 J
  下面是通讯流程:
4 @- N" P! l) {+ T' G, w! a  (1) 接收方发送一个字符’C’,也就是十六进制0x43。代表接收方已经处于接收数据的状态
. w0 s, s' T, m$ |3 v  (2) 发送方接收到’C’之后,发送头帧数据包,数据包格式如上所述,此时数据区的数据是文件名和文件大小 . \  e  Y( `4 C& b* O* V
  (3) 接收方收到数据包后发送ACK应答正确,然后发送一个字符’C’,发送方收到’C’后开始发送第二帧数据,第二帧数据即使第一个数据包 ; O  M& c& \7 W2 N" S. F. T
  (4) 接收方收好数据包后,发送ACK正确应答,然后等待下一包数据传送完毕,继续ACK应答,如此循环 , M2 X: s& F, `" D4 J
  (5) 数据传输完毕后,发送方第一次发EOT,第一次接收方以NAK应答,进行二次确认
4 B% [! L3 p" W  N* p- T* }  (6) 发送方收到NAK后,第二次发EOT。接收方第二次收到结束符,依次以ACK和C做应答 ) E: H* v0 d6 |
  (7) 发送方收到ACK和C之后,发送结束符
- O8 f$ W& H* n( B  (8) 接收方收到结束符之后,以ACK做应答,然后通信正式结束
- @7 P/ D3 |. F2 e$ A
2 D4 d( B0 {5 i5 ^; t& H! ?  ymodem协议除了接收数据包外还会将数据包按照指定地址写入FLASH。本SDK使用的是HAL库。
; X/ H0 [" Y4 X% g* t3 [) ~3 R  这部分的实现在ymodem.c的Ymodem_Receive()函数中:. o( f" U- |' v1 F; R) @" C7 o' L

, K2 s( R+ o. ^9 v
  1. #define PACKET_HEADER_SIZE      ((uint32_t)3)
    5 _7 T( O& s# T5 e
  2. #define PACKET_DATA_INDEX       ((uint32_t)4)+ g& u% _$ Q5 [3 @" ~
  3. #define PACKET_START_INDEX      ((uint32_t)1)
    3 d$ X, p( v( J& e. S! E/ a! C
  4. #define PACKET_NUMBER_INDEX     ((uint32_t)2)
    / I7 n5 r: J) ^2 ^" s
  5. #define PACKET_CNUMBER_INDEX    ((uint32_t)3)2 S+ D1 ^2 m1 C
  6. #define PACKET_TRAILER_SIZE     ((uint32_t)2)
    2 d, |5 |* M6 n# Q9 V6 x- _8 `
  7. #define PACKET_OVERHEAD_SIZE    (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE - 1)  \) ~0 }* M# d+ b
  8. #define PACKET_SIZE             ((uint32_t)128)
    1 [) O( `5 u; a3 U$ p1 {9 a
  9. #define PACKET_1K_SIZE          ((uint32_t)1024)
    7 l; U- i" ^$ Y" L% E# b
  10. 9 @" @) _7 E, r: r; B- L, A7 p; l# c
  11. uint32_t flashdestination;
    / {" h. |7 t2 g6 ], R! s
  12. uint8_t aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE];
      o9 J! d0 i$ ~* V8 @5 ~) ?' D. s

  13. * W$ z  L1 c* K
  14. static HAL_StatusTypeDef ReceivePacket(uint8_t *p_data, uint32_t *p_length, uint32_t timeout)! T- A: ?! x+ W9 G% n4 v4 c( Y+ I
  15. {0 B, E' a( O0 V$ y- r
  16.   uint32_t crc;
    7 x0 y% b  {0 r! E2 r! T0 U6 \; ^" s
  17.   uint32_t packet_size = 0;
    " o3 q3 t# v! ]* r4 S; F
  18.   HAL_StatusTypeDef status;
    + N9 \  E8 |5 x1 B2 P2 d
  19.   uint8_t char1;& g& x( O8 N) m- F& E3 w- W# F
  20. ! i0 ?8 `1 Y- V
  21.   *p_length = 0;
    % @$ e2 Y- V6 N5 p- ^6 j
  22.   //调用HAL库函数接收字符,UartHandle是数据包的句柄
    ! A: x  B9 l/ H& Y! D
  23.   status = HAL_UART_Receive(&UartHandle, &char1, 1, timeout);
    + V; e( g: V8 I! T) V+ O* q- c
  24. " t4 f+ N  E& O' d+ Y+ z/ ]
  25.   if (status == HAL_OK)
    9 f8 m/ A0 ^) t4 ]$ j. l
  26.   {. }' Q+ c# R& X( W9 }
  27.     switch (char1)
    ! M  |- |7 K& E$ S: k) O, T
  28.     {
    8 s: q$ p( p4 y, g/ x4 Z
  29.       case SOH:
    . d3 \( Q3 ?1 k5 i$ N  p
  30.         packet_size = PACKET_SIZE;      //PACKET_SIZE为1280 U3 p4 w: t4 I1 Q
  31.         break;
    0 P+ p5 U* x  v2 U5 K3 l: l4 m
  32.       case STX:
    $ g& K: x; v0 t& |+ c0 X, v
  33.         packet_size = PACKET_1K_SIZE;   //PACKET_1K_SIZE为1024
      I! ]+ V: k+ K; s- ^3 Z7 ~
  34.         break;' Y$ m1 l) J. F  @/ Z  ]
  35.       case EOT:                         //数据传输完毕( H+ ~" q4 Q' h- p! u' n+ z" J
  36.         break;
    ; h( L9 ?% J; c' V
  37.       case CA:
    . t# \% {; m' |2 M( ?, [( |* x
  38.         if ((HAL_UART_Receive(&UartHandle, &char1, 1, timeout) == HAL_OK) && (char1 == CA))
    8 T8 P+ [# G8 u4 m) v. `
  39.         {+ J* M' V9 r. U
  40.           packet_size = 2;9 |9 _% [( e9 q( D/ X
  41.         }
    ' t% ^0 I& j4 d% E5 U: V/ q( G/ b
  42.         else- c/ U! T, g& V* I6 @% g( Q
  43.         {1 i3 Z" u' a# M; l2 F3 R
  44.           status = HAL_ERROR;
    / U1 m. n( I) O0 Y" N0 a
  45.         }) P5 s: d" o- J8 y2 U& ^
  46.         break;9 W* I' b) o; T' n$ r0 @% A
  47.       case ABORT1:  t' r  q. \  y$ F
  48.       case ABORT2:( P4 i6 M0 v2 h( O" r) d
  49.         status = HAL_BUSY;
    - c5 G/ n5 }, T0 ~0 x2 o" I. N, ]
  50.         break;
    6 z3 A: v$ `  u# @. [" ]) w
  51.       default:. N! ?: r( l9 |# R
  52.         status = HAL_ERROR;
    - {" z5 `3 K' V" ], f/ }
  53.         break;
    * j& K' m* J2 V7 H. @( w
  54.     }
    0 X+ R. N4 W9 z2 Q- q$ y
  55.     *p_data = char1;; I7 U$ W. D% [, c5 l. @
  56. 7 r" j) \7 s% ~/ w0 e. @+ K) q
  57.     if (packet_size >= PACKET_SIZE )
    # t7 v6 Y; L+ v# F0 k; K6 V. m
  58.     {* b) v) L- I" Q# E) G$ `6 n
  59.       //接收真正的数据包
    4 a' k9 N2 Y& y1 z4 f9 J4 B6 c
  60.       status = HAL_UART_Receive(&UartHandle, &p_data[PACKET_NUMBER_INDEX], packet_size + PACKET_OVERHEAD_SIZE, timeout);
    3 z/ H3 U" ?& i: N0 Z$ Z! I) l2 h
  61. / M. b$ D8 D* V5 i' G. Q" C
  62.       //检验数据包$ m1 M  n1 m/ D( I8 u: u+ @
  63.       if (status == HAL_OK )! n/ J! u. N5 v! J* i: J
  64.       {$ q: r7 o7 x) ^: c* N/ O
  65.         if (p_data[PACKET_NUMBER_INDEX] != ((p_data[PACKET_CNUMBER_INDEX]) ^ NEGATIVE_BYTE))
    % Y# D; U  d; G) A5 o: P; N
  66.         {$ ?- _5 ?" I4 I) `, c- m* g  _  ?
  67.           packet_size = 0;
    $ m: I% u% |  U; N
  68.           status = HAL_ERROR;
    " q: a0 t, M" m+ g
  69.         }
    5 P  N4 I  T2 D
  70.         else
    7 z4 z) r$ u" |# P% L# z, w
  71.         {
    ! c8 Z3 v8 P  C( E/ P4 z% n  K
  72.           /* Check packet CRC */
    % x, W7 V6 W9 I( q$ P5 v
  73.           crc = p_data[ packet_size + PACKET_DATA_INDEX ] << 8;$ r6 U8 g1 K) X( N( q
  74.           crc += p_data[ packet_size + PACKET_DATA_INDEX + 1 ];
    % \( q- N9 E$ j, K: L3 w' y
  75.           if (Cal_CRC16(&p_data[PACKET_DATA_INDEX], packet_size) != crc ). O+ a0 @7 h  T* k; t
  76.           {
    3 i8 K/ C. e( s1 L% Q
  77.             packet_size = 0;# Q& d5 O) ^7 }  z. O* J5 f
  78.             status = HAL_ERROR;( O+ h# V0 l8 I/ v
  79.           }
    * G: n( A3 G% H! d8 I8 X5 s4 V
  80.         }: }* u; B, [/ i: ~+ q9 P8 g
  81.       }
    * I' f5 N: ^8 f
  82.       else
    ( ^! z) A! O5 `% i# H
  83.       {
    ' t% x8 P9 T: c; p
  84.         packet_size = 0;/ v. E- w' j5 O# g, _% A
  85.       }$ S! O* L7 E4 X/ k$ j$ p5 w
  86.     }; C3 J- b' T$ \
  87.   }
      o. U9 Q! Z& r9 F: h9 r. L
  88.   *p_length = packet_size;1 V5 y! T+ [* Q% e& A! j
  89.   return status;
    + l+ J$ ]% r( t' h' q9 W
  90. }; T( {4 }- q, g7 U! t$ K

  91. $ x' }' N# d, p$ R+ S
  92. COM_StatusTypeDef Ymodem_Receive ( uint32_t *p_size )  //p_size为输出型参数,用于存放从PC端发来的文件的大小
    + T. O6 F+ m) K# q5 I% Y3 q; s1 T
  93. {0 L- d# v: S$ x& R' P
  94.   uint32_t i, packet_length, session_done = 0, file_done, errors = 0, session_begin = 0;5 n3 M8 p7 P/ T

  95. * p( L! m3 H9 t1 G2 A; o
  96.   uint32_t ramsource, filesize;8 A8 C; }+ a3 P; i/ t
  97.   uint8_t *file_ptr;, c7 m2 f3 k1 h& n5 z
  98.   uint8_t file_size[FILE_SIZE_LENGTH], tmp, packets_received;
    - z1 P' B( Z, Y
  99.   COM_StatusTypeDef result = COM_OK;( g( I4 f: W; k2 c$ J# K

  100. " J! `! q) l' g6 W6 O& H7 H% ?
  101.   /* APPLICATION_ADDRESS是App的起始地址 */( k, c* K( b% |. D
  102.   flashdestination = APPLICATION_ADDRESS;3 t6 [' Y" j* y' E

  103. 5 |5 x, [: Z! R
  104.   while ((session_done == 0) && (result == COM_OK))
    3 M* _7 p/ t3 m6 Q0 p
  105.   {
    2 j: ^: `  j0 q% |+ |$ n% o3 _6 D0 E
  106.     packets_received = 0;       //记录接收到的数据包的个数
    * m; c; Q2 T' a% v( ?  w
  107.     file_done = 0;& s  l& w7 h: n  o' J$ A# O
  108.     while ((file_done == 0) && (result == COM_OK))
    0 s9 N/ o3 U1 Y* L' D
  109.     {
    & @& M' Z$ {3 s. p  a
  110.       switch (ReceivePacket(aPacketData, &packet_length, DOWNLOAD_TIMEOUT))) a8 `, n% M- ^# F5 y4 ~
  111.       {% t: b- p) _  h; @! n  O
  112.         case HAL_OK:
    4 S1 }9 V; y, o# R
  113.           errors = 0;
    5 f7 C- u3 u# k# R
  114.           switch (packet_length)) N" Y8 n& ~1 s" p5 O- u7 g
  115.           {6 D* r) E/ |: T  W" {" Q, @. L
  116.             case 2:
    5 @/ D) W( `7 J( u
  117.               /* 发送方终止发送 */7 g: b% Z; d# a- s5 E/ D
  118.               Serial_PutByte(ACK);
    3 y4 Z0 J% W9 I
  119.               result = COM_ABORT;
    6 Z% n& V  P" l+ ]; }% b. T9 V4 V
  120.               break;
    ( ~0 {8 ~3 }2 K. V  L
  121.             case 0:
    / \; v+ {$ p( O0 @5 r4 L
  122.               /* 正常结束传输 */
    ! y# w8 j4 q; i9 W5 G3 r. ~
  123.               Serial_PutByte(ACK);3 w; u8 F" m( u% I  A2 L# I
  124.               file_done = 1;+ `/ u2 `6 `# _; l+ H
  125.               break;
    : D, r- `; n+ d2 ]0 u3 S2 F' r( c9 n
  126.             default:
    & U5 K! D$ v3 y$ a
  127.               /* 数据包编号出错 */1 i! O1 Y- \$ l) I7 d% i: W1 B
  128.               if (aPacketData[PACKET_NUMBER_INDEX] != packets_received)
    . y, k6 k) e* S3 ?4 x6 r  U4 }# ~
  129.               {
    6 v" \% k! ^1 c* m$ l3 t; w) m+ j
  130.                 Serial_PutByte(NAK);4 }/ x! `0 m) E+ n- J5 R
  131.               }
    # ~" s1 O; |- M
  132.               else) ^7 [0 A/ w% v
  133.               {# p& {2 g- j5 I( Y' t. l
  134.                 if (packets_received == 0)" i* V8 y  n( N+ Y6 P: m: ?& K/ D
  135.                 {" ]* x7 n- y3 g- v, r
  136.                   /* 数据包编号为0,证明这是数据区存放文件名的帧数据,事实上这个判断是有误的 */
    6 |$ N& o+ S5 `' ~, M% j% c( H0 @2 R
  137.                   if (aPacketData[PACKET_DATA_INDEX] != 0)0 O; c$ Z  w" [1 }# f
  138.                   {* l, a. B/ T% B
  139.                     //读取文件名; |! g, k! Q$ M  R
  140.                     i = 0;
    : p- }" X2 [9 I' t# p: U0 a! \
  141.                     file_ptr = aPacketData + PACKET_DATA_INDEX;
    ' W2 s* l0 b! T  e5 g
  142.                     while ( (*file_ptr != 0) && (i < FILE_NAME_LENGTH))
    : R& p- b2 r9 l$ m, \
  143.                     {
    & L+ ?/ H/ j+ a
  144.                       aFileName[i++] = *file_ptr++;! C' S  |2 q! R2 e( `- u) T
  145.                     }9 X+ X8 C% u: `# b, r
  146.                     aFileName[i++] = '\0';
    ( e7 B. n6 `6 f$ v; c/ E

  147. % \: Y9 k  D/ ~
  148.                     //读取文件大小, R. l5 y$ F: Z+ ~4 D3 j+ f. s
  149.                     i = 0;
    9 g( l3 r" X9 K- O$ {9 f" T# D
  150.                     file_ptr ++;, w/ h% D7 A; C- ?( B
  151.                     while ( (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH))
    6 z: Q( t1 B" ]- K- e, E5 t
  152.                     {# f' s) q! P1 \/ S: |
  153.                       file_size[i++] = *file_ptr++;$ k& h3 {9 e7 Z/ H& f( t0 @
  154.                     }
    ) Q: Z9 q0 u' E, p9 i, G7 h
  155.                     file_size[i++] = '\0';
    1 P4 I, w9 h5 ^# d" h9 \
  156.                     Str2Int(file_size, &filesize);# w! Z% z' u1 r( J- i- e5 l4 B6 w
  157. ' E# }2 o$ z& r* U4 n! k  [* _& \- i
  158.                     if (*p_size > (USER_FLASH_SIZE + 1))  //文件大过于可供存储的FLASH的空间3 x2 {, A, H+ z) b
  159.                     {
    : B- C. s5 G  A$ V8 D- w6 R! Y0 \
  160.                       tmp = CA;- H2 L5 z; M' c& C
  161.                       HAL_UART_Transmit(&UartHandle, &tmp, 1, NAK_TIMEOUT);& W  m( v7 H5 p- l4 p
  162.                       HAL_UART_Transmit(&UartHandle, &tmp, 1, NAK_TIMEOUT);* H. D# G6 C7 Q& R% E5 g5 ]6 {$ i
  163.                       result = COM_LIMIT;
    - Q0 m% z: C& s2 K; d: V
  164.                     }
    - q7 T/ P/ @* x% Z& x
  165. + ?6 H, D* @( C% g# L: S
  166.                     /* 擦除扇区 */
    . z  ?5 B' f. y$ k. D& @# Z% l! A
  167.                     FLASH_If_Erase(APPLICATION_ADDRESS);4 p# u+ r$ w& y9 ~
  168.                     *p_size = filesize;
    " w; a9 v9 A4 X5 ]& S5 C" z' v% C

  169. ( m, a3 n; S7 c
  170.                     Serial_PutByte(ACK);. o- u6 b2 [8 o9 {7 r% o
  171.                     Serial_PutByte(CRC16);3 i! o% L! R: G' R7 q: F2 G
  172.                   }7 |2 p% s# y- @$ m0 y
  173.                   /* 文件头为空,传输结束 */
    / ?; J! W' w1 }) ?$ Q
  174.                   else
    / u/ p! n* z& V8 K, ?+ `1 T4 x' u- }; m3 ~
  175.                   {
    ! ?# U- V5 n% _% U& P
  176.                     Serial_PutByte(ACK);' H* A! c, t, Y; E9 w
  177.                     file_done = 1;
    6 E. w/ o! a( X+ j6 Z3 S9 }. u
  178.                     session_done = 1;
    ; u# W. N4 |! {" ~) ^6 q' A
  179.                     break;' ]" |" ?7 a. J( h
  180.                   }7 _, S& n$ Y, h  y; E# C
  181.                 }( V/ x* y! c7 t/ t
  182.                 else /* 真正的数据包 */7 g6 h8 {& ?' p9 P+ ~7 O# A
  183.                 {
    3 B2 z# X2 S" l
  184.                   //将收到的数据存放到FLASH
    ) l: C8 l0 u3 {+ ?5 Q* |; q
  185.                   ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];5 x' l9 s6 S5 n8 |; M$ v1 g
  186.                   if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)
    7 h+ m9 [; S1 S
  187.                   {
    . S  s7 s% g& Y' b0 |" i
  188.                     flashdestination += packet_length;: n5 P6 O* T4 X. X  ?! b( X
  189.                     Serial_PutByte(ACK);9 N! h8 L$ g- c
  190.                   }3 L4 M' m5 a2 ~# a% [( G
  191.                   else  //写入失败
    3 c% U" o/ U2 t2 t' v7 M3 L, M- t; ]4 y
  192.                   {3 O( o' c/ N2 D8 S- c+ I
  193.                     /* End session */
      V1 F1 J: P0 M- Z
  194.                     Serial_PutByte(CA);9 Y* w) W$ `9 ?& h2 e
  195.                     Serial_PutByte(CA);" Y. L9 n% m7 [& v3 m
  196.                     result = COM_DATA;4 T4 D* s! e+ a- s5 N" T( z! x
  197.                   }% h+ ~- G; d/ U* t- H6 [
  198.                 }$ Y2 L4 g4 x/ |" h
  199.                 packets_received ++;! H" \: }5 q! \. @
  200.                 session_begin = 1;) A3 A6 c% `$ V( @' w- B
  201.               }. ~$ W' c5 c8 C/ u( [  D& V! b
  202.               break;
    3 d' r+ P" V3 J- T$ b+ n3 g
  203.           }
    ( S! N$ v9 K0 F2 a# i2 Z: \' K7 B. [
  204.           break;7 @$ {; \8 x% t! e
  205.         case HAL_BUSY:
    . C1 s8 r" x2 R9 N3 k
  206.           Serial_PutByte(CA);! `1 s% f, a$ w( \  e
  207.           Serial_PutByte(CA);/ \( [+ W1 @- q8 }  t  b- {# k
  208.           result = COM_ABORT;
    9 C! O8 @+ B% v& v/ H
  209.           break;, z4 k8 a  H+ I" Z2 q0 R" D' N) ~
  210.         default:
    * D& q' y$ s0 E$ l9 z
  211.           if (session_begin > 0)
    - z1 m3 Z$ |* l9 j' q3 _4 m
  212.           {4 h: K/ I; \% V5 j0 i$ M
  213.             errors ++;  
    5 i+ V5 i% Y( R0 \
  214.           }
    0 r/ ^2 E# B3 D2 b0 @! t$ C
  215.           if (errors > MAX_ERRORS) errors大于MAX_ERRORS,PC端将收到"接收端未响应的提示",终止传输: t8 ?# v; _6 h: O
  216.           {3 z( Z) L- o8 J; n5 i- x( n. ?
  217.             /* Abort communication */
    & w* z2 X! L* f/ ~
  218.             Serial_PutByte(CA);5 J  f. R- X0 A. x# c2 S
  219.             Serial_PutByte(CA);
    5 c- }# k  i; F8 x
  220.           }
    . b* ]. k; A* N% O, M7 j% U; F3 T
  221.           else% L$ ]# O  w0 d
  222.           {  M  Z, Q/ K) ]( M4 a4 ?# m) P
  223.             Serial_PutByte(CRC16);  //返回'C'字符,PC端提示接收端未响应并记录次数,超过次数PC端也将提出
    6 T, e: ~# \4 l4 q
  224.           }
    1 S  _0 }( U; o' Z; m
  225.           break;
    $ }9 G  M0 P& H7 a
  226.       }' m, O/ W3 Z$ }) ]& k' B* X% v9 b
  227.     }1 E2 ?# `$ m5 D+ ?8 k
  228.   }
      s8 x% S+ s4 w
  229.   return result;
    , W  K  S# i5 m/ t
  230. }
    6 X4 E5 `4 v& {8 D0 L* x
复制代码
% K+ V9 }2 q7 G% o$ u
事实上,对数据包的接收处理操作是有问题的:packets_received用于记录数据包的个数,它是uint8_t类型,取值是0-255这确实是符合ymodem协议的,但是超过255的数据包呢,观察上面代码可以发现并没有对第超过255个数据包,也就是第256个数据包的处理。第256个数据包的编号也是为0,会进入:- h% y1 Y. `% @# y2 [9 F; G

4 c! ~. }$ P$ N4 ]2 r: _! \: I8 k
  1. if (packets_received == 0)
    8 T$ D8 N, `) g( r( y" a; V; H
  2. {
    ( U1 \: G" _! _6 K  U! M* W3 m3 c; |0 w6 \
  3.     if (aPacketData[PACKET_DATA_INDEX] != 0)
    7 i, f! H4 j3 G  M) `# J  ^+ t% T- \
  4.     {
    6 I) g' j; Z# A  }) H
  5.         //进行读取文件名、文件大小、擦除FLASH操作) Y! M. c$ K# T
  6.     }
    & k+ c: H7 K5 A
  7. }7 [) x# f, H2 P" M: L6 z$ I! O
复制代码
3 s, j2 ^5 m& Q5 A" N, r! B3 U( A
但是编号为0的第256个数据包的数据区事实上是数据,收到该数据包还是要以真正的数据包的写FLASH等操作。
+ g5 X$ o* C$ q
, R: Z! o, \% M) K5 f, X8 e7 \  一开始我利用IAP传输小于256K的APP的时候是正常运行的,后来传输400+k的APP就会出现问题:无法跳转至APP。经过多番调试才定位于此,所以简单修改上面的代码,代码片段为:) ^' o& X/ {" }$ y* a

9 `, `4 Z' J8 X( g/ V
  1. volatile int8_t is_first_pack = 1;9 B, v% A4 l5 J( _" s
  2. switch (RPreturn)
    & w; w% ~/ N- w+ ]
  3. {
    8 e, I  f. q6 I: T- g# z
  4.     case HAL_OK:
    4 k( ], E2 d: g$ l* w7 B
  5.     errors = 0;4 v$ M4 B9 _+ x
  6.     after_isp = 1;
    7 {/ r$ Z; s$ M9 [9 b5 s0 N
  7.     switch (packet_length)
    ' k. l8 @, N+ u7 z1 Y9 a
  8.     {3 b" H. P# g; H( [% v: I
  9.         case 2:# o& F0 D  v7 `  r
  10.         Serial_PutByte(ACK);) M' j8 z0 O7 Q2 W
  11.         result = COM_ABORT;- {6 B. E3 _: b7 E) B/ T$ A' ?
  12.         break;
    4 a4 Q) c" W6 B* X8 U( H
  13.     case 0:+ H& ~7 s$ V* W) Q' g& {
  14.         Serial_PutByte(ACK);        & H6 g8 h% n; W. e# Z( Y
  15.         file_done = 1;9 J$ p2 B  M7 R- Z7 _- K6 n! \, X
  16.         break;2 e1 U. W1 J' m0 J
  17. ; C, V! ?% W+ d" W+ n% T+ u
  18.     default:
    $ L  z  V. [8 h. `( m) {
  19.     if (aPacketData[PACKET_NUMBER_INDEX] != packets_received)   //ÅжÏÊý¾Ý°üµÄ±àºÅÊÇ·ñÕýÈ·
    , O2 r* Z( ?/ T, r8 ~9 }9 e
  20.     {
    ! y& A, Z5 h- t+ d3 L
  21.         Serial_PutByte(NAK);
    , d. `4 g/ L& ]+ |2 D
  22.     }
    2 [4 }& u& s! a
  23.     else
    ' c6 _$ y0 w* T* ~$ Q/ a
  24.     {7 J4 h2 n  e. g( j# |- ]1 `6 L
  25.         if (packets_received == 0 )
    8 ]+ z8 F! |6 Q# Q) {$ z) Q
  26.         {                           " H& @- b0 ^5 a: r( p! D" T( `
  27.           if (aPacketData[PACKET_DATA_INDEX] != 0 )' `* P4 G5 b2 w5 k# t6 K" q- w
  28.           {$ T# y6 A% W# A' w; f/ [1 S
  29.             if (is_first_pack)  //第一个编号为0的数据包
    " {' h7 E4 J! K, Y0 r5 I
  30.             {
    ! ]1 w# c' d3 R8 ]4 X
  31.                 //...               7 E0 a$ r# Y( j  z
  32.                 is_first_pack = 0;              ! M# |$ W# Y# T! d# v- g3 v6 l
  33.             }
    / a& Q1 C, X/ B" S% c$ m& u
  34.             else    //即使数据编号为0但不是第一个数据包,采取存储操作          ; k5 Z5 Q" Q6 ?% j! G# L$ a! B
  35.             {
    ; g# a/ H& s- O# g: g9 a% g
  36.                 ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];        , ~# v" I  C" ]0 W+ ~0 B
  37.                 if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)$ f+ |7 F6 }. m* ?/ V* x2 N/ [0 T
  38.                 {
    5 q/ |! f( ]# \2 o/ M
  39.                     flashdestination += packet_length;
    6 J# y6 x! Y  r1 n( j) ^9 K
  40.                     Serial_PutByte(ACK);. [" s. U) @' |! V# b5 v+ D# d
  41.                 }
    / A  _9 t4 t( p* O. y; f% t3 |
  42.                 else4 S2 F' d8 P) Y: c2 s( g; t- w/ Q
  43.                 {. u2 y: k$ p# `& o: \
  44.                     Serial_PutByte(CA);
    - I5 s$ u# @- z; j" ~" V( d9 z
  45.                     Serial_PutByte(CA);
    3 Y7 D5 h( Z5 `5 n1 c# ~/ U
  46.                     result = COM_DATA;
    ' B4 T, c! X8 l
  47.                 }                ' Q. J8 l% d4 d: Q8 K6 ^! j
  48.             }
      H, h* I6 O$ a5 b
  49.           }
    * R6 O7 o2 o7 ?. U( }5 L
  50.           else ' e$ p! r: F( T2 \
  51.           {
    - |% {& N4 ^0 H5 T! U7 V2 ^) ?3 V
  52.             Serial_PutByte(ACK);
    % B3 d+ E. b$ p3 [; B3 C
  53.             file_done = 1;* K' \$ O0 c( W: H) l$ B- W
  54.             session_done = 1;
    & [) I' D" {/ D3 J# _) N# h- C
  55.             break;$ m* Q4 w; `/ ^3 k$ s" u' \
  56.           }! O$ H& Z" B4 Q( I$ P4 O
  57.         }
    # R" a6 y8 O4 A# G, {; A' A
  58.         else
    . G* l) {$ N# ~1 L" S
  59.         {4 c0 j3 T6 v% P" H: s6 o

  60. ; G7 n6 w8 C- P9 j* B' y7 x' P* h9 x
  61.             ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];                        1 A" T* S1 S6 @, I# \% R
  62.             if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)
    2 m# p# n- d  u
  63.             {' p! K' X& s( ?
  64.                 flashdestination += packet_length;0 \" O* A/ O9 _
  65.                 Serial_PutByte(ACK);. V. L% x/ }  H0 f3 K
  66.             }
      S& d0 n" t* t2 J
  67.             else$ c2 z$ j7 b0 l6 h
  68.             {; P* K" C% e. e0 C" l. ^: i5 J+ o
  69.                 Serial_PutByte(CA);
    0 @4 H' t: i+ k' M7 P
  70.                 Serial_PutByte(CA);' x7 s! n4 v# B. f: t( R3 U
  71.                 result = COM_DATA;) ?1 q5 X1 m# k  a
  72.             }        , _  B0 h' G' `  L8 h( P! [
  73.         }0 B2 H* |& I0 @1 P4 A
  74.         packets_received ++;
    ( x9 ~* E1 z9 F* {
  75.         packets_received = packets_received % 256;7 k7 Z3 J% r& U" f! h! g
  76.         session_begin = 1;
    $ v  e' H# f9 r* i4 q+ Y
  77.       }
    6 G. N! m1 L; a, q$ r  `
  78.       break;
    / w  j" T' A$ U
  79.     }
      Y% T) E4 Y/ [2 m1 L2 |/ k* a  u
  80.     break;
    , I* M+ {$ t1 P, X
  81.     //..
    4 V6 u4 @% C) r* k1 M
  82.     break;
    2 ^& n2 A. ?) G, a/ n1 @/ n* r
  83. }
      b8 u( w: |6 Q( D6 a3 |+ Q; J
复制代码
# A. c# ?# t. ~3 C& c
3. Bin和Hex文件的差别
" q2 u- _% `! O8 N5 i9 y5 l. G" S; ^0 x  g0 a5 I  s9 x
       第一次接触Hex文件一般是在学习51内核单片机的时候,通过烧录器烧录到开发板上使用的就是Hex格式的文件。Hex文件是以ASCII文本形式保存编译后的二进制文件信息,注意这里强调的是文本文件(而非数据文件,即二进制文件)。文本文件是人们可以看得懂的文件,但是计算机/MCU只认识二进制数据文件(Bin文件),Bin文件才是MCU固件烧写的最终形式,也就是说MCU的ROM中烧写的内容完全是Bin文件。由此可得,我们通过烧录器烧录Hex文件到单片机的ROM时,烧录器其实会将Hex文件的数据转为Bin文件的数据,最后才烧录到ROM。
$ {9 \! V5 O& N4 L$ J( B3 m. S8 H) c; C  v. o1 `
  其实明白这一点我们就知道在IAP工程中,APP文件需要是Bin格式的文件而不能是Hex文件。既然Bin文件是MCU/计算机最终想要的,为什么我们不直接生成Bin文件,而却要生成Hex文件?其实Hex文件保存的不仅是Bin文件的内容,还有一些附属配置信息,随便拿个项目Hex文件分析:/ A( @; S4 ~' N8 Z

& w+ r. l' Z( ~$ b6 ^

  1. & r( E9 F: k. G: D1 F
  2. : 10 9AB0 00 6841298459D0420F9AC4641EFBC10408 2E
    + m4 i& F+ F7 z+ B: o- W
  3. : 10 9AC0 00 E7604BC5CC64011029B85A6980413C55 08) S% k5 b/ o. Z, N2 a' A
  4. : 08 9AD0 00 55557C2964291C81 15
    ' }0 j6 u& o# T/ T+ e2 I
  5. : 04 0000 05 080081AD C1* @$ X) i9 c# A' q9 `
  6. : 00 0000 01 FF
复制代码
5 e6 `* i2 q. h& B+ m5 l
Hex文件中的数据是ASCII编码,所以是人们能看懂的。上面3行内容,每行都是以’:’开始的,之后是数据长度、地址域、数据类型、数据域、校验和。
0 u' o" c8 c& a3 i) a
" B4 R& A7 `' h
  1. /* /-------- Hex Data format -------------------------------------------------------------\6 [9 }/ g( c( }
  2. * |   0     |  1     |  3、4  |    5     |    6   |    7   |  ...   |   n     |   n + 1  |
    - l* T* o; l8 A, G7 e9 Q
  3. * |--------------------------------------------------------------------------------------|
    & y+ Q. \; r1 ?
  4. * | : 开始 | 数据长度 |地址域 | 记录类型 | 数据域 | 数据域 | 数据域 | 数据域n |  校验和  |
    ; h# G4 Q" ~5 S; \1 L* ^* Y
  5. * \--------------------------------------------------------------------------------------/
    6 s2 S* q4 O( }) d4 _
  6. * 每行数据都是以冒号开始的                 */
复制代码
+ x* m6 x9 X7 O2 q5 W
注:记录类型的意义   V" K, {7 f, d, g9 e) ?% b
  (1) 00: 数据记录 & ]- C0 d: C" {3 _( _
  (2) 01: 文件结束记录 ) Y% C$ C/ x$ C' o
  (3) 02: 扩展段地址记录 ) ~% a' D( e6 e: F# B
  (4) 03: 段开始地址记录 ( `5 s( r% X8 {% o; O1 \
  (5) 04: 扩展线性地址记录
( U7 K* T5 Q2 G: Q7 K3 T$ Q/ s6 }8 f  (6) 05: 线性地址开始记录
& G: _( j+ t( [3 y0 [3 C
  b; o4 C) C& }- q  由此可见,生成Hex文件的意义在于: 5 h. Z, p  u+ ]4 _$ n, N2 q
  (1) Hex文件使用ASCII文本保存固件信息,方便查看固件内容
# e9 Z% N# w$ X# P  (2) 文件内容每行的校验和与最后一行的文件结束标志,在文件的传输与保存过程中能够检验固件是否完整* _. U* Z% S$ K: w. `' _) P4 S5 Y
) C/ X" p) B" u! x/ ^1 g; V
  因此hex文件更适用于保存与传输。相比之下,Bin文件纯二进制文件,内部只包含程序编译后的机器码和变量数据。当文件损坏时,我们也无法知道文件已损坏。不过在IAP中,Bin文件仍旧是不可替代的。; n2 \5 J$ p0 L# ^% r2 [
- w( m: ~7 h) p/ {3 v4 H
4. RS485通讯, J7 u; h3 Z& S7 a) F! v
6 n; X  C, q8 r4 k* {9 P$ T5 J) P
  从ST官网下载的的IAP SDK是基于RS232通讯的,即ymodem默认是从串口接收在APP数据的。但是我这个实际项目中用到的是采用RS485的通讯。RS232转为RS485,在软件上只是多了一步方向控制操作。因为RS485是半双工通讯,所以在发送单片机需要发送数据时需要将RS485总线设置为发送状态,接收数据则需要设置为接受状态。关于收发控制,软件上实现只是拉高/拉低对应控制RS485控制芯片的接收状态的GPIO即可。我的做法是默认是接收状态,当要发送时在发送函数内切换为发送状态,函数退出之前又切回接收状态。
7 M# \& t# ^, s4 L; N1 M0 ?, e) J; Y* a( i0 }- v
5. IAP的main代码片段分析0 N/ s' ?& \# q1 M

: @* s( c( v6 |2 k5 k       IAP升级代码工程,一般需要基于一个能跑起来的通讯(这里是指RS485)和LED灯(用于指示状态,当然也可以用LCD等其他提示状态)运行正常的工程代码。下面是main()函数:% i9 D6 t1 x& U, O

5 A; [0 I0 n" M- ?* B5 r$ I5 [
  1. void SerialDownload(void);/ Z5 S8 ~2 X. R& b/ K
  2. " ?  o5 a: U) _$ C
  3. //定义函数指针,用于跳转到APP: z$ z# s( c, X8 q9 L4 B
  4. typedef  void (*pFunction)(void);   % |% l3 e# h3 e: u; I* ]' \- z' v
  5. extern pFunction JumpToApplication;! ~9 T6 _- G' F& p2 W2 \
  6. extern uint32_t JumpAddress;
    * v( i0 w* s. L9 J
  7. UART_HandleTypeDef UartHandle;
    * g& ^$ r+ l% \1 a) t
  8. 2 Y* X7 k* r' G, @; I0 F9 A6 E
  9. int main(void)
    9 |# [% ~; v& D/ u2 D2 Y
  10. {5 u$ n3 K; I& J4 x( h
  11.     HAL_Init();6 D  ?5 ~" [9 \4 Z
  12.     SystemClock_Config();5 Z4 s6 [3 N5 j1 ~$ S" A
  13. , Z( M: B. M: t2 w
  14.     LED_BSP_Init();" J/ B5 P1 h8 d- Y% P
  15.     FLASH_If_Init();% j& j' p' `5 T  r! J) S
  16. 9 ]6 i* O/ e+ K$ z& b1 q# ?
  17.     //初始化RS485: D9 r& W% z/ S. a% L9 e& [$ c
  18.     UART_Init();) D6 I- J1 ?; C
  19.     RS485_RX_ENABLE();      //默认设置为接收状态
    8 x; _: w* y; c
  20.     HAL_Delay(10);. q9 s- B" J* n3 H; L1 y7 r% U
  21. 8 ^/ z- q, H1 e7 p# F; }& Q1 X
  22. IAP:   
    . }+ F' p- c- r/ Y! g7 X3 u: ^1 p
  23.     Serial_PutString((uint8_t *)"\r\n====================================================================");/ ?$ f+ N" o& H5 K
  24.     Serial_PutString((uint8_t *)"\r\n=                       IAP For STM32F429xx                        =");8 [+ p. y8 k# B& R3 p. C
  25.     Serial_PutString((uint8_t *)"\r\n====================================================================\r\n");3 s( ^: h/ q6 V
  26. : m/ e$ q: o, _3 d6 n/ Q8 R; w
  27.     SerialDownload();   / q" p1 N7 ]) d2 M
  28.     HAL_Delay(10);
    ) u, s; m8 I3 C' f

  29. - D! I, a  r- s8 [( h/ f# z
  30.     //APPLICATION_ADDRESS是在FLASH中存放APP的起始地址5 C! j* u! i' ]7 k, r
  31.     //此判断是为了保证APP的栈地址是在SRAM中。其实结果并不一定是0x20000000。有些APP可能定义的全局变量较多,那么栈的起始地址会偏移2 F; L; K0 q9 j  n
  32.     if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)   . N8 h3 }2 @3 U. z: X6 w" [
  33.     {( {  Z; r. g1 B% W6 e

  34. 4 O( y0 i' O) m# v% i/ N" h
  35.         Serial_PutString((uint8_t *)"\r\n=======================  Run application  ======================= \r\n\n");
    " O7 c* f+ |9 G; e0 ^2 p/ d
  36.         HAL_Delay(10);
    0 f  G' ]* L' T% S  p9 W. [6 ~
  37. # F' v7 p! J( i& m
  38.         /* Jump to user application */: N6 U0 P. S7 ?+ G7 V3 C2 o
  39.         JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);- ~1 z8 i1 q8 T( f/ V6 V
  40.         JumpToApplication = (pFunction) JumpAddress;$ B% G# s$ k1 L, n7 L
  41.         /* Initialize user application's Stack Pointer */- i6 }1 P3 z$ i: ]
  42.         __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
    - W- D% \8 ~2 f
  43.         JumpToApplication();( G2 A9 R4 Y$ q5 d/ }- X
  44.     }8 v' ]% S* V5 m1 h: p+ D
  45. 0 j3 i: T2 }) E3 d% b
  46.     Serial_PutString((uint8_t *)"\r\n ======================= Download error, once again ======================= \r\n\n");% l! b" Y( J0 u: ?9 v- M0 C5 x
  47. goto IAP;  E0 A0 f- C+ U" D3 k
  48. }
    . t1 A9 N3 F- `
  49. 0 r) \$ V, o) W+ G+ l( D% L' Y/ g
复制代码
2 V  e% R7 ^; K2 d' T( \3 v
IAP的实现还是十分简单,但是中间走了N多弯路,特别是在调试ymodem接收大于256kb的APP上。关键要注意上述几点内容。当然,上述内容属于个人见解。
  l8 L7 A) v  _& C& r
. U9 ~$ j: q9 h% e: Q. L6 w. |$ U

4 ^/ [* x, i/ M4 `8 Y转载自hsade" J$ v2 H: d' e! \2 n

, M: D6 L4 p* d
, l* O3 f3 i8 I+ n% ~; r

" c. f/ c! K( v5 n- I  ~! P+ X1 L, O0 K; C; h7 I% q8 v* A

& p) j  [* v: H) G
收藏 2 评论0 发布时间:2018-5-22 09:44

举报

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