前言* \% E! r0 u |+ c
本篇主要介绍 STM8Lxxxx 如何实现在应用程序中编程(In-application programming)。
% D) F# W" ]. ?3 {: s0 W# V, u0 h2 c- s; T1 j+ f3 {9 ?
1. IAP user Flash 分配框图及中断向量表重定向$ `2 I# W y" w. ^, n
4 w8 @0 L7 `+ G9 _1 T1 \+ K& Q
& \4 \5 ?6 x0 [: e& z6 ~8 b
; {2 D {+ V9 O8 L注意:/ L$ q1 _* U$ c' u" d' H
1 和 2 为 IAP 代码空间。为客户自己的 bootloader 代码,应用程序中更新 Flash 的代码。8 W h+ r, s$ r
3 和 4 为客户应用程序代码,实现产品具体操作功能。' T6 v6 K5 q5 \; |4 c& t1 J5 d
中断向量表 1 中除了 Reset,其他存放的是指向中断向量表 2 的地址;中断向量表 2 中放的是具体的 APP 中断处理程序入口地址。发生中断后 PC 指针先指向中断向量表 1,然后再指向中断向量表 2,最后到具体的中断处理函数处。客户应用程序起始位置(上图是 0x9000)可以根据客户需要设置,比如可以改 0x9000 为 0xA000(如果客户的bootloader 程序超过 0x9000 但没有超过 0xA000 可以这样设置)。
' k) }, K4 R. f# o. ]8 `$ a1 v2 n
$ |# \+ j, ^! |2 w" ?8 n
/ f' v) p. e0 F: g/ h6 y9 Q# Y
2 A% S7 D7 M ?2. IAP 对中断向量表重定向的说明
4 j9 T" X3 U3 N# u7 W" eSTM8 的中断向量位置是固定的,大部分是 0x8000 开始处。IAP 程序中需要手工处理中断向量表。并且 boot 程序不能使用任何中断(IAP 程序中的中断跳转(中断向量表 1)会使 PC 指到 0x900x 处,如果此时 APP 没有程序,程序就会出错。),程序中的所有中断(向量表 1)的处理最终都要跳转到用户程序的中断向量表 2 处。
8 v6 A$ B& y3 ` z; j3 kIAP bootloader 参考程序为 STSW-STM8006。
" ^7 O ^ m p# k: }, ^2 V \下面以 STVD 工程 Cosmic 为例进行说明。" r% d j% H& t) n& F
…\STSW-STM8006\AN2659-IAP_using_user-bootloader\Project\STM8L_User-Bootloader_example\STVD\CosmicStm8_interrupt_vector.c: 下面的中断向量表 1 位于 0x8000 到 0x8080 地址空间。
# O: ]) K- d2 |0 V4 X4 `假设发生了 0x8001 中断,指针经硬件定向到了 0x8001,在 0x8001 里存储的是跳转到 0x9001 代码,然后程序跳转到 0x9001 位置去执行,而 0x9001 中的代码是跳转到具体的中断函数处。(具体代码中使用函数指针来实现)( w& U/ A8 k+ u; ?# m4 H- C
0x82 为操作码,意思是跳转到后面的地址去执行。如下例:9 a' H4 D' G1 g4 k1 z. w
F! n3 e( i' b' k# v6 ~; y( r; ]0 d) J: |
- % M1 i6 \5 C7 s7 p9 |& r+ q
- //*********IAP bootloader 程序中的 中断向量表 1 **************************
' Q m0 J5 D; j3 j/ @ - extern void _stext(); /* startup routine */6 J6 x7 ` i' w/ [9 f5 P7 w4 q
- struct interrupt_vector const UserISR_IRQ[32] @ MAIN_USER_RESET_ADDR;0 ~ t* d! n2 F. a* ^# ]: W8 ]
- //redirected interrupt table, w* b. j+ q! w; l) Q
- struct interrupt_vector const _vectab[] = {
* [0 {- B% {5 z3 I7 H9 }+ \5 ~ - {0x82, (interrupt_handler_t)_stext}, /* reset */ //note:向量表代码开始位置为
, R' z2 n/ t3 L& a - 0x8000,0x82 后面的 24bits 存放 0x9000
: z& a# v ]* V: L - {0x82, (interrupt_handler_t)(UserISR_IRQ+ 1)}, /* trap */ //note:代码位于
; e1 m$ G% S5 I4 V; R+ m K0 v - 0x8001,0x82 后面的 24bits 存放 0x9001
( A! J; b* S# c- L5 e+ X - {0x82, (interrupt_handler_t)(UserISR_IRQ+ 2)}, /* irq0 */( O) {8 M, S( u$ `& b& H; D+ Y
- {0x82, (interrupt_handler_t)(UserISR_IRQ+ 3)}, /* irq1 */. w' c# l0 j. G6 w
- {0x82, (interrupt_handler_t)(UserISR_IRQ+ 4)}, /* irq2 */) ]7 X& C J W3 e P
- {0x82, (interrupt_handler_t)(UserISR_IRQ+ 5)}, /* irq3 */
0 Q7 y9 y) K/ z/ C" N4 M) j - Step1 发生中断
5 \) [* p, c1 \ |# o Z3 s1 ?" ` - Step2 跳转到向量表 2
- r2 i S. r/ q+ f( v - Step3 跳转到具体的处理函数7 R* v3 C. Y- }- n# P {
- Step4 APP 软件发生中断1 i. F. d; {. X9 t& t* E) O% ]
- {0x82, (interrupt_handler_t)(UserISR_IRQ+ 6)}, /* irq4 */
0 k# o a& R' r5 t" |5 U - {0x82, (interrupt_handler_t)(UserISR_IRQ+ 7)}, /* irq5 */0 K+ E3 J, |/ e Q$ b- i
- {0x82, (interrupt_handler_t)(UserISR_IRQ+ 8)}, /* irq6 */
" q1 ]* ~. n7 x7 b - {0x82, (interrupt_handler_t)(UserISR_IRQ+ 9)}, /* irq7 */
& a; T5 J7 O$ u( D- b. X - {0x82, (interrupt_handler_t)(UserISR_IRQ+10)}, /* irq8 */' D5 u3 C v1 P) S, E
- {0x82, (interrupt_handler_t)(UserISR_IRQ+11)}, /* irq9 *// p( A5 O; `8 D; @- U8 N
- {0x82, (interrupt_handler_t)(UserISR_IRQ+12)}, /* irq10 */
+ s# K, F- F$ H - {0x82, (interrupt_handler_t)(UserISR_IRQ+13)}, /* irq11 */
' n- w4 x8 J, l- C - {0x82, (interrupt_handler_t)(UserISR_IRQ+14)}, /* irq12 */
$ F, m% T8 g4 I3 f/ V* V - {0x82, (interrupt_handler_t)(UserISR_IRQ+15)}, /* irq13 */
% } F5 @: S2 T: Q' n. w - {0x82, (interrupt_handler_t)(UserISR_IRQ+16)}, /* irq14 */
+ Y5 }: V5 A! ~- l3 n - {0x82, (interrupt_handler_t)(UserISR_IRQ+17)}, /* irq15 */# \& i' Q3 g- a) N. P% n) r
- {0x82, (interrupt_handler_t)(UserISR_IRQ+18)}, /* irq16 */, M5 ]5 i" [$ q `& S4 K
- {0x82, (interrupt_handler_t)(UserISR_IRQ+19)}, /* irq17 */
- r0 D% d8 S0 P - {0x82, (interrupt_handler_t)(UserISR_IRQ+20)}, /* irq18 */
. m- v; a+ p( r0 w# S - {0x82, (interrupt_handler_t)(UserISR_IRQ+21)}, /* irq19 */" K. @! l; i$ ^- R1 N
- {0x82, (interrupt_handler_t)(UserISR_IRQ+22)}, /* irq20 */+ n9 }% ]* A) f8 z# G; W
- {0x82, (interrupt_handler_t)(UserISR_IRQ+23)}, /* irq21 */
, \" V& g( D1 t2 n: c2 N* E, u+ W - {0x82, (interrupt_handler_t)(UserISR_IRQ+24)}, /* irq22 */
5 ~/ h! Q- P/ [/ [! C ^1 d9 M, {/ p - {0x82, (interrupt_handler_t)(UserISR_IRQ+25)}, /* irq23 */- l+ H5 {$ W* Z( \9 J' ]% J: z
- {0x82, (interrupt_handler_t)(UserISR_IRQ+26)}, /* irq24 */' g4 V) e7 q: m [& w6 s S
- {0x82, (interrupt_handler_t)(UserISR_IRQ+27)}, /* irq25 */% Z! s8 u( f$ h$ W. g5 {
- {0x82, (interrupt_handler_t)(UserISR_IRQ+28)}, /* irq26 */( r% Y: U+ f" a0 W, O
- {0x82, (interrupt_handler_t)(UserISR_IRQ+29)}, /* irq27 */9 K0 Z- k0 ?$ E7 E- u0 f7 T
- {0x82, (interrupt_handler_t)(UserISR_IRQ+30)}, /* irq28 */, j% l4 b u3 c+ i" u
- {0x82, (interrupt_handler_t)(UserISR_IRQ+31)}, /* irq29 */' i0 Q% |$ B K4 N1 d
- };- Y E( u x1 F: M p" b! Y
- //*********************************************************************************
复制代码 8 i; B) y& O; C% l, y
对 0x82 的描述在文件 PM0044 有如下描述: 0x82 为内部指令:6 S! T7 {) Y! ^! g7 r0 I
8 l: Z7 v0 l4 R/ v4 i
$ R0 `6 C+ h5 V+ p) H9 K/ a( Y
# Q* \; F; C. ?( A0 f0 Q
在 main.h 里有下面的代码: MAIN_USER_RESET_ADDR 的地址为 0x9000。所以 APP 代码开始的位置在 0x9000,开始存放第二个中断向量表(中断向量表 2),此处为中断处理函数入口地址的重定向表。
) O4 H/ o: T& J+ W/ J$ y- //*********************************************************************************7 k- w6 v. m. }' r+ R
- //user application start (user interrupt table address)4 L9 ^3 _$ Z$ z, r7 h2 |/ y
- #define MAIN_USER_RESET_ADDR 0x9000ul
# c. A+ _- }0 x( q$ m - //*********************************************************************************
复制代码
( ?( w' }4 c* i5 k3 n- K: F2 _( u3. STVD 软件中,下图 Project/Settings 中对中断向量表位置的处理可以看到。中断向量表 1 是从 0x8000 开始存放(Vector file name and Vector addr.)。) c, r ?2 V; `" J
% y# L# H' V, U0 ^, l
1 C7 S9 t+ c8 D6 }, S k
8 V1 z( a, e6 J/ [# R) h对“Vector file name”的定义为“stm8_interrupt_vector.c”;“Vector address”定义为“0x8000”地址。如下图。
6 _( I& i; I/ Y5 I/ O; p
+ n- R3 }# `% t4 @
: x* S- Z1 H2 F4 g. R1 L' e- u% b2 \. }9 Z
…\STSW-STM8006\AN2659-IAP_using_user-bootloader\Project\STM8L_User-Bootloader_example\STVD\Cosmic\Debug有文件“userbootloader.lkf”,有代码如下:(当这个文件中的设置同 STVD Project/Settings/Linker 对画框中的设置不一样时,优先使用对话框中的设置)
, {; E3 Q) D7 G# d- //**********************************************************************************; w: I, f8 ?/ J' f% F4 m p
- # Segment configuration - section reserved for STVD
: }/ C2 x5 v8 X* t( h( D6 F - #<BEGIN SEGMENT_CONF>
" Z1 H3 |) m; A2 m3 r) {4 G4 S ` - # Segment Code,Constants:
( [- C D9 R; A4 h( a* w - +seg .const -b 0x8080 -m 0x7f80 -n .const -it
& z. f W) l& I1 n0 _5 k - +seg .text -a .const -n .text9 ^& v/ b# b6 F1 {# U
- # Segment Eeprom:
$ Q6 F: [" H& ^, a& Z- ~ - +seg .eeprom -b 0x1000 -m 0x400 -n .eeprom+ J8 R! B* }' Z
- # Segment Zero Page:
o% T0 a8 R0 n- ~ - +seg .bsct -b 0x0 -m 0x100 -n .bsct/ Q1 ?7 Z! F$ f! U
- +seg .ubsct -a .bsct -n .ubsct; ` t6 x, C2 g2 _" m. s3 ?
- +seg .bit -a .ubsct -n .bit -id/ |# [8 | k% s2 g
- +seg .share -a .bit -n .share -is$ I. X* Y6 r2 C1 n3 @: z
- # Segment Ram:
: \ p2 a9 @. R: v3 C+ [& o - +seg .data -b 0x100 -m 0x4ff -n .data
# H; }" s ]$ w3 O. |$ @- m, t - +seg .bss -a .data -n .bss
* n) u) R: D" |' c' |# x - +seg .FLASH_CODE -a .bss -n .FLASH_CODE -ic
; N2 A# `7 ?& K- Z: C0 e2 d9 X - #<END SEGMENT_CONF
1 Z/ e0 n% L. E6 O9 e) A3 Y" U - …
; ]* y! J, ~. ~! M9 F# a J2 \, r/ B: ^; X - # Interrupt vectors file - section reserved for STVD9 a3 A. A/ f9 V8 I8 e: K3 ~, L1 h
- #<BEGIN VECTOR_FILE>- v; `1 }2 ?3 J
- +seg .const -b 0x8000 -k
5 h8 c, Y- X; G ?4 e& i9 J6 k/ p - Debug\stm8_interrupt_vector.o
1 M r% A( i% i - #<END VECTOR_FILE>( Z7 k+ e/ ?- b0 Q& c0 q* |
- //*******************************************************************************
复制代码
4 X: {8 x9 o8 r" C! G* _& E% _
4. User Flash 空间烧写方式* N `: Z2 P! o; }, v
Bootloader 程序可以烧写内部 Flash、EEPROM、RAM。适用于产品软件的更新。% N; S$ D/ g6 i* {& T' P. e0 x
5 K: ^8 V% B; j' C6 J( X# J* J( ?
& s% z0 {, g+ p i4 X$ b# L3 k9 m) K. F2 U: k7 X
5.对 _fctcpy 的说明,对 Flash 的大块区域编程的代码在 RAM 中运行的问题解决。 _1 K' K: P4 D; d. o v! s
对 Flash 进行大块区域的编程的代码需要在 RAM 中运行,为了实现这个,需要一个自定义代码段,并将这个代码段放在RAM 中。在例程中定义了一个“.FLASH_CODE”段。 请查看下面代码红色区域。
' D5 `8 K& L& N4 h- //*********************IAP 的 bootloader 程序*********************************+ o4 ~3 Y6 ?" P0 G* j0 C
- #ifdef _COSMIC_
( c: w7 m8 j: s, P! f3 T! h - #pragma section (FLASH_CODE) //定义 FLASH_CODE 代码段' h7 d8 L: x/ a, c* G+ G
- #endif /* _COSMIC */
5 t) \* U- d6 S$ j& _; N/ f - #ifdef _IAR_
; h- b. _8 Q* P" B. x$ h - #pragma location = "FLASH_CODE"
* S9 U3 V0 I+ Q( R# s( K- \) C' m - #endif /* _IAR_ */
# x% t$ `- T$ b, D& l: D# c; I# Y - #ifdef _RAISONANCE_& Z, @5 E' g. o; E0 P9 G
- void Mem_ProgramBlock(u16 BlockNum, FLASH_MemType_TypeDef MemType, u8 *Buffer) inra( z2 A0 T& L& T) C1 e
- {. ^: G3 \, a' i0 k
- u16 Count = 0;# B* g1 k( u: r3 Q: ]
- u32 StartAddress = 0;: ^9 ]; h+ R \- b! U/ e
- u16 timeout = (u16)0x6000;. g) q5 I/ i" U4 a$ q
- % B. U& }! i& |9 x8 H8 V
- /* Set Start address wich refers to mem_type */- |4 S! {& L8 X7 e
- if (MemType == FLASH_MEMTYPE_PROG)) w8 o* q3 ?, d+ w
- {
$ v' I* ?5 a# _5 a; r6 C - StartAddress = FLASH_START;
3 R: k6 k8 E3 X1 G# F. N+ Z& Q& T - }
1 S4 M v0 L$ C" F( t0 y - else
) `/ S3 C. a9 s- |- T' F - {. M; e6 j/ I' Y, L# S, L& p. A% y# z
- StartAddress = EEPROM_START;5 x8 P3 n* s+ d# y9 _2 H
- }
0 F% f- Q J/ D4 a. s; c - /* Point to the first block address */
9 m8 B" S, a7 [+ C, ` - StartAddress = StartAddress + ((u32)BlockNum * BLOCK_SIZE);
- `+ _9 [/ X) d9 @# ? - /* Standard programming mode */
+ O7 ~9 r5 Z( J: a6 j! k4 q - FLASH->CR2 |= (u8)0x01;
m, E- U) |+ N0 {! j6 J8 `
1 Q* ~6 p! j# _ y# @! ?! A- /* Copy data bytes from RAM to FLASH memory */
( ~) T9 n9 a* k! J5 Z, s1 [8 i - for (Count = 0; Count IAPSR & 0x40) != 0x00 || (timeout == 0x00)), q+ ?8 d9 L5 S; Z) |
- {9 g; K6 K9 P3 H1 C. g- W8 _
- timeout--;* B c% [* k. f' ?
- }( ~% d. p( P: W& Y1 B9 R8 |/ a2 i, `
- }" F( z1 _+ w- n. L) M+ C
- #endif /* STM8S208, STM8S207, STM8S105 */
3 A9 j1 ?. C( q- p1 ]' z+ L: q - }( r% h) o8 ]$ ~# j
- #ifdef _COSMIC_
7 V& b* R) w! [5 E1 S4 R, O- g - #pragma section () //普通默认代码段
0 l4 P" C! A w$ u' x/ J - #endif /* __COSMIC__ */9 b3 {1 @% Q# Q: a8 P
- //*******************************************************************************
复制代码
2 R4 k% N- Z$ Z7 ^" X_fctcpy 功能:% d6 j0 D* p8 t7 f, j
COSMIC 中的函数_fctcpy 是将 Flash 中的代码拷贝到 RAM 中并运行。_fctcpy 寻找 linker 定义的描述符(此描述符是在 RAM中定义段的第一个字符),在 STSW-STM8006 中定义了一个段 FLASH_CODE (#pragma section (FLASH_CODE))。因此第一个字符是‘F’.' j h3 s% X: Y: ]" Z% N, `
' ^4 D5 u! \$ C6 K
在 mian 函数中调用_fctcpy('F')的作用是把 FLASH_CODE 段拷贝到 RAM 中并运行。
% \8 u; r! u& s" zFLASH_CODE 是一个可以移动的段,需要在 IAP “boot 程序链接配置”的 RAM 区添加。
5 L, c- R( a% u: D. v7 _: y在 RAM 中创建一个“.FLASH_CODE”段,并在 Option 中输入“-ic”;“.FLASH_CODE”表示在 RAM 中定义一个7 e" A+ Y3 P; J
FLASH_CODE 的段,程序可以从此地址运行。“-ic”表示标记这个段为可移动的段,可将 Flash 中的代码移植到此。* I2 y0 Z4 P/ T' v" W) @/ ]
- void main(void)' g U# _$ s, Q- ?7 u7 U8 i
- {
7 I2 N V; i0 ^( F5 ^+ v - …( H) s0 t9 i, G y2 m0 ?3 @: E
- _fctcpy('F'); //把 FLASH_CODE 代码拷贝到 RAM 中并运行/ `4 @: v: x4 V. ^3 p
- …
+ B2 l4 E* [' y. y8 Q3 ]8 [ - }
复制代码
4 q6 r ?4 S7 L+ u( H2 S! n! l
0 _: t( s/ F0 h3 b) P+ f
- l! F2 v% V# r! v' Z) j& I" b1 U
可以在生成的*.map 文件中检查生成的段是否正确,示例代码中有如下的 map 文件
5 H( c5 F5 ]+ ?, B- //*******************************************************************************! c3 m9 D% f; v: F
- ---------------------------------------------------------------------------------! \! ~! c; P7 }$ ]2 A& K& Z
5 w2 ?; T. P4 A, P- Segments
) i; f, J! G% w - --------
( l5 q2 v3 K$ S3 \: }$ X
: e9 b" Z# a7 ?: A- start 00008080 end 000080a2 length 34 segment .const+ C: L, D* R( O+ a' q
- start 00008104 end 00008be0 length 2780 segment .text
- b r* ^& B% ~5 @" f1 r - start 00001000 end 00001000 length 0 segment .eeprom
, Q' v% g6 D0 j - start 00000000 end 00000001 length 1 segment .bsct, initialized
5 ^2 N) w! Z8 ]0 m9 l5 I2 _ - start 000080af end 000080b0 length 1 segment .bsct, from
+ z/ b' h- c8 x, q# z9 S8 N0 p0 z0 W - start 00000001 end 00000091 length 144 segment .ubsct9 Y, i$ M% e0 d7 K; ~" P
- start 00000091 end 00000091 length 0 segment .bit
" O0 T3 a+ O- _ - start 00000091 end 00000091 length 0 segment .share9 i8 r; J0 x% d9 \1 U
- start 00000100 end 00000100 length 0 segment .data9 f/ @- \$ C. F- u
- start 00000100 end 00000100 length 0 segment .bss
3 G6 U4 x7 C/ O/ H1 p0 | - start 00000100 end 00000154 length 84 segment .FLASH_CODE, initialized
' X4 L' I! h, r, q# o5 [ - start 000080b0 end 00008104 length 84 segment .FLASH_CODE, from' U, v# I ~. F: Z6 E. F. p
- start 00000000 end 000004de length 1246 segment .info.
# {9 W% v. ?* c# U- Y2 I - start 00000000 end 00000e70 length 3696 segment .debug; E0 P9 D0 [' b
- start 00008000 end 00008080 length 128 segment .const
1 V, n5 `- q3 ^! C: v( c - start 000080a2 end 000080af length 13 segment .init* ?$ W1 ^$ C7 W
- //*******************************************************************************
复制代码 , p) k- z c4 U; D5 x
“from”为存放代码的部分。“initialized”为执行代码的部分。! v: P1 |( i5 z1 w/ k
IAP 的 bootloader 和 APP 分开为两个独立的工程文件。上面的内容是针对 IAP 的 bootloader 的程序工程的处理说明(属于图 1 中的“1.中断变量表 1,复位后硬件 PC 指针从这里开始”和“2.客户程序中的 bootloader 代码” 段),下面来说说APP 工程段(图 1 中的 3 和 4 代码段)。
" [6 f: R7 A0 U9 d; P
0 y% Z1 j+ N. m8 @/ f7 ]" E( e6. APP 工程说明。/ n7 o( j$ @+ S. @! V- k0 [
在 APP 工程里,设置中断向量地址为 0x9000(上面以 0x9000 为例。如果从 0xA000 开始,就设置为 0xA000)。代码段的起始地址为 0x9080. 编译后生成的二进制文件就是需要通过 bootloader 烧写到 Flash 0x9000 之后地址的 APP 程序。& ~' i/ ]1 Y, d
; X7 ~& g9 G% h0 n
|