本帖最后由 黑色裂变 于 2017-9-18 09:22 编辑
7 Q8 R5 m+ G" U$ `9 T
' V q2 O, X- ?( q7 d5 w: H之前做的一个小项目,因为刚开始没有考虑到以后会经常升级,而每次升级都要旋开4颗螺丝拆壳,然后烧程序,再装壳,如果只要更新几个倒没啥感觉,但是一下更新几百个,那工作量。。。。,正好板子上有SD卡,就想着写个Bootloader程序,通过读取SD卡中的Bin文件进行IAP升级,这样可以大大简化以后的升级。IAP升级原理就不多说了,网上相关的资料和帖子一大堆,这里简单介绍我做的IAP方案,欢迎大家批评指正!
/ f! u9 l: C' v! R7 f" `5 lBootloader程序设计# f" R* ^7 r3 b. |7 {
bootloader程序的设计思路很简单,流程图如下:6 y, g! T7 t# C+ a( ^( S. g
% Y7 O1 r* _4 i! ~6 k- Y, W
- P% n3 Q: K0 a7 \ 初始化程序就不介绍了,比较简单。主要介绍下Bin文件检测以及IAP过程。我将IAP过程分为5个步骤,如下:5 E, C! L I% L- N! p# V$ i4 z
Step1:检查是否存在升级文件,若存在,打开后跳至Step2,若不存在或者打开失败,跳至Step5
& `3 i) G# ]- _! d( ]3 PStep2:擦除App程序对应的扇区,擦除成功后跳至Step3,若擦除失败,跳至Step5, V$ h' k E& P+ l: I4 a# J
Step3:使用f_read()函数读取Bin文件,每次读取2048个字节,并写入Flash。当文件全部被写入flash后跳至Step4,若中间出现写入错误,跳至Step5
5 w4 y( g2 @" A* q3 PStep4:检查栈顶地址,跳转至App程序。若栈顶地址非法,跳至Step5
4 \# t" T) W: e' Z- Q+ b8 [$ KStep5:此步表示本次升级失败,死循环,同时LED提示升级失败,等待重新上电
3 i$ Z2 Q0 b% u7 v9 A9 l3 h6 {查找升级文件时我固定从Update文件夹查找,所以只要将Bin文件拷贝至Update文件夹就行了。
& R# r2 g- A- `- ?+ T9 b5 L五个步骤的转换是通过switch函数实现的。代码如下:0 J8 _% l# x0 @) x
- while(1)1 f$ R9 r) C( d/ n' n
- {
1 \7 m* l( a. `" L" K( @ - switch(iap_step)8 L1 X* o: D+ J- z' C/ ~. w& x
- {- p3 j% f5 h' e$ Q1 {* B8 K
- /* Step1:检查是否存在升级文件 */1 J' ~# A" R9 ?9 f2 y% |% u3 R
- case 1:
4 ?9 Y; j" O; \ ^% J: I - {
' j" E# q8 f& L4 m% M7 S - /* 查找升级文件 */
5 F' X3 z% @( A: \3 i4 B; L' W) b - result = f_findfirst(&dj, &fno, "0:/Update", "FDR_update*.bin");
; D1 o& c7 N3 c e - % _* i, b, f# x, J0 v4 [: ?
- /* 存在升级文件 */; [9 n9 h! q# h3 k
- if(result==FR_OK && fno.fname[0])3 ?" l4 m2 k& D0 a( _8 U, d
- {9 _5 v1 X8 i5 m+ I2 g3 ?
- /* 获取文件名字符串 */* h* z/ }6 t- b
- #if _USE_LFN9 j# V: i' |5 J9 H) ` t6 O" @
- fn_str = *fno.lfname ? fno.lfname : fno.fname;1 @, J& l5 U% M' \9 Y* ^9 N. e
- #else
$ q& }8 Q5 S! z# t2 d9 ` - fn_str = fno.fname;6 v7 g7 ?1 r; `0 L' ^6 r8 \7 h! U
- #endif $ f$ x; q/ t K! K. Y' g
- /* 得到完整的文件名路径 */- I7 p) {- S* N( z; h
- sprintf(fname_path,"/Update/%s",fn_str); 1 o8 D) p$ l* Y# }, B" f
-
) X5 T' _* E% y. q7 B8 d$ @% K - /* 打开升级文件 */$ L! f" ?' I5 U0 D, S
- result = f_open(&file_fdr,fname_path,FA_OPEN_EXISTING|FA_READ);4 t* j/ k4 M3 h) g" S
-
3 {9 `* {2 d3 R - if(result==FR_OK)
1 b8 M, ~% P4 J, {- ^: M: M - { }2 E4 S) i5 t. V9 S
- /* 打开成功,准备升级 */. o6 @9 W# P# U% i/ q# ]" d; b! ^
- iap_step = 2;4 r$ p( ~: e3 E
- }
' w* r" D) @% }9 m9 f - else
8 M# N9 R. S; Y7 v8 R' N3 y5 f$ D - {0 b2 ?( V$ `" E# N. Y2 Y
- /* 打开失败 */
- ]; x$ V# x$ h% I" G4 y - f_close(&file_fdr);
# ?1 Q6 n, ^% k& A- e - f_closedir(&dj); V# @+ |, l7 y7 D
- iap_step = 5;
* `" q. k* \- W7 f - }; R% l. F. E6 x
- , d6 U# l0 K! g# `0 P3 C1 J
- }
: \5 \3 k. \+ m, I0 O - else7 @+ X4 [; R. l- D
- {. \" Y" P H3 A1 Z0 m
- : }" W9 A: S) B2 }% _
- /* 不存在升级文件,直接跳转 */
+ U0 ], i5 Z* l; v" W& H - f_closedir(&dj);1 ^. q9 H9 G* `! W! r t. y4 K2 ^* ?
- iap_step = 4; q9 n& \9 V4 ]3 P! c% [
- $ D0 w/ d5 \ t$ y" F8 s9 J
- }0 I2 S# u- a+ \9 @" f
- break;
6 w1 m& f2 y; D" r. h9 r - }
, n* c1 J# c! a; w2 H3 U' b c+ L - ) I% K4 K8 b$ N) j
- /* Step2:存在升级文件,先擦除扇区 */
$ x1 P! M+ H( z& f) k9 ~9 `3 D) p6 `2 L3 c - case 2:
4 P4 V" x3 }6 v - {) x1 z6 r9 ~$ y' F( n3 c
- FLASH_Unlock();# Q, `: B: ~, M& {1 J L
- res = IAP_FLASH_Erase(APPLICATION_ADDRESS);; @0 i' z6 i- U) @: B
- FLASH_Lock();
, }& v5 y2 p1 U) n; r- u6 | - if( res )5 z0 y8 z3 E' k) a8 T$ N5 s, P6 @
- {
; ~: p3 z% o6 Z2 t$ n8 F - iap_step = 3;
8 W4 ~6 I2 m$ r {5 K - }' M2 H Z p: K+ @8 b% x/ ?
- else9 ? g. C% }& n3 i) [6 b
- {6 \. }7 X) V; p1 d
- f_close(&file_fdr);
( ?. v' ?1 J2 i# B5 j6 b$ a+ z - f_closedir(&dj);
* h% B* G/ c8 `& z4 \ - iap_step = 5;
/ a+ ^. o# u3 Z; f& j. E2 S - }
U7 D( @ u V! L - break;
! t/ U3 |/ k+ ?6 l: ^ - } |+ l4 `) L7 b9 O7 n0 d1 q
-
; n* ], }! B6 m" S9 q9 E, Q3 N - /* Step3:扇区擦除成功,准备依次读取并写入 */1 H5 H' x1 P4 l+ C9 w2 w
- case 3:
1 l; t$ {# z% X; [( x/ V) J$ r - {) D6 y* N8 X. v
- memset(appbuf,0xFF,2052);( K. H& c8 o# X6 u
- f_read(&file_fdr,appbuf,2048,&br);' j3 n0 o$ s. V7 d/ n: Y# ?
-
& W: Z: i! \+ t" M) U - FLASH_Unlock();' w/ Y8 q( e4 j
- . M: ^* P4 j$ m5 H% B6 r
- res = IAP_FLASH_Write((u32*)appbuf,(u16)ceil(br/4.0f));
+ Q$ b3 Z1 W* B5 ~1 j -
" y8 z3 G/ [, u: a4 l6 R; [9 y4 U% f - FLASH_Lock();
! m, P; x* L9 g - / r, I' I1 i- U" [/ ]3 T- r, s
- Toggle_LED_AP();1 |$ p. J6 ^3 C0 H$ B+ T! T
-
8 v" ]$ z. @: I' _0 O1 i. d5 M - if(res == 0)
) V- ]/ E4 Z+ q( P" H - {1 e/ ]$ t6 w9 a( t" d
- f_close(&file_fdr);+ }! Y+ t$ J7 r( `
- f_closedir(&dj);8 m) w" C. W- Z, x+ y
- iap_step = 5;
+ {, A3 K* D' f4 N5 R -
$ i8 t, K% w5 ]& U - }9 G5 b% ?( F, O5 ^/ r, \
- else
# [9 }; ~: @* ~ p# g8 U - {
. i1 Y6 F t/ Y1 @2 Z) `( `# l - /* 文件读完了 */, @, @" _" |9 _$ E! L. Q
- if(br<2048)
s" l& u! z- o2 p% I - {7 T5 [! v4 P% R) s+ z6 @
- f_close(&file_fdr);
& N7 n9 F, }# ?0 U - f_closedir(&dj);- R" @( m# `$ N* U: W
- f_unlink(fname_path);
7 b6 ~; @0 d7 R - iap_step = 4;
$ A3 ?: `7 y# S: n8 p/ v -
+ o g; x+ C) @! w - }
2 i" E- Z# i+ k7 k -
3 R" D2 K9 `: f/ F. G$ `' { - }1 Q& r3 o5 [7 t+ H# ?$ s! v
- break;
& E; e. _2 l$ m* l7 k* h - }( r3 v! N/ T' O+ B: o. z
-
1 H6 h! t+ {& { {! t - /* Step4:跳转至App程序 */
, ~$ f B% H2 t+ m0 M - case 4:
! ~- |; J1 K# _. J- ~ - {
' F4 s5 ]- u% X) G, E# T; w$ w - /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */' B! N1 Q! n0 A5 u
- if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
& K7 f4 U0 P2 S - {
# m% G1 r! j2 Q3 \0 g- ~2 Q - /* Jump to user application */$ j9 G) ^ R% E* x
- JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);% R$ |- H2 k% m: J2 t' c
- Jump_To_Application = (pFunction) JumpAddress;
0 l& u( o5 V0 N) O$ \( U; ~7 a6 W' c -
. z5 [' z: s, W0 A - /* Initialize user application's Stack Pointer */- Q2 p* D7 ?4 {: T3 P
- __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);; U, m3 _) U0 ^3 U+ c
-
- C/ O3 S" z- o Z - /* Jump to application */- t1 O; _: L; O
- Jump_To_Application();
. }( d Z8 u5 w9 ~; I - }
; B7 a1 b( v2 a - else
, P& o, \' ?2 g# C; o/ G4 J3 s- s2 f( { - {
" i9 X7 c3 N1 n# \$ p8 Q - iap_step = 5;
% p$ Z; J+ j. L2 V! o! t3 |$ R5 b! O - }6 T3 T! N. E. K( a
- 1 P8 j5 G) v' T% I
- break; E9 l3 t" |: g& ?4 O
- }0 k# g% I% J- J! |3 h( q! x
-
! q2 k4 |% N! Z2 F7 T - /* Step5:升级失败,等待重新上电 */
I7 Y4 O/ S2 t+ F5 f7 @* z - case 5:/ i+ T7 q* D, ]/ J
- {
" W( Z! L" G& g& i; j - if(GetFreqFlag(FREQ_0_5HZ))
8 F" r* z4 i8 F - {, M2 G/ e2 [0 O& l
- Toggle_LED_AP();+ P, r9 {6 o( Y6 i( o
- }
) o7 L, K1 r: y/ q+ Z5 S- w; U - break;
; `2 ]/ g" P7 @' T7 ?0 ~4 ?7 e - }5 F+ p4 P2 d- g6 g2 w
- 7 U3 }1 f3 x6 E* y
- default:8 I) v% [& _, m) V
- {3 {" V4 U. g5 }. F+ l7 k% w
- iap_step = 1;
3 q2 e/ Z6 }7 B4 J - break;# \8 A6 v6 o* j9 G
- }* ?' o m: ]/ P1 V
-
7 \, @# c# [6 ^/ }9 b9 d6 r6 { S6 V - 2 x# ^4 g5 _1 S& c* T
- }//iap step switch
6 H7 `, i4 M; O5 A* K% j - + F! D. }4 ]; v) U! y$ M$ m, p
- }//end of bootloader while(1)
复制代码 特别要注意形参uint16_t Datalength是指的字数,就是uint32_t类型变量的数量,而f_read读取的是字节数,要除以4进行转换,刚开始就是没有转换导致写的flash数据不正常,跳转后死机。
8 P& V0 N! S9 d3 @; h0 c跳转程序也是参考的官方例程。我设置的App程序起始地址为:0x0800 A0003 G% d& L; a: s& c& e9 e+ G0 K8 X
此外bootloader程序的IAR工程配置如图,flash地址范围:0x0800 0000 - 0x0800 9FFF,占用40K
0 h; Z. _0 J& u
, ?# P7 ]7 @( j4 `5 ?! V% R$ i0 S( P& l1 m
App程序设计* y' Q% a% a: v3 z
1、App程序主要在原来的程序基础上修改flash起始和结束地址,以及中断向量偏移。Flash地址范围我设为:0x0800 A000 – 0x0801 FFFF,占用88K,IAR配置如下:7 j$ f) S( q. b* I8 F; _
" n' V+ c( S( q# T( V6 ~6 r& N5 e6 P& N. n8 h7 F9 \
7 p/ x* @, q5 z5 e# \: G0 j) u' ]' H6 H* p1 h1 i
* R. R: m H, l. t6 E M' t
) T4 S# O }4 X3 q! w; m: y2、由于STM32F0没有像F1,F4那样的中断向量偏移寄存器,需要通过进行内存地址映射来实现,具体实现原理参见http://www.51hei.com/bbs/dpj-40938-1.html, @9 Y3 H0 u! @& R1 _3 g
所以在App程序main函数开始的地方加如下代码:(参考官方例程修改)
app设置
, Y) r" b" ^! Y' t9 |) ~9 ~, A 其实在官方例程中为 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);这并没有打开系统配置时钟,应该改为RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);我也是看到网上其他帖子才发现并改正过来的,在这里感谢网友们的分享!! _( G, j, b6 U
以上就是我做的STM32F0的IAP升级方案,实际测试感觉速度很快,可能我的App程序不大,50K左右,升级过程基本在3秒以内。
L; a7 e/ f& g% Z2 H! l
: A9 l) h8 o( d* \
4 @4 c# r2 r: c: ~+ R. S* m6 L0 a6 x# c* F8 n
3 j, w: g z5 z. b |
谢谢版主,发现少了一幅图,重新编辑上传了
谢谢
谢谢分享,支持一下!