这次出差是为了升级程序解决Bug,用户已经将产品封装起来,无法开盖,只能使用CAN总线来更新程序,用Bootloader实现。其实就是通过上位机把.bin/hex文件以CAN通讯的方式发送给单片机并存储在规定的Flash中。这个过程与手机端更新APP类似。
4 W/ B x( l8 S以STM8单片机为例,该如何实现Bootloader呢?今天和大家分享一下。01
" h L+ X! E1 T8 k6 p2 U
% K8 K; ?$ s' F4 Y/ A1 w0 S! ]/ @5 @
: e3 |$ u/ Y Q什么是Bootloader
7 @& P- z/ v$ b8 f9 e+ b6 TBootloader是一段用于更新自身应用软件并独立运行的代码,常被用于升级产品和修复产品bug。STM8单片机如果要下载hex文件的话需要通过STVP和STLINK来实现,单片机的引脚SWIM就是下载接口。如果产品在用户端被封装好了,无法通过SWIM来下载该怎么办呢?就使用Bootloader,STM支持CAN和UART实现Bootloader。就是通过CAN通信或者UART通信将数据存放在STM8的Flash上。02
$ }7 G+ C6 Z, o+ W1 @
0 g" v; `# ?2 q) E) f( k [( D. Y% S* e% N* P' O/ _+ V
4 V9 u0 y3 B- i+ z
划分Flash区间
0 o" e4 j) l/ N4 s. D4 V5 Q6 F BSTM8的中断向量跳转地址是固定的,会跳转到0x8000对应中断的偏移地址,所以会把Bootloader存放在0x8000开始的空间内,比如给Bootloader代码所划分的空间为4k,那么空间起始地址为:0x8000-0x8FFF;那么应用代码的起始地址就可以从0x9000开始。03
7 @$ M8 w6 D1 M1 v8 l' K8 i+ Y* l, i* B; [3 p( M. y R# R
2 |% u! @* ]! j7 L
! w7 g3 k+ U1 N+ \/ g& x修改中断向量表 / G* d% W& R X2 e m
STM8的中断向量表通过一段特定的代码来实现,并需要根据前文提到的空间划分来修改。STM8的应用程序起始地址是0x8400,默认的向量表如下代码所示。- ' w+ F. e e$ \7 D
- __root const long reintvec[]@".intvec"= - k0 |$ l4 h8 G ]0 v4 s
- { d* `8 a/ u6 G5 I
- 0x82008080,0x82008404,0x82008408,0x8200840c,
: m) I0 P6 P. @: ? - 0x82008410,0x82008414,0x82008418,0x8200841c, ( p) r6 f1 n9 V% I8 I- H, D
- 0x82008420,0x82008424,0x82008428,0x8200842c, % X4 V" C! y' Z; B7 \
- 0x82008430,0x82008434,0x82008438,0x8200843c, . K5 E$ |4 W0 Y/ E
- 0x82008440,0x82008444,0x82008448,0x8200844c, * N, G/ O) C# Z1 ^" W5 J( k
- 0x82008450,0x82008454,0x82008458,0x8200845c, 0 W U4 j, n4 g9 ~6 Q5 N! |
- 0x82008460,0x82008464,0x82008468,0x8200846c, * W6 N1 ]/ r6 d+ G# g
- 0x82008470,0x82008474,0x82008478,0x8200847c, ) [" ?" V5 H$ b$ k0 ^$ @/ E
- };
复制代码 前文已经将应用程序的起始地址修改为0x9000,所以,向量表的的修改方法为:除了第一个元素为,其余的元素将8改为9,修改完后代码如下:; r% I- k; A" |9 ~& i9 ?, N
- ( d8 X! ?1 b6 w( @, }# ~) W
- __root const long reintvec[]@".intvec"=3 e$ `6 R/ W6 h: K0 |
- {
; j% w$ D. @5 X - 0x82008080,0x82009404,0x82009408,0x8200940c,
# K, R$ @/ e- g: q& U' W - 0x82009010,0x82009014,0x82009018,0x8200901c,
( }' ~* K( I) h" ?- W1 v - 0x82009020,0x82009024,0x82009028,0x8200902c,! f2 g; L0 V6 B2 ^7 t. z3 |
- 0x82009030,0x82009034,0x82009038,0x8200903c,0 U9 j. F+ _7 b4 Q8 f
- 0x82009040,0x82009044,0x82009048,0x8200904c,/ w) W9 |- m% K( F3 G) q& A: h7 B
- 0x82009050,0x82009054,0x82009058,0x8200905c,$ U& E& ~% \& H& H a2 C
- 0x82009060,0x82009064,0x82009068,0x8200906c,
) T1 H4 g9 x, e8 X5 D: M: g' r - 0x82009070,0x82009074,0x82009078,0x8200907c,
; T" p: G2 v7 y% |2 F - };
复制代码
; Q+ U8 R2 z, V d, V04
_6 M- a2 [3 e- s! X) q' L
) U4 e! v. c5 z+ V* N; [
+ Q) p3 s' q+ W) p& g1 V& b( I+ r; \. W; E( n! r
修改ICF文件 ICF文件存放在编程环境的安装目录下,每个型号/系列的单片机都会对应一个ICF文件,需要修改一下ICF文件,这里需要根据flash空间的划分来修改,前面将Bootloader的终止地址设置为0x9FFF,所以修改如下:
1 K5 H8 Q9 k ~
, M3 {8 k% W5 G9 B" l+ q- define region NearFuncCode = [from 0x8000 to 0x8FFF];' W2 \7 O$ N0 Q4 t& L
- define block INTVEC with size = 0x80 { ro section .intvec };$ m( R) m8 h8 \( n T+ N
- place at start of NearFuncCode { block INTVEC };
复制代码
- D$ w% w: ~! Y' ]7 c05
5 Z. { B: r' q5 b$ M4 h( L6 |9 @2 b
; Z# i, x0 m6 W" U
6 \8 C) N* X7 u% Z# k3 O0 H- V# O跳转命令 所谓跳转,就是在应用程序中跳转到Boot中去升级;升级完成后需要跳转到应用程序中,所跳转的起始就是起始地址。Bootloader跳转到应用程序的代码如下,9 ^8 m$ F$ a, s6 S6 G. q' x0 R% S1 X3 c
% k' L7 U$ M( \. F4 p- asm("LDW X, SP ");6 x: h9 U' p+ S7 u( B, \) v4 l
- asm("LD A, $FF");
2 _4 f, H5 j" F }) K6 p$ Y. D- } - asm("LD XL, A ");
$ x3 H' a/ z3 [7 R - asm("LDW SP, X ");2 }/ H" D9 d/ E. K& N1 b
- asm("JPF $9000");
复制代码 应用程序跳转到Bootloader中的代码如下 l: z& E+ b5 v N" ]4 V& w) H
- , f- z( M$ C2 |; a
- sm("LDW X, SP ");% u' `+ m N2 c+ Q! B* T7 v; R
- asm("LD A, $FF");
( W: M; W; E# f% v% X, i: } - asm("LD XL, A ");) |5 d3 S. @3 q
- asm("LDW SP, X ");2 E2 b# z) c$ o& x
- asm("JPF $8000");
复制代码
" z0 S) f+ F1 N) c: ^06 $ Q) ^( s7 m$ {. O3 r( x) `
8 J8 J0 `) z: M2 N# v8 H" y$ F) P. L! e; C6 }$ }
( ]$ w a& G- V- l编写交互代码 以上设置好之后,就可以编写交互代码了,所以交互代码其实就是一套协议,规定了应用程序如何跳转到Boot,在Boot中如何处理和存储数据,这需要上位机的支持。如果只是用于自家的产品,就可以自己定义协议。也可以使用标准的协议,比如汽车行业中的UDS(统一诊断服务)。Bootloader实现之后,就可以使用CAN/UART来实现产品固件的升级了。这就是Bootloader的关键内容,本文你学会了吗?留言讨论一下吧。. }2 x3 T: L- P9 l
+ X9 W: \% |4 Q
|