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

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

[复制链接]
攻城狮Melo 发布时间:2023-10-23 22:37
一、串口IAP简介
  F/ @# |( M3 a7 u/ F+ S) u4 E1.1 什么是IAP
# w) R3 B  ^( D7 e: S4 L
IAP,英文全称In Application Programming,在应用中编程。很好理解,就是在程序运行过程中我们进行程序的烧写,或者叫升级。) k2 Z! o0 g) c# `

  l; |( }" S$ P, K* o3 |) O1.2 STM32下载程序
' t: y( ]' q- S6 A) {; s0 a我们都知道,STM32可以利用串口下载程序,这是因为ST公司在产线上就在产品中内嵌了自举程序。所谓的自举程序,实际就是支持我们通过串口下载程序的代码。自举程序被存放在系统存储区,因此如果我们需要通过串口下载程序,需要将Boot0接高电平,Boot1接低电平,让程序从系统存储器开始运行,运行自举程序。下载完成后我们再将Boot0接地,让程序从主闪存存储器开始运行。自举程序是我们用户无法修改的。
$ y3 N+ ^: K. L) e% B9 ]' O9 {6 i/ a6 Y, l1 d, c4 ]- R5 n
8 G$ z2 N5 ?+ y1 p, {+ h' g
二、串口IAP有什么作用. D9 ^' u( J! D& i
上面我们介绍了什么是IAP,那么这个IAP到底有什么作用呢?! y# m( q6 f4 T  \

9 T2 x0 e1 s# g+ g$ R首先介绍两个词——Bootloader和Application。Bootloader实际就是一段引导程序,单片机上电后先执行Bootloader程序,然后再执行用户编写的应用程序Application。介绍完这两个词,我们来介绍一下IAP有什么作用。比如我们生产了A,B两款产品。A产品是某个精密器件的一部分,B产品是一款物联网产品。我们的产品销售范围很广,远销海外。: f' c8 B$ Y+ e4 f
3 u' w' {8 J. F8 Y+ t/ Y+ ]
某天A产品的某个客户反映了一个Bug,我们编写好了程序,需要进行程序更新,或者叫升级。利用IAP,我们可以在程序运行时,通过预留的通信接口直接烧写程序。而不需要再把整个设备拆开,像我们调试时那样下载程序。甚至我们可以直接给客户邮寄一个小设备,客户直接进行傻瓜式升级。
  i: k* o+ N, f9 ^' b8 N! J* V0 c3 P
' }7 B9 o. [* e- l# U5 G
又过了一段时间,B产品的程序出现了一个Bug,风险等级比较低,但是依旧需要全体升级程序。我们总不能挨个产品派人去升级,成本极大。这时候又轮到我们的IAP出场了。它可以在所有设备在线运行的情况下,直接通过网络下发升级程序,实现在线升级,节约了大量的人力成本。; U8 i' A+ m7 [" d
& R& r. T' f, Y
通过上面这两个例子,大家应该能够基本了解IAP的用处,使用IAP让我们不需要再使用调试器进行下载,甚至实现设备的在线升级。
3 x9 ~* W1 s& N- D, w& I7 E* I9 c; {) ?+ `8 i) ]8 m" L' a5 _+ j
! d5 Z5 U" Q, _7 M7 g& ^0 g. Q
三、启动流程
, K1 J1 g* e9 d# ~在介绍如何实现IAP之前,我们先来简单了解以下STM32的启动流程。( k% [& n( p% e( N4 e
% C5 c/ [. W( b6 }. }
3.1 正常启动流程
( i3 s" p6 Q' H4 c& |3 P# P- R- e这里的正常启动流程指的是,没有添加IAP的流程。, l, F$ l1 ^, E8 ?& z3 m  q; V

8 n. j- U. P* `; G) d) y. o. ~
7 J. w: R: E! U9 W* B- W
微信图片_20231023223616.png

1 E- O1 z% u: r4 G* ]* b% \# a
正常启动流程

# _9 x  o  ~+ ?; ]+ T0 \
2 q4 H' Z1 D0 B7 T$ ]
程序启动时首先开辟栈空间,配置栈顶指针。然后配置堆空间。配置完成后,建立中断向量表,在中断向量表中找到复位中断,开始执行复位中断服务函数,然后跳转到main函数中,执行用户代码。当用户代码中有中断请求时,会回到中断向量表,根据中断源执行相应的中断服务函数。
: y% |% R4 M) m* {2 S0 F1 c% G6 J1 _) ~' f  x/ t  @+ D$ A
3.2 加入IAP后的启动流程, d5 X- T$ G9 M2 _2 Y. I2 g8 d1 Z3 r8 X- K
下面是加入IAP之后的启动流程。( L" z  ~/ p7 O! l' h. Q  E
4 r6 ^1 c7 L+ C( W* ^$ n! _/ P
微信图片_20231023223619.png
- l, d3 s$ ]% j3 E8 a1 A' G1 j
加入IAP启动流程
- \/ a; J; y6 q5 y3 |

2 q; z1 T. `# Z* d: h/ j' |5 p! Q
6 ~' q+ d4 U# F3 x: N* x$ \  q' C7 ~- N
可以看到,与上面不同的是,加入IAP后,执行完复位中断服务函数后直接进入IAP的main函数。在执行完IAP之后,跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main 函数。
, Q- Q  p0 N! t, r# l8 z

$ j. M+ {5 F/ Z2 j4 L由上面的两个启动过程我们可以看出3 B9 u1 Y( s. V/ O8 X
• 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始。
/ [' S7 B5 ~4 D; A" `* v% v* f• 必须将新程序的中断向量表相应的移动,移动的偏移量为 x。! ^8 ]5 ]% `1 a

7 {  M" l0 R) Q9 g
6 T6 \& \" N8 A; ~& [
四、必备知识
- h) c5 Z" d, _% q4 X44.1 修改程序运行起始地址
* b: k) J  v  J' |3 r2 w
点击魔术棒,选择“Target”,修改运行起始地址和代码大小。
' ]5 x6 [" S& |# e: c
; [' r3 V( q  ^6 l/ V! a- d
微信图片_20231023223624.png

' r. D& S0 F. Y& [* M  X$ d$ j
修改App运行起始地址
3 a& z. o( p, z; [- h/ I! K; x
/ c" }- a% t0 M$ I- u( C

1 C7 Z/ `5 N+ \; J% |7 r
4.2 设置中断向量表偏移
# h9 y: T) L* U2 r1 x) I& ~! _VTOR 寄存器存放的是中断向量表的起始地址。如果要设置中断向量表偏移,只需要在main函数最开始添加如下语句即可
) X0 |, {0 e7 z& L
  1. SCB->VTOR = FLASH BASE | 偏移量:
复制代码
; w7 @7 H1 m0 \& u; z# u" [
4.3 生成.bin文件
* G; X% X" q& E. G8 {( P点击魔术棒,选择“User”,按照如下配置,输入下面的内容
6 j4 x! ~' O& p# U
  1. fromelf --bin -o "$L@L.bin" "#L"
复制代码

+ ^  D. r5 j6 z" `1 e
微信图片_20231023223706.png

) W6 }, t7 U" w0 j
生成.bin文件配置
. h) r; P$ w, k$ [$ D
6 \/ a7 f( r1 Q8 Q
$ L  r' A8 |. |: D& k
点击编译,不报错就可以,去设置的输出文件夹中就可以找到对应的.bin文件。
( I8 q8 i, S7 p

( d* d, M6 w, R. _$ u: [* A% v
微信图片_20231023223710.png
# t; B6 w& X( u1 ~
编译提示
) {1 ], c5 G% w( u! B( P( {, }

: b" J: q1 n/ x
9 D- |9 J2 J4 D9 a9 f* E
五、串口IAP实现
- x6 I4 A3 Y% R本次的目标是实现一个串口IAP,也就是编写Bootloader,在程序运行过程中实现程序的下载。Bootloader程序应该可以通过串口接收上位机发来的.bin文件(App程序),检查后将.bin文件写入到Flash特定位置,然后跳转到App程序运行。
# Y8 k$ i8 ^5 y' W1 p
/ A3 v0 L6 M: T- A+ K0 ]7 X5.1 串口中断服务函数% |4 |' {  g. q7 ?( P( l4 @
本次的串口中断服务函数与之前不同,这里单独贴出来。需要定义一个接收数组,接收数组的起始地址限制为0X20001000。接收数组最多可以接收55K字节,可以根据需要调整。但是需要注意的是,数组的大小需要比App程序要大,而且不能超过芯片的SRAM空间大小。
) d, I- `( b3 m2 i1 w
  1. /*9 ~1 h8 Y7 H  b  l! J# V' j" n3 Z
  2. *==============================================================================: _" F1 ^- G( j
  3. *函数名称:USART1_IRQHandler
    " I* m7 x. L  @8 n
  4. *函数功能:USART1中断服务函数
    0 T9 H# c" e9 ?: ^' t1 ^) T) P: A+ @" l
  5. *输入参数:无
    : B, g  ^- j# L# q
  6. *返回值:无7 u/ g; ?$ Z) S6 U1 E( X7 @/ L2 i
  7. *备  注:无
      G. ~! ?" |2 D
  8. *==============================================================================8 A$ D7 j: k5 A% |; L% K4 i% j6 E
  9. */
    & v# i8 ]* e$ c$ ?4 A6 H
  10. u32 gReceCount = 0;   // 接收计数变量, [- u( j+ f3 L$ r+ Y+ J5 v$ ^
  11. // 接收数组0 j0 M) B1 a! P( a4 _; D
  12. // 限制起始地址为0X20001000/ {. B2 R, _+ Q' {& r
  13. // 保证偏移量为 0X200的倍数" L6 T! ]0 D7 R1 c( B8 D
  14. // 是为了给App留SRAM空间5 p5 ?8 f$ f8 A" V9 j
  15. u8 gReceFifo[USART_RECE_MAX_LEN]__attribute__ ((at(0X20001000)));" [% d" a0 c2 O* }# H$ ^# k" G* \3 @
  16. % v7 _4 Y4 h, L; U
  17. void USART1_IRQHandler(void)  
    1 }) x  l) t3 m1 z, i! ]) h" Q9 [
  18. {0 r+ g5 g3 `4 A
  19.     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   //接收到一个字节  0 L5 u8 n8 V! ~1 J' R2 z
  20.     {& O# w0 p' Q* c0 G
  21.         if (gReceCount < USART_RECE_MAX_LEN)+ j- a( {2 j& o- H+ e3 x
  22.         {
    9 h; S! T/ s) K8 q
  23.             gReceFifo[gReceCount++] = USART1->DR;
    " q; H& K7 k, x+ g! S
  24.         }
    - p1 J/ f" m2 G% b0 Q
  25.         else
    / S/ @4 n, f1 U: x  x- k
  26.         {
    ) s: |8 e# N5 K& {. c( c$ b5 s
  27.             printf ("APP code out of memory!\r\n");6 J; M6 q1 M8 n! F# a. Y1 B. D( \$ v
  28.         }
      A* @! k! M" L9 a1 s
  29.     }
    5 V2 f. p% c% Q6 R! n' E* \
  30. }
复制代码

5 b. `5 u8 j/ H! {4 _# C' q- Y8 o5.2 Flash写入程序
5 F+ [' W$ X# a4 L( V2 p关于Flash程序,这里就不在赘述,只是贴一下带检查的写入程序。其他具体内容可以到博主STM32速成笔记专栏查看。3 P# C: H9 g3 x
  1. /*
    ' N1 I* w+ q, x! G0 q
  2. *==============================================================================( r! Q  H' b/ \
  3. *函数名称:Med_Flash_Write
    9 C$ U" L% q0 Q5 U6 U
  4. *函数功能:从指定地址开始写入指定长度的数据  B) z8 N* f! m6 A) C
  5. *输入参数:WriteAddr:写入起始地址;pBuffer:数据指针;
    $ d5 L8 D7 ~0 S3 m. w7 G8 R: G
  6.                         NumToRead:写入(半字)数3 [6 J" m, b, I- H1 r/ }9 W
  7. *返回值:无
    6 M- v- z/ i* d; W4 z+ F/ \' j% G
  8. *备  注:对内部Flash的操作是以半字为单位,所以读写地址必须是2的倍数
    5 `. \4 G; P" G" N/ a
  9. *==============================================================================0 L$ ?  u" ?( A! V
  10. */
    + g7 Y, _+ D! C! H, @
  11. 1 g! i5 a7 E4 U; N& \+ i2 o
  12. // 根据中文参考手册,大容量产品的每一页是2K字节
    2 b: }% T0 ~  Z; e2 b  h
  13. #if STM32_FLASH_SIZE < 2568 J7 `2 C! |9 W9 h0 ~
  14.     #define STM32_SECTOR_SIZE   1024   // 字节
    . k2 y% V- f8 `' m, V; z. y# r' u, R
  15. #else
    6 S) q) @1 R9 c5 C" U! d2 B
  16.     #define STM32_SECTOR_SIZE   2048
    : U" B! T5 f+ K3 ^3 B; D
  17. #endif8 N) |* f7 `, @
  18. . u) h! n/ r  t" f$ E
  19. // 一个扇区的内存! u8 a5 _7 e( Q7 I
  20. u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];7 A, T4 o9 q9 L: x5 p- ~- U0 [
  21. ! ~' _4 R0 T. _9 N! }- V+ X
  22. void Med_Flash_Write (u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)* Q  g+ x- `. F5 O- Z: X
  23. {! |% T' x4 L3 c8 _/ u# F5 J
  24.     u32 secpos;   // 扇区地址
    , ?* `4 ^# y9 @9 B4 d9 l
  25.     u16 secoff;   // 扇区内偏移地址(16位字计算)0 Y# ~7 }0 @' C/ ?6 {5 k6 z
  26.     u16 secremain;   // 扇区内剩余地址(16位计算)   
    $ A! F$ k2 G3 D4 j
  27.      u16 i;   
    2 ?) c6 r: j1 e- c5 A6 T
  28.     u32 offaddr;   // 去掉0X08000000后的地址
    : }' a; a8 f  }0 u! _5 m8 l1 ?
  29.     4 G- ?# c' `0 p6 c- E4 ?5 F
  30.     // 判断写入地址是否在合法范围内3 i1 r5 j* \: O4 H' g; D+ b9 R4 k
  31.     if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE))); T* V% F  o+ V$ G4 v, c8 a
  32.     {
    6 v7 g* H$ p9 j$ t" l- [0 X  S# k
  33.         return;   // 非法地址
    ! e# j9 T% c" t( U
  34.     }
    . Y$ ^5 w* ~: n3 f  L8 e( M, A
  35.    
    ' ]. V, q) T& ]( M
  36.     FLASH_Unlock();   // 解锁
    . w: m, c5 ^  N  z
  37.     offaddr = WriteAddr - STM32_FLASH_BASE;   // 实际偏移地址
    " B- v6 \9 w% p" [
  38.     secpos = offaddr / STM32_SECTOR_SIZE;   // 扇区地址3 Q4 K$ L& s! G" y+ C
  39.     secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 在扇区内的偏移(2个字节为基本单位)0 b3 J  [( b  w! z' j
  40.     secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇区剩余空间大小& a' A* z$ N* |
  41.    
    " I0 j8 `9 p" Z9 M/ i: W# Q! q
  42.     if (NumToWrite <= secremain)
    & l, K) ?% G+ e) {9 d4 O
  43.     {
    1 d7 d& l! g& _7 C! k
  44.         secremain = NumToWrite;   // 不大于该扇区范围  a! s7 M7 K) u' v9 q
  45.     }
      A7 n/ |- N2 n9 ]
  46.    
    % e. S: K' O% s# n0 ?" x
  47.     while (1)
    6 J2 i4 f! w$ `* S! d6 I4 l7 `5 Q
  48.     {# o6 N1 L8 V! V
  49.         // 读出整个扇区的内容& n( u( k7 Z2 }# S5 F% ]
  50.         Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);  N4 Y- L1 `3 ]7 @- R; ]
  51.         
    6 p% ?9 h0 r( V4 p) ?8 A
  52.         // 校验数据% m" W+ W$ n6 W6 x
  53.         for (i = 0;i < secremain;i ++)" P7 H+ ^& E7 @% P; E: F* u
  54.         {* Y9 y; D7 V/ E' m- ]( q
  55.             // 需要擦除 - R% t: S+ b- m! q+ ]
  56.             if (STM32_FLASH_BUF[secoff + i] != 0XFFFF). n2 c+ N" z2 @& a, `9 o
  57.             {8 k9 W2 l: s, F8 G) Z3 P' f2 c0 ]
  58.                 break; ! k; h2 d: C  E
  59.             }    $ R: |4 `2 m: D8 i" B
  60.         }
    ! c) D. l* {) H6 a! y9 @
  61.         // 需要擦除# i: q$ J/ w' r5 J! g, _
  62.         if (i < secremain)+ v% I' b- v+ A& g
  63.         {
    1 E" e1 Z! `9 K, [5 b
  64.             FLASH_ErasePage(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE);   // 擦除这个扇区
    % M. h' N& l5 P( q1 h. j% O
  65.             
    - x7 }$ _5 B. |5 B' r' y" b% F
  66.             // 复制
    1 O9 M3 ], ^8 D8 C
  67.             for (i = 0;i < secremain;i ++)  ~+ N* L' K8 ?. G# N( L0 n3 e) Q% l
  68.             {
    2 {+ m1 v2 b7 v7 ?' ^: s) E0 D+ i2 V  \
  69.                 STM32_FLASH_BUF[i + secoff] = pBuffer[i];   
    5 t  b; g5 W9 I0 P( _6 J
  70.             }
    . ^- f, c+ G. f8 H9 W' C
  71.             
    7 u) _, k) |3 a( K
  72.             // 写入整个扇区
    5 D; w& q/ g6 K. [* g& D/ y
  73.             Med_Flash_Write_NoCheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);3 U  ]( q! U3 ~8 O2 Z3 \# w+ d
  74.         }
    * i& W) P1 V1 g+ n$ g) z2 Z
  75.         else: f: M" ?% l4 c1 O
  76.         {& X# B4 x6 l3 ]; {$ w! _
  77.             // 写已经擦除了的,直接写入扇区剩余区间
    - y6 w$ M( k# c& \8 B4 Y
  78.             Med_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);
    - U' t; p1 c9 N, d. H
  79.         }, U# d' J5 f1 L6 r) t6 M/ J4 ^" |, r  }
  80.         - c) n1 X0 f( |7 q. H9 Z
  81.         if (NumToWrite == secremain)  [+ n" y  ]* b
  82.         {
    8 g5 E& ?7 A9 ^- j  d+ F0 n9 i
  83.             break;   // 写入结束了
    9 a9 a; x# w/ x- c) }
  84.         }
    $ \6 a* g! ?. N5 v5 d
  85.         // 写入未结束# `( q1 O6 n* ], @
  86.         else
    , [7 Q! g1 z) H" k: C2 {
  87.         {6 h( j9 c0 }7 f0 _  d3 x' f
  88.             secpos ++;   // 扇区地址增1
    ( I, ~" T3 c( q* k
  89.             secoff = 0;   // 偏移位置为0   & R* @; \! b1 x4 c- w, I
  90.             pBuffer += secremain;   // 指针偏移
    3 r1 I' ~! R' A7 X( `
  91.             WriteAddr += secremain;   // 写地址偏移   
    ; P4 u/ J8 @* O3 t$ C
  92.             NumToWrite -= secremain;   // 字节(16位)数递减$ P* x/ h  ?/ j6 N& ], N& P& Y+ V! [
  93.             if (NumToWrite > (STM32_SECTOR_SIZE / 2))6 l+ b; x3 q6 U, r' u9 m' \3 x- p7 v4 c
  94.             {0 \# e5 w6 W, |) j
  95.                 secremain = STM32_SECTOR_SIZE / 2;   // 下一个扇区还是写不完2 Y( W! h6 Q4 x+ ?% b! e! n1 H5 N
  96.             }
    0 x- B+ P% h6 V7 P
  97.             else7 _8 Q) ]$ Z% Y. B$ ?8 I- g
  98.             {+ U" K' a" F' B6 T* Z5 W- Z  I) C
  99.                 secremain = NumToWrite;   // 下一个扇区可以写完了
    , r- X9 X& Y: w9 z7 j
  100.             }
    4 b% E  M3 `, O/ U1 x0 \4 ?; c
  101.         }  
    ! d$ d  y) U+ y" ]/ q2 p. v! X% e
  102.     }
    3 q  a4 H/ {' }- n' t; K! s
  103.     FLASH_Lock();   // 上锁# A' g( j9 F, k+ c5 {+ M+ ?
  104. }
复制代码

3 k! X' i$ S4 G& Y# u: b5.3 IAP程序
" {. @1 U3 ~& U3 b$ B7 ?/ TIAP程序包含两部分,一部分是将接收到的.bin文件写入Flash,另一部分是跳转到App执行。
* u$ z1 C' h) G' h, }
  1. /*
    ' r" n+ J, F1 [% ]' r+ S  _
  2. *==============================================================================
    0 y) j3 Q- s7 F3 z4 {0 D* U5 ~6 H
  3. *函数名称:iap_write_appbin7 K8 \; }" U1 Y7 y, |; o% A) u& h0 r/ O
  4. *函数功能:写入.bin文件. c6 f1 ^3 x% @% ]# |* @) c
  5. *输入参数:appxaddr:App程序起始地址;appbuf:缓存App程序的数组;) `3 u; q- F! e
  6.                         appsize:App程序大小
    : Y# }0 ?1 ]! `2 o: F' E" u- z
  7. *返回值:无
    & t+ Q) S( H+ {3 y- {
  8. *备  注:无
    ) C! o6 c8 U! T: B$ m
  9. *==============================================================================  \% m. ]. g1 n
  10. */7 a6 R2 `( I- V
  11. // 对Flash操作的最小单位是16位
    . ~( Y) g$ D$ ?
  12. u16 iapbuf[1024];   // 要写入Flash的内容
    & p( s' C1 }6 n; H! q3 x
  13. ( V% P6 G) b/ Z/ T8 A5 l- h
  14. void iap_write_appbin (u32 appxaddr,u8 *appbuf,u32 appsize)
    ( A6 q3 Y# z% T, I! }2 U
  15. {! M" x5 c0 {4 A8 L" b+ r
  16.     u16 t;   // 临时循环变量
    ) A, O. s7 X4 X
  17.     u16 i = 0;   // 自增索引
    & w1 }, b; e8 k  H" {) G" @8 E
  18.     u16 temp;   // 临时计算变量9 a- _$ c) [* J- a8 s
  19.    
    1 o4 Y3 {5 I* R9 q. P
  20.     u32 fwaddr = appxaddr;   // 当前写入的地址8 k# b- |8 ^/ z" s
  21.     u8 *dfu = appbuf;   // 指向App代码数组首地址的指针
    2 Y6 A! z; F" z9 ?9 S) y
  22.     . R' s- p8 }7 s  k
  23.     // for循环,2K为单位进行写入! \9 |# M* j! r. W6 u& |! `9 z. [# l
  24.     for(t = 0;t < appsize;t += 2)0 H$ b. b' m( d; i, J, A$ C# H
  25.     {    @9 ~9 X5 ]# L% {, ^; B# q
  26.         temp = (u16)dfu[1] << 8;" E1 n( g& [: \/ Y
  27.         temp += (u16)dfu[0];   
    . ?$ M' F, r" ]  ?
  28.         dfu += 2;   // 偏移2个字节
    ; w+ x  J0 }" D
  29.         iapbuf[i++] = temp;     
    0 M, a+ |# A8 `* Q0 P1 ?. H8 Q( ^
  30.         if(i == 1024)
    * n4 {5 S+ I$ L% ]
  31.         {
    3 u* C6 Z# f) ?3 w& n6 @* h; A
  32.             i = 0;% L3 C4 g; x0 d, j$ q
  33.             Med_Flash_Write (fwaddr,iapbuf,1024);
    , w# U) |: \4 @' ]$ F4 R+ ~
  34.             fwaddr += 2048;   // 偏移2048  16=2*8所以要乘以22 G9 C8 m( D: @9 r
  35.         }
    ( V, ?, Q7 C$ Y! A
  36.     }; C$ X+ F& z0 R; W% L" R. i* F
  37.     if(i)
    " M& p) D# q' j' q
  38.     {2 K2 o. p! s' X+ d
  39.         Med_Flash_Write (fwaddr,iapbuf,i);   // 将最后的一些内容字节写进去4 t# `6 m5 D  R. X+ V1 u& a6 P$ Z
  40.     }
    & y8 @; g" X  s* G
  41. }
    4 {0 [7 q7 \  i) w
  42. /*
      S" X) U0 ?; X! U" X7 @
  43. *==============================================================================
    6 u$ f; Q" Z. [& _% x
  44. *函数名称:iap_load_app) Z& _  e& {+ |6 ^. u2 g4 Z
  45. *函数功能:跳转到App
    ! \" R% T, b  i: m
  46. *输入参数:appxaddr:App程序起始地址  R8 Y8 T& A( D9 O9 L6 A% _- H
  47. *返回值:无9 x, R; F: O% d. r
  48. *备  注:无) L( h1 d+ r1 C  K: t
  49. *==============================================================================& h9 u% s2 [, M" k
  50. */
    ( C  G) ^& s8 j7 P% W
  51. iapfun jump2app;6 e0 g1 C8 V3 Y

  52. & U* S# _) H6 X+ d& G6 G# S+ j
  53. void iap_load_app (u32 appxaddr)
    + \, m, l( J, C; v" g5 P$ s+ c
  54. {
    6 n+ H" j  N$ V, i+ H
  55.     if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)   // 检查栈顶地址是否合法
    , O2 u9 ~3 r7 `6 f
  56.     {
    5 K$ d8 x, f6 d! V; |
  57.         jump2app=(iapfun)*(vu32*)(appxaddr+4);   // 用户代码区第二个字为程序开始地址(复位地址)  ! s4 q+ t( z; q3 {9 r& X" E
  58.         MSR_MSP(*(vu32*)appxaddr);   // 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址); s+ L3 g. {$ Y5 [# [$ L" N
  59.         jump2app();   // 跳转到APP4 B- g. M$ i! |0 J8 U9 D7 }# ?
  60.     }
    * ^3 }6 j3 ^/ v. Z
  61. }
复制代码
% f" p) e; P5 }$ d
5.4 main函数  V( N* V6 K$ f2 q$ v, b; ^
main函数设计如下% B8 m' o3 v1 }; i- ~' e! M7 f
  1. int main(void)5 @  q# V9 O1 a: p! S9 e* t
  2. {
      s+ ~9 ^5 q' N1 Z. j/ W- W9 b
  3.     u32 curRecCunt = 0;   // 当前接收数量
    % B! d( U) W, W# B
  4.    
    . {. k$ ]+ _0 B2 ^/ ^+ K" u
  5.     // 设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    , i" Y$ `% O# E* @
  6.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    ( q9 g- z* z& y3 S* Y
  7.     delay_init();   // 延时初始化
    ) t- {$ w% v, g9 [9 {4 i" k) w
  8.     uart_init(115200);   // 串口初始化! z+ v1 y& l* g" }6 ~& @" P
  9.    
    5 q. }- \- T, K& u2 w5 u
  10.     printf ("Enter the Bootloader Code!\r\n");
    9 O2 {9 e, D2 }1 t+ H% i
  11.    
    5 l5 j9 y# J- V( ^
  12.     while(1)0 _% V) ?# @  w: N1 E0 Z+ _" m
  13.   {* U7 M* X0 @: b/ I! \
  14.         // 如果接收到内容且传输完成
    . _5 O! j5 T4 k6 m9 ], c* z' S
  15.         if (curRecCunt == gReceCount && gReceCount != 0)3 ~. S; W" B, o- y9 K  `7 K) A  x
  16.         {+ A: k9 s% M0 t: x3 _% I
  17.             printf ("App Code reception complete!\r\n");# D& m# ~, E0 |" u( Z
  18.             printf ("The length of the received App code is %d!\r\n",gReceCount);
    8 T/ n' ~4 s( P4 c
  19.             , \3 @2 W3 M9 ?4 [) M
  20.             // 开始准备写入Flash
    ! H7 i. \8 R- R: e. e$ i
  21.             if(((*(vu32*)(0X20001000+4)) & 0xFF000000) == 0x08000000)   // 判断是否为0X08XXXXXX.
    " ?9 y- _; n, R4 g& J
  22.             {
    $ N# O2 W+ C& D. I9 f+ |( b
  23.                 iap_write_appbin(FLASH_APP1_ADDR,gReceFifo,gReceCount);   // 更新FLASH代码  
    4 C. A$ m# v( ~- G5 d
  24.                 gReceCount = 0;   // 清零接收计数变量
    + d7 L' W) H, f2 y* l# Y1 e
  25.                 printf("Update complete!\r\n");& R' z$ E0 H6 @- I4 ?1 f  I: \
  26.             }
    8 f6 _6 \' c# R* C7 Y7 Z
  27.             else ( k: X* |1 x7 S. @% ]- J! ~
  28.             { " p! {( u! ~7 F
  29.                 printf("Update error!\r\n");
    . N- r1 ~( Y3 Y$ i. |' ~' o" t. L9 @
  30.             }
    " a8 G$ }3 f& D+ i  ]
  31.             - c0 W" H. ?, Q: S8 F
  32.             // 准备跳转App执行
    : ~6 E0 |0 L+ ]2 v
  33.             if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)   //判断是否为0X08XXXXXX.& s- e' p  Z, B  u. c
  34.             {+ L, \: j: e# m: j# t
  35.                 printf("Enter App!\r\n");1 r8 i- b$ A5 D3 k2 G
  36.                 iap_load_app(FLASH_APP1_ADDR);   //执行FLASH APP代码: ?. Y7 t3 f5 F; i; l) w
  37.             }else
    $ E9 s2 f9 a6 C: X" `/ z5 Y; x1 \( E
  38.             {
    ( x& c2 Q# m2 H$ P
  39.                 printf("Enter error!\r\n");, T- U' m, p% S& |# w6 H: O; F+ }
  40.             }
    ; M0 N, M5 |2 J. D; O$ y/ z
  41.         }3 p8 B) ?8 z" Z( l+ f
  42.         else   // 未传输完成; n9 ~& z7 U" d; b( {/ Q
  43.         {# \$ ]# @4 p& V" l' z
  44.             curRecCunt = gReceCount;   // 更新当前接收数量/ F  {. r: z3 I* T  H
  45.             // 延时一定要有,给串口中断留出时间
    , h. w! p- u  T
  46.             // 如果没有会导致接收不完全
    : R! L  V* m& Z; b, N
  47.             delay_ms(10);: {' J4 f9 C4 s9 q: m9 J" t
  48.         }
      @4 U; G4 Z0 V6 l* `& E
  49.     }/ L. X5 O# `& ]* D8 y2 c7 W1 s' @& ?
  50. }
复制代码
# ]9 h( ]* V5 @7 h
六、注意事项% X, Q( E+ S5 U, h& I& j+ ?
需要注意的是,上面的程序中App程序是从0x8010000开始,需要配置好程序起始地址和中断向量表偏移。
. |0 \% [8 R  ]+ c( }$ J3 O9 E0 e6 `% m1 H( e$ o. y+ x% {( E
  w& Y! t; Z# Y0 k% m1 }; t
转载自:二土电子
. k" Z0 w9 i3 n5 C, X如有侵权请联系删除1 P# s1 z& G& D1 u- U/ O3 V" l
2 ~2 |! {! g& X4 I2 U! K8 q
赞 收藏 评论0 发布时间:2023-10-23 22:37

举报

0个回答

所属标签

相似技术帖

官网相关资源

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