本帖最后由 黑色裂变 于 2017-9-18 09:22 编辑
) _4 h" ^- a8 e A+ c1 i
* v9 O1 [+ p* ?8 |& W2 h7 v; e. o之前做的一个小项目,因为刚开始没有考虑到以后会经常升级,而每次升级都要旋开4颗螺丝拆壳,然后烧程序,再装壳,如果只要更新几个倒没啥感觉,但是一下更新几百个,那工作量。。。。,正好板子上有SD卡,就想着写个Bootloader程序,通过读取SD卡中的Bin文件进行IAP升级,这样可以大大简化以后的升级。IAP升级原理就不多说了,网上相关的资料和帖子一大堆,这里简单介绍我做的IAP方案,欢迎大家批评指正!
j: M: G5 a0 K1 S0 o/ OBootloader程序设计
* n, @% {/ j+ g2 y! Z2 [bootloader程序的设计思路很简单,流程图如下:* G- M9 U; @2 [, R; H0 s4 `
8 O% {: R) i( `" d# o0 w( h

1 M7 e m. g. _* J 初始化程序就不介绍了,比较简单。主要介绍下Bin文件检测以及IAP过程。我将IAP过程分为5个步骤,如下: v* _1 d" H/ L0 ^$ b+ @( h: U& b
Step1:检查是否存在升级文件,若存在,打开后跳至Step2,若不存在或者打开失败,跳至Step5
B" j% U. {# f( x9 T- Y' `+ ~& FStep2:擦除App程序对应的扇区,擦除成功后跳至Step3,若擦除失败,跳至Step5
7 H4 r) v3 S- O2 y1 GStep3:使用f_read()函数读取Bin文件,每次读取2048个字节,并写入Flash。当文件全部被写入flash后跳至Step4,若中间出现写入错误,跳至Step5+ w- _4 [( m; o s6 m9 D
Step4:检查栈顶地址,跳转至App程序。若栈顶地址非法,跳至Step5, J( c6 i7 u9 }. r
Step5:此步表示本次升级失败,死循环,同时LED提示升级失败,等待重新上电% v; k6 i0 s) B7 w3 @
查找升级文件时我固定从Update文件夹查找,所以只要将Bin文件拷贝至Update文件夹就行了。
o7 F! N9 B7 l( `8 ?5 @5 Y五个步骤的转换是通过switch函数实现的。代码如下:
0 n$ k% C0 P$ P- while(1)- ~" m: W9 e7 @/ }( F* O
- { v/ E! w" s" I1 N; b0 f& M
- switch(iap_step)
7 I( T+ `8 ]+ s! f - {
8 i5 n1 x8 R ~+ t- ] - /* Step1:检查是否存在升级文件 */
& C& M0 G& ?4 B4 d' | E - case 1:
z* ^& ?) b0 R! e1 a& F& s - {
6 X- H7 h$ y/ i7 j$ L - /* 查找升级文件 */
$ O8 x( l; m) W - result = f_findfirst(&dj, &fno, "0:/Update", "FDR_update*.bin");
' q2 k8 Z! \! v* g$ } - ~+ A# Q- w" }/ v
- /* 存在升级文件 */
& F5 e9 B& `" e X - if(result==FR_OK && fno.fname[0])
; v7 o) C# H+ T3 ]" [0 @ - {" }# o* V' B& c" [& b e
- /* 获取文件名字符串 */, _3 E; S+ f: j, m
- #if _USE_LFN
/ _, L6 A! B/ B0 D7 K% A - fn_str = *fno.lfname ? fno.lfname : fno.fname;1 p+ T+ K, T7 Q1 N8 ^4 o n
- #else
8 j5 M2 g! N! U - fn_str = fno.fname;
\% R- Y4 i' Z7 K) u - #endif * X; w1 A8 g9 b# _( o
- /* 得到完整的文件名路径 */5 f5 z% B2 j" H# e& p7 @
- sprintf(fname_path,"/Update/%s",fn_str); : }& k& W9 s7 ]+ U/ M9 r2 X- r7 I# J
-
. E% d" O2 f( `8 u; C/ r - /* 打开升级文件 */
c9 Y& u4 V6 n& t8 t, m8 q5 a+ O - result = f_open(&file_fdr,fname_path,FA_OPEN_EXISTING|FA_READ);0 C8 j; t+ v" {9 U1 d; B4 T& P
-
* C- Z# I7 }) h8 Y, ]3 Q2 t - if(result==FR_OK)$ |: {4 ?( q9 i" _
- {
. m0 y( S: R% X8 s6 F - /* 打开成功,准备升级 */- o. C8 y! b- N; z$ \8 n
- iap_step = 2;7 {5 _* E/ _1 }( f
- }* W. K: m. j$ ?1 |' @
- else
. o4 s* l5 W, \) p - {
7 W/ d* k3 {* F. o: s- C& ]3 Y& }, e - /* 打开失败 */
7 Y$ X9 b; [: C3 E- L" @2 f - f_close(&file_fdr);" F2 W2 M- O) M* ?( m2 L
- f_closedir(&dj);
* g" T! M8 ^4 T$ |- x z - iap_step = 5;# \1 u2 }) q+ ]# T8 V2 G ^
- }
3 x+ _9 o) ]. Q2 L( V' a -
$ A' n( @" \# I' [" Q! q+ t - }
' c1 q( c9 l% B) e, j, C: b - else
$ B% L& Y" i& |9 E* ?1 z - {
q- A1 ^$ J/ ~9 V5 _: f* Q2 Z7 W - % t0 v' h: }/ O$ M3 Q
- /* 不存在升级文件,直接跳转 */" y7 e2 U9 q* [, X
- f_closedir(&dj);7 z6 e4 Y8 X+ s. |7 m# ~
- iap_step = 4;
1 p& z( x: X. e) A& {! Y5 k9 q -
- N$ L6 v( `* h1 H - }5 h4 A' C$ b. \. q6 |- S! K
- break;( E8 D7 K8 A6 J0 u
- }
" j7 o+ l) Y. W - }, B0 z- I/ }7 K$ G" P' U& j7 P4 E
- /* Step2:存在升级文件,先擦除扇区 */
, w. X* H1 h& v: P) c, l, x# \; Z - case 2:" i/ [9 e& n& y( Q8 k# Z b
- {- [/ T/ E. d$ r: _
- FLASH_Unlock();
6 j5 j" r6 c, _0 W3 w& O - res = IAP_FLASH_Erase(APPLICATION_ADDRESS);0 G+ U% g1 I, n# }& k0 u
- FLASH_Lock();
1 d5 p4 s" c8 Z, Q- r$ u - if( res )
( `8 b$ P0 b7 N9 b$ Z$ I - {! i v2 P& P9 I! t1 x
- iap_step = 3;
: I' ?0 H# O4 N+ A& ] - }
% T) _* b3 m3 ]9 `2 v8 ^9 n4 _ - else' Z5 O6 B% S- d. p
- {0 V0 ]/ I! W6 c9 `
- f_close(&file_fdr);
9 O, Z( N, A2 g6 a1 q- B" I- \ - f_closedir(&dj);
5 L7 s' O- r: Y - iap_step = 5;) _! d0 G( P$ B2 J, {! \# {% _7 x
- }, d9 R# l5 ~0 l6 y
- break;6 E/ ?3 n* l e+ H7 r# k! v- G
- }+ P+ D7 ~5 c6 s7 \
-
" T& \! m7 d7 Z3 E1 t0 ~. R+ t+ S - /* Step3:扇区擦除成功,准备依次读取并写入 */
8 {# V# }6 n0 X% J" U! W/ v" k$ X - case 3:
% B! m, t# k7 ^' u, H( [ - {2 m6 V2 A9 H1 H7 v( ^" m
- memset(appbuf,0xFF,2052);% }! k9 T9 ]- n ?, S2 D; R5 o
- f_read(&file_fdr,appbuf,2048,&br);
. p8 l6 Y/ N$ _" E1 D) M - w+ d3 Q; \# ?
- FLASH_Unlock();
$ {$ G1 A$ i- f* c" C1 J- F) | -
( {4 |, |% U/ ?! u" j2 F* p) m - res = IAP_FLASH_Write((u32*)appbuf,(u16)ceil(br/4.0f));
7 f, \- Q. Z4 _( c/ o - - {. w: O T' [; V
- FLASH_Lock();
, C' Y7 J4 i1 F% f3 q- @3 P -
9 B' s/ w. S1 Y, o3 T - Toggle_LED_AP();
4 v6 j! Y8 ]2 g4 q/ l8 | -
, j- s- [$ B% M! a: e - if(res == 0)
5 c1 E! E- s) B# t - {
7 p: W% z; `) b( m9 h9 ] - f_close(&file_fdr);3 T& B5 x0 X6 a" ?0 }
- f_closedir(&dj);
: P9 O$ s5 o5 F; D7 V x# T, ^ - iap_step = 5;& V' n, {- Q9 ^3 E- k) \! g2 _
- ) f7 U! s, Q1 ^ N" _2 V. S' h5 S
- }
7 A$ G4 V* V( \1 @ - else
; _" [5 W3 p7 V- k, L& \/ J, R V - {
9 H% o/ }4 l, H: Z - /* 文件读完了 */
/ [- k1 e" w9 j3 O0 U0 Z - if(br<2048)9 O$ r' z0 ?5 |6 [
- {, a1 @- ]3 k) O0 I( D3 k
- f_close(&file_fdr);. y. ^" w. o* ~* C9 b/ [
- f_closedir(&dj);
W0 H1 Q$ O6 U7 X1 [6 r - f_unlink(fname_path);
1 r1 p4 ~9 v: t( S$ m - iap_step = 4; ; o/ ?* b) m& Q8 N* P
- 8 P. Q7 m4 x- U" b8 [8 @5 v6 t1 S$ E
- }+ {) z* H' e" b, @! O- q
- $ n% z$ D. l& V9 ^% V' x* R
- }
1 X4 ]" X: K0 ^. V' \ - break; Y9 Q/ a1 K# }2 b) T- T5 ~
- }9 X' D6 \2 n* P/ @: J
-
! _- e) x! _' B6 @" F - /* Step4:跳转至App程序 */, M! U& T6 t2 {1 L+ B: [: Y9 P- E
- case 4:
. e! E+ v8 K8 l2 o - {
& Z- n' X; ?$ M4 h7 `4 U. J - /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
. e2 G+ Z7 u' a5 k. j" { - if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
, v: P x) E, X, }/ ? - { 1 Z5 S3 J. _. U+ i6 ~2 H) T
- /* Jump to user application */
, b$ J" g: j3 W, G& k - JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);; l3 y( Q4 C( x
- Jump_To_Application = (pFunction) JumpAddress;
& E4 i1 T/ w% r- N8 [5 O# Z - 9 N+ K4 x. {# |: j3 a5 N) J# Z
- /* Initialize user application's Stack Pointer */
* G S6 H7 r$ Q# u/ Z" d - __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
& x! V2 h1 y+ { - 6 X. Z, Y _0 A8 ~( Y. R* `
- /* Jump to application */9 J$ T/ X' X. b2 U1 O w
- Jump_To_Application();: Z+ X! x4 g: a6 B
- }) }" x a! C% R7 p" J! h. [
- else {, F, l7 a! L; l/ d7 |) ]0 V" Z3 H
- {
! a/ L0 r! o4 w6 G% I* S - iap_step = 5; $ a$ l. w6 L. M+ U8 D. P$ @ f
- }
) ~4 c5 H8 @2 H! z& V; Z - 1 q0 J& U8 Y: i, t
- break;
% }3 i+ S1 t1 I& p - }/ @( I, v* ]- w% y4 ?( U" v- q
-
! R5 }/ Y* g# X2 b) c9 c0 h - /* Step5:升级失败,等待重新上电 */
4 l. O. K; \9 g. d1 g - case 5:% T4 s. A2 l( \
- {# b5 n! Y( o' p; o) v" p
- if(GetFreqFlag(FREQ_0_5HZ))
" T* `3 U+ g5 Z; O# e8 e - {
) ~' P+ A) `: b: l1 e - Toggle_LED_AP();
' m- E3 ^; ~. F/ v" G - }- g* J2 v! `8 Q# F( H7 T
- break;
% d6 _1 R) C8 p/ S6 w- C - }# c$ y r5 w; D3 p% M5 ]; j$ ~
-
+ e& \+ h2 g+ B% S1 { - default: _% L7 \6 K: I9 ~* E
- {1 X9 ~, _$ x$ h$ C, {# k* u5 L* p
- iap_step = 1;3 ^; R) `& c6 \$ \) w5 g& z
- break;7 A2 j1 v( E, _7 e z3 o
- }
1 }. t: v1 J7 }( ] j Y! N - . u# x8 e$ |5 \3 V! @8 g, X
-
4 x( B- J' m0 w" t& T - }//iap step switch
$ U: | K) h T ~3 u6 x ~ - % n& c) f0 u0 K; m6 v, Q. B8 N
- }//end of bootloader while(1)
复制代码 特别要注意形参uint16_t Datalength是指的字数,就是uint32_t类型变量的数量,而f_read读取的是字节数,要除以4进行转换,刚开始就是没有转换导致写的flash数据不正常,跳转后死机。8 ^$ g7 y. L6 B7 C2 m# @" ?
跳转程序也是参考的官方例程。我设置的App程序起始地址为:0x0800 A000
: c, e# D* E$ X! _' ~& f此外bootloader程序的IAR工程配置如图,flash地址范围:0x0800 0000 - 0x0800 9FFF,占用40K/ U# r9 ~ U7 F. G
6 @% D/ Y6 X! l9 Y# d! u) l4 _. J* V$ j X
App程序设计- H% C" l, w4 q, ]( f: j# h* x4 V
1、App程序主要在原来的程序基础上修改flash起始和结束地址,以及中断向量偏移。Flash地址范围我设为:0x0800 A000 – 0x0801 FFFF,占用88K,IAR配置如下:- g+ ?) I+ ~, h
2 [+ L6 o c6 a. |. p3 p
1 ^4 O: u9 D1 o: g$ M) z: X! w
1 y+ C! _6 s# x7 B- F5 t3 Q& n& \
7 j6 N- s# N& ~3 B
- j* D4 L2 r% H
) [6 r; ]. m2 b2 z& | @- O" U$ h2、由于STM32F0没有像F1,F4那样的中断向量偏移寄存器,需要通过进行内存地址映射来实现,具体实现原理参见http://www.51hei.com/bbs/dpj-40938-1.html, G* n: [0 Z7 b$ O7 a% ~
所以在App程序main函数开始的地方加如下代码:(参考官方例程修改)
app设置
; c9 c, m* x+ ?, C5 [7 f
其实在官方例程中为 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);这并没有打开系统配置时钟,应该改为RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);我也是看到网上其他帖子才发现并改正过来的,在这里感谢网友们的分享!
4 z3 V) N/ b) h9 y/ [以上就是我做的STM32F0的IAP升级方案,实际测试感觉速度很快,可能我的App程序不大,50K左右,升级过程基本在3秒以内。
0 g, T$ A, C2 a. ]' e8 k
2 I6 V6 M* u' B, D3 Z3 W- T+ J7 o, R% H& L6 a
* D# A$ N/ R& e1 e# Z6 }" I; {1 F/ y) B9 \
|
谢谢版主,发现少了一幅图,重新编辑上传了
谢谢
谢谢分享,支持一下!