本帖最后由 黑色裂变 于 2017-9-18 09:22 编辑
; Q% c# ?! ?9 @& ^7 f! [1 Z7 V7 i' t* S1 q k2 N, a% d
之前做的一个小项目,因为刚开始没有考虑到以后会经常升级,而每次升级都要旋开4颗螺丝拆壳,然后烧程序,再装壳,如果只要更新几个倒没啥感觉,但是一下更新几百个,那工作量。。。。,正好板子上有SD卡,就想着写个Bootloader程序,通过读取SD卡中的Bin文件进行IAP升级,这样可以大大简化以后的升级。IAP升级原理就不多说了,网上相关的资料和帖子一大堆,这里简单介绍我做的IAP方案,欢迎大家批评指正!
$ L7 K* z( ]2 b" QBootloader程序设计
+ R' j$ Z! t9 W1 |4 ?bootloader程序的设计思路很简单,流程图如下:3 A' K! Y$ q: p% v' ]
, y" P: i! a8 o! d " m* E ~: M0 _
初始化程序就不介绍了,比较简单。主要介绍下Bin文件检测以及IAP过程。我将IAP过程分为5个步骤,如下:1 b0 C5 t+ c) r/ o! s
Step1:检查是否存在升级文件,若存在,打开后跳至Step2,若不存在或者打开失败,跳至Step5# h4 j8 q9 ^4 Q( D, S
Step2:擦除App程序对应的扇区,擦除成功后跳至Step3,若擦除失败,跳至Step5
/ }( R( G/ i3 F) `+ K& z* AStep3:使用f_read()函数读取Bin文件,每次读取2048个字节,并写入Flash。当文件全部被写入flash后跳至Step4,若中间出现写入错误,跳至Step56 M- v. ^( p: s6 V
Step4:检查栈顶地址,跳转至App程序。若栈顶地址非法,跳至Step50 {- I9 V3 E1 @/ P
Step5:此步表示本次升级失败,死循环,同时LED提示升级失败,等待重新上电4 e6 l' |8 ^9 ^9 A0 b
查找升级文件时我固定从Update文件夹查找,所以只要将Bin文件拷贝至Update文件夹就行了。; U! q4 I" d) U f% L! H/ I" g
五个步骤的转换是通过switch函数实现的。代码如下:
: d" T2 [5 J% Y- u" `) o4 c- R- while(1)
m2 A+ }) @& o - {
7 ^( y$ j5 E: { S) \ - switch(iap_step); n/ I# H' U+ a- G. B$ R# ?, m9 b" z0 V
- {1 \, F8 G5 Z% M
- /* Step1:检查是否存在升级文件 */" |% U! D& j a/ Y5 R
- case 1:
# E1 d8 \1 w# i# b1 Q - {% @4 \1 j) q/ E4 V
- /* 查找升级文件 */
0 @ ?9 m5 A1 u7 ~* R - result = f_findfirst(&dj, &fno, "0:/Update", "FDR_update*.bin");3 V( q5 E, T& o8 `: ~ ~' |
-
* A+ V g1 l- |& p! q - /* 存在升级文件 */
0 n' d! |& P7 M" O! Z( z% I - if(result==FR_OK && fno.fname[0]) ~! V% M( L! s" {, H! P: g
- {
! K, z7 K4 M, ~) V$ E - /* 获取文件名字符串 */- s6 a. G# R( [3 J8 r
- #if _USE_LFN# u O% x5 b Q. c- H/ T# T5 C+ s
- fn_str = *fno.lfname ? fno.lfname : fno.fname;! e/ B6 e, p% p% U( E/ H) B: P
- #else
8 O6 D4 \$ _ R) K5 V - fn_str = fno.fname;5 E5 d! M! N+ E" N; [7 k
- #endif : U& Z! v" d3 D$ v( R) e0 W
- /* 得到完整的文件名路径 */; P! R5 C1 U' l1 J% D
- sprintf(fname_path,"/Update/%s",fn_str);
; d# q* Q& p' o1 o2 a7 a; j& q% W8 | - & \- J: }# A# W9 M& \6 }* d0 y! q
- /* 打开升级文件 */& o6 l* ^' B( x) D1 {7 K
- result = f_open(&file_fdr,fname_path,FA_OPEN_EXISTING|FA_READ);
- p) T! w! V' f - # Y" b: b# E" b
- if(result==FR_OK)
9 h' S \( b0 g7 @0 s" j - {. s O9 Q( x" L# ^" E$ O
- /* 打开成功,准备升级 */
9 ^7 b% A' u: B" B8 {+ R0 l, {9 { - iap_step = 2;
& x1 a s3 W0 L$ ]) e. ? - }
2 R- e/ n+ v6 d - else, S% C" g1 z6 p+ J- d8 a9 U
- {9 W3 Z, j7 m. o! B- \2 Z7 H. c
- /* 打开失败 */. G8 `1 e/ @& G7 y; H- ] I; Z
- f_close(&file_fdr);
. Z# t$ ]. j: m( S - f_closedir(&dj);$ S) k. n- O9 {! f) M
- iap_step = 5;
$ w# J+ ^) q1 T. k! j1 f - }3 R8 i1 r6 i$ o' B4 j' u' l/ z6 J
- / [( G) N2 i: W! D4 z
- }
1 z5 f' {3 f+ Q4 e - else
& x& g0 t* t, @& W1 R8 d - {+ {4 N1 B* P5 T2 w: D0 d
-
4 P5 @# @8 W8 D' n* H4 L: P/ N - /* 不存在升级文件,直接跳转 *// n* ~# ]2 L4 {
- f_closedir(&dj);
* S: d6 u: X3 C* {* J - iap_step = 4;
+ c8 k: V% u& Z1 g1 q - % `$ U; D5 u+ G
- }
+ j2 O$ ~# D6 h$ Z# L: e) k# a% X! h3 U - break;8 N% \0 u$ ~ P6 h
- }
. v, z+ |/ y0 g \4 D* b - ! K% P: O) P8 k6 C
- /* Step2:存在升级文件,先擦除扇区 */
L4 B y# r7 y D1 p0 F6 p - case 2:
# N6 b/ }* r& r* i+ i) Q' n& q - {
! @8 |8 ]& Z7 ?# c) A4 h) y - FLASH_Unlock();
4 Y; s( w: o! }+ n6 m& v/ p6 ] - res = IAP_FLASH_Erase(APPLICATION_ADDRESS);
/ R" e% @0 Y9 \5 ?* X5 A& v - FLASH_Lock();
3 F( n" X: b4 C' [ - if( res )5 [+ T' }! f. }4 |
- {
4 B$ {+ g D; G$ j; E - iap_step = 3;) W- A; J7 Y9 B) l5 v" j
- }9 u7 W- f; t& I9 t! I+ E
- else8 o2 Y5 e- U2 w9 a2 M* W% N; G+ |
- {4 [0 o/ j) M; M& k
- f_close(&file_fdr);
7 w; s! R# u$ M4 w. K( C7 p - f_closedir(&dj);" s1 r" ^/ c' O! ?( L
- iap_step = 5;
0 I2 q f' r/ J% F - }9 ?) @9 A6 b7 B4 c/ F8 X, A
- break;
6 N, G; k2 q: i$ J - }$ u4 s* h2 Y( d9 J' g, ^! W p
-
% T2 b; F+ z( I, G2 N4 n& Z5 Q - /* Step3:扇区擦除成功,准备依次读取并写入 */
7 ^, T; U% u- f; G% ^ - case 3:' w3 F; W: R c4 B& s
- {
% P+ K- P: B1 X: v7 E! r - memset(appbuf,0xFF,2052);
- O1 y, m9 `% f- C# }4 I' E - f_read(&file_fdr,appbuf,2048,&br);* q8 U5 @. s4 {6 H
- 4 ~) }3 }+ W4 Q; k. ~! k& q
- FLASH_Unlock();- y" W2 e, m6 [) e
-
4 s9 g. q2 o0 [3 s& C - res = IAP_FLASH_Write((u32*)appbuf,(u16)ceil(br/4.0f));; S( l, x! Y+ p# z; t
- ( }, h1 E2 j* ?; V# Z
- FLASH_Lock();9 a/ ^$ v# ]3 Z3 M9 I( ~! O
-
" b& J+ M" ^' r6 a& s# ^3 _ - Toggle_LED_AP();" Q& \0 ?& ?8 L& `: K- {- h
-
2 e% l! o5 L0 H1 O$ G' A - if(res == 0)
4 K+ V% i4 T: T: `) w- S - {
: b. O2 }0 _0 G! V3 K# Y: @+ W - f_close(&file_fdr);
8 C+ ~( t* G O+ S% Z - f_closedir(&dj);
; n, m+ \- J3 H3 N; J - iap_step = 5;7 @$ y7 w' d7 ~# a* H& A
- . _. J% y' F/ i) @% O
- }7 k9 D1 e, B- A! K
- else; t( }2 M% J8 {( f* X, r* a
- {3 ~) S9 K6 w3 A; k* i. U8 C! Z
- /* 文件读完了 */% Y8 Q* W- E: S
- if(br<2048); E' r) z6 |6 T# d+ w' C
- {
! b. w4 A; v3 T! s* S$ ?8 P5 H - f_close(&file_fdr);
$ r* U! ?8 ?# n; q( i3 W - f_closedir(&dj);
8 \" L9 W: D0 V4 { @, V - f_unlink(fname_path);
& Q+ G+ n, c& S5 M - iap_step = 4;
' \+ J; r! E0 N2 m# V3 s - / N* F) A! h2 g
- }
7 \/ u. e Q- q% q: H -
9 v" l3 |: C2 D6 Y7 u4 O# Q! R - }
1 V) u/ x% c" q5 C# X - break;
7 B$ I. B; W5 Z; K! n; s9 U8 u - }
* L8 ]# W" t4 k0 C+ F -
' f7 Z+ B" E# H2 ] - /* Step4:跳转至App程序 */
) |/ z% D; P9 J6 U! n$ V - case 4:1 n) M, q2 N8 O# h% s% y! U: e
- {
+ W3 S% Z- q; `- | - /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */2 |' j, z* Z! T+ @4 S2 F( |! e k
- if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
1 L, a5 S( R0 o( ~ - { 4 r) q3 `! l+ ^2 S
- /* Jump to user application */
0 Y. D8 Z" J7 f* T% ~! N3 L* K& B# i - JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);$ N4 A* I/ [0 Z
- Jump_To_Application = (pFunction) JumpAddress;& ?9 [) a+ p) F, w" v [
-
W B& m0 r' u: ` ? - /* Initialize user application's Stack Pointer */' Q8 p! U4 h" D9 P0 J
- __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
6 `: N2 }/ M# q' f* S/ S -
* b; X8 E: x- a& F& c - /* Jump to application */
( S0 s+ Z0 `, D1 b9 c- j - Jump_To_Application();
3 H4 F- J& Q8 u% ]2 a. \$ B' Q - }5 u& W- R# v- k7 M
- else" W* L4 M( w$ v# P; B
- {
: _, T) y5 b- Y) i0 T - iap_step = 5;
1 c1 k: N$ A5 a6 G2 B3 d - }" H" k+ I& `" [2 S* G5 m
- & c; S8 S( F9 c0 O. W0 C: N, u8 f5 ~
- break;
. o6 l+ R' z, \% q( x; T4 C- ] - }# P; Z" I* D3 l# P x `2 Y, i0 Z
-
* Y. a' c* _) ` - /* Step5:升级失败,等待重新上电 */
2 Y! ^5 R! m1 t$ N$ r - case 5:; m) z/ G- u; e
- {
J; C9 Q7 P7 l0 \" @+ h - if(GetFreqFlag(FREQ_0_5HZ)): c% x) _: u8 p* S S
- {5 V/ B( b5 s$ c& w3 ~
- Toggle_LED_AP();
* t: w2 b% U) J# }$ g - }
+ U2 N1 k. |; a4 `0 X$ O, P7 T7 V9 \ - break;
. K4 l: |) ^* g! ]7 [/ a+ P- y% [5 i - }
+ o- T5 |7 a( }+ ] - " Y& q* i6 z6 I# e W- Q' W
- default:5 G. M7 Q7 b, a
- {6 U% N% s+ U& i$ P5 o; \8 v
- iap_step = 1;
: Y& a |5 `4 ^ m! L - break;! z, N3 k+ I o! w% f
- }" M' K% G; H$ w1 y6 U, C: N
-
; g8 n6 W0 }5 H -
3 b" g# Z3 ]2 l- { - }//iap step switch
, U* x; @0 M1 f% h' k - 0 R4 I1 @, U: x# R9 R
- }//end of bootloader while(1)
复制代码 特别要注意形参uint16_t Datalength是指的字数,就是uint32_t类型变量的数量,而f_read读取的是字节数,要除以4进行转换,刚开始就是没有转换导致写的flash数据不正常,跳转后死机。
/ ?) a m5 q9 \+ D# @% \' q5 H: v跳转程序也是参考的官方例程。我设置的App程序起始地址为:0x0800 A0008 ]2 a$ S/ F8 P$ G
此外bootloader程序的IAR工程配置如图,flash地址范围:0x0800 0000 - 0x0800 9FFF,占用40K
2 o6 y! d- X/ |& F- |* c; [0 G
) w4 u2 W" s# ~. a. S* S* x+ E' I1 H7 U
6 K& w, [5 y5 i( q2 d6 _App程序设计5 p, y) d% e }' u' g! k
1、App程序主要在原来的程序基础上修改flash起始和结束地址,以及中断向量偏移。Flash地址范围我设为:0x0800 A000 – 0x0801 FFFF,占用88K,IAR配置如下:
7 N0 q& B! g4 @
2 F! ?, K, W/ J7 i
( G2 ?. H* i( N s! R- y( `
) q" w8 o9 D4 d$ s2 [& b
, d J8 q U6 ^- P& K8 Y6 X8 K- F
3 q2 ^) _+ h. w; S# k% `( H5 U7 }2 M: c* R% t
2、由于STM32F0没有像F1,F4那样的中断向量偏移寄存器,需要通过进行内存地址映射来实现,具体实现原理参见http://www.51hei.com/bbs/dpj-40938-1.html$ Q7 q" ?% D( G' O( o3 W3 E) y6 B
所以在App程序main函数开始的地方加如下代码:(参考官方例程修改)
app设置
3 d# a: o/ X$ B
其实在官方例程中为 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);这并没有打开系统配置时钟,应该改为RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);我也是看到网上其他帖子才发现并改正过来的,在这里感谢网友们的分享!" f& s+ `( ^+ @# k4 c
以上就是我做的STM32F0的IAP升级方案,实际测试感觉速度很快,可能我的App程序不大,50K左右,升级过程基本在3秒以内。
$ Y& }& c$ H/ d: D) ]: ~4 F: B8 b$ c+ L8 ^
+ O! ^4 h1 t) x- I" `5 f8 Z& O7 l- _2 h$ M7 {3 P
% Y* `- W1 E. d! `. ^8 G |
谢谢版主,发现少了一幅图,重新编辑上传了
谢谢
谢谢分享,支持一下!