本帖最后由 黑色裂变 于 2017-9-18 09:22 编辑 q. [- F: s" F6 |; O
' O; u+ B* U: `* P+ u( ~
之前做的一个小项目,因为刚开始没有考虑到以后会经常升级,而每次升级都要旋开4颗螺丝拆壳,然后烧程序,再装壳,如果只要更新几个倒没啥感觉,但是一下更新几百个,那工作量。。。。,正好板子上有SD卡,就想着写个Bootloader程序,通过读取SD卡中的Bin文件进行IAP升级,这样可以大大简化以后的升级。IAP升级原理就不多说了,网上相关的资料和帖子一大堆,这里简单介绍我做的IAP方案,欢迎大家批评指正!
/ X8 M# M$ ^( m( p2 h! ZBootloader程序设计* N6 z, ^3 c, c/ L
bootloader程序的设计思路很简单,流程图如下:
8 e. N5 y+ {: Z7 B! J( |) i1 Q- r
1 F8 s. D3 r7 r3 A( \
: J8 ?0 J0 _2 L 初始化程序就不介绍了,比较简单。主要介绍下Bin文件检测以及IAP过程。我将IAP过程分为5个步骤,如下:- v1 D$ O" k, R/ q; G
Step1:检查是否存在升级文件,若存在,打开后跳至Step2,若不存在或者打开失败,跳至Step59 ~- L7 \ r8 @, x. ?6 _! [
Step2:擦除App程序对应的扇区,擦除成功后跳至Step3,若擦除失败,跳至Step5
: C5 [* J* Z. J- }' z+ @% s. qStep3:使用f_read()函数读取Bin文件,每次读取2048个字节,并写入Flash。当文件全部被写入flash后跳至Step4,若中间出现写入错误,跳至Step54 e9 N8 Z5 }+ e5 N; z! M3 {, B- d
Step4:检查栈顶地址,跳转至App程序。若栈顶地址非法,跳至Step5
& A* N& y1 ]6 G. R5 D: vStep5:此步表示本次升级失败,死循环,同时LED提示升级失败,等待重新上电+ `4 u9 p" k. _% K( {
查找升级文件时我固定从Update文件夹查找,所以只要将Bin文件拷贝至Update文件夹就行了。* l& @* p" n* ~1 {7 G
五个步骤的转换是通过switch函数实现的。代码如下:: B% H: f: }: v& d1 L1 X. w4 p
- while(1)
: Z0 M6 F a/ C' R) K% S, J - { f# w- s4 s& w1 ~& t
- switch(iap_step)7 I! q- X# _! n2 a% ?
- {
) g/ m% e) i* `5 E& s - /* Step1:检查是否存在升级文件 */
P+ h; M+ [. D" P - case 1:8 V3 G0 w8 ~0 O& W$ |2 S* ^
- {
% g5 \# a2 l& O; x7 i% t; _ - /* 查找升级文件 */
: x8 t0 m2 x7 o, w, [ - result = f_findfirst(&dj, &fno, "0:/Update", "FDR_update*.bin");
- h' u; \: P2 C/ ] -
+ l; {) p9 j2 T% c- ~# B! D# ` - /* 存在升级文件 */
8 i* B% W6 z! T9 P3 p' P - if(result==FR_OK && fno.fname[0])$ T/ [% @; G( |* R# v" `
- {3 {7 q; s8 E+ h# s
- /* 获取文件名字符串 */- f( R3 j, D2 I4 V) y! W! S
- #if _USE_LFN# U) l3 H: m6 P1 G
- fn_str = *fno.lfname ? fno.lfname : fno.fname;- Q5 Y3 b' j$ o6 W8 v+ v
- #else1 E$ Q. }( f3 H) Y0 q5 S+ e' f
- fn_str = fno.fname;
) G+ w; m, \4 D m: V; a8 E O - #endif 2 G; ^7 O% Q$ p9 ~. A
- /* 得到完整的文件名路径 */' t- n- G h: e$ u
- sprintf(fname_path,"/Update/%s",fn_str);
# P! K/ Q; T, f0 s! X -
# ~- ?( ^; C4 o. `0 N: G! H - /* 打开升级文件 */
3 a( }' }2 b( d _# k5 l3 Q$ `3 Y, C* R - result = f_open(&file_fdr,fname_path,FA_OPEN_EXISTING|FA_READ);1 t8 I1 w% F/ w
-
; O% f7 j3 k9 ?* z - if(result==FR_OK) S! S5 ^. Y% a8 B5 J2 Y
- {: x) V$ A- Z0 U
- /* 打开成功,准备升级 */
2 q8 G& K( b; U( |5 D5 @% U L - iap_step = 2;! y+ h# i0 ]- q
- }* i6 Y- N. Y& |- h
- else
! p" m8 Q' z: h# T+ x# p2 W. \ - {
4 o3 d% S: U) h% ^ - /* 打开失败 */+ @9 }+ M+ h) O, c' u, V" S. R; [
- f_close(&file_fdr);9 o U3 D7 R6 j0 K6 \+ y; U+ n) X+ q% f
- f_closedir(&dj);
' o- {, i8 F5 J5 L - iap_step = 5;
- O9 q" Y* m' A1 C% D4 t7 T/ e - }" U9 [4 ]' Y" Q2 I! i/ d7 n
- " N+ F: ^5 L' t+ m J2 _
- }
9 {; D# r( h: _+ n+ w$ N/ I - else e" m# Y3 C; P% u
- {
9 P6 b3 ]2 `0 q1 K, |+ ] @+ e - & e9 w6 g3 O- _, v) U% T+ d: T
- /* 不存在升级文件,直接跳转 */
' \: v" v" B! b - f_closedir(&dj);
2 c* T* F" e2 Z" Y# [ - iap_step = 4;
: V3 H. p% ?# F9 K6 i9 J - # Y) t) \% Z& A# ^
- }
2 F! s& G$ C! j: x+ |% o7 y - break;
2 m! e3 K( R& v- U! t. Z - }
( y8 H6 v% W/ e - ) P, G3 s+ @& z* T2 D1 ~" S6 v
- /* Step2:存在升级文件,先擦除扇区 */
+ v- ^, U1 w5 U/ k' H( a - case 2:1 N* e' X+ W/ k' H
- {
+ d; P0 P+ t) B. V - FLASH_Unlock();, j8 I" S- t+ S
- res = IAP_FLASH_Erase(APPLICATION_ADDRESS);
$ h! ~; m% o. [6 p* a$ i - FLASH_Lock();8 e& u, I: n$ B5 A
- if( res ) E6 T5 x' ^8 F, R
- {) Y( x! D) p' G2 P* G
- iap_step = 3;! H% \/ _. S6 |3 S
- }
5 {- s7 x' S1 p% A - else
4 g! V9 b+ o1 v/ K; s) `7 T6 r - {
! A! a1 Z# x- c6 p5 f - f_close(&file_fdr);
# Z& A8 B0 z& [2 g1 ~. |0 s - f_closedir(&dj);
6 Q, r1 G# X! h9 [3 E5 D - iap_step = 5;. L. E; O% `3 J" G- Y
- }
% a8 k; d7 ?) `9 w1 c- d6 A - break;
! ]7 P! o& t7 l2 i - }
# o7 I5 p1 e& v. z5 C. H - - A, X" a0 D, [2 t; {0 q k. R
- /* Step3:扇区擦除成功,准备依次读取并写入 */& ~ |' n& h ^- L' I
- case 3:5 \4 g/ S* P) Q2 M
- {+ n3 @1 f8 \& {4 m# s$ t
- memset(appbuf,0xFF,2052);
; E Q( a% R/ O# o+ K( q - f_read(&file_fdr,appbuf,2048,&br);
6 g; B0 x* Y+ d -
( T6 c' `' g* h/ F5 _ - FLASH_Unlock();$ d+ K: R- \2 e& F# o6 d4 \' S! T& A# r8 `
- 4 \( f7 p( d8 d
- res = IAP_FLASH_Write((u32*)appbuf,(u16)ceil(br/4.0f));
3 t; U: x, E0 V1 U8 i. s; {. Q* a -
! O2 }1 l9 d" m - FLASH_Lock();
( m4 K9 y- y5 u -
# A) `: Y: H( o% ^ - Toggle_LED_AP();) X' y4 V4 j; R- ~& n
- : I; z; n* a9 G1 _6 R+ B1 z
- if(res == 0)1 I2 R, a) {! S3 m
- {8 T% z; Z5 q( @: u9 i8 R% q- u) `
- f_close(&file_fdr);
( ~' G) Z/ q, N. S! u( }- p9 D - f_closedir(&dj);
6 s; r, o' o$ i& O, M4 j6 Y% P - iap_step = 5;5 k) N6 ~& `2 l6 A( g0 Y' t
-
. n3 p, O' P1 U' u - }
) |1 Z+ n, a& t4 M - else
0 }, z( ]- N/ n) o% U/ T" ? - {
0 {( \ }! V( _) F+ j" u( K - /* 文件读完了 */: y$ t9 i' E: J& V" B
- if(br<2048) i/ X, d* K% P
- {# L7 U. w2 H3 Z4 Q1 E
- f_close(&file_fdr);
: A# }" c' g. C% D; l( h# u0 x - f_closedir(&dj);9 t% r8 t/ E$ V- O8 q+ W
- f_unlink(fname_path);5 M& Y9 z* H( t& ^" |" R
- iap_step = 4;
* F* v: J! e6 B- O$ T - 8 c% k& D! g v# _
- }+ u& f, l0 ]- K! l6 c, m
-
( {: B* i, W$ M, e: X( p, k - }
8 z3 m% R' E$ I* t7 @, @$ c - break; 0 L( f3 h- |/ O% G
- }
9 d, {) s- Y# x' ?4 v" T* r - " f" T+ ~- C' X
- /* Step4:跳转至App程序 */5 ?0 C o" Z! S { l! O
- case 4:
# i1 t* Q2 U1 K0 J4 G - {
' u7 |. B5 U/ g, l - /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
- N: t( \. h; h0 n - if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
* Y5 X. N( |4 v* P0 p - {
; F* G, k1 p0 n8 ?# ^! ?! n - /* Jump to user application */; O- w, z2 ]: Z( e# q
- JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
2 p: }9 U' i' R* J - Jump_To_Application = (pFunction) JumpAddress;
3 H: H& n1 e- j6 J& s - * E& Y( H% z% B- r" K3 N
- /* Initialize user application's Stack Pointer */* D4 G4 |1 H6 A. V" T9 j( m
- __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
8 G6 N; s2 @- B- U) [ - 3 N% ~$ O4 K X) t
- /* Jump to application */
4 X9 ?. @* D, w* `) Y2 H) S b0 F) b! B, ^ - Jump_To_Application();
3 K& o H- `: k, T* e3 N - }
5 J$ I: x7 o; o% o - else7 O/ l* x# `8 A
- {* |* r: [* o w( s* s& S
- iap_step = 5;
7 ^- j# u+ a! G) T! M, Y - }
* V/ m* j* [! ?0 A. H% O6 A! F - 7 z, A# O! @% o5 ^5 q: C
- break;& [- }4 a6 F8 _3 T; Q# r
- }$ z I+ u& t8 z$ F) _
- % f- Y, F8 X& W$ W8 U7 g
- /* Step5:升级失败,等待重新上电 */) w$ w7 O- u2 S ?
- case 5:
' L" K! Z9 R, F0 z$ | - { D( N7 f" J* x* [: ]
- if(GetFreqFlag(FREQ_0_5HZ))* S8 Q: s+ b4 S' G" y
- {
6 [% ^+ p/ h* r" _ - Toggle_LED_AP();
: M9 X& M* o# A& t4 _; Q - }
, p0 n9 @7 X" t2 }; g! e: T - break;* v0 A* ?2 V. a& f! Q
- }
! A, p1 X. P' I6 Z0 P -
7 j9 g9 ^. A4 k$ M4 P! R - default:
9 X) m5 _* W; s" _6 I6 Y - {) q9 U. Z* p M. K/ R( N
- iap_step = 1;8 u6 x# b" J4 v
- break;! I, B7 t- `, ?+ J. ]
- }9 G' _' c; W$ p+ S/ ^& N
- " S* _# E J. E- g
-
8 e* I0 O M$ d - }//iap step switch' x# T8 t1 u# P5 G
-
* n3 H% h; }5 s4 F' `' M- b - }//end of bootloader while(1)
复制代码 特别要注意形参uint16_t Datalength是指的字数,就是uint32_t类型变量的数量,而f_read读取的是字节数,要除以4进行转换,刚开始就是没有转换导致写的flash数据不正常,跳转后死机。. J4 J. J# t4 t% [6 \- C
跳转程序也是参考的官方例程。我设置的App程序起始地址为:0x0800 A000
4 Y- Y( a( D `此外bootloader程序的IAR工程配置如图,flash地址范围:0x0800 0000 - 0x0800 9FFF,占用40K- l$ w4 g& c% Z2 w$ _
; R, S4 l" d) N9 P* j& t+ {4 r) |
; k/ s M$ a2 q7 n( z0 |* [8 L9 \App程序设计9 K# \4 b6 I# A9 p$ `* ~1 G
1、App程序主要在原来的程序基础上修改flash起始和结束地址,以及中断向量偏移。Flash地址范围我设为:0x0800 A000 – 0x0801 FFFF,占用88K,IAR配置如下:2 b6 y. W: h4 U! d
5 w Z o* ^% P5 g) S+ f
! N$ ?' G! l$ s8 \* K: F
) G7 ~6 ?( i& q$ i/ T$ \( x& p: X5 f0 Z) V. P5 u
7 h* }/ v' E. N
3 G) ^/ L. i- I+ l; o2、由于STM32F0没有像F1,F4那样的中断向量偏移寄存器,需要通过进行内存地址映射来实现,具体实现原理参见http://www.51hei.com/bbs/dpj-40938-1.html+ n) ]8 H' v0 j' p6 K4 [
所以在App程序main函数开始的地方加如下代码:(参考官方例程修改)
app设置
7 k. u6 Z& ]- I: @( { 其实在官方例程中为 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);这并没有打开系统配置时钟,应该改为RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);我也是看到网上其他帖子才发现并改正过来的,在这里感谢网友们的分享!- d2 X% F% J0 k
以上就是我做的STM32F0的IAP升级方案,实际测试感觉速度很快,可能我的App程序不大,50K左右,升级过程基本在3秒以内。
4 [) H2 B' c P. |3 P7 b5 C3 ]0 ?' ^/ f2 _* y- C' x3 \
3 g# t* ]0 |7 @1 s) S3 L2 l, K( |# r
( \; W! M3 [2 Q8 b. I) W/ q8 k4 I/ i/ R3 e
|
谢谢版主,发现少了一幅图,重新编辑上传了
谢谢
谢谢分享,支持一下!