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

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

[复制链接]
攻城狮Melo 发布时间:2023-10-23 22:37
一、串口IAP简介: \' O" y) k, e% m. C# l
1.1 什么是IAP
+ E1 ~4 J; `5 l  M# A- J
IAP,英文全称In Application Programming,在应用中编程。很好理解,就是在程序运行过程中我们进行程序的烧写,或者叫升级。; V9 m3 ~) {. ]" H
: c$ j' L' y0 W( L) X) O" `
1.2 STM32下载程序( b- k9 `' K6 c  `
我们都知道,STM32可以利用串口下载程序,这是因为ST公司在产线上就在产品中内嵌了自举程序。所谓的自举程序,实际就是支持我们通过串口下载程序的代码。自举程序被存放在系统存储区,因此如果我们需要通过串口下载程序,需要将Boot0接高电平,Boot1接低电平,让程序从系统存储器开始运行,运行自举程序。下载完成后我们再将Boot0接地,让程序从主闪存存储器开始运行。自举程序是我们用户无法修改的。
1 \6 O" E. U7 v  u: }% |/ x- }+ Y) z; L  f; P7 `) `8 T

& x. _; \: }9 v* l二、串口IAP有什么作用) [, g$ A& O" J" y  `* _0 Q9 a- S
上面我们介绍了什么是IAP,那么这个IAP到底有什么作用呢?% }$ x+ S8 S9 ]
2 u: f2 O" j5 F% y& P3 o) v  k
首先介绍两个词——Bootloader和Application。Bootloader实际就是一段引导程序,单片机上电后先执行Bootloader程序,然后再执行用户编写的应用程序Application。介绍完这两个词,我们来介绍一下IAP有什么作用。比如我们生产了A,B两款产品。A产品是某个精密器件的一部分,B产品是一款物联网产品。我们的产品销售范围很广,远销海外。. x* I; M& y) T  k5 x

+ h3 B5 n0 Z. E1 Q. V某天A产品的某个客户反映了一个Bug,我们编写好了程序,需要进行程序更新,或者叫升级。利用IAP,我们可以在程序运行时,通过预留的通信接口直接烧写程序。而不需要再把整个设备拆开,像我们调试时那样下载程序。甚至我们可以直接给客户邮寄一个小设备,客户直接进行傻瓜式升级。: z: K* G7 e1 {# ~  }9 L3 n

1 l9 C* I. \# ~) o9 i7 y又过了一段时间,B产品的程序出现了一个Bug,风险等级比较低,但是依旧需要全体升级程序。我们总不能挨个产品派人去升级,成本极大。这时候又轮到我们的IAP出场了。它可以在所有设备在线运行的情况下,直接通过网络下发升级程序,实现在线升级,节约了大量的人力成本。
8 R! P; N4 l. `% `/ P& T0 E' w3 N; Z$ J- s; O* E4 z0 h
通过上面这两个例子,大家应该能够基本了解IAP的用处,使用IAP让我们不需要再使用调试器进行下载,甚至实现设备的在线升级。/ f' ?9 V  W% Z, w. V6 Q1 E' I: J
; w6 g, F2 A: R: o! q* P
/ s) u& c) A5 n6 ]$ f7 Y4 u
三、启动流程2 e7 Q/ n. s6 E5 q- m
在介绍如何实现IAP之前,我们先来简单了解以下STM32的启动流程。
3 ]6 z7 L7 z* Y, N: M$ O8 O
% b5 @, W9 }' B( L- U- h3 g
3.1 正常启动流程' l2 n# Q2 y8 \/ p0 h' e
这里的正常启动流程指的是,没有添加IAP的流程。: h* I- k) c- }% t) `
8 z4 C! d  C6 i1 F/ D- u* ?! e. w
* C3 d% w3 ]: p4 k9 m6 w
微信图片_20231023223616.png
* I* \; U7 e* }
正常启动流程

8 G* O: \! f$ L* |, \

& E( I& L; s9 N, h9 G
程序启动时首先开辟栈空间,配置栈顶指针。然后配置堆空间。配置完成后,建立中断向量表,在中断向量表中找到复位中断,开始执行复位中断服务函数,然后跳转到main函数中,执行用户代码。当用户代码中有中断请求时,会回到中断向量表,根据中断源执行相应的中断服务函数。
7 a9 Y0 k1 M0 {$ q8 P, M) @, l
! j4 n% n7 x( y) R! q4 f3.2 加入IAP后的启动流程
. p1 |: [. {  Z# q% o4 I下面是加入IAP之后的启动流程。( ?4 ~/ L4 i2 e5 H

' L1 |6 F3 Z" i
微信图片_20231023223619.png

( M: p- E" G0 n& g: M
加入IAP启动流程

: t8 m* ^  I% g) A
# s3 l: }( E8 D0 S3 I6 q. c

7 Y' J- n8 \5 B0 G0 ^0 W: @2 X
可以看到,与上面不同的是,加入IAP后,执行完复位中断服务函数后直接进入IAP的main函数。在执行完IAP之后,跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main 函数。7 F% c+ G0 {- Y/ o  ^4 {! e
' `# ^- b' U( v) S$ Y
由上面的两个启动过程我们可以看出- T$ [6 x& l, {" ?! O) u# [  h& N
• 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始。% y2 n3 S2 G; a; B- v1 }( [. S- j
• 必须将新程序的中断向量表相应的移动,移动的偏移量为 x。& u/ D; U, }. g4 j  E3 Y, Z+ C
4 r+ H3 Y% L3 N: ]  R0 e

% s8 @2 N' B8 s% c1 m  y四、必备知识
8 u9 A# x3 {/ R: U- ]9 [9 w44.1 修改程序运行起始地址
+ w( z2 d: x) u  g1 r8 o7 K! N
点击魔术棒,选择“Target”,修改运行起始地址和代码大小。1 {# A& v1 Z; {5 y/ f
7 A$ K7 j8 b2 `4 _8 m5 z
微信图片_20231023223624.png

2 L) s: _( Z* j. }
修改App运行起始地址
9 ?& ?! M+ @+ j

6 W2 Q' ^# g& }0 X2 B' h" c# ~% e1 [' Y4 Z/ G
4.2 设置中断向量表偏移( y* ^' l2 }0 c' V: m5 I& o/ Q
VTOR 寄存器存放的是中断向量表的起始地址。如果要设置中断向量表偏移,只需要在main函数最开始添加如下语句即可
5 a( D) D6 v3 B" _" e3 x, G
  1. SCB->VTOR = FLASH BASE | 偏移量:
复制代码

' {8 N. T" p! C1 G0 e$ o4.3 生成.bin文件0 I0 a' k* e% D; [1 }) H4 H
点击魔术棒,选择“User”,按照如下配置,输入下面的内容
$ }: F9 N+ z, H: R
  1. fromelf --bin -o "$L@L.bin" "#L"
复制代码

/ _* |$ ]6 i( N3 u8 G2 J: c6 b
微信图片_20231023223706.png
- e6 y. d$ S" Z6 u% C! z: W# c' T6 p5 V
生成.bin文件配置

, _6 @8 r) v% M

- C; O" l$ h1 i- A. h' r9 K5 E0 Z. Z0 b
点击编译,不报错就可以,去设置的输出文件夹中就可以找到对应的.bin文件。
, \, a6 ~# |$ j3 {

$ k  c! W8 |7 }8 H7 i
微信图片_20231023223710.png

% S7 y$ J! I' D2 ^% ^# s- `) G& R
编译提示
% B: n$ s; o8 m5 t8 Z
% Y' D1 l( b0 s* b, O5 b
+ c) S' H5 z1 l1 X; ~. B
五、串口IAP实现; r6 ~  x8 w8 {$ \$ R+ N
本次的目标是实现一个串口IAP,也就是编写Bootloader,在程序运行过程中实现程序的下载。Bootloader程序应该可以通过串口接收上位机发来的.bin文件(App程序),检查后将.bin文件写入到Flash特定位置,然后跳转到App程序运行。
7 m8 e& ]) p/ D8 x+ f5 h" F
: S) D. q6 t( S" }7 f/ W, _  C5.1 串口中断服务函数
( c5 k5 x6 ^1 v  j* o本次的串口中断服务函数与之前不同,这里单独贴出来。需要定义一个接收数组,接收数组的起始地址限制为0X20001000。接收数组最多可以接收55K字节,可以根据需要调整。但是需要注意的是,数组的大小需要比App程序要大,而且不能超过芯片的SRAM空间大小。9 [3 `. [  h2 K" _  c, W& E- C& W
  1. /*# t2 u9 V$ I5 ^* g2 r2 B
  2. *==============================================================================
    ' h7 k3 P7 ~4 ^& p2 u- s0 q2 p
  3. *函数名称:USART1_IRQHandler
    7 V5 \% A; I3 E1 p
  4. *函数功能:USART1中断服务函数
    4 m; B0 ^( X5 G7 ?6 Q/ }) f6 J+ y
  5. *输入参数:无# q" {. Z. f7 U: k% ^
  6. *返回值:无
    * a+ y* T: x4 d, h9 z/ [2 K
  7. *备  注:无- f; h. C8 Z# I0 M
  8. *==============================================================================$ U  ?% }0 h- A+ C" |; M1 N
  9. */' q3 H7 G! S/ b  L( A/ ]
  10. u32 gReceCount = 0;   // 接收计数变量
    ( i  [: F2 o( @6 d' L5 D
  11. // 接收数组
    $ B* s" l. M: K' U4 a
  12. // 限制起始地址为0X20001000( C2 t) m- v0 P
  13. // 保证偏移量为 0X200的倍数
    1 `& L5 K9 h* k# r$ Q' r7 L
  14. // 是为了给App留SRAM空间! F4 M* ?8 W& V
  15. u8 gReceFifo[USART_RECE_MAX_LEN]__attribute__ ((at(0X20001000)));0 _( N. s5 ?' U. _" x- J
  16. 8 J$ H+ }* w, i4 L# P' m" ?/ p
  17. void USART1_IRQHandler(void)  ( k! x8 ~1 G* m& w0 P5 O4 t
  18. {
    & X, `- m( d  w8 c( h) p
  19.     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   //接收到一个字节  
    % R6 @) T5 u- a# x
  20.     {
    8 n9 t$ A3 y- R0 Q5 Q$ m9 r
  21.         if (gReceCount < USART_RECE_MAX_LEN)3 r3 C9 F4 N$ t; J0 d5 p
  22.         {, h4 v' S' V8 m' u: N% z
  23.             gReceFifo[gReceCount++] = USART1->DR;+ a" S# U* g6 G6 `, E: w  Q2 r* F
  24.         }
    , R# k4 D& i6 W9 L# U
  25.         else+ E, f0 x% S$ T% N' C
  26.         {- ~& Y! f$ n+ f8 O/ J. i
  27.             printf ("APP code out of memory!\r\n");
    # [; p/ z$ k  N2 A8 Z" ?' G+ ~; e8 {
  28.         }5 I: q% B6 k+ O% `  K
  29.     }
    - x% A: H& f9 Q4 r) b& b
  30. }
复制代码
- T6 Y. j4 \5 x  V% ^7 e' q: Q) s
5.2 Flash写入程序
3 ^3 Q$ U8 K) ?* @. ?关于Flash程序,这里就不在赘述,只是贴一下带检查的写入程序。其他具体内容可以到博主STM32速成笔记专栏查看。) e% v& L' M  [6 _* b/ `. y
  1. /*
      y+ R4 f0 U, v. C! J, W, r0 u
  2. *==============================================================================
    . p3 U7 k8 l7 w9 f  Q0 F- r
  3. *函数名称:Med_Flash_Write
    : g+ f& ~6 d  ?; L+ r
  4. *函数功能:从指定地址开始写入指定长度的数据
    ! f& ^$ f* Z8 R. z
  5. *输入参数:WriteAddr:写入起始地址;pBuffer:数据指针;9 D9 [! Q$ _3 e0 v0 F
  6.                         NumToRead:写入(半字)数
    ! k! `+ l# ^& [, C
  7. *返回值:无6 U3 U7 ~+ k! u
  8. *备  注:对内部Flash的操作是以半字为单位,所以读写地址必须是2的倍数
    1 l) G( z' P7 {( W1 b' L
  9. *==============================================================================
    5 X" x1 Y2 \. f3 M8 ]4 K
  10. */
    2 e* b& e) g8 n, ?

  11. / n0 r8 O& M- ?# }: T6 J
  12. // 根据中文参考手册,大容量产品的每一页是2K字节) @) _" N, u$ b# l0 I1 n% {
  13. #if STM32_FLASH_SIZE < 256- E% N  u2 Q2 s+ A
  14.     #define STM32_SECTOR_SIZE   1024   // 字节& F% E1 x0 H: K; j& N2 \9 d( C
  15. #else
    0 S# I& a  j* X" X5 J6 U/ X
  16.     #define STM32_SECTOR_SIZE   20481 l9 c' J# b0 O3 I5 b/ {
  17. #endif
    # P% S. Q4 {  b! ]

  18. , G  I. V6 W4 v. z# t- f
  19. // 一个扇区的内存1 ~' m7 b8 b+ B
  20. u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];! j' Y' A7 l7 z" @5 T. L( Z( ~

  21. * |  h1 j6 R$ z( J2 x& i- I* i# h+ G! B
  22. void Med_Flash_Write (u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
      q7 {) m  Q9 v% Z5 s" ^! g1 W, A
  23. {
    # H, o; b4 [' o8 G: ?
  24.     u32 secpos;   // 扇区地址& b8 D7 q0 F7 V9 [4 ~4 z, v
  25.     u16 secoff;   // 扇区内偏移地址(16位字计算)/ a! K8 e& o  P& D
  26.     u16 secremain;   // 扇区内剩余地址(16位计算)    " L$ Q) \% u" }6 \
  27.      u16 i;   
    . \- C5 f3 n7 @" B* G) C
  28.     u32 offaddr;   // 去掉0X08000000后的地址
    , ^. Q% f! W; q, C% h% R2 D
  29.     & P! ?0 Y! ?* o4 e
  30.     // 判断写入地址是否在合法范围内
    9 Y* ^' Z& W, b' Q
  31.     if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
    0 A, A- d6 `# Q9 B8 [8 A3 ?, J
  32.     {
    8 |( x2 j2 g4 X' t; x
  33.         return;   // 非法地址& ~3 w* {+ _! R# i2 g) j- K) n
  34.     }
    . L' l5 C1 j4 Z' J
  35.    
    7 j( j+ G  ?& \. O
  36.     FLASH_Unlock();   // 解锁
    9 D3 c! W  ]! d) P6 e4 {* N- {
  37.     offaddr = WriteAddr - STM32_FLASH_BASE;   // 实际偏移地址
    : ^7 f" s9 O* T8 u6 x. w& L0 m; y
  38.     secpos = offaddr / STM32_SECTOR_SIZE;   // 扇区地址- i+ n9 R( T* e7 T& v- M- A/ m9 S
  39.     secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 在扇区内的偏移(2个字节为基本单位). G% i, f6 w/ c, ^* ?9 h) t: \3 C
  40.     secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇区剩余空间大小, F# I3 |9 u3 g6 K& d9 E
  41.    
    & u( \, x# |" n3 }
  42.     if (NumToWrite <= secremain)- \* K7 W& K* i  F
  43.     {
    8 p, n7 K; P, n0 w) h& h- b" d) F
  44.         secremain = NumToWrite;   // 不大于该扇区范围
    2 s) {; ]! l5 X. w) F6 t* M8 P# Y$ X
  45.     }
    ( |: y; F6 }& ?( p
  46.    
    ( P1 w$ ^: e1 E4 M: V* _9 z
  47.     while (1) 0 c. Q! s$ W( G- S
  48.     {
    0 t2 H/ ?# n* o# x2 ^
  49.         // 读出整个扇区的内容
    ( d; a5 l3 d8 ^9 H7 W+ M$ J
  50.         Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);' |7 w: U0 V. {0 ]5 l, r
  51.         8 y6 d/ T1 Z! b1 h3 h6 e
  52.         // 校验数据
    3 B( q/ x; }: ?- S- b$ h3 K
  53.         for (i = 0;i < secremain;i ++)" H2 [6 @/ T" _  E4 d+ a
  54.         {
    6 ?$ D& ?7 p3 M( x* C" ^' t
  55.             // 需要擦除 : m2 v# C, A2 k, U
  56.             if (STM32_FLASH_BUF[secoff + i] != 0XFFFF)
    $ V; v& ~$ w- Z/ p$ Z9 l  Q
  57.             {
    3 i& A3 @0 Z: k: h. p& q/ ?+ q0 ~$ O
  58.                 break; 7 A* Z  @" W& P8 B! e
  59.             }   
    1 ~  C- \' I2 i* K5 ~
  60.         }
    3 X+ Q: L3 P' t1 v. R. `5 q
  61.         // 需要擦除3 p3 k; N6 x; h
  62.         if (i < secremain)
    " \5 d. p7 }4 i! \# e7 G
  63.         {
    : [2 }- F  `- t3 f5 z1 ~. d
  64.             FLASH_ErasePage(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE);   // 擦除这个扇区
    + o  f, z1 G9 H& g; G
  65.             3 i/ G# _" G/ F6 i
  66.             // 复制
    ; _" \* n) X+ f- r
  67.             for (i = 0;i < secremain;i ++)
    , V- A9 S% y- ~5 F
  68.             {2 Y- V) z! `; z6 A& j
  69.                 STM32_FLASH_BUF[i + secoff] = pBuffer[i];   ) r% _: _/ |' G4 g) q) x% \+ r9 l9 I
  70.             }
    3 ]0 ]1 Y4 z0 T* E
  71.             $ j* j* _2 s3 R, N
  72.             // 写入整个扇区
    : D4 L0 X& U6 R
  73.             Med_Flash_Write_NoCheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
      Z; e' B& l  b6 O
  74.         }' M2 O9 `+ V7 W0 y- K
  75.         else
    0 V5 K! {* w4 z, L% ^+ ]
  76.         {
    ) H6 u9 f1 u: q  R: m$ o. s: s
  77.             // 写已经擦除了的,直接写入扇区剩余区间
    ! I- o2 K* |' A, k+ Y9 w' c/ d
  78.             Med_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);
    ( H2 k6 M( Z; @. o
  79.         }4 n9 A; L0 d0 b8 \. ^
  80.         
    . D5 {5 I! r. i0 j( I
  81.         if (NumToWrite == secremain)" X) U5 V3 D7 t$ U7 k4 H' J
  82.         {
    ) Q6 ^- w! u. y3 X: [/ P. {) l* V
  83.             break;   // 写入结束了
    ) O2 H  p* I1 H5 m4 y
  84.         }
    8 j3 ]2 i+ h% c: C* k* o
  85.         // 写入未结束
    0 ]- {% {1 Q& v8 q( @
  86.         else
    # N! D# h3 m# z: A1 a, |, }
  87.         {
    7 V$ r0 v9 ]( w0 d7 e
  88.             secpos ++;   // 扇区地址增1: T$ A- b1 `) \' `$ _. B$ T5 S
  89.             secoff = 0;   // 偏移位置为0   
    $ @: W. h4 s. \" D1 F" s+ O. K
  90.             pBuffer += secremain;   // 指针偏移, Q! y; U9 N3 z9 J( q7 v
  91.             WriteAddr += secremain;   // 写地址偏移    - o  R# k. g$ c, ^9 A* F6 L
  92.             NumToWrite -= secremain;   // 字节(16位)数递减9 t0 W3 U4 F' \4 @; p, x
  93.             if (NumToWrite > (STM32_SECTOR_SIZE / 2))
    9 e, E7 g5 @# Y; C
  94.             {$ m; h* _* D# p$ |. K
  95.                 secremain = STM32_SECTOR_SIZE / 2;   // 下一个扇区还是写不完5 {  ]5 ^+ X; ?* V/ s0 \
  96.             }4 {3 ~: Y* m6 x# {& p
  97.             else
    * {. C) H7 ~: Q+ y! a% p
  98.             {
    . o# S5 A" |! E7 S# i" A
  99.                 secremain = NumToWrite;   // 下一个扇区可以写完了
    0 _$ T! y1 f. l2 w) @
  100.             }
    8 ?7 Z, ~0 O0 \% d/ h2 |6 x. S
  101.         }  1 k( m' t3 F: r( C8 w/ p; X' V( o
  102.     } / \- t+ H1 o0 z* n7 ~
  103.     FLASH_Lock();   // 上锁
    & B) T# O& b& c7 B& ^$ y3 N3 g
  104. }
复制代码

) X: M  U1 N( `5.3 IAP程序$ ^% n* t& c, A% X
IAP程序包含两部分,一部分是将接收到的.bin文件写入Flash,另一部分是跳转到App执行。; w5 A2 ?1 ^( j
  1. /*1 M% }, r, A* r" M
  2. *==============================================================================
    : Q* `1 R$ ]1 A+ E9 P
  3. *函数名称:iap_write_appbin& O2 q2 {5 l( n0 j
  4. *函数功能:写入.bin文件( h7 G0 N; d. l$ y2 r3 l9 b9 Z+ Z
  5. *输入参数:appxaddr:App程序起始地址;appbuf:缓存App程序的数组;
    2 ~8 I/ b9 X' [, G+ y$ Q' c; r5 Y/ y
  6.                         appsize:App程序大小+ O+ g# k, l  l) M. O$ F
  7. *返回值:无
    3 v; l( U- c2 Z3 L1 F" X
  8. *备  注:无, ^2 Q6 k, x" f- j
  9. *==============================================================================
    9 q% y" P, Y5 m: n
  10. */
    1 l' n8 J- I0 D. h9 p" \& g' P. O
  11. // 对Flash操作的最小单位是16位# A: m% u1 @! I8 x; Z& y
  12. u16 iapbuf[1024];   // 要写入Flash的内容& F$ }  y0 i" l8 f# U+ D

  13. 9 w) ^. M, ]* W. n, p* T
  14. void iap_write_appbin (u32 appxaddr,u8 *appbuf,u32 appsize)- n+ l# f# _; O$ N4 I5 p) i
  15. {
    : u, p4 \# ]' Z) s
  16.     u16 t;   // 临时循环变量, d4 J* E3 g: J6 S# P+ I; C
  17.     u16 i = 0;   // 自增索引( r- s9 U2 a. M3 s. I
  18.     u16 temp;   // 临时计算变量! \) `7 N4 K9 L. L
  19.    
    0 J  S5 U  S. }& H" f
  20.     u32 fwaddr = appxaddr;   // 当前写入的地址  q" V& @2 G5 v( p6 Q6 k/ D* X
  21.     u8 *dfu = appbuf;   // 指向App代码数组首地址的指针
    ) i, {& q0 S8 Z& _5 A0 F
  22.     $ N/ d! G; S' Y3 [9 ^# G4 g
  23.     // for循环,2K为单位进行写入3 B& P: O- Z/ c
  24.     for(t = 0;t < appsize;t += 2)
    . K8 H" K" F/ X4 j1 q
  25.     {  
    " ]/ v! ^+ ~; y/ k" E$ }
  26.         temp = (u16)dfu[1] << 8;
    ) ~* U4 T1 a1 H4 O! |$ N; s
  27.         temp += (u16)dfu[0];   9 [: |. R9 ]( w4 |, I9 ?
  28.         dfu += 2;   // 偏移2个字节
    ( h0 W% d( k2 s
  29.         iapbuf[i++] = temp;     
    % \' V8 z* V$ Y  u2 \& o
  30.         if(i == 1024)
    : s' S1 D5 }0 y! w
  31.         {
    - n% {% O5 V: r/ {1 F# Q2 g
  32.             i = 0;
    ( o0 w/ O! a% j
  33.             Med_Flash_Write (fwaddr,iapbuf,1024);
    : K: u" M/ A1 O9 T/ A
  34.             fwaddr += 2048;   // 偏移2048  16=2*8所以要乘以2
    ! [5 M9 a, k. y' [! l
  35.         }- Q  W% Q7 Q& _" r4 E  c3 y
  36.     }  L! C' a' K$ }5 N
  37.     if(i)
    0 W5 D6 o0 i; g
  38.     {
    1 a& D( ?/ k- H9 I( |5 P7 j
  39.         Med_Flash_Write (fwaddr,iapbuf,i);   // 将最后的一些内容字节写进去
    6 z7 k; T$ c; f. x
  40.     }$ b7 a; S  U5 M" B6 m
  41. }
    2 S5 P+ O+ {3 U# J! g5 w, x! N& V& C
  42. /*
    9 b9 j  E1 X+ u6 k. R5 l6 T8 J) [
  43. *==============================================================================
    ; O4 C# G# c* L& n8 D
  44. *函数名称:iap_load_app
    6 {2 K" N2 z8 G7 U+ f
  45. *函数功能:跳转到App$ z5 h! ?$ l, n- o
  46. *输入参数:appxaddr:App程序起始地址
    & }! f6 C: @5 T0 C
  47. *返回值:无# v/ x0 c0 ^% |& j& T
  48. *备  注:无( q9 H. v3 p# L2 G) ~+ j& k, J
  49. *==============================================================================9 b+ l" ~) c/ U4 ]. ?/ N3 p2 b
  50. */
    , H' N" S6 m& o0 ~% J: b
  51. iapfun jump2app;. X) z; J% s3 ~( F$ c, T+ p

  52. 3 |! e2 H2 i2 A  G& E- X
  53. void iap_load_app (u32 appxaddr)
    8 g; b2 o! O8 _/ r
  54. {
    & {7 L1 _9 i/ Q7 ^5 ?8 O1 v: L
  55.     if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)   // 检查栈顶地址是否合法7 o' ?- D$ P# r6 R# z5 G
  56.     { . c' Y6 z# P2 B- p3 O/ ]) t
  57.         jump2app=(iapfun)*(vu32*)(appxaddr+4);   // 用户代码区第二个字为程序开始地址(复位地址)  ) `+ V/ n' {+ t' {+ Q# P3 H
  58.         MSR_MSP(*(vu32*)appxaddr);   // 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)1 @  t- I$ t% k0 X2 W" U3 I
  59.         jump2app();   // 跳转到APP3 Y: l9 C. j3 @5 M. t
  60.     }
    * B8 p0 P2 P( a  Y2 V/ J
  61. }
复制代码

0 z# X# ]5 p/ k) ?7 n5.4 main函数
, x" J6 n  M) tmain函数设计如下
4 m( D: V6 U* G% |8 V
  1. int main(void)
    ; _2 Q" K" A. E
  2. {
    " G. X* r0 A$ l& h4 q' G( h
  3.     u32 curRecCunt = 0;   // 当前接收数量. N, R/ a6 r% h2 a9 D$ Q9 C( n
  4.    
    4 ~; [/ _# B3 d. S3 X
  5.     // 设置NVIC中断分组2:2位抢占优先级,2位响应优先级# E: a7 ]$ y* G9 e3 L& l. f) J
  6.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    ; [/ t& h) M' K; f+ M5 S
  7.     delay_init();   // 延时初始化
    - x2 k- d. g8 ^, h
  8.     uart_init(115200);   // 串口初始化6 v2 c* O- q( T( Z0 @; e# ]/ G- x# V
  9.    
    ! _5 r( B) F2 [- L% H& U
  10.     printf ("Enter the Bootloader Code!\r\n");$ @4 f# Q  \9 T& X5 _' g2 R
  11.    
    $ d- p* {- r8 l2 Z. }4 O
  12.     while(1)3 ]; @% e3 t  C
  13.   {! }9 d' F/ j6 M1 J; B( s7 F
  14.         // 如果接收到内容且传输完成$ ?* v! X& I: G8 P2 ?9 H1 N/ B0 M
  15.         if (curRecCunt == gReceCount && gReceCount != 0)
    . E9 G. j% L$ @- H: W
  16.         {4 e, g# q. C) q/ I+ L3 r: d! j' ]+ f
  17.             printf ("App Code reception complete!\r\n");( r7 w$ L; U3 E  w. y5 a8 R+ c
  18.             printf ("The length of the received App code is %d!\r\n",gReceCount);5 A* S( {# Z; y$ m6 @. x
  19.             ' U1 T. u$ c8 P
  20.             // 开始准备写入Flash' x, ], F" o0 u- N4 |# Y7 L% N
  21.             if(((*(vu32*)(0X20001000+4)) & 0xFF000000) == 0x08000000)   // 判断是否为0X08XXXXXX.$ _% t" d+ z' ]1 K! \
  22.             {1 n- o9 O# @5 ]$ g; p/ n5 @% w# ^
  23.                 iap_write_appbin(FLASH_APP1_ADDR,gReceFifo,gReceCount);   // 更新FLASH代码  , p: T- d" W* h5 I, Y# k
  24.                 gReceCount = 0;   // 清零接收计数变量
    - U! T. r/ a$ j, _  _: u+ |
  25.                 printf("Update complete!\r\n");% {( R- C* m; k8 d1 m: N
  26.             }9 n+ W* m$ @3 P4 Z& o
  27.             else
    , {% V- g8 O' n
  28.             {
    8 }! Q" ^  a9 @6 `) b# _
  29.                 printf("Update error!\r\n");8 M. i+ `0 e; m/ F. a
  30.             }
    6 `7 e: g; m1 u4 T
  31.             
    * B. O" U; S, {% {' m8 @) g0 Q5 x; k
  32.             // 准备跳转App执行
    7 l9 H* J" X: |% m
  33.             if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)   //判断是否为0X08XXXXXX.+ F6 g8 t% _) G9 [' _7 s/ i
  34.             {
    4 l, L5 Y  D% `  \
  35.                 printf("Enter App!\r\n");. r0 Y0 D$ V: e0 K6 ?
  36.                 iap_load_app(FLASH_APP1_ADDR);   //执行FLASH APP代码: F: u, I8 d6 L1 H
  37.             }else
    ' b8 R5 w. a& X+ I8 p. J9 y
  38.             {  o& Y& @3 u" P- r- ?5 \/ p2 j/ o
  39.                 printf("Enter error!\r\n");  _1 W& S1 W! c% T# R- M
  40.             }
    3 }( C, }) ~, e8 k2 _
  41.         }
    0 S! U4 W( [& R4 X7 ?3 G! C
  42.         else   // 未传输完成$ V3 Z7 T8 [0 e" v
  43.         {
    & H; r7 l) h. ]
  44.             curRecCunt = gReceCount;   // 更新当前接收数量- H% y, e$ d% t$ C, h: p
  45.             // 延时一定要有,给串口中断留出时间
    - P5 @4 E6 [) G& P
  46.             // 如果没有会导致接收不完全
    0 \" C# ~6 w% `* Y$ H$ O' y
  47.             delay_ms(10);
    * ~$ m7 t/ J, [' G+ O
  48.         }
    2 |1 D8 D: R- M. e
  49.     }0 _% T4 w+ X6 W" h  E1 C, V
  50. }
复制代码

! D6 u  w% k6 |) ?* G: q7 M8 A六、注意事项
; e- f8 L4 V4 _2 N8 Q+ _' ]需要注意的是,上面的程序中App程序是从0x8010000开始,需要配置好程序起始地址和中断向量表偏移。& @# F, D4 x8 U6 Y: W

! V" a$ B# Q* I$ O; {! l! T: v
" c; G, {4 K3 B, M3 z转载自:二土电子
9 Z2 b& R) X; a* |5 {1 j4 J7 E如有侵权请联系删除; b/ N$ |1 o7 s3 T

7 `2 J: w& P/ y0 n
收藏 评论0 发布时间:2023-10-23 22:37

举报

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