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

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

[复制链接]
aimejia 发布时间:2018-5-22 09:44
本帖最后由 aimejia 于 2018-5-22 11:19 编辑 1 r# P9 \' G& ?. b

1 q6 |, Y2 V8 s最近一周一直在基于STM32F429项目的IAP工程,耗时4天才完成,得空记录下来。' q7 s" N% j4 S3 B

9 b; D' W" Y6 ?+ F* n3 |, n, A文章主要涵盖了以下几点:
8 u. Z5 D9 B& \( Q1. IAP是什么?$ q8 j$ S* q7 K7 V" V9 j. R
2. bin文件和hex文件的差别
0 Y4 u9 i, b9 ]7 Q& O3. ymodem协议介绍及其缺陷
: Q( u& k# {3 Z! k4. RS485通讯; r# }* ~* B* D
5. IAP的main()函数代码片段 # d! S1 e8 J: b; g" s. h

2 C2 ?- S1 L5 b) X9 A项目的框架如下:
4 d. r9 `7 l: N& w' d  r* z* v0 W+ p( N
  1. ymodem协议+ X; A8 R; B6 V4 v" f0 u
  2.     PC机_超级终端 -----------------> STM32产品的串口! A  a# _& S/ j/ y) ~8 r
  3.                    RS485通讯线
复制代码
/ g5 y) f. H: X. T3 y  t1 R( ]5 }
从ST官网下载的IAP的SDK,其中包含了经典的ymodem协议和基于STM32F429的HAL库,工程就是基于该SDK开发的。
5 d9 X2 Y' f$ B' V1 T( p* `7 w- i
, F7 S6 V2 L' _1. IAP的概念
' R5 E/ ~; K8 E! P# o" K+ C9 r0 @+ ?# N8 t7 ]! P
  IAP的具体概念解析网上搜索一大堆,在这里简单描述。IAP(In Application Programming)即在应用编程,常听说的还有ISP(In System Programming)即在系统编程。ISP指的是将通过JTAG等接口将单片机程序烧录进单片机的FLASH(当然也可以是其他存储介质,如SRAM),而IAP指的是采用引导程序(Boot) + 应用程序(App)的方式烧写单片机程序,App是真正实现业务逻辑功能的代码。  c( O. N: k2 ]1 x9 s' o
/ E1 ~8 [* v" {: |
  一般产品的调试口,也就是JTAG口是被置于机壳里面的,烧写需要打开机壳,也需要专业工具和电脑桌面软件。IAP的Boot程序通过ISP的方式烧录到单片机的低地址的FLASH处,每次单片机复位后会先执行Boot程序,在Boot程序中进行判断,用户是否要升级,若是则从串口(或者网口/CAN通信口)读取App程序写到高地址的FLASH,读写完毕后再跳转到FLASH上App的起始地址,执行业务逻辑功能代码,若否则直接跳转到App的代码处理:
2 {: H% ]4 E4 t! Q3 z; n
  1. |-- 要升级 --> 读取读取串口发来的APP程序,写入FLASAH目标地址 --|
    , K( y/ x* a. h+ q! ^
  2.                              |                                                              |; Q) A& h* E( z+ P4 P
  3.     Boot: 判断是否要升级App -|                                                             |! D: }# I. s5 z. R7 D* I4 A9 [3 F
  4.                              |-- 不升级 ---------------------------------------------------------> 跳转到APP程序起始地址处理
复制代码

' @. o3 m2 F2 T; x" C) `Boot和App都是单片机程序,只是实现的功能不同,前者是为了引导App,后者是为了实现业务逻辑功能。这里有一个关键的动作,就是跳转,即从Boot跳转到App起始地址处。
1 k/ E  M5 P% d7 S4 B3 L5 j0 S5 {/ w2 q* c6 j
  需要清晰2个概念:
- y  n  x+ w' V4 s+ [2 ~  (1) 程序的起始地址 $ G5 h) Z7 A8 k! i7 y
  程序的起始地址默认是被放在FLASH的起始地址处,即0x08000000: , u. `1 h) z2 z1 E1 ~+ `2 R
/ H# r/ ~. }( V
1.jpg
4 E7 Y  W* Y3 ?0 uBoot是放在这个默认起始地址的,App则要往后移动,这里设置为0x08008000:
  \+ C% W) m# u  N/ P' ]1 H
3 F1 H* R9 [# ]7 P 2.jpg
1 g' V- U3 e+ F需要注意,这是在App没有采用分散加载时设置的程序存放起始地址,若采用了分散加载,则需要修改工程中的.sct文件。详细内容可参照杜春雷的《ARM体系结构及编程》。7 m4 M( V" b# L9 _+ M% g, a, V2 n

$ n5 {' l8 w5 b' t3 {. K0 ^  (2) 中断向量表的地址
6 [: ?/ S8 R# u+ y. t+ _; e  对于STM32来说,每个单片机程序都有一张中断向量表,也就是说,在存有Boot和App的FLASH上就有两张中断向量表,Boot根据Boot程序中的中断向量表发生中断跳转,同理,App就要根据App程序的中断向量表发生中断跳转。中断向量表的摆放位置正是程序的开始地址。所以需要将App的中断向量表的摆放位置放在0x08004000。在system_stm32f4xx.c中:
& x) c- i! J. E0 y0 ?- h4 l( U$ o3 m9 I6 J$ o5 D7 [% G
  1. #ifdef VECT_TAB_SRAM
    1 `# ], W5 W& T
  2.   SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */2 r' U) O6 p( n. W% T
  3. #else/ v( [8 e0 V( `% z6 {
  4.   SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */4 V$ U+ a& Z( A; o. U$ t2 t/ d
  5. #endif
复制代码

. \' x8 Y2 Z5 ~程序运行时候发生中断/异常/系统调度时就会去读取SCB->VTOR以获取中断向量表的地址;FLASH_BASE即STM32F429的(内置)FLASH的起始地址:
6 L* `" G# O" V( {* I9 Q8 T5 U3 H3 r% ?
  1. #define FLASH_BASE  ((uint32_t)0x08000000)* [( \, p7 ?; o
  2. VECT_TAB_OFFSET是偏移量,所以我们可以通过设置此值指定中断向量表的存放地址。8 O3 F) T$ v& `4 M. c3 G

  3. ) g9 @, g8 f0 ]2 O( u# m: l
  4.   讲到这里,顺便介绍STM32的中断向量表。表的形态如下:(摘自startup_stm32f429xx.s)+ u7 {0 |+ e: y7 H) p" X& m+ Y

  5. + I) B1 @$ ?# a
  6. __Vectors       DCD     __initial_sp               ; Top of Stack
    0 W% m* M) V" X9 \% Z+ W
  7.                 DCD     Reset_Handler              ; Reset Handler& `( _0 m9 n1 Z  z/ V
  8.                 DCD     NMI_Handler                ; NMI Handler; _: ~& I- _9 d) p! @5 y
  9.                 ; ...5 R( C- s( ?) K) j3 z% ]
  10.                 ; External Interrupts
    8 N. V) p& z6 X" B  l) z! c
  11.                 DCD     WWDG_IRQHandler                   ; Window WatchDog                                       
    - X$ C, t7 k8 s3 R7 l: I+ }
  12.                 DCD     PVD_IRQHandler                    ; PVD through EXTI Line detection                        8 |/ Q0 A9 u/ \* x7 h
  13.                 ; ...
    ; D- `1 c1 f' J' [
  14.                 DCD     LTDC_ER_IRQHandler                ; LTDC error6 h) \/ q; [$ ]8 G  c, @
  15.                 DCD     DMA2D_IRQHandler                  ; DMA2D+ W' a, O; z% a% N1 |, L  o

  16. 8 ^1 n. Q7 U( A  R
  17. __Vectors_End
    ! D6 s+ N: ?' P9 U) v
  18. __Vectors_Size  EQU  __Vectors_End - __Vectors
复制代码

( Z( ^4 w. J& K显然,Boot程序结束后程序运行指针跳转到App的__initial_sp处,__initial_sp处存放的是App的栈的起始地址。中断向量表的作用是当程序发生异常/中断的时候会根据表中的标号而跳转到具体对应的中断处理函数(ISR)。: f4 D% Q2 d- \; r
2 \& R2 w9 {) L
2. ymodem协议介绍
  H  }" E; }& x" o' |
. N0 d8 ]6 M# O, n* ~  关于ymodem协议的接收网上资料甚多,不做过多的介绍了。简单分析ymodem的数据包格式:
, e- g! \. a* j
5 }9 _8 k: b! j5 \  P  y6 L# K) K
  1. /* /-------- Packet in IAP memory ------------------------------------------\
    2 L1 W+ `" K' U8 J* @4 E/ p5 X
  2. * | 0      |  1    |  2     |  3   |  4      | ... | n+4     | n+5  | n+6  | 4 w  w/ s' E. H" I
  3. * |------------------------------------------------------------------------|2 x" K+ g5 B6 }" M. O
  4. * | unused | start | number | !num | data[0] | ... | data[n] | crc0 | crc1 |
    9 @- ^7 M& g: w5 a/ R+ o$ c
  5. * \------------------------------------------------------------------------/: t3 h5 P) ?" ~) f% A. j
  6. * the first byte is left unused for memory alignment reasons                 */
复制代码

$ \' A0 m4 |' @: E- ]; U      (1) unused:无意义,用于字节对齐 & M; `6 N4 O( H# f
  (2) start:起始信号,”SOH”表本数据包的数据区有128字节,”STX”则表示有1024字节
7 f( y' Y8 n: k, O, x. a  (3) number:数据包的编号,编号为0x00-0xFF。到数据包编号到达255后,将会从0开始计数。
! S4 X: k2 K7 r# H# A5 C5 {+ d) m4 @  (4) !num:数据包编号的反码 / b# H4 @4 J  X, V; P/ J/ `" M1 j
  (5) data[0]…data[n]:数据区。对于第一个数据包(编号为0),存放的是文件名 0 e; c( c9 U/ M$ w) z5 h
  (6) CRC0、CRC1:校验码(只有数据区参与校验)
2 ?9 \: j5 T7 L3 W- e  下面是通讯流程: ' B- s! Y8 p. c# O
  (1) 接收方发送一个字符’C’,也就是十六进制0x43。代表接收方已经处于接收数据的状态
- h2 P# T6 I- C8 d$ A% Z  (2) 发送方接收到’C’之后,发送头帧数据包,数据包格式如上所述,此时数据区的数据是文件名和文件大小 7 a& U( U# i$ O2 G
  (3) 接收方收到数据包后发送ACK应答正确,然后发送一个字符’C’,发送方收到’C’后开始发送第二帧数据,第二帧数据即使第一个数据包 % g# F% g/ Y" p5 b- Y
  (4) 接收方收好数据包后,发送ACK正确应答,然后等待下一包数据传送完毕,继续ACK应答,如此循环
" V# B; @# C1 S  (5) 数据传输完毕后,发送方第一次发EOT,第一次接收方以NAK应答,进行二次确认
. T5 T, p" ^0 S$ y1 N6 z; ~, L5 Z  (6) 发送方收到NAK后,第二次发EOT。接收方第二次收到结束符,依次以ACK和C做应答 . I5 v) k- ?1 k! k
  (7) 发送方收到ACK和C之后,发送结束符
6 K4 O$ |8 [, x$ d  `  (8) 接收方收到结束符之后,以ACK做应答,然后通信正式结束
, d5 ]: u, k; d# b, Q% N+ U5 g9 B% J8 {7 Q5 {
  ymodem协议除了接收数据包外还会将数据包按照指定地址写入FLASH。本SDK使用的是HAL库。
# a3 A1 |9 {& _  这部分的实现在ymodem.c的Ymodem_Receive()函数中:1 f5 }3 [& a; w& P

. B: j$ r( z' w% ~% h- }6 f
  1. #define PACKET_HEADER_SIZE      ((uint32_t)3): _' q% i, `  @; n# F6 D
  2. #define PACKET_DATA_INDEX       ((uint32_t)4): C- ?: k$ q; \
  3. #define PACKET_START_INDEX      ((uint32_t)1)& n& N" w$ [$ t. v4 ^) t5 d
  4. #define PACKET_NUMBER_INDEX     ((uint32_t)2)7 p$ x- H; Y3 `6 C6 R4 R
  5. #define PACKET_CNUMBER_INDEX    ((uint32_t)3): m& U2 x1 N! [8 ~3 Q
  6. #define PACKET_TRAILER_SIZE     ((uint32_t)2): S1 c0 M3 e! ]# g8 h
  7. #define PACKET_OVERHEAD_SIZE    (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE - 1)* P; B( ^$ |  m  `3 k
  8. #define PACKET_SIZE             ((uint32_t)128)# Z' w1 {4 I3 x1 }. W1 U$ I
  9. #define PACKET_1K_SIZE          ((uint32_t)1024)
    7 m/ ?0 H) _. |9 L
  10. ' \: q$ e3 n9 }6 ^& W. V
  11. uint32_t flashdestination;
    3 M1 I, _: B6 B/ T' s
  12. uint8_t aPacketData[PACKET_1K_SIZE + PACKET_DATA_INDEX + PACKET_TRAILER_SIZE];
    - t; g/ M1 P$ e, l; V( l7 S

  13. 6 J- l( m* k1 @
  14. static HAL_StatusTypeDef ReceivePacket(uint8_t *p_data, uint32_t *p_length, uint32_t timeout)) g6 [+ }# Z) i/ A; X. |6 U" ~
  15. {
    ' E7 E6 N9 z( d- ~
  16.   uint32_t crc;
    7 z6 Q$ M4 x5 K( [
  17.   uint32_t packet_size = 0;: s$ j' \, d: _/ a9 i4 ?$ N
  18.   HAL_StatusTypeDef status;1 ~; \1 E8 N8 H& O
  19.   uint8_t char1;
    . O' x& ]: q0 I5 ?9 W; O7 V' Y

  20. 5 O9 w$ {% L5 m8 P2 E3 H
  21.   *p_length = 0;
    ) A! a+ a$ t& S- N( T9 V( g
  22.   //调用HAL库函数接收字符,UartHandle是数据包的句柄
    8 U1 {/ d& }9 P6 S) z
  23.   status = HAL_UART_Receive(&UartHandle, &char1, 1, timeout);5 L6 _6 l) B: z# D& t- n
  24. % X' X- [8 ^5 f- D3 `% k
  25.   if (status == HAL_OK)$ e7 N; I( j  \4 q$ F# X
  26.   {
    1 C1 S' [2 {, o/ P3 Z% q
  27.     switch (char1)0 e( c7 O" U$ @+ h% g. r
  28.     {1 Y7 `3 c" N2 p; J/ V
  29.       case SOH:# R! j8 r6 M9 ]
  30.         packet_size = PACKET_SIZE;      //PACKET_SIZE为128* @, [8 [8 Y) \2 f8 p
  31.         break;) ]% a) d9 c" m9 r* j
  32.       case STX:/ ^+ R6 }, V& v+ J
  33.         packet_size = PACKET_1K_SIZE;   //PACKET_1K_SIZE为1024
    % U$ K8 S9 M1 z" f' R
  34.         break;
    + Z4 v" D! }9 L8 x" U! X; a! S9 E
  35.       case EOT:                         //数据传输完毕5 B% {2 R/ P! |1 r
  36.         break;0 F* C& L+ Q7 `% Q3 V8 _8 g) Z
  37.       case CA:" U. h+ D5 {! w- B4 U- b9 Y4 Z9 S, O6 E
  38.         if ((HAL_UART_Receive(&UartHandle, &char1, 1, timeout) == HAL_OK) && (char1 == CA))
    ' s) I: z) G- u& J( e# v
  39.         {
    3 F5 [& ~( s. I- r  j
  40.           packet_size = 2;2 s3 c% D/ V" T0 p3 K
  41.         }) F1 X* _3 L$ r1 ?) ~7 s$ b4 q
  42.         else
    5 L% f, E% V2 N% R
  43.         {
    # L0 U! E5 E: c8 d2 E
  44.           status = HAL_ERROR;* o6 k6 r' {" Q
  45.         }
    8 c, z- I* S4 l, ?% F8 O
  46.         break;, Y0 ]! f2 R2 p8 p, V' l& }$ x, D
  47.       case ABORT1:
    8 x, w/ C, R! a4 c
  48.       case ABORT2:0 O2 j: r: _; Y* M/ g- F
  49.         status = HAL_BUSY;
    6 O# [. h% U# ^3 u6 ~
  50.         break;
    + B' F# s% w7 Y- D. Q# K; d0 d3 k
  51.       default:3 B1 B8 i1 ^% N9 a
  52.         status = HAL_ERROR;
    : e7 r) K" B5 c; s+ \4 r7 \2 Q3 x2 J
  53.         break;
    : |5 d  d# v6 B" O% g, Z
  54.     }
    ) }- c/ x6 n$ x( C: a1 Q. ^# W
  55.     *p_data = char1;" i# l; M' A9 f+ w: z0 p2 a: L
  56. - V* k8 j$ g0 |7 ~, d
  57.     if (packet_size >= PACKET_SIZE )
    / i+ @' X0 a( ]6 W' `: N2 m
  58.     {4 P/ r' `. j* ]
  59.       //接收真正的数据包" R* @, r1 h; P) u& e+ ~. t, `
  60.       status = HAL_UART_Receive(&UartHandle, &p_data[PACKET_NUMBER_INDEX], packet_size + PACKET_OVERHEAD_SIZE, timeout);5 I: v. h. J) @
  61. 8 H& x" A( H7 X  Z4 e
  62.       //检验数据包  x+ R  t& z; l2 L# p8 B
  63.       if (status == HAL_OK )
    6 d* j- R/ `4 H0 {6 P- c" k
  64.       {0 L0 S5 r# R- t3 z; D4 B; T. P
  65.         if (p_data[PACKET_NUMBER_INDEX] != ((p_data[PACKET_CNUMBER_INDEX]) ^ NEGATIVE_BYTE))
    ( w" R$ p% s  [3 }
  66.         {
    , r9 k" Q7 e7 U# a
  67.           packet_size = 0;, f) T& e% o# q  g
  68.           status = HAL_ERROR;! F+ I. F" o  {9 z  t5 q
  69.         }
    4 x; G& s+ B- Z8 \: b+ f
  70.         else. ~. ~& j* m% d5 g; o; @$ T/ r* w
  71.         {
    % |9 J0 ~6 P# e: k0 ~/ v
  72.           /* Check packet CRC */+ g3 i6 ]" r5 [
  73.           crc = p_data[ packet_size + PACKET_DATA_INDEX ] << 8;
    ! ^8 f# l& C) n
  74.           crc += p_data[ packet_size + PACKET_DATA_INDEX + 1 ];
    1 z1 B1 {" @* C/ Z; }: f" c7 z
  75.           if (Cal_CRC16(&p_data[PACKET_DATA_INDEX], packet_size) != crc )6 E1 q; b7 Y: \8 Z/ P) _
  76.           {
    # Q; v. c% C' }' C
  77.             packet_size = 0;
    9 o7 ?- f& r. ]0 P/ A
  78.             status = HAL_ERROR;. t" o$ C/ j) H
  79.           }
    & g  w2 p% |9 @. P
  80.         }
    * o5 t' t- J. D5 q. c) o0 I2 A! h* A
  81.       }
    ' r# f* c# k" G' N
  82.       else
    . g4 _+ N) _. @7 O: \" j: q" W
  83.       {
    * ?/ A7 p# N1 A8 ?
  84.         packet_size = 0;
    % z" p' T# y6 `( K
  85.       }9 P* u8 y- b% W1 A3 e# `
  86.     }
    * t4 Q% y( z# p7 O( r/ x! I
  87.   }
    ( m$ N% h7 ?  X7 `0 n
  88.   *p_length = packet_size;5 n( c5 ~1 s8 d/ i: i
  89.   return status;0 d; ~. R% T: W2 R% q1 [7 M
  90. }0 c) v% i8 C: b. f- w" N, H
  91. % ^  e( \1 ^8 t* ^7 ?! V
  92. COM_StatusTypeDef Ymodem_Receive ( uint32_t *p_size )  //p_size为输出型参数,用于存放从PC端发来的文件的大小& ^. }0 ]8 S; o1 a4 i, G2 Y
  93. {
    : a) c- o# s4 j0 R$ U6 f
  94.   uint32_t i, packet_length, session_done = 0, file_done, errors = 0, session_begin = 0;/ B5 G. |' t0 @) [! `
  95. # \( P$ b" N$ D, `! B" Y
  96.   uint32_t ramsource, filesize;
    8 S0 i6 ^, l) n; Q
  97.   uint8_t *file_ptr;; x4 T$ g' l. I5 x( r
  98.   uint8_t file_size[FILE_SIZE_LENGTH], tmp, packets_received;
    " F5 Y. o6 J* t3 |- k) y1 d$ W
  99.   COM_StatusTypeDef result = COM_OK;6 r& |9 `+ ?  B; O& K& `! g
  100. . X5 Y) P  m0 B+ d' D4 E+ j+ ?
  101.   /* APPLICATION_ADDRESS是App的起始地址 */
    ) ^3 T4 N5 U- h( o$ p
  102.   flashdestination = APPLICATION_ADDRESS;
    . u: ]$ v' U4 t4 {
  103. 9 M) ?2 s. Y; ?1 a! o$ r3 |- O
  104.   while ((session_done == 0) && (result == COM_OK))
    : x/ @3 _. e: j- i2 O# m' `1 F" c
  105.   {! k. \2 m. G: o' `( @- K& p
  106.     packets_received = 0;       //记录接收到的数据包的个数. L: B" \: E7 p7 Y2 |# U
  107.     file_done = 0;, {, Q9 K+ X$ h0 E
  108.     while ((file_done == 0) && (result == COM_OK))
    . o7 ]# l8 |8 a' i
  109.     {' a6 T8 o$ o5 D  e# Q
  110.       switch (ReceivePacket(aPacketData, &packet_length, DOWNLOAD_TIMEOUT))* Q  u' c9 }5 ]& g6 w, j/ ]
  111.       {
    / b; _7 ~' w1 p/ H  Q, n
  112.         case HAL_OK:
    " y' R( G1 [& K* k' Y' n$ u
  113.           errors = 0;
    ! W! a* C4 \  N1 s5 V
  114.           switch (packet_length)2 \; \. r* O& k% w7 \( K9 W
  115.           {  C* L/ M8 Z8 h$ ?$ K
  116.             case 2:4 }3 |0 e  D8 A0 ^0 b8 [
  117.               /* 发送方终止发送 */( E" a* y4 c) ]* f' B3 u: W
  118.               Serial_PutByte(ACK);& ], t' K3 k, O  R, i
  119.               result = COM_ABORT;. S( x3 C. [  \/ e" X( }4 ?8 o, K
  120.               break;  l, ?+ p- v$ k. }7 o
  121.             case 0:$ ]* U6 l2 I0 j: K
  122.               /* 正常结束传输 */
    ' e. [5 A, C: P# E
  123.               Serial_PutByte(ACK);
    0 Z* F% M; T8 a% k% {
  124.               file_done = 1;$ M* Z4 v" {) O
  125.               break;+ f0 L& U) P7 u/ Y
  126.             default:$ Q# }6 E0 R+ ^) q
  127.               /* 数据包编号出错 */
    1 _$ b$ M7 }2 v- H
  128.               if (aPacketData[PACKET_NUMBER_INDEX] != packets_received)0 O& L& ^4 }& E& [/ Z- s1 f
  129.               {
    ; K( c( |  p# K% W: E: a
  130.                 Serial_PutByte(NAK);
    " `+ a; C' y& {7 o, g% r
  131.               }4 S( @; M5 @( g8 J0 [# M# @
  132.               else2 q# w) q5 |' E5 s8 J) y
  133.               {
    % A; l3 c, j5 i# ~& X" v
  134.                 if (packets_received == 0): n& @& ?1 _0 b2 B2 }
  135.                 {
    . d6 i; ?' i0 A
  136.                   /* 数据包编号为0,证明这是数据区存放文件名的帧数据,事实上这个判断是有误的 */3 w+ O! d4 H6 m: |. ]
  137.                   if (aPacketData[PACKET_DATA_INDEX] != 0)
    1 Q/ r6 \1 _1 a9 i7 ^+ ^
  138.                   {* M3 V$ p5 r/ Y6 N5 ~
  139.                     //读取文件名
    / r" m7 T( }5 V; ?' L1 e3 L
  140.                     i = 0;
    ' A) E4 y5 T" `% \
  141.                     file_ptr = aPacketData + PACKET_DATA_INDEX;
    ! Q  a. S, x& N( W! L' o% W/ ~$ m
  142.                     while ( (*file_ptr != 0) && (i < FILE_NAME_LENGTH))
    0 u7 x% |$ d, w6 ^% X& z& P) U  e4 s' F9 T
  143.                     {
    ; m% s8 O: Q1 j
  144.                       aFileName[i++] = *file_ptr++;
    7 {  M6 _: q" k9 |5 \* S
  145.                     }+ T: J! D) K- ^. E+ U' \" X
  146.                     aFileName[i++] = '\0';$ ~. U* \9 O& B+ T$ j

  147. / @9 o: W1 ]- E, m
  148.                     //读取文件大小
    : C4 c* }, v" g; f7 ]5 l+ `
  149.                     i = 0;
    " E+ H; I: |# h: E
  150.                     file_ptr ++;* ~  j0 o3 {4 ^1 C- v8 ~( p+ `
  151.                     while ( (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH)), n2 X/ T7 L6 r/ v- Z  M4 n1 m* L1 j
  152.                     {# Z3 A" E4 b9 P) `% A
  153.                       file_size[i++] = *file_ptr++;" v" \* e/ a- K1 K- D2 I
  154.                     }
    1 L# V" j. N8 W2 m) o4 o
  155.                     file_size[i++] = '\0';
    $ r+ X+ ]7 ?7 k: D
  156.                     Str2Int(file_size, &filesize);+ c" U: x' S# Y
  157. 6 X8 f& E) f; N9 n
  158.                     if (*p_size > (USER_FLASH_SIZE + 1))  //文件大过于可供存储的FLASH的空间% M+ F' I( s" N) S/ H9 F
  159.                     {
    + A9 z. w7 u6 z: m
  160.                       tmp = CA;8 d; x. m; E* S7 I5 W
  161.                       HAL_UART_Transmit(&UartHandle, &tmp, 1, NAK_TIMEOUT);2 f4 ~7 _: T  Q( ?4 ?3 a+ `
  162.                       HAL_UART_Transmit(&UartHandle, &tmp, 1, NAK_TIMEOUT);& \# \  P. o5 D# B7 ^+ F
  163.                       result = COM_LIMIT;
    2 C8 u; T: u9 n) C$ l
  164.                     }6 Q& j- {( E& i" \; a) B

  165. 8 n% o! p& {* \* i# ^
  166.                     /* 擦除扇区 */9 [) Z0 `. S; i9 Y9 T
  167.                     FLASH_If_Erase(APPLICATION_ADDRESS);) n) S4 ~9 {" m3 |7 a# [! H
  168.                     *p_size = filesize;/ w: n4 o7 e& {" j8 Z- O( b0 X
  169. / n0 v+ V  B# f% c9 d2 G
  170.                     Serial_PutByte(ACK);& T2 H, H9 Q6 R
  171.                     Serial_PutByte(CRC16);- ]& F/ b8 b! x( F
  172.                   }: A/ y7 S2 l: ?6 C) \
  173.                   /* 文件头为空,传输结束 */6 u: h! E0 W5 W% G; f
  174.                   else
    1 s9 ~  J- G& }, o9 u- w
  175.                   {
    9 ?- ^0 B0 K6 O, N* ]0 Z0 T
  176.                     Serial_PutByte(ACK);; p5 k9 I2 m6 O9 w) h. B
  177.                     file_done = 1;
    + j  g& K! b. @4 a
  178.                     session_done = 1;) m( x7 M0 y( O
  179.                     break;
    ! k) r. w, {- B& [$ t: G7 P
  180.                   }
    : w6 v7 Q4 {4 M7 Z- _
  181.                 }
    & R% ^* Q6 H# |8 u8 ]4 ~
  182.                 else /* 真正的数据包 */
    8 q+ P9 |: {1 r+ w& U: }, _
  183.                 {0 L. Z) G5 l- d6 Y, d8 l( ^
  184.                   //将收到的数据存放到FLASH! e+ d* f# c- U& _
  185.                   ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];5 _! B+ k/ M' L! d: f
  186.                   if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)8 q& l# H8 I8 }( ~! e% r
  187.                   {$ g0 L1 @6 b( L, s0 |- t  E% A
  188.                     flashdestination += packet_length;
    1 m) A* t: y" m* m2 S6 E0 J) ~
  189.                     Serial_PutByte(ACK);* K% c0 u/ C) [( o& S
  190.                   }  S: r+ H* N) ?
  191.                   else  //写入失败+ y$ @# J3 n' ?9 g! C1 S  C8 M& t
  192.                   {
    % |+ i  ]; m5 j* o
  193.                     /* End session */
    6 u+ P5 Z# k1 Q" u! p
  194.                     Serial_PutByte(CA);
    8 |# d1 X" @! ~) D% }9 V
  195.                     Serial_PutByte(CA);) p0 \& T' J6 Z$ t5 L" A7 M7 U2 u
  196.                     result = COM_DATA;
    9 |! p/ ?1 \1 v  E4 f) X! j
  197.                   }
    . I' i; }5 ^& s$ w* B, P
  198.                 }
    0 @) r) o7 A+ j1 `, [
  199.                 packets_received ++;
    ; f! X; i( ]1 M! J! L
  200.                 session_begin = 1;3 _; _+ D# Z$ h4 p' V9 n
  201.               }" b( l; U/ D- ^9 P, S1 t0 m/ j( B
  202.               break;
    6 x6 H8 U6 N6 K' u
  203.           }
    % }6 Y1 w, r" ]8 j
  204.           break;
    , t$ C, a* Z4 o  g- e3 Y, p
  205.         case HAL_BUSY:  q1 b8 s! @  A" W# B
  206.           Serial_PutByte(CA);
      z* v. S4 ]. p- F0 S, N
  207.           Serial_PutByte(CA);: @& C3 Z, c& ^" @& a/ g
  208.           result = COM_ABORT;
    # `6 b% d; H$ h7 R& W
  209.           break;' [* v9 F* J; l$ o6 {
  210.         default:
    : e. y4 l8 L- v: Z
  211.           if (session_begin > 0)* Z+ C& |$ T2 D8 v. q4 m' T
  212.           {2 G* N  Q: [& `3 b1 ~: S
  213.             errors ++;  
    2 |' z6 _: s' J. i- s9 j, ]$ x
  214.           }
    2 m  |, ]" C! |$ F- F+ }2 }1 g
  215.           if (errors > MAX_ERRORS) errors大于MAX_ERRORS,PC端将收到"接收端未响应的提示",终止传输
    5 g8 n- n: U! f
  216.           {" Q* S- f+ y& @, P0 [! q
  217.             /* Abort communication */0 l3 u; W7 A4 U8 O
  218.             Serial_PutByte(CA);7 u5 I0 t" ?7 l5 k+ ^" ?
  219.             Serial_PutByte(CA);
    1 O7 F* \% P; D5 m6 U( f0 l
  220.           }
    , x- y* t8 l9 L7 K' {# P
  221.           else
    2 T/ _& l1 q& `! g1 C
  222.           {
    ; u0 h1 P  G7 V6 c' B7 S
  223.             Serial_PutByte(CRC16);  //返回'C'字符,PC端提示接收端未响应并记录次数,超过次数PC端也将提出
    $ q" s* r7 n6 e# @4 C
  224.           }
    * K4 X; h7 Z1 o/ L5 X
  225.           break;
    0 y4 ]$ L- I- n6 }( ^2 v
  226.       }
    6 p$ q6 l- a! a0 }
  227.     }
    4 {1 x' T5 \. e$ P2 X
  228.   }
    & o# i. P! [# L5 z6 `* S, w3 D$ b& N
  229.   return result;
    ) v4 \0 k# f+ _
  230. }
    ( b, u: V' X* d6 [3 O* Y6 n6 R8 W' t
复制代码

3 n$ A% h5 P+ i2 \事实上,对数据包的接收处理操作是有问题的:packets_received用于记录数据包的个数,它是uint8_t类型,取值是0-255这确实是符合ymodem协议的,但是超过255的数据包呢,观察上面代码可以发现并没有对第超过255个数据包,也就是第256个数据包的处理。第256个数据包的编号也是为0,会进入:
) D7 B5 B& t6 z; N, M# K8 n8 O& m7 }* `4 K, o
  1. if (packets_received == 0)& T& b% F, y& ~( _& d# Z; Z9 W
  2. {
    $ A/ m9 v0 Q5 ]1 \2 u. U, ]* j
  3.     if (aPacketData[PACKET_DATA_INDEX] != 0)8 h( H" `8 U% X% k6 d1 C& v5 a
  4.     {. T2 O% W5 L7 |( K
  5.         //进行读取文件名、文件大小、擦除FLASH操作
    ) I3 E) J8 B7 q9 G) e' U
  6.     }
    ' ^1 T4 k3 g3 [8 s
  7. }
    . @5 b; Z1 ]4 u
复制代码
& v: m& q/ r" D: S; x
但是编号为0的第256个数据包的数据区事实上是数据,收到该数据包还是要以真正的数据包的写FLASH等操作。' [) o( P' ?; u3 |* P* B! I
% t+ O1 e* N3 o( J5 y! }* m
  一开始我利用IAP传输小于256K的APP的时候是正常运行的,后来传输400+k的APP就会出现问题:无法跳转至APP。经过多番调试才定位于此,所以简单修改上面的代码,代码片段为:0 M% n+ K; |; y* b, _* ]

+ h/ Z/ P- `5 V+ z% I$ U0 d- A! s
  1. volatile int8_t is_first_pack = 1;
    8 w) q% R0 I- a& n9 s+ G
  2. switch (RPreturn)
    " P( \7 G  r8 g- o6 w; \+ s7 K
  3. {+ \4 R/ U. J& X. g8 h# c
  4.     case HAL_OK:" U0 c1 B+ w1 H; Q" F) l' I
  5.     errors = 0;' c1 [# B! v3 Q" M4 w) Y
  6.     after_isp = 1;
    6 `8 _3 _5 ^, e* g; F. f
  7.     switch (packet_length)8 ^9 f. v" S9 O3 I, Y
  8.     {& G8 ^, ~$ l+ u
  9.         case 2:
    3 f" ~: h7 A5 z9 f8 l! a, _9 i
  10.         Serial_PutByte(ACK);: M8 Q+ o0 \/ p8 Q" D! Q
  11.         result = COM_ABORT;
    4 p$ a; j/ H7 L& F: T% C9 [
  12.         break;/ _3 M/ {) v- k+ w
  13.     case 0:& v  q( M# |) G9 b& @3 Y
  14.         Serial_PutByte(ACK);        
    5 O4 }+ a: [" v6 ~: Q
  15.         file_done = 1;9 u" e8 X( t5 ~' T% I0 A
  16.         break;0 _4 E+ t, I; A- ]$ ]! c4 c/ [

  17. 4 h1 I8 \. {) f$ j
  18.     default:
    # U: S0 {) E  k. N; p, z5 H' p
  19.     if (aPacketData[PACKET_NUMBER_INDEX] != packets_received)   //ÅжÏÊý¾Ý°üµÄ±àºÅÊÇ·ñÕýÈ·
    6 p& H3 R4 b6 y+ B% Z) z8 {
  20.     {
    + H/ B. Y6 A) G6 g% b3 Y
  21.         Serial_PutByte(NAK);' F* M3 @( B# R2 F
  22.     }  O; r9 P& i$ x8 L
  23.     else3 {5 s9 f( j! ?8 Z
  24.     {
    " a  G1 r% R4 ]" i& C- @% l1 e2 c  n
  25.         if (packets_received == 0 )
    4 q6 n# A  T, E2 r! |
  26.         {                           ) V- i- g5 M5 q1 G7 j# ^
  27.           if (aPacketData[PACKET_DATA_INDEX] != 0 )
    0 M5 @0 r7 G6 D# a$ [
  28.           {) y: Z# C" y. v) C6 }- S) U1 _4 Y
  29.             if (is_first_pack)  //第一个编号为0的数据包
    + W7 d  p1 C$ m/ Z
  30.             {
    ! C4 B5 m' X  W8 C
  31.                 //...               
    $ M( p2 u) |7 `
  32.                 is_first_pack = 0;              " v1 v; ]2 W9 H7 K2 p
  33.             }# R) d4 `; L/ B
  34.             else    //即使数据编号为0但不是第一个数据包,采取存储操作          & c8 G. z7 e  i# H5 K  c( Q: D
  35.             {( c( c( O3 n! u  ^4 `
  36.                 ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];        % ?3 Q# O) A& L, L7 [# N9 }
  37.                 if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)% q3 t0 {5 U1 f* F$ A
  38.                 {
    * C0 G$ ]0 e. d: i& @6 `& [" [4 x
  39.                     flashdestination += packet_length;+ L; _) |7 H, }+ z3 D4 c3 g
  40.                     Serial_PutByte(ACK);
    + b6 k6 y/ h/ ?) g# [2 _0 p
  41.                 }
    6 z2 ]! Q6 c, Q! ?" y% S' j
  42.                 else
    3 P  T/ f, _9 w+ H
  43.                 {
    # ^7 d) k+ I' ~. w% k7 l! \* i7 L
  44.                     Serial_PutByte(CA);4 g7 e! q# ]! g. [7 V' h+ d
  45.                     Serial_PutByte(CA);* c- o  U5 |6 a+ x8 a
  46.                     result = COM_DATA;5 j/ L* T- x+ q$ r; {7 C
  47.                 }                1 H  g$ A$ t' O- p, K
  48.             }  V- Y6 y7 g1 s% h4 a
  49.           }8 b0 [6 |: D% l/ |1 M5 I
  50.           else
    9 @0 g7 C7 N$ g2 [1 K
  51.           {, u: X7 k+ T" H4 U! K
  52.             Serial_PutByte(ACK);0 d: D8 [& G* A" C  s" L$ [
  53.             file_done = 1;
    4 d3 p' K: g2 V/ v& {
  54.             session_done = 1;
    9 {5 a# z4 m+ e
  55.             break;4 i1 A5 _. G  \6 w2 Z4 s0 I( V
  56.           }3 C4 G8 |  }3 B5 ^9 l" S' X
  57.         }
    ! e7 i; ?# |2 J! G5 d9 v
  58.         else
    + `; Y' z( z* u% t
  59.         {
    ' U& m" a  ]1 }) U

  60. 6 L2 ?1 B  ?- |
  61.             ramsource = (uint32_t) & aPacketData[PACKET_DATA_INDEX];                        
    % {8 v% c( i) \
  62.             if (FLASH_If_Write(flashdestination, (uint32_t*) ramsource, packet_length/4) == FLASHIF_OK)$ `; X( n2 v" g( D5 E- y
  63.             {0 x8 I9 Q1 {: X- N7 q$ \
  64.                 flashdestination += packet_length;
    1 x7 t$ `) E; h
  65.                 Serial_PutByte(ACK);
    ( }& s- c" R- I3 A
  66.             }
    " a8 a+ P3 H0 Y( y* B/ v6 P
  67.             else8 p* u+ ^8 ]8 ~2 ~$ @
  68.             {- W+ k6 y  K5 y& Z! p3 i
  69.                 Serial_PutByte(CA);' `6 Z- ~( D$ ]2 |
  70.                 Serial_PutByte(CA);
    " ]1 s4 ?0 l  T1 y) A
  71.                 result = COM_DATA;) t- q% `; P( x5 C& w1 a
  72.             }        * M$ \3 q" ^2 T. n  t' L, \0 k0 M
  73.         }/ v7 F& t$ F& N! U. a
  74.         packets_received ++;
    2 U: ]4 I2 l3 s  v- h
  75.         packets_received = packets_received % 256;
    9 H8 |/ @4 G- M1 ^# E. l" O, Y" C5 F
  76.         session_begin = 1;3 }- A% A) f$ V* J8 w. i( _: }
  77.       }0 O9 m3 C& z3 _  K7 G
  78.       break;
    3 Z* @6 N: @+ D2 ~
  79.     }
    ; G, r! _- y: L" W5 a
  80.     break;
    ! c1 X/ @& e/ l
  81.     //..! q3 o0 l2 @! L0 [3 J# N; V$ ^7 F
  82.     break;7 }; N8 u& ^4 F9 t+ N# Y
  83. }
    : q5 Q' g9 ~* n" R3 i# Z- m
复制代码

7 w4 S( g' I$ J3. Bin和Hex文件的差别
4 k$ v  [) y2 i9 @. \3 r4 Z
$ N* O: v) H3 k8 f: b; Z( f       第一次接触Hex文件一般是在学习51内核单片机的时候,通过烧录器烧录到开发板上使用的就是Hex格式的文件。Hex文件是以ASCII文本形式保存编译后的二进制文件信息,注意这里强调的是文本文件(而非数据文件,即二进制文件)。文本文件是人们可以看得懂的文件,但是计算机/MCU只认识二进制数据文件(Bin文件),Bin文件才是MCU固件烧写的最终形式,也就是说MCU的ROM中烧写的内容完全是Bin文件。由此可得,我们通过烧录器烧录Hex文件到单片机的ROM时,烧录器其实会将Hex文件的数据转为Bin文件的数据,最后才烧录到ROM。# U; U7 P# C# ~: M) F8 ~
! _3 Q" e) k" e
  其实明白这一点我们就知道在IAP工程中,APP文件需要是Bin格式的文件而不能是Hex文件。既然Bin文件是MCU/计算机最终想要的,为什么我们不直接生成Bin文件,而却要生成Hex文件?其实Hex文件保存的不仅是Bin文件的内容,还有一些附属配置信息,随便拿个项目Hex文件分析:
+ ~6 O; W0 h, b9 k6 U* `
8 {: \' [* c7 R" N3 G; I& I' U( g
  1. ' c& _* ~! W  r5 h: {2 l. u
  2. : 10 9AB0 00 6841298459D0420F9AC4641EFBC10408 2E
    . W% P! S. e5 o2 B0 t
  3. : 10 9AC0 00 E7604BC5CC64011029B85A6980413C55 08
    & h9 `  g. C! W* y
  4. : 08 9AD0 00 55557C2964291C81 15- N: X) M- Z3 m) z8 S* O8 i3 j
  5. : 04 0000 05 080081AD C1
    8 @9 H1 g5 n# Q; x( Z& D. a  c
  6. : 00 0000 01 FF
复制代码
9 ?) Z' Z9 V( M# W9 L
Hex文件中的数据是ASCII编码,所以是人们能看懂的。上面3行内容,每行都是以’:’开始的,之后是数据长度、地址域、数据类型、数据域、校验和。
! {0 V5 u, t( d* W4 M& F) j
- @! C% D/ Z: ~$ ]
  1. /* /-------- Hex Data format -------------------------------------------------------------\% L" R: t: {- F. W- X% m% @3 @- |
  2. * |   0     |  1     |  3、4  |    5     |    6   |    7   |  ...   |   n     |   n + 1  |
    ! d0 t8 r( g5 q1 T! R/ \! o
  3. * |--------------------------------------------------------------------------------------|7 t/ y; i" u% i  w
  4. * | : 开始 | 数据长度 |地址域 | 记录类型 | 数据域 | 数据域 | 数据域 | 数据域n |  校验和  |1 R5 _& w" g* t7 u% h, z  I' p
  5. * \--------------------------------------------------------------------------------------/: O2 K' z) r  P* v# k/ I* ]
  6. * 每行数据都是以冒号开始的                 */
复制代码
% P6 d2 P- x( o" R! {# H7 a' {
注:记录类型的意义 ! t$ R+ L% O/ W1 O. j! u
  (1) 00: 数据记录
; _0 H9 h4 X6 {8 G9 L" p2 v9 }  (2) 01: 文件结束记录 ' V( {2 D1 A% c* T
  (3) 02: 扩展段地址记录 9 d# K9 n; Z- @; T9 F
  (4) 03: 段开始地址记录 1 A$ b: G' }6 m. |; X0 \
  (5) 04: 扩展线性地址记录 " F' p4 h  w) m$ |: t
  (6) 05: 线性地址开始记录
; c; v' m5 r3 L2 X7 N; z- h& a+ Y7 l$ i8 R8 q7 U" m  f
  由此可见,生成Hex文件的意义在于:
2 @: x- b7 j: h! c1 G  (1) Hex文件使用ASCII文本保存固件信息,方便查看固件内容 % q  _* R# j; s
  (2) 文件内容每行的校验和与最后一行的文件结束标志,在文件的传输与保存过程中能够检验固件是否完整+ B* a6 t1 q" {* Y
6 @! d( O4 k. g+ h/ B
  因此hex文件更适用于保存与传输。相比之下,Bin文件纯二进制文件,内部只包含程序编译后的机器码和变量数据。当文件损坏时,我们也无法知道文件已损坏。不过在IAP中,Bin文件仍旧是不可替代的。
* {+ ]/ r" g( ^' U% ~- v. K2 O. L( }, V& a2 v# f# q
4. RS485通讯) R# S/ h; Q( R& H+ A7 m

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

4 x. |* v+ `8 e  j) }8 ]* q5. IAP的main代码片段分析# p  b7 ]- W. |1 i  p

4 R$ L7 o) A' y* X+ J7 C. s! W; U: ?       IAP升级代码工程,一般需要基于一个能跑起来的通讯(这里是指RS485)和LED灯(用于指示状态,当然也可以用LCD等其他提示状态)运行正常的工程代码。下面是main()函数:
6 E: I/ c; o# H& A8 m- B) O% G( J8 o* b1 J9 R3 A0 Z. w
  1. void SerialDownload(void);7 [$ H: i2 f# S8 H

  2. * a4 t( p0 r! l
  3. //定义函数指针,用于跳转到APP
    + B- U" D. O+ V9 n+ X! y  b
  4. typedef  void (*pFunction)(void);   
    9 q  p$ d- p' U
  5. extern pFunction JumpToApplication;
    4 V) }0 A% t1 @" o
  6. extern uint32_t JumpAddress;
    ; ^( Y5 B7 H* Q$ I3 Q7 g
  7. UART_HandleTypeDef UartHandle;1 V& L  w# N$ W* ~- A& X8 r7 n& d

  8. 7 x* n* R0 N1 Y. |' S
  9. int main(void)
    $ D; C* }! s( ~) [' d8 r+ ~
  10. {! z8 j4 K7 {, [$ ~7 d+ F+ c2 V% ]
  11.     HAL_Init();
    7 m& \( J2 ?5 B, h- v3 C7 A0 p6 [$ L/ T
  12.     SystemClock_Config();
    ' _5 W/ y% n* W% \6 u3 n

  13. ' T6 l0 l; g# R, o' Y8 g7 ]3 ?0 k
  14.     LED_BSP_Init();
    5 m; @2 S! w4 x( Z
  15.     FLASH_If_Init();, B' ?8 Y! @. C# f# V: K
  16. 8 b6 n( ~7 k' I. F5 F# f, m
  17.     //初始化RS485" `3 I; c6 L% Z0 x( {% c5 ], \" X6 Y
  18.     UART_Init();
    * S2 ?" n9 z2 r% D
  19.     RS485_RX_ENABLE();      //默认设置为接收状态
    9 P+ H# L% n7 Y% w# e& B' A
  20.     HAL_Delay(10);, _& ?% Z1 y5 v# }

  21. * @, H8 H( Z' U3 o( B$ w6 P
  22. IAP:    + k  E) b9 A1 d' ~  j
  23.     Serial_PutString((uint8_t *)"\r\n====================================================================");
    + b( d, Y) ?0 S
  24.     Serial_PutString((uint8_t *)"\r\n=                       IAP For STM32F429xx                        =");* n/ e, u, z! A' y6 P' y
  25.     Serial_PutString((uint8_t *)"\r\n====================================================================\r\n");
    ' ~$ g* }: b8 J/ v
  26. ' [- o2 ?. z! }4 n+ i0 a/ ]- F
  27.     SerialDownload();   - U; @: k! }( w) z9 p
  28.     HAL_Delay(10);
    4 Y, D$ l  D- G8 C7 K* l) `) d
  29. % Z* R! A+ d  K+ A7 J  q/ Q
  30.     //APPLICATION_ADDRESS是在FLASH中存放APP的起始地址- z1 a7 B/ C- B4 `( _! V
  31.     //此判断是为了保证APP的栈地址是在SRAM中。其实结果并不一定是0x20000000。有些APP可能定义的全局变量较多,那么栈的起始地址会偏移
    * H' G1 A) x3 @% p) L) {+ C0 [& z
  32.     if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)   
    / o: z# _' M# W' h2 [( y/ Y3 }2 F
  33.     {' m* K* Q- F$ d) p9 H

  34. 5 w; I3 X6 Z3 N* l5 E4 k* U+ Z
  35.         Serial_PutString((uint8_t *)"\r\n=======================  Run application  ======================= \r\n\n");; y4 q$ q- u+ a$ @! |
  36.         HAL_Delay(10);7 F# f5 P; A) V* s
  37. % z& M  y6 j/ T/ L. F
  38.         /* Jump to user application */# d" W* O  H* z' C6 B) b
  39.         JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);) _! m: i3 c- r! x
  40.         JumpToApplication = (pFunction) JumpAddress;
    ! U! g3 Q6 A- l1 r
  41.         /* Initialize user application's Stack Pointer */
    % q2 a8 E$ s# Z  g0 ]( D0 _2 f7 Y( S
  42.         __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);) \8 j. ]/ P* W% B6 G1 I9 O
  43.         JumpToApplication();: |/ C/ F/ T# D( c" X1 x
  44.     }8 Z4 a# R# _$ `% V9 S5 `
  45. / ?* n( `2 y: B  U
  46.     Serial_PutString((uint8_t *)"\r\n ======================= Download error, once again ======================= \r\n\n");3 a3 B7 z0 ]1 D+ U5 v
  47. goto IAP;
    # n  K( {( b8 H- ~- _
  48. }9 D3 M0 ]7 ?( Q$ o  K
  49. ) Y' ~9 F# o2 p
复制代码

) j  h/ S" z$ jIAP的实现还是十分简单,但是中间走了N多弯路,特别是在调试ymodem接收大于256kb的APP上。关键要注意上述几点内容。当然,上述内容属于个人见解。
+ b4 R- l& x3 b: y9 u
" f. y1 N/ b; q4 C( ?9 w
# l1 s+ E9 w, Y, U! ]7 U4 ~# K' u3 S" b- S$ B+ a( _2 K* |$ r
转载自hsade- B9 D# d4 ^. p, t( D
. h, b" [  n5 Y7 Z9 H

; C( [) D: I3 t8 J- h3 T
7 b8 e9 I; p  @9 @! o' r8 r- G' e3 ]2 \: Q1 d$ f# P

; X4 I4 M4 @6 ~; u% p# b; Z
赞 收藏 2 评论0 发布时间:2018-5-22 09:44

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版