|
什么是IAP升级? IAP,即In Application Programming,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写。简单来说,就是开发者代码出bug了或者添加新功能了,能够利用预留的通讯接口,对代码进行升级
4 `& R: t# J% k. z) p9 c% Z
UART、SPI、IIC、USB等等,当然还有wifi、4G、蓝牙等无线通讯手段,都可以作为IAP升级的方式,今天主要介绍如何使用串口对固件进行升级
5 S5 C5 s! t$ I# N/ q STM32的代码启动过程要想设计IAP,首先需要对MCU的代码启动过程有个了解,先来看看STM32的代码启动过程是怎样的吧
9 p; y( ^7 D& n7 C4 j, V0 Z在《Cortex-M3权威指南》有讲述:芯片复位后首先会从向量表里面取出两个值(下图来自Cortex-M3权威指南):
5 A/ @* D# @1 @; N' W
K/ g+ L8 u9 j3 a: G+ p启动文件源代码分析- ;******************** (C) COPYRIGHT 2011 STMicroelectronics ********************
* Q! B% Q4 C( [& e: p( a4 I2 O9 w - ;* File Name : startup_stm32f10x_hd.s
( ]% l/ j9 l' {4 l& \* B) y6 s, ?1 J - ;* Author : MCD Application Team
8 n) s2 N4 W( ` - ;* Version : V3.5.0
' j5 I+ h/ R+ V! m - ;* Date : 11-March-2011' t) D( j1 p" a1 g1 r( `% `4 j, z
- ;* Description : STM32F10x High Density Devices vector table for MDK-ARM
+ Q& c+ e* f8 F' P - ;* toolchain.
$ K& {, ?* l% x* |; i - ;* This module performs:( c; w$ T' M) e) X! C
- ;* (上电复位后会做下面的几件事情)) N3 H; A' |- d- c- v# H; T1 k
- ;* - Set the initial SP(设置堆栈,就是设置MSP的值)
: i4 O8 N" l5 e$ G. V" Y$ z/ o$ j - ;* - Set the initial PC == Reset_Handler(设置PC的值)
) v% K( W/ \" A' e E - ;* - Set the vector table entries with the exceptions ISR address(设置中断向量表的地址)
- j. i/ U4 H5 d5 J2 E9 V5 \ - ;* - Configure the clock system and also configure the external (设置系统时钟;如果芯片外部由挂载SRAM,还需要配置SRAM,默认是没有挂外部SRAM的)
0 [3 Z* l9 Y) I& |# r8 G - ;* SRAM mounted on STM3210E-EVAL board to be used as data ) `6 C5 y$ |* m) Z- J i9 J ]
- ;* memory (optional, to be enabled by user)
; h# f- u# r6 [3 W - ;* - Branches to __main in the C library (which eventually (调用C库的__main函数,然后调用main函数执行用户的)* d3 F- ?! P8 J/ Y! J5 K, {
- ;* calls main()).
2 N4 Q6 l# s% \, o+ i1 V- w) Y# Q - ;* After Reset the CortexM3 processor is in Thread mode,
, ]0 n% H5 k2 `! Z3 D - ;* priority is Privileged, and the Stack is set to Main.
h0 F4 c/ F9 e! l% z( s - ;* <<< Use Configuration Wizard in Context Menu >>> 4 ~! l3 c8 |. {) G% a
- ;*******************************************************************************+ G& g0 B6 D/ v6 Q6 J9 U) t5 }
1 j! _4 i. l6 L0 P- * I! S, Y. {' L: _, r2 ~ \
8 M) Q6 O; `2 H0 d/ R- ; ------------------分配栈空间----------------
, |2 w( m; n' L3 \$ R/ M - Stack_Size EQU 0x00000400 ;EQU指令是定义一个标号;标号名是Stack_Size; 值是0x00000400(有点类似于C语言的#define)。Stack_Size标号用来定义栈的大小& T2 k! ]# p7 v8 r, e) E( |
- AREA STACK, NOINIT, READWRITE, ALIGN=3 ;AREA指令是定义一个段;这里定义一个 段名是STACK,不初始化,数据可读可写,2^3=8字节对齐的段(详细的说明可以查看指导手册)
( G4 f0 Q2 b) W Q - Stack_Mem SPACE Stack_Size ;SPACE汇编指令用来分配一块内存;这里开辟内存的大小是Stack_Size;这里是1K,用户也可以自己修改
2 g9 E3 d7 C1 [- i; A3 Q; o& r! C1 n - __initial_sp ;在内存块后面声明一个标号__initial_sp,这个标号就是栈顶的地址;在向量表里面会使用到0 M; o# s3 |: k* p8 `; y0 B% s) G+ {% W
- + m6 L& O% K3 c+ B5 _8 @8 d
- ) |1 H$ r: `+ G* P- S& p
- ; <h> Heap Configuration
# `9 }: {( Z/ }% L$ W9 u. S - ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>7 e. H) u/ {- N& O# W
- ; </h>
, T& Q2 e5 z" `! s0 u' w! x7 `& u - ; ------------------分配堆空间----------------
! n4 R: n9 S5 U - ;和分配栈空间一样不过大小只是512字节
' h, @' s) ]* ` - Heap_Size EQU 0x00000200
( b9 J1 l4 O3 h- K5 N - AREA HEAP, NOINIT, READWRITE, ALIGN=3
+ r7 h$ X2 O' K* ]" i
# x0 ?, R' a! s- __heap_base ;__heap_base堆的起始地址7 l& T/ e1 [: I! g8 g
- Heap_Mem SPACE Heap_Size ;分配一个空间作为堆空间,如果函数里面有调用malloc等这系列的函数,都是从这里分配空间的( R6 T9 U* s2 q0 ~
- __heap_limit ;__heap_base堆的结束地址+ ^5 y9 v- |' q6 D
- M1 n* F3 H; I3 O) E- PRESERVE8 ;PRESERVE8 指令作用是将堆栈按8字节对齐; w/ G8 A' J) p" |8 u, ~
- THUMB;THUMB作用是后面的指令使用Thumb指令集% b+ w8 [. N' ]: p1 J/ O9 T& I
- % D3 ^) ]& o: s5 I/ |$ q' g+ V
- ; ------------------设置中断向量表----------------1 f/ C" n( H) d; l8 D
- ; Vector Table Mapped to Address 0 at Reset
' f! ^1 y3 z4 C& @ - AREA RESET, DATA, READONLY ;定义一个段,段名是RESET的只读数据段
/ D* j. G) E- Y2 | - ;EXPORT声明一个标号可被外部的文件使用,使标号具有全局属性% s8 B1 E# f$ C J1 O* K+ j z
- EXPORT __Vectors ;声明一个__Vectors标号允许其他文件引用
! [' J7 S7 s; F/ d4 O2 U. t - EXPORT __Vectors_End ;声明一个__Vectors_End标号允许其他文件引用
: D1 r" e3 h5 |" Q - EXPORT __Vectors_Size ;声明一个__Vectors_Size标号允许其他文件引用
1 Z" c3 d0 ?+ z. O# u7 ] - + Z2 }; ~- ~* W4 O+ Q$ J7 p" w+ m6 p0 S: K
- - u" c7 ?% P( @/ ^
-
& r9 M! Q$ J/ L5 Q# f - ;DCD 指令是分配一个或者多个以字为单位的内存,并且按四字节对齐,并且要求初始化
$ }4 w. ~% Z3 b) l
2 g* o; H' q' M$ [+ @- ;__Vectors 标号是 0x0000 0000 地址的入口,也是向量表的起始地址
4 Q! `3 k9 V7 O7 i0 l3 I. B' @. F - __Vectors DCD __initial_sp ;* Top of Stack 定义栈顶地址;单片机复位后会从这里取出值给MSP寄存器,: z3 {1 P2 i! \, m3 G; p! N
- ;* 也就是从0x0000 0000 地址取出第一个值给MSP寄存器 (MSP = __initial_sp) 4 P% q: j. O$ B/ t# H/ p
- ;* __initial_sp的值是链接后,由链接器生成: ^: N }) V) \2 h% ^ S
% U; M2 _: }0 p$ Y7 M; g& C6 D- DCD Reset_Handler ;* Reset Handler 定义程序入口的值;单片机复位后会从这里取出值给PC寄存器,! }$ @$ |8 @2 j- G$ k
- ;* 也就是从0x0000 0004 地址取出第一个值给PC程序计数器(pc = Reset_Handler)/ N7 e# V* x9 C3 K6 J# @/ T
- ;* Reset_Handler是一个函数,在下面定义
4 H3 t# n; h( G3 f - ;后面的定义是中断向量表的入口地址了这里就不多介绍了,想要了解的可以参考《STM32中文手册》和《Cortex-M3权威指南》 y- R d, Q7 D! Y( V8 s9 ?
- DCD NMI_Handler ; NMI Handler % c4 O$ [7 G. o7 ?2 G. R/ |
- DCD HardFault_Handler ; Hard Fault Handler
5 L1 V4 @( V4 u' _- ?8 ` - DCD MemManage_Handler ; MPU Fault Handler& m: {/ ~- m: R4 i" v
- DCD BusFault_Handler ; Bus Fault Handler1 h% I3 D: T# ^- e
- DCD UsageFault_Handler ; Usage Fault Handler- }0 S% ]6 v; I! E
- 1 j4 a: q4 u6 L: d$ t
/ {# k/ g# R5 S, f6 m" }0 x1 A- .....由于文件太长这里省略了部分向量表的定义,完整的可以查看工程里的启动文件
$ W. K7 k& b4 r: p& N5 t- T
/ S) m, {7 Z5 Y- DCD DMA2_Channel1_IRQHandler ; DMA2 Channel1& C0 n; x8 v! X# g' H
- DCD DMA2_Channel2_IRQHandler ; DMA2 Channel2; ~( ]8 b0 U# j2 q
- DCD DMA2_Channel3_IRQHandler ; DMA2 Channel3& ~6 ~7 i3 b9 m/ ]* `& C5 j
- DCD DMA2_Channel4_5_IRQHandler ; DMA2 Channel4 & Channel5/ n0 R* x {3 K
- __Vectors_End ;__Vectors_End向量表的结束地址) J. g: y$ J. W
7 I; w" f- P! _9 _3 Y a7 U; x/ L- __Vectors_Size EQU __Vectors_End - __Vectors ;定义__Vectors_Size标号,值是向量表的大小3 M# j) I8 P) |+ c. Z* g
, Q5 L" \1 x! }& L3 z6 x/ d- AREA |.text|, CODE, READONLY ;定义一个代码段,段名是|.text|,属性是只读) u3 q8 I4 c* B2 _) n
- 7 y; u5 L6 C5 Q+ Q1 |; b
- ;PROC指令是定义一个函数,通常和ENDP成对出现(标记程序的结束)
# C! A+ f! S t1 v; b) S - ; Reset handler% \4 _- S) Z$ T5 i6 B
- Reset_Handler PROC ;定义 Reset_Handler函数;复位后赋给PC寄存器的值就是Reset_Handler函数的入口地址值。也是系统上电后第一个执行的程序
: D1 k( N( W$ F) I) z - . Z" `' g# j, @. V
- EXPORT Reset_Handler [WEAK] ;*[WEAK]指令是将函数定义为弱定义。所谓的弱定义就是如果其他地方有定义这个函数,3 h; ?3 |# P$ A; N
- ;*编译时使用另一个地方的函数,否则使用这个函数6 r* u8 K/ q6 V+ W
- 4 N' y; f; {. `( G, a' Q# U
- ;*IMPORT 表示该标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似3 G: \+ `+ i2 B* ]( I
- IMPORT __main ;*__main 和 SystemInit 函数都是外部文件的标号
& |. x1 W% p- [' R: L - IMPORT SystemInit ;* SystemInit 是STM32函数库的函数,作用是初始化系统时钟
/ I! w; k0 ~. h4 K! Y6 s$ w - LDR R0, =SystemInit/ G* \6 q8 X$ Z2 a) U3 G
- BLX R0
% K6 q S7 d/ V9 L - LDR R0, =__main ;* __main是C库的函数,主要是初始化堆栈和代码重定位,然后跳到main函数执行用户编写的代码
/ C8 D5 ~+ V0 q- @) u - BX R0
+ N; A# j$ {# ^2 F" c; Q - ENDP& j" ?5 B$ f ?+ ?: O% Y# |
-
& U4 A) U9 |$ w& s, A! v% x# k& H - ; Dummy Exception Handlers (infinite loops which can be modified), h, s/ {/ B6 x* L
- ;下面定义的都是异常服务函中断服务函数; B, x+ N1 u7 l/ v6 p' _
- NMI_Handler PROC
/ l4 [) V1 P6 n4 a8 C - EXPORT NMI_Handler [WEAK]. G. g- n1 ?% x7 [/ u! M) h* o
- B ., \2 g5 x5 J6 d
- ENDP
2 J# i x$ J) c - .....由于文件太长这里省略了部分函数的定义,完整的可以查看工程里的启动文件9 c" u8 p4 Z2 E. Y$ C% ^- w
- SysTick_Handler PROC
( ~. X6 [. z, A9 z% U8 K - EXPORT SysTick_Handler [WEAK]) Q+ f3 a" A! o5 X5 g" w9 e
- B .
( f" g6 y# G& i - ENDP! S# }" P8 D+ _# k9 O6 }9 m2 K
- % a! M' O" A- l6 a" E3 u: f
- Default_Handler PROC
, U" [: V! g# N+ J - 9 }* [; t. m1 z; W) i6 D' s
- EXPORT WWDG_IRQHandler [WEAK]3 c) {/ ^+ j" d0 o, d5 y4 Q
- EXPORT PVD_IRQHandler [WEAK]
$ t0 i) c' u7 _" t6 R( d; _ G; E' m - .....由于文件太长这里省略了部分中断服务函数的定义,完整的可以查看工程里的启动文件
2 D! h9 L* M! h$ @; [/ R7 Z - EXPORT DMA2_Channel2_IRQHandler [WEAK]) A6 D* v; C4 d$ T/ b! |; ~" C
- EXPORT DMA2_Channel3_IRQHandler [WEAK]8 H- i( t/ I0 D$ e- r$ P
- EXPORT DMA2_Channel4_5_IRQHandler [WEAK]0 d" q& s2 F" g# b
- 4 {9 z3 M' j1 I" Z. V3 ]
- WWDG_IRQHandler
& d8 f5 |# L/ J; e- F" ~ - PVD_IRQHandler! J: J( A! U$ W6 F
- TAMPER_IRQHandler
: c- s8 E5 U$ e6 T& i+ k# }1 | - .....由于文件太长这里省略了部分标号的定义,完整的可以查看工程里的启动文件
# `2 {5 Z; Y+ N8 W - DMA2_Channel1_IRQHandler/ v3 n! D5 I: m) G. Y
- DMA2_Channel2_IRQHandler+ z" O0 C8 }9 e: Z" o, h' C# M' W8 p
- DMA2_Channel3_IRQHandler% j9 p/ v0 ~* k+ y0 Y
- DMA2_Channel4_5_IRQHandler2 w3 O8 Y1 o" \! o) u! ?7 ~; _
- B .
* k) L2 ` M* s) C. L/ D - 7 g6 e7 u% B+ f5 O! I; y% ]
- ENDP
. S9 [" t- G# G
# L: ^3 P$ M# g) Y- ALIGN ;四字节对齐
G0 j0 P' l; q( W$ @ - 8 h1 j2 G7 y1 e. K. }8 o" a
- ;*******************************************************************************0 s, @$ D& }' s+ h8 u, G
- ; User Stack and Heap initialization
8 ]- |3 n! u p3 B% o2 s - ;*******************************************************************************
+ g9 n. x: A6 Z D - ;下面函数是初始化堆栈的代码* I3 ~# d' l) g f
- IF :DEF:__MICROLIB 4 G9 C# u# K' N- n) h, G
- ;如果定义了__MICROLIB宏编译下面这部分代码,__MICROLIB在MDK工具里面定义! g" `$ d9 u' M+ X( t9 l9 o
- ;这种方式初始化堆栈是由 __main 初始化的
$ A7 P1 E0 |3 q - EXPORT __initial_sp ;栈顶地址 (EXPORT将标号声明为全局标号,供其他文件引用)# [4 S: s- Y4 a! `/ V; ]
- EXPORT __heap_base ;堆的起始地址
1 I) m/ L! `1 k9 i9 H Y, l - EXPORT __heap_limit ;堆的结束地址3 w- L' z" I( S1 m3 H/ h
-
}- d6 f" L& Z2 ^" x2 R - ELSE5 f) _5 O1 Q1 @9 D0 ?) u* l
- ;由用户初始化堆
0 N+ P& \/ R# c9 m$ X0 J - ;否则编译下面的
" [$ X z+ e/ c' z7 j - IMPORT __use_two_region_memory ;__use_two_region_memory 由用户实现
$ u; b \" M0 A: \9 u, a - EXPORT __user_initial_stackheap# A6 n4 H. i! l/ d5 f7 G
-
5 m g6 V( r6 j% z/ Q0 q9 y - __user_initial_stackheap. O- v0 X- n7 ^4 b" c+ ~
- ' z& q! a; s# B" q
- LDR R0, = Heap_Mem ;堆的起始地址) D: \/ l) D1 u. m+ o U4 X
- LDR R1, =(Stack_Mem + Stack_Size);栈顶地址' X m: [( W. T9 k
- LDR R2, = (Heap_Mem + Heap_Size);堆的结束地址7 t9 { a: h; `. a4 H4 X) e1 v
- LDR R3, = Stack_Mem ;栈的结束地址
6 c! I4 b4 n) u! D) V1 e - BX LR6 m7 ~" Q. J0 a ?! g1 I, N
7 S8 `; o0 P7 p5 D3 K- \) G7 y- ALIGN- l. W# j' T: n6 I& K
- 5 ^# e0 T7 S6 @* S: A0 N; {
- ENDIF0 j3 O6 f# x5 g T% ^
- 7 e' \( Z0 c; z+ H9 W' ~8 m" L' K
- END& d" a2 X% |( P/ b, q( N$ |
2 l/ ]' `2 K8 m! k- ;******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE*****
复制代码
( y$ |* X) C; ^9 L. T0 U, L
9 O3 M$ L& O$ M. k3 Q: R* NSTM32的启动步骤如下: 1、上电复位后,从 0x0000 0000 地址取出栈顶地址赋给MSP寄存器(主堆栈寄存器),即MSP = __initial_sp。这一步是由硬件自动完成的 2、从0x0000 0004 地址取出复位程序的地址给PC寄存器(程序计数器),即PC = Reset_Handler。这一步也是由硬件自动完成调用SystemInit函数初始化系统时钟 3、跳到C库的__main函数初始化堆栈(初始化时是根据前面的分配的堆空间和栈空间来初始化的)和代码重定位(初始RW 和ZI段),然后跳到main函数执行应用程序 4 f8 y- R, o G/ c' R
IAP设计思路大体分为两部分设计,bootloader、APP代码设计,bootloader用于检查APP区代码是否需要更新,以及跳转到APP区执行APP程序 调研了一下群里的小伙伴,下面这个流程比较通用一些,大概是下图所示升级流程: - n6 n5 G ^8 N' T* u
升级流程图! i* Q1 `& A/ K8 s
5 M! @- v& i8 z9 M, R7 W" F( c. g
' B6 B) l6 S6 g* B: j) m! l2 R
; _0 [5 L5 n; S) |3 k5 Q. m
( V" S9 |$ j1 t7 |# b7 R9 Y- V R/ ^9 @/ E9 Q/ M8 B
Flash分区是以STM32F103RET6为主控做的flash分区,主要功能:
& J$ Y9 N5 ^5 Y* B& j
, v% L- I, F# U O
 boot区:0x0800 0000 到 0x0800 b7FF 地址的flash块划分给bootloader,用于升级固件,大小是46kb 用户参数区:0x0800 B800 到 0x0800 BFFF 的flash块划分为用户参数区(parameters),用于存储用户的一些参数,大小是2Kb APP区:0x0800 C000 到 0x0804 3FFF 的flash块划分为APP区 ,(application)用于存放用户功能应用代码,大小是224Kb APP缓存区:0x0804 4000 到 0x0807 BFFF 的flash块划分为APP缓存区 (update region),用于暂存下发的固件,大小跟应用程序区一样 224kb 未定义:0x0807 C000 到 0x0807 FFFF 的flash块划分未定义区,可以根据具体用途定义,大小是16Kb ) z- [) ~5 L5 p+ J: V+ e* ?
代码实现硬件: - fallingstar-board(已开源,打板验证)
- t+ u) ~# L+ j" P+ f. }
软件: 内部flash读写 串口DMA+空闲中断 / r5 ^" E/ [( A: U: M+ f' W! p7 p' k
内部flash读写操作这部分比较简单,直接上代码: 读flash操作: - /************************************************************
b# A, f" p5 X( q) I# c - * @brief 读取2字节数据! g. J- Q( ?/ O( u
- * @param[in] uint32_t faddr
6 r* Y( `. L) ?/ v& h - * @return NULL+ o; C/ T$ L0 o! o4 t
- * @github " S/ \/ e$ O* y5 z2 e
- * @date 2021-xx-xx. c/ ^9 Y: H. y5 u( D
- * @version v1.0& @/ ^9 C8 L1 n) l, K) f; ^
- * @note NULL
+ i4 R! x/ F' m0 i5 d - ***********************************************************/
9 S7 t" \; Z% |9 Q) r) E# u - uint16_t BSP_FLASH_ReadHalfWord(uint32_t raddr)) P, S$ X6 w3 Y, n
- {0 U' J5 T/ u, F6 m: @1 D$ K
- return *(__IO uint16_t*)raddr;
& |5 M' ], n, y$ N9 q. q - }
$ }* r. v6 \4 H; T' \6 r* w% P - /************************************************************) Z' I2 G* O. S4 w5 P8 k5 u% E5 L
- * @brief 读取n(uint16_t)字节数据
$ {: l q6 H( H/ F# e; g- z+ ^; y - * @param[in] uint32_t ReadAddr2 T( @4 n5 N( t0 v
- * @param[out] uint16_t *pBuffer) _ \& J: e% i/ n
- * @param[in] uint16_t len0 a' C$ C2 V1 `( c* k+ a
- * @return NULL# }: B4 d+ e0 T
- * @github
5 {& S/ Y3 W& e+ M$ M# W - * @date 2021-xx-xx
; q* \( ~4 H% i$ H3 C+ z0 g- P, y - * @version v1.0
2 h: p+ y2 X# p# W - * @note NULL; @( k7 q2 E5 |- `9 d
- ***********************************************************/' s& X; C4 W1 }+ C7 V! a# i
- void BSP_FLASH_Read (uint32_t ReadAddr, uint16_t *pBuffer, uint16_t len )
9 l" p* {2 b# d0 y# A3 C - {
! r! O7 Y/ X1 |* i: j - uint16_t i;8 P' g6 D. F% e, Y/ F; ^
- ) M: s3 j; l8 S
- for(i=0;i<len;i++)
( V$ u7 Y$ I0 e& V - {3 }% h0 f! H2 z K$ y
- pBuffer[i]=BSP_FLASH_ReadHalfWord(ReadAddr); //读取2个字节.
& W* e4 P# ~5 c5 z) Y! D; R - ReadAddr+=2; //偏移2个字节.
7 o' N& o: ]1 E0 e0 h3 i( z, ?, o - }
$ _& K7 J: l( u6 Q8 f) U1 W - }
复制代码 + H) @1 p |' t' Q4 I
写操作,注意写之前要保证是没有写过的区域即可: - /************************************************************
! H0 h9 P( ~; G c, Q - * @brief 写入n(uint16_t)字节数据
' z& D! M( f3 L2 i - * @param[in] uint32_t ReadAddr n( z9 {: ~) y- W- M3 v1 j6 t; ~
- * @param[out] uint16_t *pBuffer
( y6 a( u' Z; @4 s - * @param[in] uint16_t len' S& w2 v* t6 n+ @- c& E
- * @return NULL; h/ q U% k! [8 k) F; ]" H& I [
- * @github - f6 F- e+ u/ p
- * @date 2021-xx-xx
A9 x% b4 {0 F, m - * @version v1.0
, ^# W: V& p3 i& i" p - * @note NULL
( r. h0 Y# j, y8 ~: O# q - ***********************************************************/
) b; ?) f4 `+ P( h - void BSP_FLASH_Write_NoCheck ( uint32_t WriteAddr, uint16_t * pBuffer, uint16_t len )
& y% Y' m( S4 E- |: m& ? - { , a: ^( }6 E, }% p9 |$ i
- uint16_t i;
$ M2 t8 H( C8 R1 J - 6 G* z+ s3 Q, `& l, b5 Y' R
- for(i=0;i<len;i++)
1 ]5 b0 j: S$ |1 z# f3 ^& e - {+ F' ^( E5 B! k3 j
- HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,WriteAddr,pBuffer[i]);
0 y6 V" j# ^* G4 o9 l* @2 ?' S5 r# q - WriteAddr+=2; //地址增加2.
, H. v+ z0 x# ~0 q3 W# x: p* p1 J - } # T$ ~" v* B" @2 B6 Q/ U
- } 0 r+ D* h2 U4 C3 c$ i$ C6 g3 X0 h+ l
- /************************************************************9 R- i6 k) c% ~0 v
- * @brief 写入n(uint16_t)字节数据
& M4 E0 t- b9 b0 E0 \ - * @param[in] uint32_t WriteAddr
. q$ v( u S$ Q - * @param[in] uint16_t *pBuffer
/ P' L; m3 ?. W- h7 M" H - * @param[in] uint16_t len) _3 |/ t) K" s& T4 N, e5 Q
- * @return NULL2 R, l% z) X$ b t3 r$ g- s
- * @github 1 @) h( d7 B+ `" w' I
- * @date 2021-xx-xx
$ z1 ^6 g6 q7 {% C* N: P1 k - * @version v1.0" L5 `; [- b8 [
- * @note NULL
( E0 v! J) h9 ^" D4 Q; i6 z - ***********************************************************/8 X1 w( L/ s; F3 x3 G w
- void BSP_FLASH_Write(uint32_t WriteAddr,uint16_t * pBuffer,uint16_t len )
% \1 `/ x5 {/ `! c w5 z" N9 q - {
3 W* q9 ]5 |# q/ c" Q& p& `; [ - uint32_t SECTORError = 0;6 _$ V w& K3 g3 O6 e
- uint16_t sector_off; //扇区内偏移地址(16位字计算)/ ? e/ f. z8 _* n* i
- uint16_t sector_remain; //扇区内剩余地址(16位字计算)
* P8 w! U7 @, b2 u( ` - uint16_t i; / A9 X3 A3 t8 l9 W7 N( w
- uint32_t secor_pos; //扇区地址
3 h- C' }( w! \, C5 s - uint32_t offaddr; //去掉0X08000000后的地址
- g5 O5 D9 l- E @4 W' c- Y - # [! L$ a& k4 ^: j1 m4 ^
- if(WriteAddr<FLASH_BASE||(WriteAddr>=(FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址# @0 b0 z3 E Z
- - v- p9 ] W8 N
- HAL_FLASH_Unlock(); //解锁$ M( t) d- _" o. ^
-
& A- y3 V2 r b5 C - offaddr=WriteAddr-FLASH_BASE; //实际偏移地址.$ [, {, L3 n4 p( I
- secor_pos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6# \( W g# H( }! m3 K1 }9 ~
- sector_off=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)
2 G3 Q- R/ p" c - sector_remain=STM_SECTOR_SIZE/2-sector_off; //扇区剩余空间大小 * B* i. J" v/ I
- if(len<=sector_remain)sector_remain=len;//不大于该扇区范围" W! _: M: G1 \# D# _6 L& L# a; u
-
5 O& {0 H- n7 N1 T8 r - while(1) % Z: i& v/ q+ b" ~/ I1 {/ I- l. h4 x4 u
- {
- q. ~% Y" c* L9 R. M0 K6 H - BSP_FLASH_Read(secor_pos*STM_SECTOR_SIZE+FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容% v! w K+ p) _2 b( T- }( W2 \
- 1 W* f7 X5 }8 J0 q9 k0 L
- for(i=0;i<sector_remain;i++)//校验数据 ]6 L- C) u# S* P2 u: X K6 Z5 ]! O( p
- {
, {: R7 }7 l+ E$ f- V. K; d. \ - if(STMFLASH_BUF[sector_remain+i]!=0XFFFF)( ~/ |) _) z" v8 @
- break;//需要擦除
6 }0 m3 Q# o9 {' | - }
+ _8 l- J, T, S3 q5 P% r - if(i<sector_remain)//需要擦除 X0 h' \2 j1 P/ x- e# F" A
- {
; |% a* v3 k6 G3 b2 r( t - //擦除这个扇区
$ r/ n7 B# D& M% ~ - /* Fill EraseInit structure*/& v' s. |; G5 y
- EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
) n% \0 L9 E" I5 K - EraseInitStruct.PageAddress = secor_pos*STM_SECTOR_SIZE+FLASH_BASE;" k& k/ l- R* [; p: B" a5 e% h8 H
- EraseInitStruct.NbPages = 1;8 U- Q9 ^" M& |1 T' P9 o
- HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError);/ v' X, |9 A% G2 M/ Q4 {' m
- for(i=0;i<sector_remain;i++)//复制- S s5 s3 p i, U% F
- {) ^3 |5 h! _$ k* \- i+ D, ]& v( w
- STMFLASH_BUF[i+sector_off]=pBuffer[i];
2 X' s9 B( l9 V Q7 H - }
6 U& K3 U# E2 W8 |; O - BSP_FLASH_Write_NoCheck(secor_pos*STM_SECTOR_SIZE+FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区
; V L% m" S& V6 \ - } Z$ }; Y) f, q; }
- else : r7 X( K! _. D
- BSP_FLASH_Write_NoCheck(WriteAddr,pBuffer,sector_remain);//写已经擦除了的,直接写入扇区剩余区间.
& z" u7 h' o6 i# ^9 a - if(len==sector_remain)
2 R3 ~/ B! N8 | - break;//写入结束了
9 A; ~. U4 D3 h/ U% X( g' K - else//写入未结束
7 ^, _9 _2 m6 M. S9 f- `6 ?: ? - {; `2 n- o6 [/ o2 c, e7 l
- secor_pos++; //扇区地址增1
% V* h2 j7 m9 N J - sector_off=0; //偏移位置为0 ' W: n+ t" W- j0 W
- pBuffer+=sector_remain; //指针偏移* {$ s2 _; S- g& `4 e6 G1 X
- WriteAddr+=sector_remain; //写地址偏移
6 r! p k2 B1 h2 }" O' L8 y& B; k - len-=sector_remain; //字节(16位)数递减
9 \. r* e# A" G9 B - 2 E* H" j; p4 C5 T8 U( K
- if(len>(STM_SECTOR_SIZE/2))) {1 x, `7 z& g4 }
- sector_remain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
( U4 ^# s% j, d2 A9 D& ^: u - else
2 ^" j- o* H: m0 j2 `; A' T - sector_remain=len;//下一个扇区可以写完了" |1 Z: q+ q0 c2 f# _' j& `
- } 5 ?$ G& n3 D3 J* r
- };
7 }$ L Z7 W1 k: F' B; {" z0 |3 N, y - HAL_FLASH_Lock();//上锁
. j6 ?9 J2 |" K+ H6 H3 _- c - }
复制代码 3 l: n3 a T) L( g
串口DMA+空闲中断接收不定长数据在上篇文章的基础上,我们对结构体做点修改,增加bin文件数据总长度记录,以及bin文件接收完成标志(小伙伴们可以采用其他办法,不必拘泥于我教程中的方式): - #define Max_RecLen 1024*3
1 W7 |$ O% S$ b V$ W6 h H" o( f - & J }* \0 ^3 u& L( g6 v5 C1 `
- typedef struct{9 C, |5 U% W' k+ o" q& _
- uint8_t RxBuffer[Max_RecLen]; //DMA接收缓冲区# E7 s, M$ x0 ?8 ^ v
- uint16_t RecDat_len; //单包数据长度
$ k8 P, E: Y; |0 q& {, O; _ - uint32_t Cur_WriteAddr; //APP缓冲区地址% N- N1 {7 u, ~ z2 h2 B
- uint16_t BinLen; //bin文件数据长度5 x) r4 P" P1 w: m
- uint8_t rec_endFlag; //单包数据接收结束标志
+ ~4 C( O7 \# L8 v1 m9 c - uint8_t Binrec_endFlag; //bin文件接收结束标志 ( S& p% c/ ^" r( N O2 }
- uint16_t DMA_TIMCNT; //bin文件下发超时计数器
$ d, g \/ c; J1 K -
: d J o3 _% s5 ] - }UserUartDMA_Typedef;
复制代码 8 X) D+ F$ Y1 m. a
然后在串口中断中: - /**8 h8 ^4 ^. S! }& N. n0 _
- * @brief This function handles USART1 global interrupt./ X9 C6 F) B: f' s
- */
! q) Z/ R* }' I: S" ]' x - void USART1_IRQHandler(void)) U" M s* P: b, ]
- {
I) n. Q- ?# y. O( U1 S - /* USER CODE BEGIN USART1_IRQn 0 */
% e1 s% p5 i1 o& ` - uint32_t idle_flag_temp = 0;
0 I" N! a8 j2 N) r. g+ ?" l7 p - uint16_t len_temp = 0;& ~; g9 P/ h+ ^* e; J: n
- /* USER CODE END USART1_IRQn 0 */
/ C( b/ L7 g' r) I5 Y+ e( B - HAL_UART_IRQHandler(&huart1);! H+ F( t: a' J2 G
- /* USER CODE BEGIN USART1_IRQn 1 */
" A) s9 X: {* n2 h2 K, E" S - idle_flag_temp = __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
2 h$ B- ~- Q9 T* i& u1 S& y f6 y -
5 E3 O0 B, R% v4 g - if(idle_flag_temp)& o e! [4 W" [" O0 p
- {
9 x" L* G0 o; Y. U6 D6 ]) K - __HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE);
0 T4 K% e# {, A- X, x/ B# E - HAL_UART_DMAStop(&huart1);4 a) I" x r7 \1 [
- 8 ` Z* e1 Q' C
- UserUartDma.DMA_TIMCNT = 0;
- U& v0 T! H5 { - UserUartDma.Binrec_endFlag=0;8 c; E9 A" S: i) v+ {5 N
- len_temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
' R& _$ d8 O% n* P( Q' |6 c - 0 t4 {0 P+ u. }9 I4 B, q) O
- UserUartDma.RecDat_len = Max_RecLen - len_temp; # |4 p, M. `, {6 B) j; s h
- UserUartDma.BinLen+=UserUartDma.RecDat_len;
" e3 R: e. {) U/ ~* S& W - UserUartDma.rec_endFlag = 1; K* q# F. ?% n7 u: @8 N, Q4 F
-
4 b. M& | B4 | - }4 W8 J. V( D4 O% ^) Z% a v
- __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);& c3 i6 B3 d( {% a; Y
- HAL_UART_Receive_DMA(&huart1,UserUartDma.RxBuffer,Max_RecLen);
& u) X, P4 @/ t1 M( } - /* USER CODE END USART1_IRQn 1 */9 m7 M* Q4 A. Y E9 N
- }
复制代码 * d% ~% Q2 Q" Z+ X$ h- j; N
IAP代码设计- bootloader代码设计
! W& o* F2 O/ H: M0 G3 z* D
扇区擦除,用于写入前擦除相应扇区 - /******************************************************
/ x9 J+ \: H, U J - * Brief : 擦除APP区9 f7 \% j( g- O. t# }, L
- * Parameter :
5 ?0 b: o( b+ k9 N- g- F% ~ - * *startaddr:APP起始地址
( j) C- G9 G6 @* f' T4 O& a/ { - * *pages :要擦除的page = APPSIZE/PAGESIZE
+ k% o! k2 P. K6 g+ A/ E" I - * Return :None.) A+ v6 l6 m5 W( O+ W( O1 C
- *******************************************************/
" p) Y) A' E: T8 d - void APPReigion_Erase(uint32_t startaddr,uint16_t pages)
- i" x8 u2 N) J4 q - {
/ b1 M, T' R7 } - uint32_t SECTORError = 0;
6 e( y1 s# ?0 V8 J
9 B2 w% Q0 {- {2 h- //擦除APP区8 i- W$ b" U/ } D2 ~% I) P
- /* Fill EraseInit structure*/. X* d* B! T! g' ^: u% w1 K) ~
- EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;7 _" t0 l/ @8 ~9 V& m& H
- EraseInitStruct.PageAddress = startaddr;
4 ^, U/ J" F* K6 F ? - EraseInitStruct.NbPages = pages;- C$ _6 ~! Q: ?! O/ V$ W; D2 h
- HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError);
- n! D5 _; L9 A& ~3 z( p/ A - }
复制代码 & z" r/ x4 H. a& l1 A) \
升级标志获取与擦除 - /******************************************************
5 n& J- B' A( \/ y7 \4 g. ]/ r - * Brief : 写入升级标志
3 ?! n; e. Y& ^1 _ - * Parameter : 7 ~3 G' o* Y% t }5 F" ]" d
- * *addr:标志存放地址
8 d+ `+ }2 i* l! e - * *pdata:写入的数据6 j7 x8 ]! b R! a# B" I2 ]
- * Return :None.
! s# ^+ }9 p V9 F - *******************************************************/+ ]$ M9 K2 c5 m4 z8 u9 `8 B ?
- void APP_UpdateFlag_Write(uint32_t addr,uint16_t *pdata)% }1 z& s5 W7 B$ k
- {( o5 {7 a$ @; m6 B4 U
- BSP_FLASH_Write(addr,pdata,1);# f1 |. T5 X6 N( p$ S9 E7 L
- }
, q* E- {: ?( _2 p& n1 w7 C - /******************************************************) C, v0 t5 r4 Z9 n5 ^; \
- * Brief : 获取升级标志# C6 [$ g3 i3 x
- * Parameter : ) }) n9 a4 }1 \" t
- * *addr:标志存放地址
0 h3 f# y5 R& E/ ?) v- C" f- J - * Return :None." `& X8 f+ @& |
- *******************************************************/
+ K" \- y3 b7 k - uint16_t APP_UpdateFlag_Read(uint32_t addr)$ K* Q! k! a' R. F+ X
- {6 E, O- c) `2 k8 g; u. \' i$ @
- uint16_t flag_temp;
$ S& a {* w; ^( K& d2 d) R - BSP_FLASH_Read(addr,&flag_temp,1);( N @2 @5 V9 K* `$ d
-
9 U3 C2 M- m) I3 h7 H O/ j5 w* T% b - return flag_temp;% H# i1 e2 G0 S# @5 n
- }
复制代码 ' o0 X% P! x+ L" }* Z2 \
接下来就是APP缓冲区数据写入,APP区与APP缓冲区数据倒腾了 - /******************************************************
) c( t; L' a( F! J' w$ P3 K - * Brief : Bin文件写入app缓冲区
+ N( `% x2 F- [5 n- P6 Q - * Parameter : 1 p% f8 ]. ?& p) ]5 P; v0 i9 r
- * StartAddr: 起始地址
* c* @9 H0 }4 c( i: y - * *pBin_DataBuf: 要传输的数据* H" K( m0 G4 l" {4 \$ J
- * packBufLength:单包数据长度
$ H6 `/ I" \0 l6 L# ~ - * Return : None.5 r, j! N) b/ F$ @! q
- *******************************************************/6 K6 I+ n8 ?4 C" d' n' P) {
- void IAP_WriteBin(uint32_t StartAddr,uint8_t * pBin_DataBuf,uint32_t packBufLength)& i# N# [$ M) m" B1 ]6 y3 q
- {- o( D3 f3 v+ J8 ~4 X
- uint16_t pack_len, packlen_Ctr=0, dataTemp;
3 v8 @( l* h. h9 c1 x - uint8_t * pData = pBin_DataBuf;
/ F* s0 [- F1 n- j' f) ~% B, e% i6 e' R - / G: J% @5 C# H* z( l7 j/ T: A
- for (pack_len = 0; pack_len < packBufLength; pack_len += 2 )
2 M% M: [+ z. m" O* Z2 [/ u' u - {
" h6 |: ]; |. i/ D x& _! ? - dataTemp = ( uint16_t ) pData[1]<<8;( [5 G4 \! e# R9 ^
- dataTemp += ( uint16_t ) pData[0];
: Z7 r- Q; @" K7 c% _" { - pData += 2; //偏移2个字节 O5 E4 E0 r' r' X
- ulBuf_Flash_App [ packlen_Ctr ++ ] = dataTemp;
8 W4 h: x2 B# s6 E+ [/ J" _8 m - }
, O" C H$ M e# P+ L" e' j
r" w" Z, w/ p/ z& J& V- BSP_FLASH_Write ( UserUartDma.Cur_WriteAddr, ulBuf_Flash_App, packlen_Ctr ); * p+ s3 o6 F* t. G! ~. X0 E5 x0 J
- UserUartDma.Cur_WriteAddr += (packlen_Ctr*2); //偏移packlen_Ctr 16=2*8.所以要乘以2.( b2 Y& q" T; Q4 x# P
- packlen_Ctr = 0;; B. h6 z; M# G( @& S7 h
- }
* O1 j. P0 \% `# N! \ - /******************************************************6 f9 p% E8 p; `! o7 l5 f& V' h( G
- * Brief : Bin文件从app缓冲区写入app区 n) J' _" {! y# |4 }
- * Parameter : # J5 Q: w/ C7 _! n
- * SrcStartAddr: app缓冲区起始地址* o* s4 |/ C; O/ ^, X& [+ T) P
- * DstStartAddr: APP区起始地址
5 V( W8 o' g3 c& h, {% { - * BinLength:bin文件长度! E8 |/ x& A [2 P) S% `6 X$ @+ l
2 I; u% J# f8 k4 z4 d9 D- * Return : None.
- C( H0 k! R% }, V - *******************************************************/$ ?6 t9 N) Z# k3 }0 \
- void IAP_WriteBinToAPPReigon(uint32_t SrcStartAddr,uint32_t DstStartAddr,uint32_t BinLength)
$ T- L. F; F* z. h- ~ - {
/ v6 _" j( y/ z - uint16_t data_temp = 0;
% Y" g1 c" H# @; D: u - uint32_t count=0;2 d% t6 U# ^7 }. d
- 7 l8 E% G/ X; r% W9 p. |: W$ E9 v
- 8 N6 c- i- I% Z& f9 S
- HAL_FLASH_Unlock(); //解锁) @2 D. e( V' w" Z
-
( s. S; M1 q# y. o - APPReigion_Erase(APP_START_ADDR,APPSIZE/PAGESIZE);
/ M1 o) i; m# y7 n0 e4 G X: w! _ - HAL_Delay(10);
1 X8 H+ D5 Q# P4 P. M: ]9 b2 Q -
8 R/ e$ A3 U" C5 C - for(count=0;count<BinLength;count=count+2)
. P( K' P) x/ y8 G' ? - {) B! d% d* r; p. z4 Z' w! N! ^& N
- BSP_FLASH_Read (SrcStartAddr, &data_temp, 1);
2 E$ J. _" S6 l( V! O7 q! N5 f - BSP_FLASH_Write_NoCheck(DstStartAddr, &data_temp,1);8 D$ f+ R3 A/ o) `8 R) m
- SrcStartAddr+=2;
/ y, [1 O+ w2 h& R+ a% @ - DstStartAddr+=2;
: j) m6 Q6 P, v) L$ P9 _' s - }
5 u( O2 _0 B# Q. o1 ^2 s0 X9 ? - //BSP_FLASH_Write_NoCheck(DstStartAddr, ,1);- \( c/ `6 R; O% E x @
- HAL_FLASH_Lock();//上锁$ {' _- `% P6 S3 `* w2 a' e! A
- }
复制代码* G- |% r' X7 Z9 A, ^' Z
最后,要设置APP程序的运行地址 - /******************************************************! z- A9 z7 m5 M! q2 `# f
- * Brief : 设置栈顶指针
- p: }* e4 ~+ M. V) ? - * Parameter : , ]3 g! y+ F6 K% a
- * ulAddr:
" h% X! l l- H0 o F2 r - * Return : None.- r6 m6 C1 ?0 E: r- c5 I
- *******************************************************/
5 H) p C+ Q) P% A: E
# i% @8 g6 `# q2 G M" I- __asm void MSR_MSP ( uint32_t ulAddr )
0 w; Y, J/ m8 } - {( a- q: X; }1 {" Z1 T: i8 L: G) v
- MSR MSP, r0 //set Main Stack value( X, m# w- A' @6 d+ X
- BX r14
! q8 ^% q; B$ a. b o - }5 `6 L1 _/ D$ h7 }
* p& A' _, K% q, b- /******************************************************
* W3 K- X! b( z) m" j/ F - * Brief : IAP执行4 B- z/ Q( s! I0 q7 w5 W. \
- * Parameter :
; B2 c+ i) J: z( |1 T& R - * ulAddr_App: APP起始地址
0 S. h9 v& O, i r1 X - * Return : None.
. d4 v: J3 _9 a7 f* ^. g0 R - *******************************************************/
7 ]* ~. b+ f/ h; X - void IAP_ExecuteApp ( uint32_t ulAddr_App ), |1 n- o# E: H) H) ]6 M
- {
/ U/ ~9 Q" Q( P6 r( G) T0 O! }' U - pIapFun_TypeDef pJump2App;
( S' t1 e( P1 Z) r - & o& G- O6 f1 r% t% K% Q. o5 t0 s1 N
- if ( ( ( * ( __IO uint32_t * ) ulAddr_App ) & 0x2FFE0000 ) == 0x20000000 ) //检查栈顶地址是否合法." Y( z) h: X) F8 t& {* q. U# D
- { 7 ` z i6 B+ m, A8 \" @' F' s
- pJump2App = ( pIapFun_TypeDef ) * ( __IO uint32_t * ) ( ulAddr_App + 4 ); //用户代码区第二个字为程序开始地址(复位地址) & i- J$ |2 e7 n2 O# h9 \8 Q
- MSR_MSP( * ( __IO uint32_t * ) ulAddr_App ); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
* [! ~ S% t' y- [( p6 \5 P: V - pJump2App (); //跳转到APP.5 F0 `' n& g }2 p) T
- }* b0 D, l( F/ w/ T: r, z/ }
- }
复制代码
6 ?6 U. X/ B3 pIAP相关的代码就这些,主要是数据的倒腾,其他倒也没什么复杂的 目前小飞哥对整包bin文件传输完成判断,单包采用DMA+空闲中断的方式,整包文件传输采用的是串口中断在5s内没有数据过来,认为一包数据接收完成,置位接收标志,这部分小伙伴可以自行判断 - static void Systick_Config(void)
+ |0 ~7 m# k7 Y - {
3 K+ e( b/ C: f - HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000); //1ms
# }" @! o* t. O1 U# ` - HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
% k/ L; _. s: Z - HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
# k j ] v s# T - }5 w8 U" m/ j1 {
- - _, b- r7 B! Y; t1 i1 ]* [0 q
- void HAL_SYSTICK_Callback(void)" W. a6 r* N0 Z; [/ i3 P+ ~: u
- {
3 p( l3 B9 `9 _1 z8 B - UserUartDma.DMA_TIMCNT++;5 w5 u; X2 F9 f3 N/ ?9 @
- if(UserUartDma.DMA_TIMCNT>5000)$ l X4 c' x! m# Y5 z
- {
' s7 V5 j7 l9 c: t5 T$ k# n - UserUartDma.Binrec_endFlag = 1;
* E$ T. n) L! w1 }/ \1 `1 d - UserUartDma.DMA_TIMCNT = 0;
? ^" f4 i. r. k - }* x0 D4 f5 t' ~0 z g4 \' y
- TIMX_IRQHandler_user(); 9 }7 M) q1 s+ \8 [4 {7 f1 c
- }
复制代码 $ @+ v* P$ u& @$ U0 F' q5 T
bin文件传输完成后,写入需要更新标志,重新跳转至boot区,检查是否APP代码需要更新: - ' Y+ ^. Z1 }- A
- uint32_t Task_02()# R9 W2 S+ n5 k6 q" X5 e. V
- {4 u7 n$ H* M( r* W& T
- 0 p" Q( c1 B7 q7 b; j# _
- if(UserUartDma.rec_endFlag)
0 |( g7 D2 D/ A+ z9 } - {
: ~% }9 c5 {0 U1 a - PRINT_INFO("update firmware\n");
+ U. Z! W0 q0 _, b, h! [ - PRINT_INFO("APP 长度:%d字节\n", UserUartDma.RecDat_len); 3 T7 O/ w2 l3 t. g! U# {9 r' @! s
-
% `. t; U+ |& ^+ c) ] - UserUartDma.rec_endFlag = 0;
9 B6 U# [2 S# S' `7 K6 ?( d - IAP_WriteBin(UserUartDma.Cur_WriteAddr,UserUartDma.RxBuffer,UserUartDma.RecDat_len);5 }' V- _8 \9 ~/ O x+ A8 U
- / e* l6 f3 O; r- K+ b' u/ }
- memset(UserUartDma.RxBuffer,0,UserUartDma.RecDat_len);
3 v& o4 O7 s8 e, a" G5 Z - UserUartDma.RecDat_len = 0;' ]$ C8 K v3 f h
- }
9 l( o4 V0 s, ?5 @* g- T - if(UserUartDma.BinLen!=0)6 U w( ? S& Q1 W7 D. H
- {
7 H; |, K) W; h - if(UserUartDma.Binrec_endFlag) D% h* g; R7 w _% \
- {3 h! Q, E T: }- s! ~5 v4 c& P
- UserUartDma.Binrec_endFlag = 0;
0 M& z+ \( |$ M8 x( D0 _ - P1 k1 L* w3 b
- APP_UpdateFlag_Write(APP_Len_ADDR,&UserUartDma.BinLen); //写入升级标志
4 ?! y# Z1 L2 b4 O/ ^3 O - APP_UpdateFlag_Write(APP_UpdateFlag_ADDR,&APP_UPDATE_FLAG); //写入升级标志( R7 I: K1 s. x. D* t% A
- UserUartDma.BinLen = 0; 1 N3 B+ s2 C. W5 A, P* a* W
- IAP_ExecuteApp(FLASH_BASE); //跳转8000000,重新启动; {; Z# z4 M, G1 D2 S
- HAL_Delay(2000);: m" F! R: j' G' U
- }
$ T* V0 G7 E( J# x& I -
6 D6 | u/ o3 {% C - }
0 j) x* x; |6 N' S1 N$ z - }
复制代码
# x4 r0 \) i" L( o- TMCU复位之后,初始化执行过程中,对APP升级标志进行检测: - PRINT_INFO("-----IAP Menu--------------\n");: w- k7 X# v2 W( B+ s m
- PRINT_INFO("-----Download APP BIN------\n");
- G, q) y; V, l7 g8 L. f - PRINT_INFO("-----Restart To RUN APP----\n"); ! h0 N0 X; T0 o: M. b( O: V
- PRINT_INFO("\n\n\n");
* B- |( [$ Z* Y% b. T% V -
* `! H( i. |7 W: X8 F- w- } - if(0x5aa5==APP_UpdateFlag_Read(APP_UpdateFlag_ADDR)). d4 W/ a! u3 X5 r) D: d/ U+ i9 F
- {
3 B8 }$ ~( ~) ^3 F7 t8 p8 Q - PRINT_INFO("-----Restart.....5-------\n");" O+ |3 Q" K$ s* `8 v8 s& z2 y7 {
- HAL_Delay(1000);+ k! s7 f5 e3 p6 L3 v1 `! m3 l+ u+ e, E
- PRINT_INFO("-----Restart.....4-------\n");; w% |% d1 c! p% D; }
- HAL_Delay(1000);
; s( T& _- L4 Y: Z2 `/ O - PRINT_INFO("-----Restart.....3-------\n");: H- N5 c' @0 B& o6 i
- HAL_Delay(1000);
- n3 N( a( j/ J& a - PRINT_INFO("-----Restart.....2-------\n");' j7 J" @0 |+ H" E- L: z" \
- HAL_Delay(1000); o9 n+ O9 c; u; R
- PRINT_INFO("-----Restart.....1-------\n");
- j% l) V; l' u- T. t - HAL_Delay(1000);
7 o7 @' E4 g: q3 x4 U. I -
s2 n/ S& w5 v - PRINT_INFO("the device need update\n");9 o) v* Q3 p* G
- IAP_WriteBinToAPPReigon(APP_Temp_ADDR,APP_START_ADDR,APP_UpdateFlag_Read(APP_Len_ADDR));
# e: K2 ~' u' N! ~' r& N - PRINT_INFO("firmware write OK\n");
; ~" G! i6 h( ~ A/ `6 D! a - PRINT_INFO("please double click the button the execute the application\n");5 j h. {( i& V
- 2 u" R0 B6 y6 [4 n- d
- HAL_FLASH_Unlock(); //解锁
& U$ ?7 r Y- W0 R8 l4 @: E- B, \ - APPReigion_Erase(APP_Temp_ADDR,APPSIZE/PAGESIZE); //擦除APP缓冲区
- b* V2 |/ y+ p V7 o - APPReigion_Erase(Flash_UNUSED,1); //清除APP更新标志 0 `3 x6 H; d, }" S' X* _
- HAL_FLASH_Lock(); //上锁
' z( p$ J5 i- N K3 f; { -
/ P9 n# F: }) v9 O1 u$ t - PRINT_INFO ( "开始执行 APP\n" );
9 G! i0 ^2 q- }( V! ^9 g& R - //执行FLASH APP代码
2 F* p+ U Q5 T, k - IAP_ExecuteApp(APP_START_ADDR);
9 r$ p& q) k+ M4 v/ f4 x$ M - }
复制代码
6 M5 T( P/ d: B9 p# {4 @倒计时5秒后,代码更新,并执行 - PRINT_INFO("-----IAP Menu--------------\n");
# ~+ O& X* C7 X4 J - PRINT_INFO("-----Download APP BIN------\n");# j' Z+ {2 |( I+ e4 L
- PRINT_INFO("-----Restart To RUN APP----\n");
8 J+ a7 ?) {/ Q& L' S8 ` - PRINT_INFO("\n\n\n");5 `0 c* U# x! h
- 0 p0 y y- u7 [7 Q0 @! V5 L; n; q
- if(0x5aa5==APP_UpdateFlag_Read(APP_UpdateFlag_ADDR))
6 ?! s# O' k8 l. k( E - {
, K$ M5 s2 M, ~ - PRINT_INFO("-----Restart.....5-------\n");6 ]- p. i. s7 u+ a2 P& ]
- HAL_Delay(1000);/ J7 ^: N/ i( e6 r3 P3 J
- PRINT_INFO("-----Restart.....4-------\n");
2 w2 C5 o3 Q! V$ e' I- _$ ]4 s9 W - HAL_Delay(1000);
: {: C* t* M: g9 E - PRINT_INFO("-----Restart.....3-------\n");& X; x1 v1 K- M4 y; T
- HAL_Delay(1000);
' s2 I6 |# E/ z( P - PRINT_INFO("-----Restart.....2-------\n");/ Q! o+ k8 f: W5 \4 [( H# F
- HAL_Delay(1000);3 F7 g, f O1 E9 {# Q. J- [ M4 o
- PRINT_INFO("-----Restart.....1-------\n");
' [ d- E7 x: K# g4 y+ C E - HAL_Delay(1000);
5 b3 ?7 T6 ?. v/ G: L1 a+ W+ Y - . I% [- }+ |7 s" G) y0 |
- PRINT_INFO("the device need update\n");
# i* x. R" O. `. p: | - IAP_WriteBinToAPPReigon(APP_Temp_ADDR,APP_START_ADDR,APP_UpdateFlag_Read(APP_Len_ADDR));
7 N! b0 R* l4 t - PRINT_INFO("firmware write OK\n");- J0 N7 H# C! U% A) t+ c/ b
- PRINT_INFO("please double click the button the execute the application\n");
. \ @, p+ M) r" ^( n -
6 [$ M: {% ^. e: ]. S8 g3 F; ] - HAL_FLASH_Unlock(); //解锁; R- m, a# D' O1 f" R; T1 _
- APPReigion_Erase(APP_Temp_ADDR,APPSIZE/PAGESIZE); //擦除APP缓冲区7 M x6 v: r2 r d s4 j. q7 V" B6 g
- APPReigion_Erase(Flash_UNUSED,1); //清除APP更新标志
& Q! S* x/ T8 |* ~+ I6 { - HAL_FLASH_Lock(); //上锁
/ M/ |# Y7 ?7 Q - 4 k& g _! C2 B
- PRINT_INFO ( "开始执行 APP\n" ); - P* e; r" g& k0 ^2 M- f5 u
- //执行FLASH APP代码
- I4 x( g H8 n: z2 Y - IAP_ExecuteApp(APP_START_ADDR);% H% P+ p% i5 X' g
- } 6 g V) O) W8 @6 q& O1 f0 K: g; n
- else//不需要更新,执行APP
8 j5 ~% S$ K+ `9 H4 ^* F, J - {
" T0 A0 @4 \, ^8 @$ W - IAP_ExecuteApp(APP_START_ADDR);
* a! {7 y X Z& W: i; q - }
复制代码 % z4 `6 p7 W; J9 T
bin文件生成 8 ?- ^ [/ V+ W( D' `) `4 S8 ^' U
: B* d2 S7 d$ z% [/ y* g9 \
输入: C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o IAP.bin UART_CircleQueueTest\UART_CircleQueueTest.axf
( o0 w8 Q+ d' k) ~. @
参数意义: 输出bin文件名称 - UART_CircleQueueTest\UART_CircleQueueTest.axf:$ n& M9 \0 e1 M. z
axf文件目录及文件 要注意boot区的地址范围,根据前面的设计,46K,拿出计算器.... 
0 A; R% G, f/ F! C2 R* N, j
' g1 D& @! m5 I6 p/ J+ m
H: O O5 t/ i, [* I- h: g
4 u5 T2 h4 Y1 e2 {0 x# P
3 B: D5 |* w! h- v+ A! o
- APP代码设计. s* ~& K* ^2 b' A/ J& |
APP代码是用户功能代码,实现业务逻辑,本次测试用的比较简单,接收到APP代码之后,会自动重启,更新APP区代码,如下:
4 q' e6 Q" i% X C' \8 K
- boot区代码执行效果
6 [% p8 h) @* b0 P1 m. K
+ Y9 w; s! E0 b# r* v
' y( i* F8 A; `2 P6 d8 X
! C, G+ Y% `" } P5 b: H2 N1 Y; V- K- APP1代码执行效果
) \3 v3 K- J& b1 p
. m( ]9 _% M, S+ C) H' Q8 Q% \
9 e7 |" c- d- }# m1 B" D
! U% n/ _, W8 _2 J! p
: f0 S0 k: ^3 W2 D; r
3 R% b5 x4 |# ?& l, V* {( m9 ` - 接下来再进行一次更新
. r# l, g' {$ z& ?0 v 1 x, P) W0 n7 c) e! O% }
" _7 C' E* C' {
9 J" t) ~5 x7 ^4 [" \' h5 u6 O, k! s: `. _; m* F8 s3 {/ y
/ \: V3 w8 m0 l' S 注意APP BIN文件的flash地址设置 ; k4 l$ A7 c4 l$ }8 m% P- U" e9 S
1 ~: t' {0 e% d% V/ T! r9 q+ p, z1 t% o. B. L) W
OK,至此,设计部分就完成了,涉及的内容还是非常多的 本文仅仅用于原理性介绍及IAP功能演示,与工程中实际使用的还是有很大区别的,后面会继续给大家出工程应用方便的教程,会涉及到签名、验签、加解密、校验固件完整性等等 9 v* m4 L3 J# P0 h
转载自:Embedded小飞哥; N4 v. D& k, @( {
如有侵权请联系删除7 Q7 c6 H: [. w/ \
. Q9 {- Z' d: C/ H' c
|