学习了一下使用Ymodem协议串口IAP程序升级的功能,移植的是ST的工程文件。附上下载链接http://www.st.com/content/st_com/en/search.html#q=IAP-t=tools-page=1;ST官网提供的各种IAP,其方法和原理其实都类似:就是将程序文件(二进制文件)写入FLASH。
! Y/ O5 s3 ~. r: o! l; e
' ^# M+ v5 G( ?: D* }
# k8 Y5 K$ F& p q
4 p- w) R# ?* G* f 1、Ymodem协议简介
8 N, Y& n5 W- e. ^( E% M Xmodem、Ymodem和Zmodem协议是最常用的三种通信协议。Xmodem协议是最早的,传输128字节信息块。Ymodem是Xmodem的改进版协议,具有传输快速稳定的优点。它可以一次传输1024字节的信息块,同时还支持传输多个文件。以下协议内容的简介,顺便添加了一点自己的见解。 0 f3 B! s Q K+ ]) T; z
8 i, A, p( e0 T" Y
; Z3 Z* c" t# D YModem,数据的发送回使用CRC校验,保证数据传输的正确性。它每传输一个信息块数据时,就会等待接收端回应ACK信号,接收到回应后,才会继续传输下一个信息块,保证数据已经全部接收。
9 [% f! s# W" i
- SENDER:发送方。& d/ n+ V8 P2 Q" E4 H
- RECEIVER:接收方。" [( u, H( n2 s
- 第一步先由接收方,发送一个字符'C'% T! V5 w4 `/ d2 W* T
- 发送方收到'C'后,发送第一帧数据包,内容如下:
7 l x5 l( ]3 F4 h' C" G0 E4 E. ^ - **SOH 00 FF filename filezise NUL CRCH CRCL**$ D5 ]6 ?0 z6 w0 C
- 帧长=3字节数据首部+128字节数据+2字节CRC16校验码=133字节
" o7 ?/ y) e- s) q5 { - 如下所示:
" J. _& I5 A1 T# I7 L( j, F+ v - SOH 00 FF Foo.c NUL[123] CRCH CRCL. J$ x* J5 ^9 h9 v
- 第1字节SOH:表示本包数据区大小有128字节。如果头为STX表示本包数据区大小为1024字节。
, J2 ]3 m5 Y$ y: x6 o -
- r9 a# j5 b9 E - 第2字节00: 编号,第一包为00,第二包为01,第三包为02依次累加。到FF后继续**从0循环递增**。这样就会出现一个BUG,那就是文件在传输大小上有限制,256x128 = 32768 字节 **32K的限制**。' F& @/ \, S: k/ Q: b& A
-
0 P' s) j5 D- Q' Q; X& p - 第3字节FF: 编号的反码。 编号为00 对应FF,为01对应FE,以此类推。+ W; C3 t4 z# z- x+ V. W
-
* |) P! m j% g" F3 T4 Q' K5 Y2 h4 y - 第4字节到最后两字节:若第1字节为SOH时有128字节,为STX时有1024字节,这部分为数据区。“Foo.c” 文件名, 超级终端下,在文件名后还有文件大小。官方dome也是因为使用了这个文件大小进行比对。这就是为什么用SecureCRT中的YMODEM协议而无法正确传输的原因。在文件名和文件大小之后,如果不满128字节,以0补满。最后两字节:这里需要注意,只有数据部分参与了效CRC验,不包括头和编码部分。2 G8 R* n$ r& Z6 D2 o v% U
- 8 h0 @ z6 e+ ]7 L
- CRCH和CRCL分别表示16位CRC校验码的高8位与低8位,高字节在前,低字节在后。
. Z" O4 I+ v# g& n -
( M1 V: b2 x- h8 N - 接收方收到第一帧数据包后,发送ACK正确应答。
* r6 |3 H% } }7 K& \1 X1 l$ ~ - $ \3 }' M6 {/ p# a6 {
- 然后再发送一个字符'C'。* [' A7 \) ?3 n+ P, ~
- * h5 K0 C- w' ]0 s+ ~
- 发送方收到'C'后,开始发送第二帧,第二帧中的数据存放的是第一包数据。. z- ~9 x1 ]5 @$ K2 N
-
7 Q+ j) I" V, X2 C7 b - 接收方收到数据后,发送一个ACK然后等待下一包数据传送完毕,继续ACK应答。直到所有数据传输完毕。& _2 f p" }1 u# w9 W
-
/ I/ J: B# u! k5 e - 数据传输完毕后,发送方发EOT,第一次接收方以NAK应答,进行二次确认。
% k; ~4 ]/ [6 N% Q2 R' I -
; N" A% G1 ?% \3 U6 }9 H1 Z - 发送方收到NAK后,重发EOT,接收方第二次收到结束符,就以ACK应答。. i4 ?' W1 m, C! |4 f
-
9 a2 d/ j) C: m - 最后接收方再发送一个'C',发送方在没有第二个文件要传输的情况下,9 `& K! Q" n b% l4 _: [
- 发送如下数据:' U: b- T8 |. N! Y
- SOH 00 FF 00~00(共128个) CRCH CRCL
5 `2 e% t0 g2 w6 @& e) z4 ?7 y - 接收方应答ACK后,正式结束数据传输。
复制代码 ' }/ Y0 M/ I- Q/ m% _
2、Boodload程序2 C% y" l) P$ j9 h1 Z
下面所示的部分是移植的文件,移植完成后的工程文件截图: + @3 J( V& [# L8 j# b* |
% A1 ~4 U0 F/ W2 h/ o' B c
# ?8 l+ Y7 O& v2 g7 X4 M1 hmain.c 文件修改添加:
2 ^. _2 q! @; b" F4 U) g( I- F0 R$ l) k: C8 k" K! w
: L& K2 K3 ^+ ^9 k5 i& B2 h , \2 }! i% X0 D9 b) z+ y7 G
- /* USER CODE BEGIN Includes */! }$ s1 c9 {1 x% ^6 Z9 c3 l
- #include "menu.h"$ C! X! d7 Q6 A
- #include "flash_if.h"" j! [( _: C; p: {' y, h
- /* USER CODE END Includes */
复制代码 . I' S$ u: {* ?
3 T4 q9 s% W1 M* }& `
% X2 `* `0 f& o, w. ^- /* USER CODE BEGIN 0 */
& {4 N: K0 l9 m( N6 p$ j- Z - extern pFunction JumpToApplication;
2 w+ f3 x9 r+ P" v) K, y - extern uint32_t JumpAddress;
) I' O1 n8 ~7 Z* `0 J8 D. c - /* USER CODE END 0 */
复制代码
9 k2 A# t* ~/ U/ f8 [2 Z3 f
, T. r7 ]4 c& }2 S) Y: o; u6 c- `2 o. |) {
* D9 t$ D# d, U: C. i; q- /* USER CODE BEGIN 2 */
- A) ^% A% i& `# s - HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
: H: x ?3 K/ D - if (1)2 i ^- C7 f9 {9 _. e& G) z, K8 q3 L. o
- {
/ \) c, n6 n, N9 F - /* Execute the IAP driver in order to reprogram the Flash */8 e" @3 m( i1 S' n
- FLASH_If_Init();
6 J3 N( i6 k) @( d) z' v! x - /* Display main menu */
4 w4 C: g3 N% |9 R# x - Main_Menu();
" L. s# I) \1 [& w% X - }
+ |# [# T" o8 ^* G! J, y/ ` - /* Keep the user application running */1 H, i+ w q l- B
- else5 L( S8 \2 L3 L; D. x
- {( C) F2 o# z% o1 ]
- /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
* \- \- J* b4 u# L - if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000) 检查栈顶地址是否合法.- K6 b& p2 E1 h
- {4 G; o+ Y" Q1 `3 G6 z& B) O
- /* Jump to user application */5 F0 s7 l* v4 ?# n. i4 ~; q
- JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4); //用户代码区第二个字为程序开始地址(复位地址)
" {5 E r% T+ A - JumpToApplication = (pFunction) JumpAddress;
" [% C" n1 r1 l! } - /* Initialize user application's Stack Pointer */
1 @0 F W& u, K0 T - __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)0 ~/ ]0 N; H2 d8 \
- JumpToApplication(); //跳转到APP.
. |& f- e- ^4 [/ ?+ I - }
8 ]8 l/ \7 p& b6 W/ E - }
复制代码 # d/ o. v! `) ~' o- f# I
还要修改flash_if.h 头文件里面的地址。
8 W! D8 }. G$ s2 E) O! L b0 \& t# ^& {: _ V
: z8 o, W/ [1 o0 S3 K' N: j: aAPPLICATION_ADDRESS 是偏移的地址,可以根据自己程序Boodload文件大小自己设置,USER_FLASH_SIZE 是 APP文件的大小。
. d: y1 |8 _7 G( |/ ?$ I* |; c- I! q
6 o2 c9 i" P I: q 2.1 传输32K限制解决5 H1 \4 ~6 O3 K( _' K: w) u2 ^. W$ H l
secureCRT每包只传输128个字节的有效数据,而每个帧的标号由一个字节表示,所以等到标号到达0xFF之后,下一个帧的标号又变为0,而官方Ymodem程序是判断帧标号为0则认为是传输文件的第一个帧,即文件名和文件大小。所以等到标号由0x00-0xff再到0x00的时候,它认为是一个新的文件,所以出错。只要加一个标志标明第一次出现的帧标号为0的帧为第一帧就OK了。
! W3 v+ M+ j& `$ m) P i, \" h/ Ypackets_received 声明为U32类型的数据,判断的时候修改如下
; A% @% t1 j6 }" s+ w! ~if (aPacketData[PACKET_NUMBER_INDEX] != (packets_received & 0xff))
0 v6 I( g+ V4 ?* F# Y" N # E9 g: s4 @) q: m* ]
! x! d6 @. C5 d9 i0 p1 g a' p! t) `0 s) l9 u
% v; n7 V: N$ ~7 r3 S3、APP程序4 G( T' A3 Y: J1 A3 m
App需要修改内容:
n4 g) \' @- y0 t7 l编译链接其实地址
8 \ D8 R! E. l6 l
, p% H9 a: E; T1 G! E1 g中断向量修改。9 j- |9 S6 b, ^# A3 y, e
7 v$ b( ^* Q& G9 L$ X
$ m. Y3 c: P+ G2 ]
4、升级测试
. k7 t7 C7 J8 p! O) E软件使用secureCRT发送文件。
/ h, J4 D2 Q5 p; l. ?3 U5 p7 u; M# R. q3 q9 n
1 r+ m% @+ O& z* ?
- ?! q/ v; s- i2 z+ E. `* f
. K5 v- \0 L# r2 ]$ b2 [8 h5 n0 ?# Q+ }& H9 u6 i
% v& Y) j( w, }3 c' r8 B1 j# c' E0 t+ t$ F9 q6 I
( Q5 o. M: [. l2 v! D: Y$ y! _6 N$ C, l: P" x
. B' o5 Y z K
|
官方版本库太旧,移植时候出现大量写保护的相关的寄存器找不到定义
这教程对F0系列App部分有点不适用
我调通了
稍后发个教程
不考虑接收方接收异常的情况么