你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32之IAP功能实现

[复制链接]
攻城狮Melo 发布时间:2023-3-26 18:50
最近因项目需求要实现STM32的在线升级即IAP功能,先将这几天的学习体会和IAP的具体实现总结出来,分享给大家,希望对同样实现IAP的童鞋有所帮助,文中最后会上传名为STM32_Update.zip的压缩文件里面包含了STM32_App、STM32_MyBoot_V1.0和升级软件STM32_UpdateSoftware的源码文件供大家参考。所有程序都经过测试,可以直接在原子哥的开发板上跑,上位机的升级软件大家可以直接打开
( q1 A2 ^2 p  [  ]STM32_Update\STM32_UpdateSoftware\Release\STM32_UpdateSoftware.exe来升级,如果需要查看源码请用VS2010打开工程文件。
* l% I* \. o, I& j, y' j& r0 t5 R1 R# G7 x0 |
最终要实现的是:
7 w1 p2 {1 A7 g7 w3 d/ c7 _6 W单片机每次上电会先运行Boot程序,检查标志位如果标志位为FLAG_TO_APP则直接跳转到App程序运行,如果标志位为FLAG_TO_BOOT,则运行Boot程序准备升级。在运行App程序时,当接收到升级的指令后会在FLASH中的某处空间写下升级的标志位FLAG_TO_BOOT,并且加载Boot程序,Boot程序会接受新的程序文件并且存储在相应的FLASH空间里,完成升级后会在标志位的空间写下FLAG_TO_APP,并且运行新的程序。
3 H: v5 \$ M5 |6 L: A  b) k4 H: P! d6 ?% Y
帖子包含如下几个方面:6 D6 k6 ~" d" X: s# C
1. 什么是IAP?
5 Y+ e: l. c5 R( B. }  g2. STM32的启动模式?
* W" A$ M, ^: p9 G: N! T3. STM32的FLASH分布?
% q( I. n$ X8 u( b  c4 j4. STM32程序的运行过程?
; b' k' f( Q3 y4 Y& q  E5. BootLoader程序的编写(如何实现程序的动态加载)?# g: V: k2 q2 N
6. App程序的编写?- Z+ h8 r' T# T
7. bin文件的转换?
9 I' ^$ Z3 v. _" @7 F. d+ \8. 上位机串口升级软件的简介" ^- R) k1 C. e+ d; A' [( P) c
-------------------------------------------------------------------------------------------------
7 F& |" K, i8 Q! j: g1. 什么是IAP?
; V4 j- P* y  R+ o: F! ]IAP的知识网上的各种资料也说的比较明白,在此简单介绍一下。IAP( In Application Programming)即在线应用编程,也就是用户可以使用自己的程序对单片机的User Flash的某一区域(一般为存放自己程序的区域)进行烧写。在真正的工作中产品发布后,可以很方便的使用预留的通信接口(串口、USB、网口、蓝牙等)来完成程序的升级,从而避免了把机器拆开使用下载器烧写程序。要实现IAP功能一般要设计两部分代码,一是BootLoader程序,这部分程序存储在FLASH的某一位置,主要用来引导、升级App程序;二是App程序,这个程序才是实现产品的功能程序。通过BootLoader来完成对App程序的更新升级,这就是IAP功能。3 R: p2 l2 l8 l( s: I+ [$ e
2. STM32的启动模式5 P1 T5 A# `: c6 p* q$ q
很多初学者对于STM32的启动并不是很了解,这在《STM32的参考手册》以及网上各种资料里也有介绍,下面再简单介绍一下:
3 }( B- k6 O8 S' hSTM32有三种启动方式,主要是通过管脚BOOT0和BOOT1的连接方式来控制的,如下图所示,因为我们要让程序从主存储器启动,因而在硬件                设计时要选择第一种方式把BOOT0接到GND,BOOT1任意,可以拉高也可以拉低。
# B3 M6 {+ _6 ?2 ~) \' R0 \0 R8 a
* ~' Q) {6 ]  U1 B+ b, J% w& ?
c91314369df09c28031186c12525caaa.jpg 7 \3 r. j0 z: w2 ?; U
- ^! P% S  o; j' q) [
note: STM32上电启动并不是直接进入main函数,而是先进行系统初始化,这个函数的调用是在启动文件startup_stm32f10x_hd.s(因为我的单片机是STM32F103RCT6,大容量芯片所以是这个文件)中执行复位中断Reset_Handler时被调用的,执行完复位中断才会进入main函数。
# B% a( k. S" t0 M3.  STM32 FLASH的分布
/ O( n, T5 Y6 A: ~# G- bSTM32每种型号单片机的FLASH大小及地址分配在芯片手册里都有介绍,我使用的是STM32F103RCT6的型号其FLASH为256K属于大容量产品其        
7 e; ?2 O9 N2 T* ]- Q" FFLASH的分布如下图:主存储块的地址是从0x08000000到0x0803FFFF共256K
- ]& f9 `1 T' |/ B8 ~+ |0 R- j+ T$ I9 `$ ^* U. F
e994608d127eb7c447b6f6cd9e71ca5d.png
* F# Z% ]3 }( D% h. s+ T! G0 t6 U) A1 c4 b1 N- K

) l  H" I: m8 t$ a& L! N' U# }" y我们在设计程序时把FLASH分成3部分,第一部分从0x08000000到0x0800FFFF共64K来存放BootLoader程序,第二部分为0x08010000        
, V5 n9 b, ?( R  Q: L# I+ j% V到0x0802FFFF共128K来存放App程序,第三部分从0x08030000开始到0x803FFFF共64K来存放程序运行的标志位和其他,如下所示:
- x: x0 j. x# O; x  C8 j! c* r4 x
; \5 s% r5 K+ |) R, Y
b612c47828b9cd89d9b8ff01577eaae1.png
, @: H; V  c  u4 v

3 d2 G2 J* k1 V+ P3 F/ |$ h4. STM32程序的运行过程2 r1 ~  A0 I) F
STM32的程序运行过程在很多资料里也都有介绍,因为STM32F103的单片机是基于Cortex-M3核的,它的内部主要是通过中断向量表来响应各种中断,内部闪存的起始地址是0x08000000,中断向量表的起始地址是0x8000004,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,当中断来临时STM32 的内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行相应的中断服务程序。
2 ?' Z1 X1 [2 [3 V; t# L  ^
% o3 Q7 t7 @9 d, e, w
cf92af9939b87a2e8496b3be492ae39d.png
' T2 r! i  b1 K# b* u  j

0 W: m9 ^$ {; {. x! {. ^如上图所示STM32的正常启动流程是:
9 r: y" x8 W5 f3 Qa. STM32上电后会从 0x8000004 处取出复位中断向量的地址,并跳转执行复位中断服务程序,如标号1所示;, N6 K' w. `% G
b. 复位中断复位程序执行完成之后就会跳转到我们的main函数如标号2所示;
- Y( V1 b0 r. `9 {& H: gc. main函数一般为死循环,当其收到某一中断请求之后STM32会强制把PC指针指向中断向量表,如标号3所示;
7 G# Z5 \4 z# v( z% Nd. 查询中断向量表,根据中断源来跳转到相应的中断服务程序中执行响应的操作;如标号4、5所示;
3 A% F7 W* {. g- n. m5 ve. 执行完中断服务程序之后会再回到main函数中,如标号6所示。
  q9 g8 p  P' a# Z( X& n以上是STM32的正常运行过程,而当加入IAP程序之后,运行流程就如下所示:
! `. d1 y% y8 ^7 U3 X& `2 o( l) p0 Q' P$ P
ffa172a80f1e605cfb515751b8657634.png ( N1 [: |: w1 |/ `! Q

- @+ v! G/ _8 Y- O7 H加入IAP后程序运行如下:6 Z1 p; b- Z7 C$ I+ }
a. STM32复位之后还是从0x8000004处获取中断向量表的地址,并跳转执行复位中断服务程序,如标号1所示;
3 V2 ^# m; \; S. {5 v0 x9 qb. 执行完复位中断服务程序之后回调转到IAP的main函数中,如标号2所示;
1 j; C' K( Q3 |0 s8 rc. IAP的过程就是通过某种选定的通信方式(如串口)来接收程序文件,并且存储在指定的FLASH空间里,随后会加载新的程序,而新程序        
" q2 g6 W4 m0 h. Z2 `; x的复位中断向量起始地址为0X08000004+N+M,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转
* O( y9 g$ \1 p0 A至新程序的 main 函数,如标号3、4所示;
4 ^  d- x( p" l4 F+ h- T  _d. 此时在STM32的FLASH里面会有两个中断向量表,在新程序 main 函数执行的过程中,当中断来临时PC指针仍会回跳转至地址为
0 l# V- [# E& g/ w. y8 y0x8000004 中断向量表处,而并不是新程序的中断向量表,这是由STM32的硬件机制决定的,如标号5所示;
% r3 \3 w( v0 P* xe. 查询中断向量表,根据中断源来跳转到新的中断服务程序中执行响应的操作,如标号6所示;9 q. V: R( H' L. @
f. 执行完中断服务程序之后会再回到main函数中,如标号7、8所示。2 w' z( o6 h( Q! q5 T
note:
9 _! U  r/ s4 _: a由上可知新的程序在FLASH中必须放在IAP程序之后的某个地址里,这里我的程序中设置的是0x08010000 即偏移量为0x10000,而且新程序
, T2 I% ~+ O# q- O1 B/ E! `! Z的中断向量表也要做相应的偏移,偏移量也为0x10000 (地址的设置可以通过编译软件来实现,下文会有介绍)。
' i8 D/ s. ~  ~! g) w
* O) n  J" X% {; p! r5 y5. BootLoader程序的编写
& f0 O: {3 G( R$ z# Y   BootLoader程序主要的功能是接收新的程序并把它存储在FLASH的特定位置,然后加载新的程序运行。单片机每次上电都会先读取一个6 i4 a* E5 h. U
标志位,根据此标志位来决定是运行APP程序还是来运行自己来升级。
$ o$ j/ ?* i6 j1 y2 D3 W5 z- Y3 ?flag = STMFLASH_ReadHalfWord(FLASH_ADDR_UPDATE_FLAG); (FLASH_ADDR_UPDATE_FLAG 是0x08030000的地址)
+ W- Y6 o& \$ h" |$ {" A! }当flag = FLAG_TO_APP 则加载App程序,否则执行升级程序。9 a4 \' Z* L; u9 ^( K
在我的程序中通过串口来完成程序bin文件的传输,为了通信安全制定通信协议,串口接收分为两种:
6 ~3 o+ y3 W: Y9 Aa. 指令的接收,长度为16个字节,协议示例为: G* ?8 r+ B/ `) H, M
test[16] = {55, aa, 01, 指令长度,命令码,00,00,...00, 和校验位}
( r" o# Y  r/ `& n3 y3 T和校验位 = 0 - 前15字节的和,. b8 X5 {+ H) a9 m+ X7 `
b. 程序文件的接收,每包数据为(2048 + 6)个字节,示例为:
# ]7 S* h0 ~9 S2 E! W) htest[2054] = {55, aa, 01, 包号,命令码,数据文件2048字节,和校验位}% X' F& E* j1 ^% x7 E5 ~3 ?% ~
       之所以设置以上的通信协议就是为了保证数据传输的正确性。& M$ y4 K6 x: a, y5 o
6 `3 Y4 R$ o8 a! K" ^/ [
Boot程序的主函数
/ t( I! V; u1 F2 Z# s2 UBoot程序的main函数里主要是读取标志位flag根据flag的值来决定是加载现有的App程序还是运行自身的升级程序,在自身运行时会定时给上位机软件发送BOOT准备完成的指令,告诉上位机我准备好了,并运行ReceiveUsartData();根据串口中断里的标志信息来完成对指令和程序文件的接收。
* \6 I. P- q5 t2 [4 P4 C. c
  1. int main(void)7 @0 f9 Q* h1 A. A  f/ I
  2. {; z* }& e( z) z% [+ M4 v* K$ I
  3.   int flag = 3;0 [3 m5 {$ [/ ?" M8 G
  4.   int nCount = 0;* d9 b: B- s$ w7 ]2 E* O
  5.   delay_init();  ( e, u! s. O% x: Y. _
  6.   uart_init(115200);
    % @  s$ V2 L- |; B) O
  7.   LED_Init();5 Y8 w+ i! J7 U" B9 e
  8.   TIM3_Init(99, 719);  //10ms定时7 h2 ?6 \$ ?) c; I
  9.   flag = STMFLASH_ReadHalfWord(FLASH_ADDR_UPDATE_FLAG);  //读取标志位
    + k5 b. o2 W! S/ M9 Q9 W5 v
  10.   while(1)( G  ~+ \  s; s; i1 h
  11.   {        
    $ L; k' \5 J' C/ d$ ~
  12.     //FLASH_EraseAllPages();  //仅在擦除所有FLASH时打开
    1 c. N+ @# t" }# i4 Y
  13.     if(flag == FLAG_TO_APP)
    . y- d( R7 [8 Y4 b/ n. p9 `
  14.     {8 U4 `7 D) [# E' ~8 {
  15.         Iap_Load_App(FLASH_ADDR_APP);/ d$ G! I( L/ w6 P" s3 q1 v% j
  16.     }        
    ( Q( F6 h' z0 D, o  l0 {' F* X
  17.     else
    ' x# N0 T7 C' r. H/ ~. L; b& l5 X
  18.     {      & W2 n5 A% I* |
  19.         ReceiveUsartData();   //串口接收
      n# m9 j8 ?, l8 ^9 ^4 _
  20.         if(Flag10MS == 1)  ; L' h% M* D" Y2 t
  21.         {         2 E! F7 I2 f2 v- o4 C/ ?
  22.             Flag10MS = 0;
    1 w' f6 a) h; ^5 @
  23.             nCount++;
    / U* c+ j5 F+ j' x
  24.             if(nCount == 10)  //100ms
    $ B2 B/ `- w# w) p& M7 K
  25.             {7 C8 q4 w+ C/ Q6 |& H4 [" h2 p
  26.                 nCount = 0;3 r5 i& w& V9 ~. |, ^8 e
  27.                 USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_PREPAR_BOOT_OK); //不能发送过快否则会有脏数据
    2 g4 [& |, Q1 R8 t
  28.                 LED0 = !LED0;) n( w- T9 P8 _; j
  29.             }                           9 y9 v3 ~( A% k" n
  30.         }' T3 b, a1 s# h: z) l
  31.     }& u# `4 a  X. g5 P4 S; S
  32.   }   9 A6 p0 t/ d: i) l5 m, e
  33. }
复制代码
( l/ @, i0 q, C) q7 b( t
串口初始化程序
6 u" Q$ N2 Z- C! U$ ]1 z  使用STM32的USART1,设置波特率为115200、8位数据长度、1个停止位、无校验位,        
8 f6 s% U: q7 ]% R% l" d5 o  具体实现见源码的uart_init()函数。
: n: T( U6 y) _5 B2 `2 [. E

0 Y+ t: _" N( {/ l: H) K; G串口中断服务程序 void USART1_IRQHandler(void)9 J& r9 ~7 }: m' D
在串口中断服务程序中主要是接收上位机升级软件发过来的数据,当UpdateFlag置1时为接收bin程序文件的数据,当UsartRxCodeCount        * o6 O. D  g) ?
  的计数等于每包传输的总字节数USART_RECEIVE_CODE_DATA_SIZE时,置接收完成标志位UsartReceiveFlag = 1并且置NextPageFlag = 1
/ }( J; y* h' H% E" Y) T  ~  跳出中断去ReceiveUsartData()处理,把接收到的数据存储在FLASH的指定位置,不断循环直到文件全部接收完成。升级指令的接收方法
# s+ Q/ S( o0 _  相同,详见代码。2 I! c) _  Y- Z( z/ ^( X6 C
  (note:在中断服务函数里,尽量不要做其他的操作,只设定标志位,具体的操作去外面的函数执行。)* d* s0 R1 s  X
$ T: ~: Y8 ^* t; u
重新加载代码的程序
& p$ q( z& h, m5 l$ V为了实现Boot和App程序之间跳转,则必须在升级完成之后重新加载新的程序文件,其中涉及到在C语言里内嵌汇编语言,代码如下:% [+ s& t, U3 v& F# w6 S" |
  1. void MSR_MSP(u32 addr); @1 q1 p0 v7 K3 o1 j
  2. {
    ! [/ O+ Z* l: y% q# V9 |
  3.     //asm("MSR MSP, r0");  //使用Keil内嵌汇编时使用这两句7 v; j- A/ v  J9 r% O+ T
  4.     //asm("BX r14");& \7 |) F# x) `; T7 K0 `0 r
  5.   __ASM("msr msp, r0");  //set Main Stack value 将主堆栈地址保存到MSP寄存器(R13)中# s# q( @8 C4 F1 {6 Y9 _
  6.   __ASM("bx lr"); //跳转到lr中存放的地址处。bx是强制跳转指令 lr是连接寄存器,是STM32单片机的R14
    # i$ r( q* l1 t" ~* Z5 A. R* Q
  7. }
    * R9 A& }! F: S4 v- V
  8. % D1 c+ a- Y: q1 k8 q/ L8 i
  9. ( _3 s$ ?' V  A! E8 m$ m
  10. typedef  void (*IapFun)(void);                                //定义一个函数类型的参数1 S& _( F% A# H
  11. IapFun JumpToApp;
    ' H: p& R: {: @3 H& w: U
  12. 7 W$ ?! V, }6 ~+ B

  13. 6 _5 c& Z4 m: n2 R
  14. //跳转到应用程序 AppAddr:用户代码起始地址.
    ' @& g3 D  a: G' i( l: J
  15. void Iap_Load_App(u32 AppAddr)8 m2 Q% _2 q1 n" Z  D# a( }2 J* S# `
  16. {2 }, ]. g% Q& L" R9 {
  17.         if(((*(vu32*)AppAddr)&0x2FFE0000)==0x20000000)        //检查栈顶地址是否合法.
    # w; O7 l% [" N* h0 N& [+ w
  18.         {% g* W5 ^3 I5 ^9 Q: I
  19.                 JumpToApp = (IapFun)*(vu32*)(AppAddr+4); //用户代码区第二个字为程序开始地址(新程序复位地址)               0 p. r. E( A- `0 A  H4 A7 s
  20.                 MSR_MSP(*(vu32*)AppAddr);                 //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)5 _" c, H0 T% V0 N* s
  21.                 JumpToApp();        //设置PC指针为新程序复位中断函数的地址,往下执行( |4 E& R" `! n) E
  22.         }
    3 |& N' [! |2 H4 D) ?" x5 X
  23. }
复制代码

( s& j; }8 ~: e% j/ @' F6 i首先 if(((*(vu32*)AppAddr)&0x2FFE0000)==0x20000000) 的作用是检查栈顶地址是否合法,(*(vu32*)AppAddr)是去除用户程序首地2 Q0 P+ [# L7 O4 N8 M
址里面的数据,而这个数据就是用户代码的堆栈地址,而堆栈地址指向RAM,RAM的起始地址是0x20000000,因此可以用上免得语句判断用
. h6 C0 y9 y9 T2 `3 Q户的堆栈地址是否合法。
# q& Y9 l% P/ e4 x当判断栈顶地址合法之后取出新的复位中断函数的地址即(vu32*)(AppAddr+4),并把它赋值给函数指针JumpToApp,然后调用* f* [; F2 z1 c% c2 ?& _+ c
MSR_MSP()函数把主堆栈指针赋值给MSP寄存器,最后调用JumpToApp();来执行新的程序。
9 C4 s0 Z" G7 @; S* c9 n3 G   (这里涉及到函数指针的知识,一定要理解函数名本身就是该函数的入口地址,它的实质是一个地址。)5 C4 I* X# \4 a( f& m* P; }
上面涉及到嵌入汇编的知识,可能讲解不是很透彻感兴趣的朋友可以参考《Cortex-M3 权威指南》获取更多的了解。
. O1 f! N3 e  I* P% g( o% @* q, h0 F+ _3 ], \9 Y
5 I2 {4 C: x, w
中断向量表的设置和起始地址的设置(IAR软件)2 [+ G! y) w) u- ]' n+ L
  在IAR软件中设置程序的中断向量表和程序的入口地址的方法如下:
3 X+ l0 L+ L! `" i4 t( F3 d6 \1. 打开工程,在工程名STM32_BOOT_v1.0上右键--Options
% R7 l: J$ ?5 |& S# J" h# J
4 J6 J1 r: H- U5 H' k  P

0 n7 ~  w( X, [3 P* A8 |8 x$ g2 Y  o f7780a1410547390d965847b3396e395.png
# j* s4 p5 V/ s1 J7 R$ f( j: k' c8 R4 y& W
2. 选择Linker--Edit., \. q. p9 k4 X, {% a4 Q4 i

7 D, r* ~7 O* E% {: w: Z

$ @1 }0 {1 v7 [! U1 g' V8 o e570294b5c36604e5b7211f09c51627b.png " L% l4 i3 n) ]+ E

; G0 `" X- j2 ~" N, }
3. 设置中断向量表的地址Vector Table 和 Memory Regions的值
" |! L4 e7 c8 H9 \5 J' d0 ^
% a3 c+ ]  W+ U9 M+ |
dddd97fd325b534ab13a4d72d0e2ef5b.png
8 b/ n% P4 R$ w/ a) F' p9 x! w: M% ]: V; [( `) @
c08f9e99b255c19732ee02639171297c.png 1 a! D1 |) e3 t8 u
5 Q9 [: O2 y  {4 u! h7 }/ L
6. App程序的编写0 V) l) F! b7 q7 q/ \- `" v7 Y2 K
App程序相对简单,它主要包含两部分,一是程序要实现的主体功能(比如点亮LED),主要是你想让App做什么就实现什么;二是通过串口来查询升级指令,当收到升级的命令后要在FLASH_ADDR_UPDATE_FLAG 的地址处写入FLAG_TO_BOOT的标志位,并且调用Iap_Load_App()l加载运行BootLoader的程序来完成升级,详细请看源码。; T/ B% D. t7 l9 u9 X
对于App程序的要设置其中断向量表的偏移通过语句 SCB->VTOR = FLASH_BASE | FLASH_VTOR_OFFSET;来实现,FLASH_VTOR_OFFSET这个变量在程序中是 #define FLASH_VTOR_OFFSET  ((uint32_t)0x10000) 因为我们的App程序存储地址是0x08010000相对于0x08000000来说偏移量即为0x10000,而且在程序编译时要设置Vector Table 和 Memory Regions的值为0x080100003 t+ J8 C, K* v- K) V( O
+ v3 i4 O  Y& M) H2 L7 W7 R
6 L9 v8 f9 N0 {# K; n% d
16e51abf27f1279032edf154a52096bc.png
' e2 D, ^  d6 F
5 m3 k4 _# N% P. Z' S, j$ d0 L1 }8 t

' W$ M" j( b3 S; i7 c# j a5410d1faca9e9cd2db303a07c6cee05.png ) t/ z" |) a: ~+ r9 t! R# g2 r

9 @- E* x6 R3 M, c4 h2 e0 j7. bin文件的转换
) @6 m7 I* @# F升级程序时编译出的程序文件最好选用bin格式的文件,因为bin文件比hex文件要小的多从而占用的FLASH更小,这是比较主观的优点,使用IAR软件编译时可以通过对软件的设置来输出bin格式的可执行文件,设置如下:, h. b8 b' e  `" q" ^9 H* ]
a. 打开工程的Options选项卡选择选择Output Converter4 ~8 _! u, p! g( }# d' H
9 N7 v- _5 W9 T: q, h7 C% X9 v
7bec9e364c1e4ab9c4a99046650f577a.png " e/ Q4 S3 I* Y' [. L/ E

7 x5 M* X( ?, j2 [
$ z$ m+ _. j6 V% ~
    b. 在Output format选项中选择 binary格式,同时把Override default输出文件的后缀改为.bin,这样在相应的工程目录下(我的是
& `9 b" ^1 u* ]3 X9 q8 a       STM32_App\Project\EWARM5\Debug\Exe) 路径下就可以找到编译输出的bin格式的可执行文件了。
5 C2 A- u2 q: d8 `1 {+ ]" s. r8 l& s, f, f" H
8. 上位机升级软件的简介
/ n: c) p) V! Y/ u* Z   我的上位机升级软件是使用C++写的,具体编码不做介绍了,想了解的朋友可以参考源码。对话框界面如下:" g* F9 T5 R" k0 i. P) I9 Y
- i& S* ~% a3 K2 |, h6 l1 _
149f9018cc08565701d07d714d85b25c.png
$ d" w) U+ X/ V6 [1 n

/ E5 H7 \* n3 m8 g/ R) f9 j1 y" a首先设置端口号和波特率,然后连接串口,连接成功之后,点击“选择要升级的文件”来实现升级。
% _3 Q/ l6 V  V2 y& t1 W3 ], L
$ Z! J( l4 u. ~8 |! X8 ~
0505ab7d9ecac3f9bb14e16b5d1f2819.png + u/ E9 d3 c% l0 V, W( Z

5 M/ t4 i3 y9 ?  \* c升级完成之后会提示“升级完成”。( D% K2 r8 v4 z
* k$ y( A2 N. V7 ~6 `) R- u" ^
4f7116831ef38cf720a643ed9f7d3f2e.png
8 J, \; W7 i+ W8 }4 x

& Q: g2 ^- h" w     到此我的IAP实现介绍完成,如果大家有什么问题或者我的程序中大家发现了什么bug可以提出来一起探讨,希望以上内容会对大家学习STM32有所帮助。. K+ v5 R. I8 l- N
————————————————
2 n+ W' W$ ~  N+ g, a1 u* g版权声明:被大佬糊弄的只会点灯的小菜鸡& ]/ a$ A/ p6 m1 G- T
如有侵权请联系删除
2 T0 S( g  ^: {0 g, f) F4 ]( f. Y5 f7 U
0 Q; C$ M7 U9 g% ?' Q

1 E5 v1 m, I5 K8 Y: G5 i( n. Z  P' k8 b4 s2 C3 v
赞 收藏 评论0 发布时间:2023-3-26 18:50

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版