学习了一下使用Ymodem协议串口IAP程序升级的功能,移植的是ST的工程文件。附上下载链接http://www.st.com/content/st_com/en/search.html#q=IAP-t=tools-page=1;ST官网提供的各种IAP,其方法和原理其实都类似:就是将程序文件(二进制文件)写入FLASH。 . g- Q& N$ D% y( ~
$ z3 Y" b5 W8 e" n0 ?
2 y5 P% @; C- O& z9 [% g$ Q* {: H) V+ C0 _1 ^, N
1、Ymodem协议简介! e/ v: \6 L7 _1 r; u, \+ q
Xmodem、Ymodem和Zmodem协议是最常用的三种通信协议。Xmodem协议是最早的,传输128字节信息块。Ymodem是Xmodem的改进版协议,具有传输快速稳定的优点。它可以一次传输1024字节的信息块,同时还支持传输多个文件。以下协议内容的简介,顺便添加了一点自己的见解。
' _! R7 ~- A! n6 a/ _$ G/ F3 h$ s$ I
1 z( g, e- r$ S& ?+ c! O2 U( ~# d
YModem,数据的发送回使用CRC校验,保证数据传输的正确性。它每传输一个信息块数据时,就会等待接收端回应ACK信号,接收到回应后,才会继续传输下一个信息块,保证数据已经全部接收。
5 X$ K) N8 _9 ^; x, D+ Y( |
- SENDER:发送方。
' ^: m' P, Q1 q. E' e9 ?* b - RECEIVER:接收方。, Q; z! }: A& W9 R q+ T
- 第一步先由接收方,发送一个字符'C'3 W6 O; D5 Z! }
- 发送方收到'C'后,发送第一帧数据包,内容如下:
6 K c @7 e" v - **SOH 00 FF filename filezise NUL CRCH CRCL**
8 L& j6 Y- B( d - 帧长=3字节数据首部+128字节数据+2字节CRC16校验码=133字节5 z& @# ]& O! T
- 如下所示:! w9 `5 X3 c }' s r$ I$ N
- SOH 00 FF Foo.c NUL[123] CRCH CRCL- H. f) T. \' K( R) \) T1 s8 X' E
- 第1字节SOH:表示本包数据区大小有128字节。如果头为STX表示本包数据区大小为1024字节。* S4 n# [7 D5 e7 a( n, U
- 7 C: }8 t; Y1 F: [' B# ]. {, K
- 第2字节00: 编号,第一包为00,第二包为01,第三包为02依次累加。到FF后继续**从0循环递增**。这样就会出现一个BUG,那就是文件在传输大小上有限制,256x128 = 32768 字节 **32K的限制**。
9 G4 ^# X3 U1 m* G3 ~9 m -
# c% r, G r, @$ o% C1 H# K1 Q - 第3字节FF: 编号的反码。 编号为00 对应FF,为01对应FE,以此类推。
+ ~5 n) P9 g1 ]3 `8 @) y5 \ - 8 @6 e3 \2 q" T- X) r- `1 F7 J
- 第4字节到最后两字节:若第1字节为SOH时有128字节,为STX时有1024字节,这部分为数据区。“Foo.c” 文件名, 超级终端下,在文件名后还有文件大小。官方dome也是因为使用了这个文件大小进行比对。这就是为什么用SecureCRT中的YMODEM协议而无法正确传输的原因。在文件名和文件大小之后,如果不满128字节,以0补满。最后两字节:这里需要注意,只有数据部分参与了效CRC验,不包括头和编码部分。
$ S ?7 w# j" n$ Q0 Q6 \2 c -
9 ?- O4 L% L0 @1 r - CRCH和CRCL分别表示16位CRC校验码的高8位与低8位,高字节在前,低字节在后。
+ L" |( A0 D# v) C - & g- d/ V* `2 d9 F3 @
- 接收方收到第一帧数据包后,发送ACK正确应答。
* ]' u( d$ A) D9 S - . b+ ^' ]* o ?& {) c5 P
- 然后再发送一个字符'C'。: W" j# t' F4 [4 _- T1 }
-
5 r4 l9 z5 U+ u - 发送方收到'C'后,开始发送第二帧,第二帧中的数据存放的是第一包数据。
4 N5 M, \" Y' Z x8 t2 y - ; A8 Q9 b! ], J1 l4 n& Q
- 接收方收到数据后,发送一个ACK然后等待下一包数据传送完毕,继续ACK应答。直到所有数据传输完毕。
; K2 p& M' n- U' l9 A( k; n -
; v# c& r' D: K+ x - 数据传输完毕后,发送方发EOT,第一次接收方以NAK应答,进行二次确认。% u2 G c- M) a% U( v5 q
-
5 l& P7 e9 T5 i( N9 G! Q: J9 k* [4 t - 发送方收到NAK后,重发EOT,接收方第二次收到结束符,就以ACK应答。
* [" |/ Y! C C4 ]- [ - 2 `" p& g; C5 Y) m
- 最后接收方再发送一个'C',发送方在没有第二个文件要传输的情况下,
2 \/ d& t0 K6 {& M& I8 p, F8 Y - 发送如下数据:
$ G2 v6 {& ~1 d0 h9 t6 @: a - SOH 00 FF 00~00(共128个) CRCH CRCL / U! m* o7 L9 S8 e% a7 w' Z& t
- 接收方应答ACK后,正式结束数据传输。
复制代码
& f* a. E, G2 R/ y7 w" \6 w2 E5 [. V& Z2、Boodload程序+ k2 N" }/ K3 O
下面所示的部分是移植的文件,移植完成后的工程文件截图: : p0 C# _3 z4 U7 h: A. v U
' C" R' ?& i/ m5 }* r
: H# f/ S9 J% _: B6 b) G
main.c 文件修改添加:& E; i+ n& _2 R/ D* { [0 L
E" t& W/ m7 u$ `
, _: D9 \: ]& W! U+ s
8 B3 `) H4 D. T* M/ m- R- /* USER CODE BEGIN Includes */2 I7 x2 O; n+ H/ t
- #include "menu.h"/ {- @: I# @4 P
- #include "flash_if.h"1 @2 s# ?- O% s0 B. I. z
- /* USER CODE END Includes */
复制代码
% K8 C1 H2 j9 @3 p: r
2 @9 }7 P3 K( L
8 M$ }/ G2 W& ]6 e0 c3 A
- /* USER CODE BEGIN 0 */' d# J6 ], u9 w3 ]* t |3 @3 p
- extern pFunction JumpToApplication;5 H- P' R! G1 M" v; o Z/ X
- extern uint32_t JumpAddress;
/ h& `4 u2 _. ^7 Z - /* USER CODE END 0 */
复制代码 3 [' K- t, u" o
% H, W( w) W6 }+ w
) a/ S2 s/ m& ]/ X. T$ ?) o: x4 j, F* d# W) ~: K; Q5 t) C
- /* USER CODE BEGIN 2 */
' T% d' P& r) A! @) V* ]- C4 i7 ` - HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
" w1 ] [) A! x, B# j/ P* X - if (1)$ a. V) o L* p2 n* s
- {
0 _- D8 _8 [( `. A! l' l - /* Execute the IAP driver in order to reprogram the Flash */5 O2 k6 [( t6 d, W
- FLASH_If_Init();
2 {2 X$ q7 M: \$ m6 `' C) ]' N- M - /* Display main menu */
! C3 D; B+ Q1 s- G- | - Main_Menu();6 M, t" _% @; s8 p+ ]
- }
; p. ]- }& a0 T+ i' f; n - /* Keep the user application running */1 F6 s' g6 K! I& n
- else. F0 `- W* j+ m$ I. K
- {
9 o0 j" K( L* B/ \/ q* F - /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */5 B$ Q& A+ I6 k- V1 ]
- if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000) 检查栈顶地址是否合法.
' u5 K8 _. j+ c* k! F' J% Y - {+ {$ ~0 W6 k- g1 ^, h* }; j
- /* Jump to user application */
0 E6 D) q# u" \1 {& p, a - JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4); //用户代码区第二个字为程序开始地址(复位地址)) }% ?' H: {# C: ?2 H" c: T7 v
- JumpToApplication = (pFunction) JumpAddress;) x+ Z$ w/ M6 P
- /* Initialize user application's Stack Pointer */5 J! S/ w1 f; {2 b+ h# e8 x# G
- __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
- R7 V$ k; m) P" \/ C - JumpToApplication(); //跳转到APP.; J' z4 T8 Q6 z7 [& `
- }
V# ]" [: _ h5 P \ - }
复制代码
8 Z. u M# `& |9 ~& M( e7 K& y还要修改flash_if.h 头文件里面的地址。
' S# c) o& ]) G* _& S
% x- I2 ]1 `% ~% L* T1 \1 a2 O; Y4 k4 g% I7 B9 H# I5 L5 l
APPLICATION_ADDRESS 是偏移的地址,可以根据自己程序Boodload文件大小自己设置,USER_FLASH_SIZE 是 APP文件的大小。
2 ~9 E2 L; i. ~7 F, i
& v1 {5 S, c% D. d# A3 V# K
2.1 传输32K限制解决
* e( g5 G3 c! y$ Y$ o$ B/ T secureCRT每包只传输128个字节的有效数据,而每个帧的标号由一个字节表示,所以等到标号到达0xFF之后,下一个帧的标号又变为0,而官方Ymodem程序是判断帧标号为0则认为是传输文件的第一个帧,即文件名和文件大小。所以等到标号由0x00-0xff再到0x00的时候,它认为是一个新的文件,所以出错。只要加一个标志标明第一次出现的帧标号为0的帧为第一帧就OK了。
6 v+ Z' V1 f- m$ d4 Epackets_received 声明为U32类型的数据,判断的时候修改如下( a! Q9 N: l) y: P# W
if (aPacketData[PACKET_NUMBER_INDEX] != (packets_received & 0xff))
6 x/ R& x+ D" s+ d2 d1 v# W 4 ?; p5 T [$ N6 a
" k, h" n o4 n& { Z- [0 s0 A( t
I& u6 y7 l E0 w) P
' K; ?! u5 Y: k
3、APP程序 S. N2 C& D: E, w& F/ v/ l1 u
App需要修改内容: 2 ^$ [. }2 v: v! m6 W
编译链接其实地址
! y/ B$ Q5 B+ l
0 E) I `: ]* R( U中断向量修改。
! J N, e$ F, {* z1 a0 T( w+ R! C; z$ V2 U* l/ ?
8 a# W; E# W9 v+ E* U 4、升级测试. a- R, h& h/ J4 p
软件使用secureCRT发送文件。
- m7 j/ y, ^$ K, T+ d( A. z2 ~* e/ y. }- p
2 v- U8 N. I$ X$ p
1 ~6 c H( H& j
" F! q! T7 r9 C7 j+ r$ b: G" s t6 T8 r$ H0 Q; y9 `
6 Q; }, r1 v+ u4 _4 i2 }
0 Y- C) @; a: M. J3 Y
5 v' O5 L" S2 R4 o$ ^8 f8 N, l
0 K( n X4 F+ @2 b; @/ s
. Q' v" o$ J b9 {( C. b- e |
官方版本库太旧,移植时候出现大量写保护的相关的寄存器找不到定义
这教程对F0系列App部分有点不适用
我调通了
稍后发个教程
不考虑接收方接收异常的情况么