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

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

[复制链接]
攻城狮Melo 发布时间:2023-10-23 22:37
一、串口IAP简介4 r% W- c8 D. D/ D
1.1 什么是IAP

' Z+ N( _7 Z1 ?. S4 r& G: [IAP,英文全称In Application Programming,在应用中编程。很好理解,就是在程序运行过程中我们进行程序的烧写,或者叫升级。# ?! B% y* K& Y. H4 ~! ~
2 s& m3 D3 F8 Q2 }: O- R
1.2 STM32下载程序
, c4 |3 c" r) [, u7 R我们都知道,STM32可以利用串口下载程序,这是因为ST公司在产线上就在产品中内嵌了自举程序。所谓的自举程序,实际就是支持我们通过串口下载程序的代码。自举程序被存放在系统存储区,因此如果我们需要通过串口下载程序,需要将Boot0接高电平,Boot1接低电平,让程序从系统存储器开始运行,运行自举程序。下载完成后我们再将Boot0接地,让程序从主闪存存储器开始运行。自举程序是我们用户无法修改的。
7 G4 ?6 L6 Y* l8 Q2 y
1 n: Q% n7 C9 L  R3 D
6 D  ?$ j. b; C+ U9 T# Y4 ^: S: N
二、串口IAP有什么作用) l1 s; v, [0 H
上面我们介绍了什么是IAP,那么这个IAP到底有什么作用呢?
* @% R2 j6 r8 j9 l) N  }6 g# `5 n3 @- D
首先介绍两个词——Bootloader和Application。Bootloader实际就是一段引导程序,单片机上电后先执行Bootloader程序,然后再执行用户编写的应用程序Application。介绍完这两个词,我们来介绍一下IAP有什么作用。比如我们生产了A,B两款产品。A产品是某个精密器件的一部分,B产品是一款物联网产品。我们的产品销售范围很广,远销海外。
9 ~0 j$ u/ p! G# g3 [" V  e7 }9 M$ U# L. u) u
某天A产品的某个客户反映了一个Bug,我们编写好了程序,需要进行程序更新,或者叫升级。利用IAP,我们可以在程序运行时,通过预留的通信接口直接烧写程序。而不需要再把整个设备拆开,像我们调试时那样下载程序。甚至我们可以直接给客户邮寄一个小设备,客户直接进行傻瓜式升级。- z) n# a, V8 F9 b

) w% E' ^, h7 I; ?又过了一段时间,B产品的程序出现了一个Bug,风险等级比较低,但是依旧需要全体升级程序。我们总不能挨个产品派人去升级,成本极大。这时候又轮到我们的IAP出场了。它可以在所有设备在线运行的情况下,直接通过网络下发升级程序,实现在线升级,节约了大量的人力成本。
* \0 F& T2 S; g! G9 Z" G1 O5 I0 ]  d; ~4 C8 U2 _4 @
通过上面这两个例子,大家应该能够基本了解IAP的用处,使用IAP让我们不需要再使用调试器进行下载,甚至实现设备的在线升级。
& R- h% Q. H/ j$ |: A) }: c9 F
! t+ h! w& W( w# j

) \, C5 q1 q! P3 b三、启动流程" @$ n/ h' [2 z( N2 m/ o3 A8 Q
在介绍如何实现IAP之前,我们先来简单了解以下STM32的启动流程。
; @( G9 p; p. {# P( m, `: B2 ~, Z: t1 V
3.1 正常启动流程
1 d. E; Z, U5 t$ P5 {! @& ~+ B这里的正常启动流程指的是,没有添加IAP的流程。
% W6 ?! a3 R5 M, s5 }. G" x6 ~: t
; z& U8 ~, i( v+ M
2 V. h6 v# c8 j5 L1 Q, @
微信图片_20231023223616.png
! `$ m* |0 b, ?$ |) u9 K
正常启动流程

& Y% Y" g! b, i2 e! M) y
0 f0 Z8 M, E* J- S. v) `6 _, L% d9 m
程序启动时首先开辟栈空间,配置栈顶指针。然后配置堆空间。配置完成后,建立中断向量表,在中断向量表中找到复位中断,开始执行复位中断服务函数,然后跳转到main函数中,执行用户代码。当用户代码中有中断请求时,会回到中断向量表,根据中断源执行相应的中断服务函数。9 Y7 J/ w0 |( x. z5 \7 S

% d6 n9 t/ `' l1 S' U3.2 加入IAP后的启动流程
: U; p" q; y2 d/ h" v6 K8 H0 G下面是加入IAP之后的启动流程。
# d. B1 n  L0 e- g0 S8 s. F% j& q# ~3 |
微信图片_20231023223619.png
/ O5 T, b* x: S' x
加入IAP启动流程
, j0 G2 T6 C! F+ p1 B8 o; q

% K! j( K( o/ M+ P. d- H5 x/ N# l$ I& |7 }4 x1 U( ]
可以看到,与上面不同的是,加入IAP后,执行完复位中断服务函数后直接进入IAP的main函数。在执行完IAP之后,跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main 函数。
; u, F# Z6 n' N- ^3 P. Z8 ~$ [: [0 g
  n# d* y! e$ J+ ?; M
由上面的两个启动过程我们可以看出
. B4 t& b; q. _' }  t  \7 [+ s• 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始。+ y! w2 r$ [  i1 i# T6 j
• 必须将新程序的中断向量表相应的移动,移动的偏移量为 x。
( a4 A5 l( }& _8 E' |4 K, z
1 p5 Q& T3 t2 {( |; K0 q2 ^1 I* [

, H) ~3 |( [& ~1 H+ R四、必备知识3 i4 F: w3 T* j8 |: f* j
44.1 修改程序运行起始地址
/ a3 m8 ?  |' f/ f2 h1 T! _
点击魔术棒,选择“Target”,修改运行起始地址和代码大小。
! a( k1 B" F: W& v0 }5 D$ k# P" k
微信图片_20231023223624.png

$ z& |: R1 D% L7 Y
修改App运行起始地址
3 g% H- o6 n9 x9 q: [1 H
+ F; O9 \$ T8 c6 a4 ]" H4 y1 m

% ~5 R3 m+ C0 a% a* @. t5 z: \
4.2 设置中断向量表偏移
+ E  \1 Y, {% h3 A$ OVTOR 寄存器存放的是中断向量表的起始地址。如果要设置中断向量表偏移,只需要在main函数最开始添加如下语句即可; r0 _. O* y/ B- h+ M& P1 o
  1. SCB->VTOR = FLASH BASE | 偏移量:
复制代码
  Z( o+ |( v. |
4.3 生成.bin文件
1 A  N8 h. S! Y: O$ c1 Q点击魔术棒,选择“User”,按照如下配置,输入下面的内容; n* v2 s- P& \' b
  1. fromelf --bin -o "$L@L.bin" "#L"
复制代码
# }7 V6 O1 f" H7 G4 A7 w
微信图片_20231023223706.png
* r1 G6 e1 y. X5 O8 ?! o
生成.bin文件配置
- j8 V+ V6 J  s. P6 H5 z: H4 U  f

% S; F+ S7 r# q: x- i* ?
/ `+ {+ o7 I5 F; ]) }8 [' z+ k
点击编译,不报错就可以,去设置的输出文件夹中就可以找到对应的.bin文件。
. G! f* `# B7 o: c% H$ m  D+ {

! {: y# [0 D* m& W# b
微信图片_20231023223710.png
! m2 G# l9 p+ f4 ^
编译提示
% O! }' a5 _7 M' R3 c2 e

/ W$ z, o( [8 X
7 V1 W; I  M( a9 R$ s) }
五、串口IAP实现3 H: E% m  O: Y, Y' ]/ y: s
本次的目标是实现一个串口IAP,也就是编写Bootloader,在程序运行过程中实现程序的下载。Bootloader程序应该可以通过串口接收上位机发来的.bin文件(App程序),检查后将.bin文件写入到Flash特定位置,然后跳转到App程序运行。1 ?' U7 H2 O% j% `6 V. M

) |: h3 N6 S) p5 y. \7 Y( Z* M) r! P5 e5.1 串口中断服务函数
1 G+ w& P* p( f! S2 q" \本次的串口中断服务函数与之前不同,这里单独贴出来。需要定义一个接收数组,接收数组的起始地址限制为0X20001000。接收数组最多可以接收55K字节,可以根据需要调整。但是需要注意的是,数组的大小需要比App程序要大,而且不能超过芯片的SRAM空间大小。
- c, T5 T& L' ~1 C, c8 b( D" U
  1. /*7 Z3 {: W6 S5 B: {: D6 G' r) X
  2. *==============================================================================
    & b8 y& f; J0 x8 x( {
  3. *函数名称:USART1_IRQHandler3 _2 L! Q; F; N3 L6 n
  4. *函数功能:USART1中断服务函数) _7 \, r! c8 x! D, F
  5. *输入参数:无
    3 B8 V9 l+ f1 T8 b
  6. *返回值:无
    3 b  _3 N5 F: Y* N. G# ]2 W8 ?  o
  7. *备  注:无
    0 b( h7 r1 U. J
  8. *==============================================================================
    8 `* A2 B% |# Z6 A1 ?: U* J5 X
  9. */9 a; }; p" U% U3 x7 M) i2 m9 u
  10. u32 gReceCount = 0;   // 接收计数变量
    0 F, [+ M8 e  ^; c* l
  11. // 接收数组
    6 U7 X0 P/ g7 k2 D
  12. // 限制起始地址为0X20001000/ h/ C2 [$ I; L6 ^0 f, K
  13. // 保证偏移量为 0X200的倍数
    . Y0 W# L' h1 J: `% r6 M, v. a; ~
  14. // 是为了给App留SRAM空间: V. Y) Q$ r8 Q: g- w9 j
  15. u8 gReceFifo[USART_RECE_MAX_LEN]__attribute__ ((at(0X20001000)));  m3 A6 Z2 L1 z7 X* N. R" q8 _
  16. : p, e: g* s% T8 }
  17. void USART1_IRQHandler(void)  
    0 i# B% W3 }) W7 l
  18. {
    ' s. N# [* H# }$ a7 z2 T* e! a3 }4 d
  19.     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   //接收到一个字节  ! P; C, P' v. H  t2 b: S) L
  20.     {  S( I9 {" F" u0 N3 R$ V5 C9 h0 Y
  21.         if (gReceCount < USART_RECE_MAX_LEN)
    . \  e& P/ a7 \: l$ A' E, {8 `
  22.         {
    " K- a2 ~+ h" l; H# ?& |: z/ y4 B
  23.             gReceFifo[gReceCount++] = USART1->DR;
    2 y" _) V  {7 ^  t! w2 g
  24.         }2 w0 U; L5 {/ W' I! f, s5 s
  25.         else; h2 c$ ?0 p5 V$ {1 G4 u5 U( k$ X
  26.         {
    / ~7 b) J* w3 u8 P$ z8 ~# z
  27.             printf ("APP code out of memory!\r\n");" A3 g" H0 k! h4 p% S3 |$ U
  28.         }
    8 W) \  r6 e- n) l
  29.     }
    & |' N# d! ^: E% n2 t5 M
  30. }
复制代码
0 t1 K1 u: D* }9 W) N$ b8 O
5.2 Flash写入程序  B1 f  z* Q/ _0 k3 B: k% `
关于Flash程序,这里就不在赘述,只是贴一下带检查的写入程序。其他具体内容可以到博主STM32速成笔记专栏查看。+ z8 n+ @( `* ~8 T' f6 z
  1. /*
    2 R  `: O4 n, l( d# s( r
  2. *==============================================================================
    & R4 z# a- e. T; d! J
  3. *函数名称:Med_Flash_Write
    % N! Y5 Q- T7 y( o( }2 F
  4. *函数功能:从指定地址开始写入指定长度的数据, z2 j$ d1 u& ^
  5. *输入参数:WriteAddr:写入起始地址;pBuffer:数据指针;7 W" ?8 |; n1 w- j
  6.                         NumToRead:写入(半字)数
    ; \4 y# k0 U+ @& i3 W: S& k# n) I
  7. *返回值:无
    # t% N8 L- h1 u$ P* {& V( O
  8. *备  注:对内部Flash的操作是以半字为单位,所以读写地址必须是2的倍数1 q% i' V  y, }8 e! `+ l: h
  9. *==============================================================================$ w$ C" l' Y3 w& ?) [% h* ^, k3 v
  10. */
    . J- P4 [9 j/ Z+ S, s
  11. 7 j8 D) b; H: }# t% c( F
  12. // 根据中文参考手册,大容量产品的每一页是2K字节
    6 @- r. D; n/ f& I- o% {! c6 C
  13. #if STM32_FLASH_SIZE < 256
    0 z/ y* l) p/ H: C# o2 w2 K) a% z
  14.     #define STM32_SECTOR_SIZE   1024   // 字节& Z1 {; o9 C: G% ^3 `
  15. #else " ?5 v! \/ X' I$ q, R
  16.     #define STM32_SECTOR_SIZE   20480 r5 @7 V: m3 c$ A* b4 Y% V4 W
  17. #endif
    5 K3 {0 S8 [0 k' g" F7 a
  18. - F$ u5 K( d; \. P& ?. S; w# d
  19. // 一个扇区的内存
    * |! A( Q. e5 W+ D5 X5 q0 v$ K" I
  20. u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];5 }4 \  b! T& B2 U) Z

  21. 6 k6 c- I" Y; m8 ~
  22. void Med_Flash_Write (u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)/ q" s; L  A/ U0 }1 Q: d
  23. {
    + k( O2 C9 E+ A3 w- f9 Y9 Y2 K
  24.     u32 secpos;   // 扇区地址0 X  x' S( f8 D) b4 M1 K, `/ s/ G# _
  25.     u16 secoff;   // 扇区内偏移地址(16位字计算)  N' H- C  d* G
  26.     u16 secremain;   // 扇区内剩余地址(16位计算)   
    9 C% }+ G3 Q3 d' Y9 d: k$ N; \
  27.      u16 i;   
    , ]* s' _0 w6 z
  28.     u32 offaddr;   // 去掉0X08000000后的地址% ~; j; E) k( ~/ W4 g$ C
  29.    
    # P7 x  g+ \+ F; _" Q; R
  30.     // 判断写入地址是否在合法范围内
    8 |+ q1 I  y* E2 |6 `# ?
  31.     if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
    9 q! X, }) f# [$ ^- G4 H
  32.     {
    ( b  h+ L" x0 j7 X  b- V' e7 I
  33.         return;   // 非法地址: j* i0 @  I% Z, Y1 O4 t
  34.     }" z! i$ ^( Z7 |1 k; X  ?
  35.     ! {- I) _( f8 j: |, ^+ [. _5 ~
  36.     FLASH_Unlock();   // 解锁6 b5 ?8 {1 T( n% `  @2 e
  37.     offaddr = WriteAddr - STM32_FLASH_BASE;   // 实际偏移地址
    9 R) @7 E; f4 z: Q* r! K. ^$ s
  38.     secpos = offaddr / STM32_SECTOR_SIZE;   // 扇区地址* L8 S- g" ], t  g! a
  39.     secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 在扇区内的偏移(2个字节为基本单位)8 t0 w6 E6 |! s
  40.     secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇区剩余空间大小9 N' p9 h, l+ L; W9 h6 B4 `
  41.     1 H$ M; ?5 c. r0 {+ K4 B
  42.     if (NumToWrite <= secremain)  _8 n4 f6 ~3 G3 k8 a+ O9 {- U
  43.     {
    2 j: h& s8 W! S& W
  44.         secremain = NumToWrite;   // 不大于该扇区范围
    2 O# q0 N$ `" f! ~; ?& t) ?
  45.     }1 O! l% b- i" \( |
  46.    
    9 l. O6 m$ ~$ u) N# \; T; j
  47.     while (1) 1 @8 J3 d2 R0 L; D* y8 r
  48.     {
    & k- S( X( {' ]& [3 `
  49.         // 读出整个扇区的内容8 B5 o' I7 \& v
  50.         Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);' F$ S+ i! \8 \6 a1 B. q% U+ a; L
  51.         
    6 D6 m& o6 C0 Q; |! L" w' f
  52.         // 校验数据
    " m  V% f' x- y! A( S% Y% K0 {$ D
  53.         for (i = 0;i < secremain;i ++)
    7 K5 [5 [. ^& n- l. W5 ]4 k; l
  54.         {
    6 o" T; Z0 P" ]" E
  55.             // 需要擦除
    $ p$ ?* v) K' b. Z% F# p) v
  56.             if (STM32_FLASH_BUF[secoff + i] != 0XFFFF)
      T0 l) E4 ~/ H
  57.             {; Q0 B' l0 s, ~/ x* x- h) A
  58.                 break; # K! ?: x; L8 k+ w! Z. O
  59.             }    - p! f/ l1 O4 Q% b9 a  J
  60.         }% J3 i. g6 v2 b. O) O
  61.         // 需要擦除
    $ H4 \% X/ |2 L* Z* [/ S
  62.         if (i < secremain)
    7 I& V4 v0 k$ b- @: {( M, j1 o
  63.         {
    9 E) K% z3 Q; u" @$ D4 V  m
  64.             FLASH_ErasePage(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE);   // 擦除这个扇区
    7 E- x1 r) P+ e  T, h5 P- Z9 }5 h
  65.             0 ^' x5 c$ Z3 \5 \1 I7 u
  66.             // 复制
    0 v7 W6 l/ W' x8 U
  67.             for (i = 0;i < secremain;i ++)
    % \  \2 z$ i& l( E0 ^3 |
  68.             {& C1 l8 r' X* c8 w; t5 H) ~% A4 S
  69.                 STM32_FLASH_BUF[i + secoff] = pBuffer[i];   , [- n+ Q8 C" N" F: ^* F
  70.             }, v# R( z% g2 K* d8 w1 F
  71.             
    5 V! K! w$ A9 E3 r# y1 ]( C
  72.             // 写入整个扇区
    : t, g. e  W& ?& n# ]6 A, ^1 A" F7 w
  73.             Med_Flash_Write_NoCheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);  w# q- M1 w; n5 B5 d. |4 F9 q: A
  74.         }
    7 c7 v- q, e$ b9 z; |3 P
  75.         else! c9 P" b2 L7 ~" M) o+ ]
  76.         {' ~5 o' @; ?( s; x- {0 _2 \
  77.             // 写已经擦除了的,直接写入扇区剩余区间7 T2 h; o8 e- q0 y! |
  78.             Med_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);
    . r' N; i: h. J3 n8 a+ U8 [
  79.         }
    6 G/ |" `7 @! Y
  80.         5 j8 k" c) u( I) w. `* d, L
  81.         if (NumToWrite == secremain)- o' ?' O# ?, L; v- I$ L9 g! j0 Q
  82.         {
    , O4 c: G% t; h% C3 k5 `
  83.             break;   // 写入结束了* ~5 O$ F, f* U  d2 A3 G" N( u
  84.         }
    4 |! b" r! k! H/ c" n/ M
  85.         // 写入未结束
    - q8 m, O$ S5 q7 Y, n* y
  86.         else# H9 n, g. S3 E" D' z2 _; t
  87.         {/ z' D& ?& \. p1 o1 n2 S
  88.             secpos ++;   // 扇区地址增11 K# R/ n2 E. k, b% A
  89.             secoff = 0;   // 偏移位置为0   ( |1 V) n- q9 d; A5 W9 ]" V
  90.             pBuffer += secremain;   // 指针偏移) ~  W/ ]8 Q- i" J
  91.             WriteAddr += secremain;   // 写地址偏移    : p3 m) ]. z& J) v( C
  92.             NumToWrite -= secremain;   // 字节(16位)数递减
    0 l) I/ t( u: V! n2 O
  93.             if (NumToWrite > (STM32_SECTOR_SIZE / 2))
    2 W# ?2 k' }/ D' x" M: r$ i
  94.             {
    1 O* V6 u" S, V! x: M+ L1 _/ d
  95.                 secremain = STM32_SECTOR_SIZE / 2;   // 下一个扇区还是写不完
    ' S% p5 j8 {2 Z8 [8 U
  96.             }5 ~8 ^. m6 M6 Z, K- z$ i; {
  97.             else
    * h1 ?4 s0 G* x1 ~, r' x
  98.             {
    ; \3 W# X, ?4 _1 I! X9 N* R$ p$ L
  99.                 secremain = NumToWrite;   // 下一个扇区可以写完了
    , j4 C9 r/ _5 {  }
  100.             }
    7 t8 v, r( y5 `* {
  101.         }  " w; d! N5 v: a- H
  102.     }
    - ^8 B  d8 l2 B' d
  103.     FLASH_Lock();   // 上锁& z5 {) l$ ^4 T& B7 z' o
  104. }
复制代码
/ w0 j" n' f! Z4 D' g
5.3 IAP程序+ f) x3 }6 D7 M* Y, o$ M& Q
IAP程序包含两部分,一部分是将接收到的.bin文件写入Flash,另一部分是跳转到App执行。
$ b) Z; l* J4 a
  1. /*
    1 I% Q0 F- I9 e0 `0 O
  2. *==============================================================================
    ( T6 F7 g/ A; t
  3. *函数名称:iap_write_appbin. F% |$ E, w8 @/ p* S0 e
  4. *函数功能:写入.bin文件6 d# ^& w# M! V/ f* z7 ~) P' J
  5. *输入参数:appxaddr:App程序起始地址;appbuf:缓存App程序的数组;/ Q( D( X6 k* t% t0 v( V" ~
  6.                         appsize:App程序大小
    / @# C! {, T& b; W
  7. *返回值:无
    , W) }/ G" I7 D- B/ P
  8. *备  注:无3 `# _: a5 o; M, W& M' a  ?
  9. *==============================================================================! _' M- S1 L- H% C% q) ^# E
  10. */
    1 S6 K( N) s* l3 ]5 X0 G/ A* O* h
  11. // 对Flash操作的最小单位是16位: z3 _" u! P' S; j- a
  12. u16 iapbuf[1024];   // 要写入Flash的内容. u( l9 L! {4 t  R1 S; E
  13. # s+ d. K1 H) l( }' q1 H) k/ k
  14. void iap_write_appbin (u32 appxaddr,u8 *appbuf,u32 appsize)" @& I' j6 D! f; J+ F7 l4 N* P
  15. {" L( F) b, Z  D6 L
  16.     u16 t;   // 临时循环变量
    * t2 v; S4 e( r. U" S$ K: Z4 _9 I
  17.     u16 i = 0;   // 自增索引, F" T! z; t5 i8 t" H2 d) }3 B
  18.     u16 temp;   // 临时计算变量" ?0 o( c5 X0 }5 K9 n4 n5 ?' i
  19.    
    9 b6 h4 H& V: Z: n8 V& D3 _9 w+ M
  20.     u32 fwaddr = appxaddr;   // 当前写入的地址  I% _1 f9 }$ G7 A* E
  21.     u8 *dfu = appbuf;   // 指向App代码数组首地址的指针
    # Q3 j* U4 ~) `. |1 k
  22.     ) p- [% d( I- |7 q: m  ?
  23.     // for循环,2K为单位进行写入
    0 [, i8 a' s2 [
  24.     for(t = 0;t < appsize;t += 2)  r+ q: z5 Q6 U6 {* Z% _; e
  25.     {  . l, P4 p6 P: C+ Z8 M: A2 ^
  26.         temp = (u16)dfu[1] << 8;
    6 }/ s/ N, t* {, g$ O& H
  27.         temp += (u16)dfu[0];   & Z% q: L$ X8 E! Z( n3 _1 L! q
  28.         dfu += 2;   // 偏移2个字节
    2 t. J5 U. X5 W3 @" O1 \
  29.         iapbuf[i++] = temp;     8 ]0 p- }* X; j* y& Q# Z) @: j
  30.         if(i == 1024)
    # r  |" u" Z+ e9 V
  31.         {7 Y$ V9 h% P. y
  32.             i = 0;
    + X1 G+ j" P5 q5 S  K6 \( {- v
  33.             Med_Flash_Write (fwaddr,iapbuf,1024);
    ; m. B6 U# T2 o' b8 Y
  34.             fwaddr += 2048;   // 偏移2048  16=2*8所以要乘以2" J# W# ~2 ?: U5 |: ^2 q3 d7 K: d
  35.         }
    5 e  {! ?7 z. A7 l$ g; o
  36.     }  o' P! N: F+ s- x6 c
  37.     if(i)
    # u: M9 s2 R2 N& Y$ e# Z) f
  38.     {0 b% B8 H+ @8 R6 G  P
  39.         Med_Flash_Write (fwaddr,iapbuf,i);   // 将最后的一些内容字节写进去8 k! F4 q4 I# `
  40.     }5 r" R0 h2 V4 q
  41. }8 C7 h+ b+ {. {& y8 G
  42. /*
    6 m$ `* T; F8 h1 R# O
  43. *==============================================================================  F7 B0 Q0 _7 t+ F) d+ h& e0 M, T2 ]
  44. *函数名称:iap_load_app& B$ e7 F" B1 Y$ m
  45. *函数功能:跳转到App) ]" G8 b) k" E0 s, h& D& x, h* E2 b
  46. *输入参数:appxaddr:App程序起始地址
    + p8 h/ ~& c1 v' \* Y* N" A
  47. *返回值:无9 @' H; T" G9 o$ w
  48. *备  注:无
    & X* G1 c1 N% f' P7 R
  49. *==============================================================================
    ( l" d( N1 G3 w7 Q* p1 {; C
  50. */
    & v9 F' e7 S% p" f' e
  51. iapfun jump2app;
    5 H# ^" `1 T" }: C, h& i3 l/ t
  52. # ?# K0 q4 D* Z& Y; S( l( s
  53. void iap_load_app (u32 appxaddr)% G' |0 v6 Z2 X  d. o' V3 W  L# T
  54. {
    # [2 p' u8 Y) R" q& Z4 j1 i2 c% _
  55.     if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)   // 检查栈顶地址是否合法) a3 J2 Q& Y5 k% R  e& y/ D* g
  56.     {
    8 A9 r) n& e; @% V  q
  57.         jump2app=(iapfun)*(vu32*)(appxaddr+4);   // 用户代码区第二个字为程序开始地址(复位地址)  
    0 t+ ?  O( q! U7 ?
  58.         MSR_MSP(*(vu32*)appxaddr);   // 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)1 W- r- f7 H9 X' g; _9 K* t
  59.         jump2app();   // 跳转到APP: ~. d$ h; m4 U5 f
  60.     }
    - H, J8 n; D# h& _
  61. }
复制代码
, H; ~7 M0 g* l
5.4 main函数- q7 d" @! f0 U5 U# k# O
main函数设计如下% x& n' X' f- G; J3 V. Q$ c/ k. u
  1. int main(void)) }  V+ Z& Q6 |2 Q" ?0 e
  2. {
    ' x& k$ R8 P$ a
  3.     u32 curRecCunt = 0;   // 当前接收数量
    1 N6 F; x) O, i! r9 s# l  C& C
  4.    
    ; f% f$ a7 a" B! G) R) m' S! Y
  5.     // 设置NVIC中断分组2:2位抢占优先级,2位响应优先级" Z4 n9 @" D7 m$ p+ u2 |
  6.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    8 l, J- O" y- ~0 ~
  7.     delay_init();   // 延时初始化( t. }. N7 f6 k
  8.     uart_init(115200);   // 串口初始化5 }* p1 C' y0 S$ _) {5 r2 S6 y
  9.    
    6 n# V$ d& r9 R9 q- W. g& H
  10.     printf ("Enter the Bootloader Code!\r\n");4 o) x  X$ p; n- p
  11.    
    ! m- l& ?. m) Y
  12.     while(1)
    + v( M4 i) m# K6 \
  13.   {
    . a) n$ B6 P2 l" q- c$ W( W" n
  14.         // 如果接收到内容且传输完成
    4 l8 g! c% {. x9 z- N9 |
  15.         if (curRecCunt == gReceCount && gReceCount != 0)$ G" {* i; |# _, Q: V. k- @
  16.         {, n, Z3 B* t' c2 m% g: v" f. z5 J
  17.             printf ("App Code reception complete!\r\n");
    0 q( `" F% Z* H) ?1 j
  18.             printf ("The length of the received App code is %d!\r\n",gReceCount);
    , L1 l, C8 R7 G* q+ u. `
  19.             5 C' p5 v4 g! f
  20.             // 开始准备写入Flash. |* C( s$ E$ B& u" A- n
  21.             if(((*(vu32*)(0X20001000+4)) & 0xFF000000) == 0x08000000)   // 判断是否为0X08XXXXXX.
      l! ^. e8 H; c% z* Y- ~. c; @* r! ^
  22.             {
    1 b; n5 R9 n8 I6 n4 q/ d& ^
  23.                 iap_write_appbin(FLASH_APP1_ADDR,gReceFifo,gReceCount);   // 更新FLASH代码  ; q! n7 @1 a8 }9 E% `, |
  24.                 gReceCount = 0;   // 清零接收计数变量
    1 y- m% p) |' N+ p. H
  25.                 printf("Update complete!\r\n");/ g: s6 R6 D3 J; ~8 j8 r
  26.             }
    ( V6 ~* D! a/ T; L( A
  27.             else
    ) H+ m+ Z; n2 M2 |
  28.             { , c6 _# C+ E, z
  29.                 printf("Update error!\r\n");
    , M0 \; ?' {( _* Y' Y0 _: P
  30.             }
    3 p  m% {5 @8 _* p: W' y8 A8 e" T
  31.             
    ! a' v: s5 E" I$ t/ |5 g
  32.             // 准备跳转App执行( a/ g8 x) G) S5 d
  33.             if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)   //判断是否为0X08XXXXXX.; J1 Y- v! o! M+ D
  34.             {
    2 B& k% z& M# N& w* I5 U8 a' `
  35.                 printf("Enter App!\r\n");7 i7 F1 R* V* {+ u
  36.                 iap_load_app(FLASH_APP1_ADDR);   //执行FLASH APP代码3 |2 G- h% w% J; {
  37.             }else
    ! z: ?2 E, R# A1 s! C6 u
  38.             {- @4 r5 n1 n# i# c# O9 T
  39.                 printf("Enter error!\r\n");) J; S0 Q! W; j8 t7 n$ J' J  |" @
  40.             }
    3 d/ |1 h4 P5 R6 Q4 V: f
  41.         }, `, ^  j; M" a0 w: y* i+ b/ A
  42.         else   // 未传输完成; `! {/ ~4 e6 A% I9 S
  43.         {
    ; q/ H' Q7 B( S
  44.             curRecCunt = gReceCount;   // 更新当前接收数量) s( C% [% k3 Y, _$ W6 |- Z
  45.             // 延时一定要有,给串口中断留出时间2 i5 {7 b9 N* w/ N( x
  46.             // 如果没有会导致接收不完全$ t9 v; F0 d/ p  p) _- T
  47.             delay_ms(10);( x4 |3 d5 V% ]: @5 M. V
  48.         }
    0 k5 I( w7 [7 X! n
  49.     }2 Z+ L2 Z- J4 \9 M/ k  m4 ^
  50. }
复制代码

0 |% Q, t+ H0 y+ l6 [' Z六、注意事项
: D, |  d! R* ^) G需要注意的是,上面的程序中App程序是从0x8010000开始,需要配置好程序起始地址和中断向量表偏移。( \' x  v: j  P  y9 I$ |

. d" @, I. F, q3 P
$ k  D# l$ k( f/ J) O9 A4 |$ s转载自:二土电子6 j' W6 E( u/ t8 F3 J0 H
如有侵权请联系删除
2 D% m) J4 H1 _7 @: |- C6 x
+ ~8 M! W* F$ O: I* }5 J
收藏 评论0 发布时间:2023-10-23 22:37

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版