本帖最后由 黑色裂变 于 2017-9-18 09:22 编辑
8 t4 d7 ]6 L3 M" V- D4 p" `' _7 u) M
之前做的一个小项目,因为刚开始没有考虑到以后会经常升级,而每次升级都要旋开4颗螺丝拆壳,然后烧程序,再装壳,如果只要更新几个倒没啥感觉,但是一下更新几百个,那工作量。。。。,正好板子上有SD卡,就想着写个Bootloader程序,通过读取SD卡中的Bin文件进行IAP升级,这样可以大大简化以后的升级。IAP升级原理就不多说了,网上相关的资料和帖子一大堆,这里简单介绍我做的IAP方案,欢迎大家批评指正!
& t9 ?) d; Z y- p; E+ v- E: |, ~Bootloader程序设计$ ^6 p" z; W) Y! ?, B
bootloader程序的设计思路很简单,流程图如下:
. ~, ]7 K0 O8 U! g* d/ V. G
" f) H# G/ Q& L$ b1 H8 q 6 S% J5 _! ?0 f) u6 A
初始化程序就不介绍了,比较简单。主要介绍下Bin文件检测以及IAP过程。我将IAP过程分为5个步骤,如下:
+ z, ~, ?6 T* N2 ?2 {3 kStep1:检查是否存在升级文件,若存在,打开后跳至Step2,若不存在或者打开失败,跳至Step53 V) o5 h% B# r$ @" g) i
Step2:擦除App程序对应的扇区,擦除成功后跳至Step3,若擦除失败,跳至Step5
# I. t5 s" }' K( B9 `Step3:使用f_read()函数读取Bin文件,每次读取2048个字节,并写入Flash。当文件全部被写入flash后跳至Step4,若中间出现写入错误,跳至Step5
+ P$ h6 H& d5 f; G% O. d1 k5 i% A2 k& sStep4:检查栈顶地址,跳转至App程序。若栈顶地址非法,跳至Step5
* J; E$ U. u) |2 G' @. jStep5:此步表示本次升级失败,死循环,同时LED提示升级失败,等待重新上电8 R: W& H! z1 p5 @. q$ E
查找升级文件时我固定从Update文件夹查找,所以只要将Bin文件拷贝至Update文件夹就行了。
! j6 q/ [& e0 r8 ?4 s/ f, [五个步骤的转换是通过switch函数实现的。代码如下:
1 M( o" ]4 i* [5 r' j7 W- while(1) \+ _7 Z8 ?+ }# q+ F/ X% K
- {
4 f! M$ n7 {' P2 t3 Y3 V5 }) h - switch(iap_step)
6 [( c& j7 C: h2 X U! R - {
# G2 R. o8 u7 ^4 y7 _- z - /* Step1:检查是否存在升级文件 */
+ \9 ~7 P3 ^0 b' ? - case 1:, j! Z6 e3 G1 z* B
- {; k1 x% o$ e, S3 B
- /* 查找升级文件 */" t* e9 v+ z3 c+ R2 f
- result = f_findfirst(&dj, &fno, "0:/Update", "FDR_update*.bin");" }/ j0 Z7 L( W' j
-
! m* ]; q7 ]/ W% [& s - /* 存在升级文件 */; f. ]) U4 M# u! V" `) p, b4 @
- if(result==FR_OK && fno.fname[0])
4 T8 }) ^9 i& S1 U - {6 N4 @$ e! o+ o! V& h; ?8 b
- /* 获取文件名字符串 */$ a1 H+ E+ K* B) P, Y8 p
- #if _USE_LFN$ E; t$ A8 M- b9 a. L
- fn_str = *fno.lfname ? fno.lfname : fno.fname;/ x3 w( Q3 J u S' T& F: O/ S
- #else/ R+ J" u$ e4 T) d
- fn_str = fno.fname;; G* S( {8 T2 U, c2 [9 x- [
- #endif ! v# Q" \) g- I" W' i& K
- /* 得到完整的文件名路径 */
6 N& o7 s- Y+ _' K - sprintf(fname_path,"/Update/%s",fn_str); ( f- n2 C9 G" E- Y4 p) O2 q
-
& R! n" k, T, q b1 z - /* 打开升级文件 */: ^. ?( G) w0 f, D0 i! _5 k
- result = f_open(&file_fdr,fname_path,FA_OPEN_EXISTING|FA_READ);+ z" y& L$ S5 X; t4 R
-
1 h* O7 c* K* B- O& j8 T2 \ - if(result==FR_OK)
, ~9 D8 ^* ?3 P1 j. ^+ r! r: V - {' Z6 `' m( V- D* `
- /* 打开成功,准备升级 */
+ W" N% J m9 j6 p - iap_step = 2;4 o) W) J$ Y! u; O `/ D
- }
& D8 P6 i- }: k% @7 K3 } - else r6 g7 Q \" Q. j- v* v* M
- {
; Q6 O: {2 U- {. \ Y/ K - /* 打开失败 */; Q+ Y* r# F2 Y5 |( F3 h3 n" @
- f_close(&file_fdr);/ c6 v- B! M6 `% ^7 c
- f_closedir(&dj);* `3 |; k$ F9 b4 z( E8 q
- iap_step = 5;
/ e+ F( m5 {9 q( O( O - }
" T: _* l; @+ T* p: N+ l - ) u# c5 R: H: I0 _( M& k9 Y# K, {
- }
2 ?* Q; S' ^6 U2 H2 ^3 b - else
- z9 R/ {+ j' I% x7 L6 N3 N( j) i4 S - {
6 g' [5 G5 o O$ ^5 m4 V/ B - $ ~3 M* O8 Y& f( r3 j7 L- B; ~5 z% T
- /* 不存在升级文件,直接跳转 */6 {. L- e9 d! \( B' x+ E
- f_closedir(&dj);& R) c5 U8 |2 |
- iap_step = 4;
' f$ T' q H0 x - . x9 o8 g- F+ g
- }
/ @1 {7 v$ q( k/ B( y, S& U - break;
2 o) f3 i& w% x7 I - }
$ N0 E+ ~" A+ _% i - 4 v* H6 F6 V. e/ Z) z
- /* Step2:存在升级文件,先擦除扇区 */7 f: r1 \# ~! F
- case 2:5 P6 v! x$ s& O3 O! e+ u
- {5 u1 _4 J. D4 v9 ?0 j
- FLASH_Unlock();) z9 @$ W+ z, N# {) Z4 U
- res = IAP_FLASH_Erase(APPLICATION_ADDRESS);
+ K% H, R9 i/ t - FLASH_Lock();
7 B% s& \! Z; [& S, w7 r0 L8 W - if( res )
. |0 Y7 a; F. [ - {$ `& b* V F$ @6 r0 O
- iap_step = 3;5 a; g: }3 m: L% L& [% D/ H6 W
- }
: _; t& V6 j1 t. d - else+ G7 j: q6 d; ^" ]6 p. P% \
- {6 f* Z5 c% [5 t9 c
- f_close(&file_fdr);
6 d! i: J [+ z }1 k% m - f_closedir(&dj);& z8 }; g8 ?5 b+ d/ z
- iap_step = 5;/ U* V1 W0 C6 Z% X; @) G& k' \
- }+ m' B; b" ]! \
- break; K' C9 s2 n) d: Z2 F
- }8 f* r- B8 b; u' _: ~
- ) G$ _ \ S6 ^; {* p/ S+ s% f
- /* Step3:扇区擦除成功,准备依次读取并写入 */7 d0 X& O5 Y0 a+ Q6 N. _
- case 3:
5 Z' `. Y- k* ~! s) @1 [ - {# v1 U( Y1 l" n, o' H
- memset(appbuf,0xFF,2052);) G6 P& H5 k3 n; a
- f_read(&file_fdr,appbuf,2048,&br);" K0 H' D. x1 e
- 5 `! r3 z6 A" F+ i: m7 U
- FLASH_Unlock();0 I' e8 k, }) @- W
- ( _* T6 c: P6 a9 l/ n) c/ S
- res = IAP_FLASH_Write((u32*)appbuf,(u16)ceil(br/4.0f));" Y1 O6 Y& y |' L9 I' f; K, ?
- ' ]) M* E' T7 d6 f
- FLASH_Lock();
) @. F! T# i* H% D8 m* C# C' \ - 5 z" o0 p7 X V# c4 x% d8 z: g
- Toggle_LED_AP();
' T3 h5 l3 x" V/ e4 v -
9 p3 l! K) k. R3 J- C) U) V - if(res == 0)" n8 s5 U: ~* r9 A2 p$ [; i
- {
, |' c, p/ I1 x - f_close(&file_fdr);
) D' a: i2 k& f' a) x+ l - f_closedir(&dj);" S- C1 f2 K. ]0 ?+ N! f
- iap_step = 5;
1 s$ L3 j. A% h - ' L1 a) z# W& y/ u D
- }
$ b; h8 {8 F5 p1 m, I. i - else1 ^. a8 n* @8 s z/ ~
- {3 ]" A" F4 j. O0 |$ y
- /* 文件读完了 */
. x. N$ t0 A( h7 n! b1 I0 u# S% y8 j - if(br<2048)) b# J% a' z7 u( L! [. | ?
- {( ~, Q; C' a, v" N
- f_close(&file_fdr);+ k7 d% o! m) {3 r" y" W9 i
- f_closedir(&dj);
) \! d0 N' C# X+ U- W - f_unlink(fname_path);6 `. O( Y1 L0 ^0 p& x. c4 ^
- iap_step = 4;
3 g1 _! u# e T3 k -
* G1 A6 W" v5 o/ Y - }6 g- S- s* ^: y7 O* q( M
- : c3 E$ I+ G4 n0 L
- }
0 ]* h+ x4 y& _0 m7 @ - break;
, `, w. I4 _4 D - } z1 t9 b9 U) g$ T
-
+ U8 s8 g% u. Z2 Z( \ - /* Step4:跳转至App程序 */
* [8 V* K% ?3 a0 k - case 4:* d. ?7 t2 A4 c
- { - K6 L9 U+ {1 s+ i+ [
- /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
; V8 s: _) h1 J* p, K: M5 G - if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)8 M3 K) c; _; i8 d4 k, H2 k
- {
- V9 O: d4 r/ D s! \ - /* Jump to user application */
( w0 _; k: w* d p5 U8 s; x - JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4); r# [( t+ M' r1 b
- Jump_To_Application = (pFunction) JumpAddress;5 Y6 m2 J* B! C2 |: h. |" E" ^( o
-
$ [( j: v' d- _# U( c - /* Initialize user application's Stack Pointer */
: N* [7 Z6 |$ b: v+ A - __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
8 h) o: T# a& R( V1 ^7 H) Z5 W+ P4 L - 4 Y' l/ y3 U& t$ d3 [
- /* Jump to application */
& q0 L2 j U7 z) b, v - Jump_To_Application();, d+ V9 D, ^! O1 K) Z
- }
* X D% U" U9 Z0 p5 p - else4 i; X# \/ S! I" V, A& q
- {
/ O5 [' j, T" o; w, ^ - iap_step = 5;
: g5 b9 E$ ^* o2 f+ W7 \ - }( s% y/ T. H; d" I- T/ k P
- 2 A" Z/ x3 q a; v/ t& f
- break;
% d2 i: v8 w) J5 B# e% g4 @% _( s - }3 w1 s# M1 y& g C; V% n* P
-
8 B) v# [ B- U4 I8 u: `3 w! M2 X - /* Step5:升级失败,等待重新上电 */
! S+ T/ \2 [; r) @1 k( w - case 5:, n' b1 W1 L3 v \! g% m
- {
# N7 p1 L4 h8 _, s7 k% Y: S - if(GetFreqFlag(FREQ_0_5HZ))
' ?8 B% E; x5 @ - {4 D) n* p' ?. k- x$ _. b4 G
- Toggle_LED_AP();
, j d3 }3 p/ D( f& H1 G: w; n - }1 ^" t4 {8 Y( x7 Y& x9 {8 J
- break;1 t- }+ ]! e- p7 o0 l( s
- }
& v5 o5 `$ X9 K! u c4 g - ! ? U% u: N/ S
- default:& q3 d3 K" f( v! o6 z# y
- {4 k) F1 H- R5 P
- iap_step = 1;: y' y* L$ Y' d& R6 j
- break;) Z" C9 F6 W9 J7 h7 o- m$ A3 h1 G
- }
& c3 T6 d) M. ?+ B" @5 ?0 i - ) y# J7 F. \! `/ ~
- 3 x+ J) F7 u! P
- }//iap step switch! Z6 L2 `4 P/ `7 c6 o. C
-
5 l/ y6 d/ O5 _ C" [' V+ T/ _ - }//end of bootloader while(1)
复制代码 特别要注意形参uint16_t Datalength是指的字数,就是uint32_t类型变量的数量,而f_read读取的是字节数,要除以4进行转换,刚开始就是没有转换导致写的flash数据不正常,跳转后死机。
! m& Z0 c$ _/ ?' Z. a T跳转程序也是参考的官方例程。我设置的App程序起始地址为:0x0800 A000
- B& x0 }; l5 `1 P1 H此外bootloader程序的IAR工程配置如图,flash地址范围:0x0800 0000 - 0x0800 9FFF,占用40K
8 O V3 d0 L! G/ Q: `8 N4 M1 i
$ v$ {# d% y3 W2 j& L3 V* LApp程序设计5 [7 S* y% ?% r3 C
1、App程序主要在原来的程序基础上修改flash起始和结束地址,以及中断向量偏移。Flash地址范围我设为:0x0800 A000 – 0x0801 FFFF,占用88K,IAR配置如下:7 c- k' a" D8 {/ I) m
+ B6 Y V9 o9 \8 ^9 Y
1 l: J2 j1 R/ K; p) m* [: a/ `; Q6 \
! j2 W$ M5 d) [- r6 P
% z7 x0 A6 }$ \! c& c3 Q f; N" C% @7 U3 O- `- _
2、由于STM32F0没有像F1,F4那样的中断向量偏移寄存器,需要通过进行内存地址映射来实现,具体实现原理参见http://www.51hei.com/bbs/dpj-40938-1.html! f& j- v( G4 o F$ T
所以在App程序main函数开始的地方加如下代码:(参考官方例程修改)
app设置
2 e% m6 ^& J% ?1 `' k$ J! \
其实在官方例程中为 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);这并没有打开系统配置时钟,应该改为RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);我也是看到网上其他帖子才发现并改正过来的,在这里感谢网友们的分享!, n1 v' p9 F4 H% }" C0 m! J% s
以上就是我做的STM32F0的IAP升级方案,实际测试感觉速度很快,可能我的App程序不大,50K左右,升级过程基本在3秒以内。: m/ u9 [9 U4 S! N6 b+ m( f* _
L2 C9 K; h2 g* H+ Q; b( f3 f; L' @1 s
8 b% X$ k4 V" p' p
6 n6 I" W) F6 c
|
谢谢版主,发现少了一幅图,重新编辑上传了
谢谢
谢谢分享,支持一下!