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

基于STM32F1的CAN通信之之串口IAP

[复制链接]
攻城狮Melo 发布时间:2023-10-23 22:37
一、串口IAP简介
: {$ m' \- {* P1.1 什么是IAP

0 k  P# K  s! O- a: y6 _+ mIAP,英文全称In Application Programming,在应用中编程。很好理解,就是在程序运行过程中我们进行程序的烧写,或者叫升级。, M* D' c8 l1 }& V+ e

9 s. k- Q/ }$ a$ X1.2 STM32下载程序2 {4 v; u6 f6 F$ p4 _
我们都知道,STM32可以利用串口下载程序,这是因为ST公司在产线上就在产品中内嵌了自举程序。所谓的自举程序,实际就是支持我们通过串口下载程序的代码。自举程序被存放在系统存储区,因此如果我们需要通过串口下载程序,需要将Boot0接高电平,Boot1接低电平,让程序从系统存储器开始运行,运行自举程序。下载完成后我们再将Boot0接地,让程序从主闪存存储器开始运行。自举程序是我们用户无法修改的。! N; L2 v+ o$ M. ^+ f! j6 R

7 L* Z8 v9 ^. v6 ]  n& Q0 Y
; [' g% {3 V8 }' B7 E
二、串口IAP有什么作用
" D1 N# |  _* N+ L. v上面我们介绍了什么是IAP,那么这个IAP到底有什么作用呢?
, `7 V  w# V" J: @5 q4 W5 Y. E, y
. G5 [& w9 [6 @7 F首先介绍两个词——Bootloader和Application。Bootloader实际就是一段引导程序,单片机上电后先执行Bootloader程序,然后再执行用户编写的应用程序Application。介绍完这两个词,我们来介绍一下IAP有什么作用。比如我们生产了A,B两款产品。A产品是某个精密器件的一部分,B产品是一款物联网产品。我们的产品销售范围很广,远销海外。
" v$ q+ J: V. K  \' J" |1 |( x
0 R$ h) G  X7 n/ u- I8 u3 N7 j3 Y% g某天A产品的某个客户反映了一个Bug,我们编写好了程序,需要进行程序更新,或者叫升级。利用IAP,我们可以在程序运行时,通过预留的通信接口直接烧写程序。而不需要再把整个设备拆开,像我们调试时那样下载程序。甚至我们可以直接给客户邮寄一个小设备,客户直接进行傻瓜式升级。
6 \1 n% s2 `- f/ j' j

2 h# X1 S3 u3 V' ]又过了一段时间,B产品的程序出现了一个Bug,风险等级比较低,但是依旧需要全体升级程序。我们总不能挨个产品派人去升级,成本极大。这时候又轮到我们的IAP出场了。它可以在所有设备在线运行的情况下,直接通过网络下发升级程序,实现在线升级,节约了大量的人力成本。9 |# G5 T; ?) g" j% F% R
5 i) D! f) P- ]' Y
通过上面这两个例子,大家应该能够基本了解IAP的用处,使用IAP让我们不需要再使用调试器进行下载,甚至实现设备的在线升级。
, O& u+ J7 Y6 f2 ~7 |/ E7 q  t) ~* r; m; T8 j, ~: w+ h
3 }& d/ {) _3 o: r
三、启动流程" L# _0 F7 }2 v* c& p: n
在介绍如何实现IAP之前,我们先来简单了解以下STM32的启动流程。: ^' ~7 F" K( k# l' E
* Z& F  [9 Q' ~& x4 ]7 O( Y) r- L
3.1 正常启动流程; Y% W* }3 I1 o6 C( c7 d+ H
这里的正常启动流程指的是,没有添加IAP的流程。$ X* ?7 g8 h6 y

- x5 s  B0 H: ?6 M" @
4 \% s( P' w  ?4 K4 w" t
微信图片_20231023223616.png

: R& R  Z& v+ c; Y( ~
正常启动流程
% ~7 E9 @  t( i. f$ W' {. ~

' y" {. C- E- Y0 N/ i' L+ t8 O! s
程序启动时首先开辟栈空间,配置栈顶指针。然后配置堆空间。配置完成后,建立中断向量表,在中断向量表中找到复位中断,开始执行复位中断服务函数,然后跳转到main函数中,执行用户代码。当用户代码中有中断请求时,会回到中断向量表,根据中断源执行相应的中断服务函数。
: J* J/ d/ B8 {" [- ]/ i3 C
' q( ]/ g  w% v4 B3.2 加入IAP后的启动流程
: k; [& C$ E5 _4 e. V下面是加入IAP之后的启动流程。
0 S( N8 r6 ^% T( |7 G" O4 ?3 r; i( h4 A
微信图片_20231023223619.png

4 r/ t+ ~) t, y7 I9 E8 P5 H7 ?
加入IAP启动流程

6 y! B7 o1 u- ]# `
6 D: U! j, m7 F* ]1 m3 S; [
/ G7 H1 L& Z9 K, Z" X& [( _9 Y
可以看到,与上面不同的是,加入IAP后,执行完复位中断服务函数后直接进入IAP的main函数。在执行完IAP之后,跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main 函数。
1 A, [; Z: {: s: ?+ y; S) N# J
# q* ]* m* D/ u  n8 j2 X
由上面的两个启动过程我们可以看出$ ]. _: ~2 E% c, M7 f2 D& k
• 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始。- B* @0 I  O* A7 O7 F' X
• 必须将新程序的中断向量表相应的移动,移动的偏移量为 x。$ C$ `$ s+ B# Z' E# a( E

4 P! ^+ M6 v7 w" X3 w& P
/ B3 ~& T# f2 w+ O7 i+ ^* x
四、必备知识, e6 J0 g$ Q% X0 m' s
44.1 修改程序运行起始地址

  V) y9 C5 I, X9 b$ u+ Z3 a点击魔术棒,选择“Target”,修改运行起始地址和代码大小。3 `; \8 }( V2 W8 s4 {, S* T: F% [3 s- t
5 \5 V1 `8 F( g, N% t, `3 h
微信图片_20231023223624.png
1 ]) x' }. h2 n' p
修改App运行起始地址
' b, ^+ V: @' g; \7 h
( A  [, X) p' Q; }8 E: x
! W+ u! v' D, _' U+ u
4.2 设置中断向量表偏移
% g- A" @+ ~5 k3 n# _7 m, v5 oVTOR 寄存器存放的是中断向量表的起始地址。如果要设置中断向量表偏移,只需要在main函数最开始添加如下语句即可% h! ~. T# k7 I: ]; t# {2 e' d
  1. SCB->VTOR = FLASH BASE | 偏移量:
复制代码

( w$ v8 g8 e( b' u4.3 生成.bin文件
! {3 _" }, H- x点击魔术棒,选择“User”,按照如下配置,输入下面的内容7 i; ]7 A5 m! t" r. d5 v1 i7 K
  1. fromelf --bin -o "$L@L.bin" "#L"
复制代码

. I" E9 ^# p& s8 z- R
微信图片_20231023223706.png
  W% J  E" A7 V3 ]( Q' H0 g- e# B. C
生成.bin文件配置

4 N: w" Q+ S% Y  r7 Z9 c
9 [5 m0 S% J) X; g& g$ G
) E4 D7 B0 C4 W% K; R$ X, Q
点击编译,不报错就可以,去设置的输出文件夹中就可以找到对应的.bin文件。8 |7 K5 d; b% X

2 H( O& h/ f5 W7 @4 Y" D/ @
微信图片_20231023223710.png
: r# k) }! g9 v2 I3 `0 G
编译提示
- b7 h& r! w' n- ^& c/ m1 k: ~
) n$ J3 x/ O/ O: c  u$ F% @

2 |9 z$ F% S5 A& q6 Y1 e, c
五、串口IAP实现
' ]) F2 \! U, O1 `# n+ E% i本次的目标是实现一个串口IAP,也就是编写Bootloader,在程序运行过程中实现程序的下载。Bootloader程序应该可以通过串口接收上位机发来的.bin文件(App程序),检查后将.bin文件写入到Flash特定位置,然后跳转到App程序运行。
6 g7 {( P5 w6 H# T2 M3 f0 P$ v/ p  _; S; M$ h& ]
5.1 串口中断服务函数+ f2 d6 r4 h  ~8 f2 G; P
本次的串口中断服务函数与之前不同,这里单独贴出来。需要定义一个接收数组,接收数组的起始地址限制为0X20001000。接收数组最多可以接收55K字节,可以根据需要调整。但是需要注意的是,数组的大小需要比App程序要大,而且不能超过芯片的SRAM空间大小。
4 z9 S( ]- M' h; P" j* m8 u
  1. /*
    4 g/ c( h; i5 t; t* @% N
  2. *==============================================================================; r- }4 d+ W8 z  v1 C
  3. *函数名称:USART1_IRQHandler
    2 N' D0 j0 e" j* X$ @
  4. *函数功能:USART1中断服务函数
    2 Y# K4 |: @! W1 U3 f
  5. *输入参数:无
      _0 [3 f! J8 E
  6. *返回值:无
    9 [) c& f3 e9 q6 q0 X
  7. *备  注:无& @6 K& o  O' C/ u) K
  8. *==============================================================================6 v8 [( G/ z: U3 d: E8 P% n
  9. */
    0 U- u/ W$ g+ q1 H, J8 H/ B* l
  10. u32 gReceCount = 0;   // 接收计数变量# d1 H. b& z. r5 ]3 @+ U! q! m
  11. // 接收数组$ ]8 `0 H) N  x& S
  12. // 限制起始地址为0X20001000
    3 b: `8 v% R& K5 j1 M. `9 T0 F2 v
  13. // 保证偏移量为 0X200的倍数7 u5 T( c: j& g  I9 I6 r
  14. // 是为了给App留SRAM空间
    8 ^; x& ?5 R" O% m# S  J
  15. u8 gReceFifo[USART_RECE_MAX_LEN]__attribute__ ((at(0X20001000)));/ K$ g$ f$ p- |) G$ }7 G7 `
  16. ) s0 A" \8 `* {: y! Q
  17. void USART1_IRQHandler(void)  & B; t& [6 v7 B
  18. {
    ! n8 t; |/ g6 V! \
  19.     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   //接收到一个字节  8 \& T( R8 x  S/ W7 P
  20.     {4 k1 x) d1 E+ z1 y' t6 T5 k
  21.         if (gReceCount < USART_RECE_MAX_LEN)5 f; \, r# C! N4 J. P% x" Q
  22.         {  Q5 b7 h# Y5 y, t0 _
  23.             gReceFifo[gReceCount++] = USART1->DR;4 J9 b5 K5 M, c, ?) }6 U# Y. r
  24.         }& o% P: A( t  z
  25.         else* D4 I  q1 B' l+ @# q5 k
  26.         {' v( u% s4 P$ \) E/ z: U% A1 j
  27.             printf ("APP code out of memory!\r\n");
    % `0 ?+ P, u, p5 Y  [
  28.         }; i2 ~3 q; U+ T$ Z: J; C0 p/ {
  29.     }" X/ B3 @% s: [
  30. }
复制代码

! |# D' X+ C0 I$ X- N( I5.2 Flash写入程序& J3 a6 H' \. h1 Y; L; Z
关于Flash程序,这里就不在赘述,只是贴一下带检查的写入程序。其他具体内容可以到博主STM32速成笔记专栏查看。
  Y+ [- u6 E2 P# v' x- s
  1. /*
    0 {8 [2 c+ Y' y% r# X
  2. *==============================================================================3 I' K! P) `: o. {" b+ K* F8 ^
  3. *函数名称:Med_Flash_Write
    ; f# A, C; i( m: F3 [
  4. *函数功能:从指定地址开始写入指定长度的数据8 P, E& u/ {: T
  5. *输入参数:WriteAddr:写入起始地址;pBuffer:数据指针;7 @" d. {; [+ ^/ x9 O: b
  6.                         NumToRead:写入(半字)数
    : [* z7 {: a- V
  7. *返回值:无
    ! D0 M) b$ B4 E: I2 G) x0 a
  8. *备  注:对内部Flash的操作是以半字为单位,所以读写地址必须是2的倍数2 v# E( r; b9 z
  9. *==============================================================================! R9 |/ \, l: C+ A) r; Z  \. F
  10. */5 L' _" B) p# _/ b* a" }
  11. / t; \% f7 ^: r
  12. // 根据中文参考手册,大容量产品的每一页是2K字节/ L: t' l, Y9 ^' Y! a+ }4 k9 m9 w
  13. #if STM32_FLASH_SIZE < 256
    3 Q3 {- k; Z6 Q9 U! R
  14.     #define STM32_SECTOR_SIZE   1024   // 字节
    7 |4 ]" m& _6 Y4 t+ J% g, l
  15. #else
    - w: W0 m$ r+ A2 H& K2 v
  16.     #define STM32_SECTOR_SIZE   2048
    . o! U1 b  [7 `3 y4 B" G, }5 @( J
  17. #endif
    0 N1 ^9 ]6 E9 ?( J

  18. 3 v2 d2 U) H# n8 X: A( r% k# g$ o
  19. // 一个扇区的内存) \) f8 b: H7 {4 e4 G
  20. u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];
    - D! p; m, F. h  n$ j+ ~9 x
  21. / S7 E, r( z3 V0 i4 h- u7 A
  22. void Med_Flash_Write (u32 WriteAddr,u16 *pBuffer,u16 NumToWrite): h6 |. x, [0 y5 q) s: _3 U# L
  23. {6 j/ o! O& Z( y1 @
  24.     u32 secpos;   // 扇区地址
    ! w( U+ U2 ~8 @6 N1 M* t
  25.     u16 secoff;   // 扇区内偏移地址(16位字计算)( u" O8 y  T# V8 v( X% x
  26.     u16 secremain;   // 扇区内剩余地址(16位计算)   
    1 W! S2 L; O0 |5 A# N
  27.      u16 i;   
    - C5 }+ L: U" w$ l" `* J/ D6 n' @  O& H
  28.     u32 offaddr;   // 去掉0X08000000后的地址. L( s& `6 z; ]4 G' K4 P4 O
  29.    
    2 j' k8 ]: \/ A- s) ?# X) e
  30.     // 判断写入地址是否在合法范围内' p% E  V4 T+ C. f' D' s' f8 A6 H
  31.     if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
    9 ~" e# i5 y9 |, ~& }# L$ }3 M
  32.     {5 M( `) w4 S* g7 ^
  33.         return;   // 非法地址" Y3 U$ N" l7 h
  34.     }5 }! i: D3 ]- u! `
  35.     9 S% Z4 U5 P# b1 ]
  36.     FLASH_Unlock();   // 解锁' f) X: b) Z+ `# }# e( `7 M
  37.     offaddr = WriteAddr - STM32_FLASH_BASE;   // 实际偏移地址. y' x4 C; T6 u+ J- F9 }
  38.     secpos = offaddr / STM32_SECTOR_SIZE;   // 扇区地址
    7 Y7 E! a) [& @* k
  39.     secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 在扇区内的偏移(2个字节为基本单位)" I# d; `  A# j6 u+ D9 d. y! B- O
  40.     secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇区剩余空间大小
    ! r$ C3 N1 W( f' G9 V9 T1 x
  41.    
    . u$ P# G# @; J, M+ l# b' c9 ~
  42.     if (NumToWrite <= secremain)) r, H: v# c2 C4 B. L! c
  43.     {, o; N5 y8 Y5 e# N+ }9 C9 n$ U
  44.         secremain = NumToWrite;   // 不大于该扇区范围+ R: n. Z0 m) H* K
  45.     }) f  t$ T) e) ?$ W
  46.    
    7 b: p) T- X$ @$ b' G
  47.     while (1)
    2 b& W+ b" R4 C/ h+ Z: e
  48.     {
    # X8 x( X: a$ w
  49.         // 读出整个扇区的内容
    : U  B% w& V3 ~# ]0 I
  50.         Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);0 X" H2 t/ d9 U6 V0 Y9 G8 `6 k
  51.         
    ! ~, m9 e2 t' F9 R# l# t
  52.         // 校验数据+ k' s% U6 U6 H! r  o( u" ^6 {
  53.         for (i = 0;i < secremain;i ++)
    " t& X' \( z5 S5 M% B- ~- Y, f
  54.         {
    + k0 U7 Y$ a, n2 o& O9 H
  55.             // 需要擦除
    1 y( ^) J3 d' n3 k7 g
  56.             if (STM32_FLASH_BUF[secoff + i] != 0XFFFF)0 D; L; I! y( g; n
  57.             {0 F6 t7 U7 f( I
  58.                 break;
    % G& f; t7 l) ]
  59.             }    / }  p! o9 d2 R6 u" |
  60.         }5 u8 S" D8 Q; J/ i
  61.         // 需要擦除
    ) l" i3 v! n4 M5 Y( q' c" t+ _
  62.         if (i < secremain)1 a' e! L& i$ r. b1 T9 }
  63.         {) s! \2 I7 L0 h6 l6 ]) O$ \# y
  64.             FLASH_ErasePage(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE);   // 擦除这个扇区: V/ q4 a" t' }+ y9 j; c
  65.             " i) K5 Z  t4 A
  66.             // 复制
    ' q- c& w( T- ^. e) T& S& O9 S8 K
  67.             for (i = 0;i < secremain;i ++)
    1 D$ r+ ^( l' l/ O: N: @+ |* L8 O; F
  68.             {0 h2 U4 j0 P7 h  E) M2 m7 E: [( D' E
  69.                 STM32_FLASH_BUF[i + secoff] = pBuffer[i];   
    # C8 m; P, L0 y- B0 E6 F
  70.             }  j. P$ i2 F* W2 N1 Z7 T+ Z
  71.             
    8 L$ q; I* @/ e, T5 i! W) b6 s
  72.             // 写入整个扇区7 ^# J1 \0 j0 h. v1 B
  73.             Med_Flash_Write_NoCheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
    * a5 r1 S% m) _! }9 y, Y
  74.         }, K. C1 u6 u+ i3 f! n
  75.         else1 R8 p& E: }1 i, n. M7 Z7 Z
  76.         {
      w- g6 [0 `! {
  77.             // 写已经擦除了的,直接写入扇区剩余区间
    6 B, u$ ?- ]. o0 S% V, L' O" j
  78.             Med_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);  A- y1 n2 @! U( r
  79.         }
    1 ~  E4 g0 p! V  D$ y
  80.         
    6 ?. B5 H. Y3 `% w; h' y
  81.         if (NumToWrite == secremain)
    ! a" t/ ^7 o5 y# I1 \" C
  82.         {  e9 s, |7 J3 d, u1 S1 A2 ~
  83.             break;   // 写入结束了* X; J( f' A. D! s% e
  84.         }% e+ l7 x& E6 `) s& ?/ R2 x
  85.         // 写入未结束. l5 _. _& K7 j2 F5 Y! G% G
  86.         else
    3 Q- R3 x+ g0 Q1 P5 ~
  87.         {
    : H; @% d0 b% E+ D/ e
  88.             secpos ++;   // 扇区地址增1
    9 h% ]. w" S3 n  u8 b6 Z' T- w
  89.             secoff = 0;   // 偏移位置为0   
    4 F# j, f, w# L; M) H' h' O
  90.             pBuffer += secremain;   // 指针偏移
    * [9 ]7 E7 M* v5 m! g6 u3 X
  91.             WriteAddr += secremain;   // 写地址偏移   
    0 R- s- r, }% Y1 u% }
  92.             NumToWrite -= secremain;   // 字节(16位)数递减
    3 Q" j4 k8 Q3 V8 x- z: ^
  93.             if (NumToWrite > (STM32_SECTOR_SIZE / 2))  o# [/ `$ \. `  J
  94.             {
    6 p* _2 Z$ Y: s2 W
  95.                 secremain = STM32_SECTOR_SIZE / 2;   // 下一个扇区还是写不完
    $ n; A1 U, s, y1 Z& Y- o, I8 p
  96.             }
    & x; U+ V3 e3 M5 B- U  C: p# R, c) |) v( U
  97.             else
    - X7 y) i' J4 f# u( Y5 y4 E1 ~/ d+ v% _
  98.             {
    9 s3 |9 p4 F6 {9 E4 }7 o
  99.                 secremain = NumToWrite;   // 下一个扇区可以写完了3 d9 t: ^# a/ a5 _
  100.             }
    $ \4 t" ?1 ^7 j$ }
  101.         }  * R6 y2 _  n3 r/ `: r9 ^3 N
  102.     }
    + e. C, p9 x$ {# C/ y7 n3 K
  103.     FLASH_Lock();   // 上锁
    - o! k5 |  H- n4 G7 {! O+ S; v1 R( E9 @
  104. }
复制代码
' ~3 V& A# m: h0 Z2 `$ f
5.3 IAP程序" f( l; P: I) S1 T
IAP程序包含两部分,一部分是将接收到的.bin文件写入Flash,另一部分是跳转到App执行。4 Z; @' ]# U3 u$ K0 A& m3 N
  1. /*7 X# e' N" l) i2 u
  2. *==============================================================================
    7 \& n9 c- D0 D0 f0 T" S, r5 B
  3. *函数名称:iap_write_appbin
    , ]9 h+ n5 x4 W( L/ i
  4. *函数功能:写入.bin文件& {5 L& o2 U7 k! I: J- r5 H) ^4 A9 H
  5. *输入参数:appxaddr:App程序起始地址;appbuf:缓存App程序的数组;
    ' S' [9 w: ^4 C$ h+ T: V% _9 [
  6.                         appsize:App程序大小
    0 J5 I" N" `& g/ {2 R7 B: Y
  7. *返回值:无# u! i. g8 [1 |$ V5 J- j
  8. *备  注:无( e7 x! L+ y/ _9 e( h. A; e
  9. *==============================================================================
    9 E8 \7 h; g4 |% q' s
  10. */
    ) O) i8 z# C& D/ _
  11. // 对Flash操作的最小单位是16位& J1 _2 k2 s) e
  12. u16 iapbuf[1024];   // 要写入Flash的内容2 ^# L; T( H9 _5 J" ?2 \" s: c" N0 I

  13. : f$ D- \8 {- X* d
  14. void iap_write_appbin (u32 appxaddr,u8 *appbuf,u32 appsize)
    ; D: D8 T& U0 i! y( s
  15. {/ B3 b9 d' D9 p$ k% b$ ]$ \
  16.     u16 t;   // 临时循环变量6 J- k0 J. {/ c5 a
  17.     u16 i = 0;   // 自增索引
    / T; P) g' a) L; f
  18.     u16 temp;   // 临时计算变量
    4 c. }6 [. _& R1 w, e) ]
  19.    
    . [; n+ `5 \/ l6 q' V' \5 @+ Y9 W
  20.     u32 fwaddr = appxaddr;   // 当前写入的地址9 I$ y8 M0 {/ K5 k: f: d; N% V
  21.     u8 *dfu = appbuf;   // 指向App代码数组首地址的指针
    ; N4 T+ z2 @$ |) [( [8 M
  22.    
    6 Y5 A9 Q7 J# D( }& M
  23.     // for循环,2K为单位进行写入8 L$ z2 t; V% ~. f. s) \% b+ W
  24.     for(t = 0;t < appsize;t += 2), [! V8 U9 r" v+ M
  25.     {  * o8 |3 f- |* [. \( F
  26.         temp = (u16)dfu[1] << 8;1 T  e! _" Z" _( M
  27.         temp += (u16)dfu[0];   9 _. P6 f, g2 Q1 |. T* U$ Z* [) u4 w
  28.         dfu += 2;   // 偏移2个字节
    $ Z3 f/ X3 c5 g
  29.         iapbuf[i++] = temp;     + F) i$ E; ]8 O, k7 l; [
  30.         if(i == 1024)3 g6 I0 m- |( o  A( K+ @
  31.         {. y4 y5 p" f8 A4 }6 ]
  32.             i = 0;" h4 c" j/ z0 M- ^0 }& `- c" e6 h2 z
  33.             Med_Flash_Write (fwaddr,iapbuf,1024);
    / ~3 Y- F' D# }1 D. f2 N5 I
  34.             fwaddr += 2048;   // 偏移2048  16=2*8所以要乘以2. h! \% x7 q, @3 b+ b
  35.         }
    . i& V# k0 _0 f
  36.     }
    7 d* Z& P* f+ E, {4 W
  37.     if(i)4 L" I- }' ]6 m  j
  38.     {' |. r% ?2 {+ I, I4 u. F5 K4 j* \* u
  39.         Med_Flash_Write (fwaddr,iapbuf,i);   // 将最后的一些内容字节写进去
    % D# C# ]* G/ e1 L$ @8 W6 q
  40.     }
    9 Y) C) z, b! N
  41. }
    % X8 w% L0 p) b2 g5 i$ {
  42. /*# ?, Q6 v; B; B
  43. *==============================================================================
    % A) a8 }- ^+ Y( @. }. y/ N( ]
  44. *函数名称:iap_load_app& S. f, f9 F2 w6 y
  45. *函数功能:跳转到App
    * u2 ]' K* \; A' Y9 k
  46. *输入参数:appxaddr:App程序起始地址0 E, V! }/ m0 |
  47. *返回值:无+ I0 f  [) R& X  @$ X& Y! {
  48. *备  注:无
    5 D. Z3 I% n" m) B, c5 j/ S/ N
  49. *==============================================================================
    ( J2 ^( u$ @( m1 C; m
  50. */6 _8 e; U$ x; A. d6 P
  51. iapfun jump2app;
    ) W$ V) Z8 @8 m! n/ W

  52. ) L1 y& \9 Z7 W% v
  53. void iap_load_app (u32 appxaddr)
    8 o/ o: O3 J9 ]" b, n
  54. {& ~6 v. P6 `- }( a( B* a  \$ h
  55.     if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)   // 检查栈顶地址是否合法$ |. B  j# |8 @2 a6 g0 @
  56.     { 2 D  ?: X' b2 B
  57.         jump2app=(iapfun)*(vu32*)(appxaddr+4);   // 用户代码区第二个字为程序开始地址(复位地址)  9 F" W$ V' y" ^# y* T
  58.         MSR_MSP(*(vu32*)appxaddr);   // 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)4 m; F: M, o' U, k1 E
  59.         jump2app();   // 跳转到APP
    . k! e8 ]) A) \8 B8 l
  60.     }- d" J! k4 N4 Q; M
  61. }
复制代码
( v2 c$ u' y% x% v2 r+ A6 g0 n* u
5.4 main函数0 n6 `, I5 D+ a+ n
main函数设计如下
& `( [. W9 l2 F' o. G% v' D) J
  1. int main(void), ?% A- p% r' |2 |! Z
  2. {, K  K, C8 B0 g1 p" _- ^$ Z
  3.     u32 curRecCunt = 0;   // 当前接收数量
    / I+ B$ p0 o* O6 M( f4 U
  4.     * I: O8 `* Z$ f/ }# `
  5.     // 设置NVIC中断分组2:2位抢占优先级,2位响应优先级7 F) }# W* q; L8 M1 g$ h
  6.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);! s" B5 D% D2 ^
  7.     delay_init();   // 延时初始化
    7 f6 J0 k6 n8 ^! v* o* A! ]$ ^8 H" F
  8.     uart_init(115200);   // 串口初始化) @/ s3 f) d! q6 l0 `
  9.     6 u$ t5 C( a* l$ ~& O
  10.     printf ("Enter the Bootloader Code!\r\n");0 u" E& a' H' f1 i1 F2 @' T) ?
  11.     $ g, W! i& [- |) W
  12.     while(1)$ j. z  q: ~4 e: ~
  13.   {
    1 D7 I9 p- z1 t- F; \1 Y$ H
  14.         // 如果接收到内容且传输完成
    & p4 C; u; |7 {+ b4 G2 y9 M
  15.         if (curRecCunt == gReceCount && gReceCount != 0)$ g4 K2 r6 n5 ^5 z
  16.         {  \# n  c1 f9 k" b/ a- c
  17.             printf ("App Code reception complete!\r\n");2 L" Q* Q  u, \. ~' m
  18.             printf ("The length of the received App code is %d!\r\n",gReceCount);5 u; S' f  }, L9 w  d
  19.             # K- S6 B2 _) Y4 ~
  20.             // 开始准备写入Flash
    ' R1 O$ x' ^& e/ d, y
  21.             if(((*(vu32*)(0X20001000+4)) & 0xFF000000) == 0x08000000)   // 判断是否为0X08XXXXXX.( ]. q8 R# S# u: q
  22.             {1 o# F% Z! D) \6 _
  23.                 iap_write_appbin(FLASH_APP1_ADDR,gReceFifo,gReceCount);   // 更新FLASH代码  
    * A# H  h* g- J8 C! F3 Y/ [
  24.                 gReceCount = 0;   // 清零接收计数变量
    4 h0 s1 I' l) _; B6 m4 Q+ l$ o
  25.                 printf("Update complete!\r\n");
    4 s7 u' [0 B" M: W. |2 R
  26.             }: e( J- S; u" t7 T' j
  27.             else 1 M, E1 ?- l1 p7 J& J
  28.             {
    % b2 X& X; W/ r. D4 X
  29.                 printf("Update error!\r\n");
    5 b# `' J2 ~) |! }5 b3 O
  30.             }
    ' R7 g: J! R! V
  31.             
    $ M4 P( q8 F/ b. ?
  32.             // 准备跳转App执行" c( r& E7 `" O5 Z! C" J
  33.             if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)   //判断是否为0X08XXXXXX.0 \% C$ M6 v$ `+ H# g
  34.             {  v1 M; f+ k* E+ a5 q+ |
  35.                 printf("Enter App!\r\n");! T- V, \1 D( }" q2 z, k
  36.                 iap_load_app(FLASH_APP1_ADDR);   //执行FLASH APP代码0 G& U+ q' K, }3 f8 ^, S* k% Z
  37.             }else
    3 R' `# z+ ~3 V; M. {& u2 u
  38.             {
    - ?2 F8 m1 U( i% P, _
  39.                 printf("Enter error!\r\n");
    9 _/ F2 c2 u! k
  40.             }
    : E, u* |1 Q: G
  41.         }9 `% s2 @+ ^1 C" u3 Y
  42.         else   // 未传输完成
    $ n  b6 v$ W, i( J  g6 L- Z
  43.         {
    & ^% e3 _% I( a* w# V- K
  44.             curRecCunt = gReceCount;   // 更新当前接收数量: ^( r0 o% T9 u3 F  `
  45.             // 延时一定要有,给串口中断留出时间( R5 U' B( ^+ {: v# S
  46.             // 如果没有会导致接收不完全+ d, z& ?1 w, f8 @2 ?
  47.             delay_ms(10);
    6 Y" r: `  R$ k* J4 b4 M$ l! J" U
  48.         }% g; u* h  y9 _
  49.     }+ {" e- @2 r, s$ d9 \! O/ H  a
  50. }
复制代码
+ i+ ~, E2 p- h$ y& s2 ?1 W
六、注意事项
8 L0 B& x( j# e% D需要注意的是,上面的程序中App程序是从0x8010000开始,需要配置好程序起始地址和中断向量表偏移。
% c8 c& O2 N4 F, m8 ~# s4 d$ _7 E& v" P3 V1 R7 i. ~& \1 v

. b- F1 M* x2 [' ^2 ]/ q# U转载自:二土电子: p) K: {3 l$ D0 r7 Z- U
如有侵权请联系删除
. S# R( M, x2 g
$ w# {; u6 `- y4 @: d
收藏 评论0 发布时间:2023-10-23 22:37

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版