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

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

[复制链接]
攻城狮Melo 发布时间:2023-10-23 22:37
一、串口IAP简介
+ k% [9 f  H* I0 U  z$ B9 _1.1 什么是IAP
, Z# `* Z2 F( N8 F8 T
IAP,英文全称In Application Programming,在应用中编程。很好理解,就是在程序运行过程中我们进行程序的烧写,或者叫升级。9 {3 ?, Q& E% {- g6 U7 o  v0 T! A9 `
9 d9 V4 Y7 u4 y; m" e8 V0 ~7 \; S5 {
1.2 STM32下载程序
; y! w* E3 y' H' i; y/ Z我们都知道,STM32可以利用串口下载程序,这是因为ST公司在产线上就在产品中内嵌了自举程序。所谓的自举程序,实际就是支持我们通过串口下载程序的代码。自举程序被存放在系统存储区,因此如果我们需要通过串口下载程序,需要将Boot0接高电平,Boot1接低电平,让程序从系统存储器开始运行,运行自举程序。下载完成后我们再将Boot0接地,让程序从主闪存存储器开始运行。自举程序是我们用户无法修改的。
+ H! {4 u6 M" u5 P
+ U' |5 s, e1 q, E

& S0 V4 O0 ^+ z" w" u' S二、串口IAP有什么作用9 O( ~: ~+ f5 _/ z
上面我们介绍了什么是IAP,那么这个IAP到底有什么作用呢?
+ l) F$ ^, h' U' O5 D) h! Q) j; O6 ~+ F
首先介绍两个词——Bootloader和Application。Bootloader实际就是一段引导程序,单片机上电后先执行Bootloader程序,然后再执行用户编写的应用程序Application。介绍完这两个词,我们来介绍一下IAP有什么作用。比如我们生产了A,B两款产品。A产品是某个精密器件的一部分,B产品是一款物联网产品。我们的产品销售范围很广,远销海外。
) J2 W1 b6 U, s' \! t" y
. u8 G( X& q0 s: I' T某天A产品的某个客户反映了一个Bug,我们编写好了程序,需要进行程序更新,或者叫升级。利用IAP,我们可以在程序运行时,通过预留的通信接口直接烧写程序。而不需要再把整个设备拆开,像我们调试时那样下载程序。甚至我们可以直接给客户邮寄一个小设备,客户直接进行傻瓜式升级。
+ b+ b4 g7 Y4 @$ Q$ `, _' {

' e. [" p0 O7 ]; L0 a: C# C又过了一段时间,B产品的程序出现了一个Bug,风险等级比较低,但是依旧需要全体升级程序。我们总不能挨个产品派人去升级,成本极大。这时候又轮到我们的IAP出场了。它可以在所有设备在线运行的情况下,直接通过网络下发升级程序,实现在线升级,节约了大量的人力成本。& b$ A1 S$ }; j7 K

1 K1 Q4 u# a% r& p; ?1 [通过上面这两个例子,大家应该能够基本了解IAP的用处,使用IAP让我们不需要再使用调试器进行下载,甚至实现设备的在线升级。  I6 Q1 t5 i* L# ?" \

. A# [6 V" u' M+ U
+ ?1 u$ U2 s; _" c
三、启动流程8 V9 K. @# x& Z+ o; H7 }
在介绍如何实现IAP之前,我们先来简单了解以下STM32的启动流程。$ Z4 _  X$ ^) u/ e6 g6 |& U5 O( e
2 e2 J, n( b2 s7 k, R
3.1 正常启动流程+ F( B% l4 o# L( e1 F8 |6 d
这里的正常启动流程指的是,没有添加IAP的流程。
5 C2 S; C% V$ m( \% O. W6 Z5 C8 x4 f+ N, d
  q5 t& |; G) y! k) U+ [
微信图片_20231023223616.png
- Z  P8 i. C$ `" O
正常启动流程
7 l) S9 V, ~& f. {7 {$ [

6 g, b# m- J9 E# V+ C& Q! M
程序启动时首先开辟栈空间,配置栈顶指针。然后配置堆空间。配置完成后,建立中断向量表,在中断向量表中找到复位中断,开始执行复位中断服务函数,然后跳转到main函数中,执行用户代码。当用户代码中有中断请求时,会回到中断向量表,根据中断源执行相应的中断服务函数。
$ y% Y( l+ R1 F( I" c6 k6 j" w* d6 W6 s6 }9 K
3.2 加入IAP后的启动流程
+ z8 ^, r% T- O$ i下面是加入IAP之后的启动流程。
) C6 C2 K+ t4 f/ ~9 ], h; m* ]( z7 |, O: _3 u, g+ J
微信图片_20231023223619.png

3 f' M" O3 Q6 l5 G, n
加入IAP启动流程
# Q4 [" O: K+ }- |0 j4 [
, J, @7 o8 a3 d' l3 r) k
4 v; r! q8 N, M0 o
可以看到,与上面不同的是,加入IAP后,执行完复位中断服务函数后直接进入IAP的main函数。在执行完IAP之后,跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main 函数。* d. U" y1 ~; F0 T+ M( {. L: }2 I
+ m* e1 k+ ~  K& _9 X/ c/ A
由上面的两个启动过程我们可以看出: L5 q- g  k5 }* u
• 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始。
. z3 x5 O7 L+ L5 v: p6 i- T• 必须将新程序的中断向量表相应的移动,移动的偏移量为 x。
8 a3 l& W( g$ x2 v, b3 @# d" U3 G& L3 |

" `8 F4 I2 T  w四、必备知识
7 g/ U* ]  @! W- m! I; i- L44.1 修改程序运行起始地址

0 T3 d1 X; c" H$ @点击魔术棒,选择“Target”,修改运行起始地址和代码大小。* P/ E: x6 i% D7 ?: I1 d# }
2 x% Z" g6 g4 K- v( |
微信图片_20231023223624.png

7 D7 I3 l3 q1 }, A6 ~! s/ R
修改App运行起始地址

' o5 |5 x5 t# K; z" E' [5 I& p
' j4 t5 V) _/ ~$ j1 q1 C( m" F

2 S( _* N3 ^. v6 C6 g2 m8 N9 b
4.2 设置中断向量表偏移2 ]$ N; L/ c6 v- U2 O" c
VTOR 寄存器存放的是中断向量表的起始地址。如果要设置中断向量表偏移,只需要在main函数最开始添加如下语句即可! H0 o) @6 ~! }4 J8 g
  1. SCB->VTOR = FLASH BASE | 偏移量:
复制代码

1 Q$ n5 J& e2 Q7 v  Z( O/ n: }' I4.3 生成.bin文件
  Z) U+ a0 {; t# y3 S9 J3 x点击魔术棒,选择“User”,按照如下配置,输入下面的内容/ ?% V  F/ q: H5 \7 J1 X
  1. fromelf --bin -o "$L@L.bin" "#L"
复制代码

5 x) [8 y' E3 t( j0 u" Z% k
微信图片_20231023223706.png

2 {0 ]  W' y$ x! j- V: E2 H
生成.bin文件配置

* z# e8 |, W% o0 c# I2 D6 ]: N0 I

: u. t. m% c+ T0 v
, D  R+ t( [1 q  f
点击编译,不报错就可以,去设置的输出文件夹中就可以找到对应的.bin文件。
  u6 e0 C/ Q9 `3 v5 b5 m

' ]  f2 j8 ]$ N0 W  ^- q1 L
微信图片_20231023223710.png
! @% K/ S& s0 Z& b1 g. J
编译提示

# S5 q/ p( g9 D

1 m) Q) }" P. w) ]0 M1 ]* g. s$ o# ?4 l' A! |& k* i
五、串口IAP实现
# E# B  Y0 _, F( b$ j, T3 m本次的目标是实现一个串口IAP,也就是编写Bootloader,在程序运行过程中实现程序的下载。Bootloader程序应该可以通过串口接收上位机发来的.bin文件(App程序),检查后将.bin文件写入到Flash特定位置,然后跳转到App程序运行。
/ x8 y7 h, V. X: U8 ?) H3 O9 n- [
5.1 串口中断服务函数9 r- b7 @3 n* v4 `9 P/ S
本次的串口中断服务函数与之前不同,这里单独贴出来。需要定义一个接收数组,接收数组的起始地址限制为0X20001000。接收数组最多可以接收55K字节,可以根据需要调整。但是需要注意的是,数组的大小需要比App程序要大,而且不能超过芯片的SRAM空间大小。5 E' n' ?* V  K5 S! O* s# z! H4 F
  1. /*+ D/ E' m- r3 D7 T7 @
  2. *==============================================================================
    # q: C6 P5 P# \8 ]& R$ O% C
  3. *函数名称:USART1_IRQHandler
    7 e$ ~8 |$ q) @7 V
  4. *函数功能:USART1中断服务函数
    8 N& W: v1 e9 u( V  B& u
  5. *输入参数:无
    8 y0 H2 H- y6 F1 ]. U9 ]
  6. *返回值:无- m1 w$ y; I& ]1 K3 m5 P
  7. *备  注:无
      U) C" B$ S# M3 k, W3 G" b$ e
  8. *==============================================================================
    & e& }  Y6 p* A  M( c4 }
  9. */
    ' b4 q4 u1 ]( d& G! {% k
  10. u32 gReceCount = 0;   // 接收计数变量9 w9 H/ {' w& t: q1 C1 D" J0 b
  11. // 接收数组9 H8 l$ O. L: {+ o: x- \3 J
  12. // 限制起始地址为0X20001000
    2 J9 `9 F- Z6 G5 i. Q6 n6 c
  13. // 保证偏移量为 0X200的倍数9 y: U* X4 x8 I; ^# `  q
  14. // 是为了给App留SRAM空间2 }6 X! Q$ T2 @- Q# Y9 w8 ]6 `
  15. u8 gReceFifo[USART_RECE_MAX_LEN]__attribute__ ((at(0X20001000)));* f/ k1 I* W+ n6 S! C0 n" ]4 T
  16. 7 i8 _) ^+ t4 b( o0 ]/ r) N
  17. void USART1_IRQHandler(void)  ! z+ t6 o7 B8 ]/ N) o  E
  18. {5 k; z2 k# K, }6 f' o/ I1 v
  19.     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   //接收到一个字节  
    " O) K3 V- V% y% h9 x
  20.     {' r6 A: [# A- A
  21.         if (gReceCount < USART_RECE_MAX_LEN)
    ' k% E" f# |: o) o1 V
  22.         {  l' D* t' {. Z& X! P* D/ M- k
  23.             gReceFifo[gReceCount++] = USART1->DR;
    3 f" U! y. M, O, a8 t! E/ T
  24.         }
      ^+ ]" E& B7 I% c
  25.         else7 t' L$ R! x  @* `, P6 C: P
  26.         {
    ) D* Q' E7 w4 ~$ N* ]
  27.             printf ("APP code out of memory!\r\n");
    8 ^- T5 }; b1 t& |
  28.         }
    " Q% `% {" A: }9 i9 b4 n
  29.     }# ^0 L. U  h! S6 c+ F
  30. }
复制代码

; @% {7 o  W" |& z3 b* f! j7 E; _$ j% v5.2 Flash写入程序
! @2 g6 k2 p) p9 w% K关于Flash程序,这里就不在赘述,只是贴一下带检查的写入程序。其他具体内容可以到博主STM32速成笔记专栏查看。
4 O! p% i+ @- ~& l7 f& h5 E
  1. /*
    / ^$ @- u# c( W3 A% z% S4 S
  2. *==============================================================================
    ) E$ k, C& Y2 x6 e- z( U+ Z5 \. ?
  3. *函数名称:Med_Flash_Write
    0 n% F* K* Z7 {1 F( R3 _
  4. *函数功能:从指定地址开始写入指定长度的数据6 S) W$ |& h) `( l& r: y
  5. *输入参数:WriteAddr:写入起始地址;pBuffer:数据指针;
    3 p' F- G  s: |) w- K, J' Q% z8 U% o
  6.                         NumToRead:写入(半字)数
    ( K+ L2 j4 U# J4 _+ g% ]
  7. *返回值:无! ~) D' y$ B( s. m
  8. *备  注:对内部Flash的操作是以半字为单位,所以读写地址必须是2的倍数1 l4 Z0 B3 I! e3 s; o; G- L
  9. *==============================================================================
    / Y+ g: ~# G# a7 s
  10. */6 Y5 C7 N" b9 j& Y, z! k

  11. 2 f: r" t- S( x$ m: c, M7 M
  12. // 根据中文参考手册,大容量产品的每一页是2K字节
    ! n  O) K0 V8 {9 A" Y
  13. #if STM32_FLASH_SIZE < 256
    + G. @" N4 A1 k" G. Q# {
  14.     #define STM32_SECTOR_SIZE   1024   // 字节
    - h/ n2 Q. R5 k+ e& z/ u( a0 Y
  15. #else
    - d" t9 l, ?, m% s+ E: H
  16.     #define STM32_SECTOR_SIZE   2048! h  A) M- d5 r# s
  17. #endif# Q3 j- V. _3 ^5 r" e

  18. ! x, W: X, h2 O2 f
  19. // 一个扇区的内存2 S* h$ H- r! ]# A: r0 O- y
  20. u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];
    - |4 Y8 N+ k/ l1 K6 x" T

  21. ' e  I, J1 |. r: }  [0 H$ D% N
  22. void Med_Flash_Write (u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)( f; ]5 V7 J4 K$ h8 m' x4 s
  23. {+ m! Z& b6 g; v, |: v; L
  24.     u32 secpos;   // 扇区地址, k) Z9 [5 r5 i6 t
  25.     u16 secoff;   // 扇区内偏移地址(16位字计算)9 p+ N+ e1 I% p2 x$ [
  26.     u16 secremain;   // 扇区内剩余地址(16位计算)   
    4 X% i7 I7 [: U- w) N
  27.      u16 i;   
    - X7 L0 [1 P+ L; K
  28.     u32 offaddr;   // 去掉0X08000000后的地址( g3 N) x) ~# n, \8 s# d
  29.     ; X7 U5 y, p* M7 ?
  30.     // 判断写入地址是否在合法范围内
    3 i( J  y/ v0 G$ L0 D/ f* o: d7 C, O
  31.     if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
    : k( K6 l2 U3 g# c9 a+ ]
  32.     {. N' u+ e# R: Z  a
  33.         return;   // 非法地址2 W) D* D0 H3 Y% G& ]
  34.     }0 c7 }6 a. @1 P, u
  35.     : L' [, x5 C3 ~- f8 h( ~
  36.     FLASH_Unlock();   // 解锁1 E& i9 K. x: u8 `) l. b
  37.     offaddr = WriteAddr - STM32_FLASH_BASE;   // 实际偏移地址
    : ]0 O) I( H% y
  38.     secpos = offaddr / STM32_SECTOR_SIZE;   // 扇区地址
    ' u! F4 ]' \. s* [% E2 y
  39.     secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 在扇区内的偏移(2个字节为基本单位)
    $ h& C9 F3 N' F( X9 z
  40.     secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇区剩余空间大小
    5 K% O7 l" i; O8 t. Y6 I
  41.    
    ' L% [" B6 s. q2 J$ l; D
  42.     if (NumToWrite <= secremain)4 S: d1 n0 F' S& A7 ]! o% V
  43.     {/ O) o. a4 x; u" D* Z  ?$ i" f' D$ E
  44.         secremain = NumToWrite;   // 不大于该扇区范围
    7 ?( j7 V7 X' I, `: L( Z
  45.     }
      l8 X, t! ], |8 l+ ?( h( E8 M
  46.    
    # M: u6 s% I0 l4 a* {& N: }. b- N
  47.     while (1) % \+ I( u! d8 E% o8 y6 Q7 D
  48.     {9 H/ g! @% J( ^5 p$ ?
  49.         // 读出整个扇区的内容, z$ X1 B, L9 ]) |  m+ ]* u/ ^7 j
  50.         Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);& o2 Y0 E6 R+ Y: T  S0 R1 h
  51.         
    ; U1 M: d6 W& a" d' `- |8 s
  52.         // 校验数据
    - n! X* y( A9 g4 p
  53.         for (i = 0;i < secremain;i ++)
    5 D+ q5 j' `2 f+ F7 A4 H3 W, H1 R
  54.         {
    " X: b) a; R) k/ z5 M+ V' P* E
  55.             // 需要擦除
    ( |; b! Q- O* B+ Y4 s( A, G
  56.             if (STM32_FLASH_BUF[secoff + i] != 0XFFFF)
    - J. N/ G/ a0 }1 V2 y4 s7 Z! I  V8 f
  57.             {
    3 x$ E; Q( |/ P9 d
  58.                 break; " P  H! ]4 `: D
  59.             }    2 O6 n! k/ P0 ~" l
  60.         }
    + I. V6 O& w) A% v+ {
  61.         // 需要擦除; u0 Y. t! P# W$ D9 \
  62.         if (i < secremain)
    4 x/ x7 Z2 X, K1 |+ S7 c
  63.         {
    4 z! a4 N1 Z4 ?4 t" i* ]
  64.             FLASH_ErasePage(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE);   // 擦除这个扇区
    0 p0 y, q5 Q4 U
  65.             5 t/ M; _  J- u/ {/ ]  w
  66.             // 复制
      v& @9 T4 Z1 c/ w- w" i) Q4 F
  67.             for (i = 0;i < secremain;i ++)$ _* ^5 u7 t+ P5 L+ H  {
  68.             {  l8 Q* h; F  M
  69.                 STM32_FLASH_BUF[i + secoff] = pBuffer[i];   
    5 K! Y% u, {. {; I
  70.             }9 L9 Y4 U: y3 `( N2 D# ~
  71.             
    / {' O, c8 X  k) ?8 _( E" m. ]
  72.             // 写入整个扇区" ~# o$ S& s5 t
  73.             Med_Flash_Write_NoCheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
    ( e, R% b) `) X: K- F  t
  74.         }
    ; c0 b: I) [4 a, o/ u/ ?$ P
  75.         else7 ]$ @& Q* g2 Q8 z
  76.         {5 ?' l8 T; {! Z( _4 s5 x+ \
  77.             // 写已经擦除了的,直接写入扇区剩余区间
    ! `1 A% N4 A" A$ S4 ]7 j7 H
  78.             Med_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);
    * D# J' k+ U- \+ y( F4 I: Z9 ]
  79.         }
    4 L( m$ a/ b* @% ~, H5 {2 ^( u
  80.         $ z" m. M6 A  v8 Z) |: @; r
  81.         if (NumToWrite == secremain)
    1 u) A5 e' N; N, Q; J
  82.         {
    ) T4 T! e) \: h9 J% g" t
  83.             break;   // 写入结束了
    $ i0 L* S' S& q5 d. ^
  84.         }
    0 K3 p, u4 N* _1 @
  85.         // 写入未结束
    1 Z1 M- d+ R& w: J
  86.         else
    ; V6 a1 a. O( N) ]+ h) Y
  87.         {. Q4 F8 }* T6 ^" `
  88.             secpos ++;   // 扇区地址增16 d2 _5 T6 Z# ^  \: V
  89.             secoff = 0;   // 偏移位置为0   + _7 _! V7 w0 m. ~
  90.             pBuffer += secremain;   // 指针偏移
    5 ]5 f+ H" R! u! q
  91.             WriteAddr += secremain;   // 写地址偏移    0 b9 x) J7 m8 s0 X' X2 l$ W
  92.             NumToWrite -= secremain;   // 字节(16位)数递减
    / P3 L3 x. [1 ~, c
  93.             if (NumToWrite > (STM32_SECTOR_SIZE / 2))
    . u1 N) W0 q. Q
  94.             {1 @; t' t& ^$ g  `: n4 T8 y
  95.                 secremain = STM32_SECTOR_SIZE / 2;   // 下一个扇区还是写不完
    % L$ w; c9 w) I
  96.             }
    8 H/ y4 [6 {7 N6 T8 q  n8 x7 j9 G
  97.             else5 m# d9 k- ]6 _2 P) p
  98.             {
    9 x; S( ]$ z$ M7 Y- ?
  99.                 secremain = NumToWrite;   // 下一个扇区可以写完了
    & e2 r% H9 `1 Y" g
  100.             }" m5 C' u3 T9 _
  101.         }  0 x3 o" M: P  L" m
  102.     }
    5 z5 P/ ~& W/ g# Q2 G1 [! a
  103.     FLASH_Lock();   // 上锁
    $ A+ P! |$ A, X' z# u9 ^+ c4 h
  104. }
复制代码
9 p$ V8 r( l, W" E$ I" J8 {1 A
5.3 IAP程序9 ?2 }# \- p! z3 C$ i3 I
IAP程序包含两部分,一部分是将接收到的.bin文件写入Flash,另一部分是跳转到App执行。& f( u7 S$ ~" X: v( Q# c) r8 |, I/ z: W
  1. /*$ w$ R- z2 M! [
  2. *==============================================================================+ l$ Z3 E: a$ K. {0 f& W0 T
  3. *函数名称:iap_write_appbin
    . b# E6 m! X% T& }  [" b
  4. *函数功能:写入.bin文件, Q: N; ]( H. h0 Z7 ?/ o
  5. *输入参数:appxaddr:App程序起始地址;appbuf:缓存App程序的数组;0 t% j' a' s) M' P! P: v
  6.                         appsize:App程序大小
    ) r: m/ |6 N  Z* K+ I4 c4 x3 @( {! M
  7. *返回值:无
    - F$ h, V/ G# c
  8. *备  注:无
    & Y: z* K, s: b# ~; M) N
  9. *==============================================================================
    6 w1 i! h! J  f2 V6 T
  10. */; H5 \4 N4 x0 O" E8 F6 F
  11. // 对Flash操作的最小单位是16位
    % t7 H0 F$ F$ f0 C. M$ c2 h
  12. u16 iapbuf[1024];   // 要写入Flash的内容
      R( l0 G, |- r/ ]

  13. 8 O- Y& d7 c, y* `" j" `! L$ Z( c/ h, M
  14. void iap_write_appbin (u32 appxaddr,u8 *appbuf,u32 appsize), W( N& z$ q* k9 c! w, ]1 }
  15. {
    4 R( s4 S& j( |' {5 G' ^
  16.     u16 t;   // 临时循环变量
    0 b$ `' J7 h3 B& E+ }& m
  17.     u16 i = 0;   // 自增索引3 n( G7 ]% u( G* Q' d" i% b
  18.     u16 temp;   // 临时计算变量
    4 d6 ^' ~3 _- c9 x. g4 U
  19.     & z5 [) H% C1 z5 W( o! D
  20.     u32 fwaddr = appxaddr;   // 当前写入的地址0 Y/ V" m; c- l% H# ]; o
  21.     u8 *dfu = appbuf;   // 指向App代码数组首地址的指针. ]" S# F2 P9 ^' w# c6 U) q6 |9 i* R/ n
  22.     8 D' n8 A0 F( m3 e& j2 c
  23.     // for循环,2K为单位进行写入  t" ~1 b, Y0 V+ O1 S
  24.     for(t = 0;t < appsize;t += 2)
    ; L. l% m2 }1 f0 t
  25.     {  
    1 _% j) D2 x: @, R5 p# |
  26.         temp = (u16)dfu[1] << 8;
    9 }' M0 i0 e5 B
  27.         temp += (u16)dfu[0];   
    7 z8 O  l. q5 N& Y, i
  28.         dfu += 2;   // 偏移2个字节, @5 _% d* ]) K' n
  29.         iapbuf[i++] = temp;     
    ; R8 u, g9 H" A+ G
  30.         if(i == 1024)/ q! B' e$ H, m4 I
  31.         {, L8 o# t# z7 l# I& n; _" B8 B
  32.             i = 0;
    6 N8 j8 S1 N# q" N# A
  33.             Med_Flash_Write (fwaddr,iapbuf,1024); 1 a& W; r' V* [2 s/ o
  34.             fwaddr += 2048;   // 偏移2048  16=2*8所以要乘以2- T9 _& e0 F! u  N- E7 J# f, \1 ]
  35.         }* R2 ~' `0 y* v( S0 c$ X
  36.     }& `; `. E; w( I5 Q0 {: e3 h
  37.     if(i); @0 {# f, Y8 w5 ?3 }2 G; @
  38.     {
    $ }+ w0 `0 Q# }/ U- Z: I
  39.         Med_Flash_Write (fwaddr,iapbuf,i);   // 将最后的一些内容字节写进去
    / ]+ k8 U+ Y2 N6 [0 F
  40.     }
    * r% h6 ^( H9 v# D" U+ q. S5 c
  41. }# H7 U& Y$ Q/ N  W- G; l
  42. /*
    . C% K( p* ~/ D
  43. *==============================================================================
    9 O$ i+ u) C. i+ ?1 l
  44. *函数名称:iap_load_app/ t# }1 R4 q# M
  45. *函数功能:跳转到App# p/ L7 r2 z: u! P- ~! Y! I
  46. *输入参数:appxaddr:App程序起始地址; }7 {; f7 V. F4 }
  47. *返回值:无
    ' R1 f. d9 o0 M3 y- E  L+ b
  48. *备  注:无
    ! d! P; F1 U/ h' Q
  49. *==============================================================================
    & B) _% H2 u; y( T; C( L
  50. */5 t0 K* e! @+ E8 }& @  b
  51. iapfun jump2app;6 e2 e5 d- h# f6 n/ q

  52.   W) D- D, }( |
  53. void iap_load_app (u32 appxaddr), ]4 F3 K9 T7 J7 U; h; ^7 ]  b$ [
  54. {, b* t* H& J; E! t
  55.     if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)   // 检查栈顶地址是否合法
    8 I% S  t) l) M" f: h$ E; K7 V; x% V7 a
  56.     { - {! Z4 S4 T' u# j( B( {7 S
  57.         jump2app=(iapfun)*(vu32*)(appxaddr+4);   // 用户代码区第二个字为程序开始地址(复位地址)  / l1 `* c- g3 Y" Z' X, B
  58.         MSR_MSP(*(vu32*)appxaddr);   // 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)  `: W1 J2 N1 B& L
  59.         jump2app();   // 跳转到APP
    ! D1 [" g, f5 W8 W2 r
  60.     }
    % r8 N3 f1 ]2 C$ G
  61. }
复制代码

* s( s+ M$ T# u8 E! J* f5.4 main函数4 ?: z* K- u4 M& [* D; x  [3 T
main函数设计如下  ?/ o  ^- @7 a$ l6 ]
  1. int main(void)
    * J+ w: u. n& A. b& p8 B
  2. {
    & t) n. Z+ P+ z( G
  3.     u32 curRecCunt = 0;   // 当前接收数量
    1 t8 J& ]' E+ @* M
  4.       D" x, F: ^1 N3 U
  5.     // 设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    & V6 _' P! P6 W% \0 ^) z
  6.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);, a* k, `8 g& i3 b6 m; U! b0 K5 B
  7.     delay_init();   // 延时初始化
    ( ~. ~8 A, F! Q% D9 ~: A. L
  8.     uart_init(115200);   // 串口初始化
    $ c% A7 g4 |+ a1 e1 ^6 F1 V% I
  9.    
    5 N0 S) A8 C0 V1 n/ p' v9 ?
  10.     printf ("Enter the Bootloader Code!\r\n");
    ( ]& `! K  \- z+ y1 ^# K
  11.    
    8 e- F! r" D  c: p/ r4 l
  12.     while(1)
    5 ?9 G9 h8 T3 o
  13.   {: R; Z1 L" ~3 w+ X6 [) p) I/ G
  14.         // 如果接收到内容且传输完成
    * m& U# `( |( t8 P* w. j2 |" P
  15.         if (curRecCunt == gReceCount && gReceCount != 0)& i1 e# Q8 |* T1 S- w$ K( s
  16.         {" g; P1 S' X# B3 ~, f& _
  17.             printf ("App Code reception complete!\r\n");
    6 L7 ~3 }/ Q0 J1 A) [) e5 f( k
  18.             printf ("The length of the received App code is %d!\r\n",gReceCount);% ^9 T; o, A- G! k% ~% a1 p9 S
  19.             
    ; C! d1 y& k0 ]/ b1 s
  20.             // 开始准备写入Flash- p7 {) W* z* l( t& s
  21.             if(((*(vu32*)(0X20001000+4)) & 0xFF000000) == 0x08000000)   // 判断是否为0X08XXXXXX.) ^9 e3 @! i- x  j0 Q# L) @- i
  22.             {% ~7 H% h( i  C0 @
  23.                 iap_write_appbin(FLASH_APP1_ADDR,gReceFifo,gReceCount);   // 更新FLASH代码  ; V8 \' i7 P$ K* o
  24.                 gReceCount = 0;   // 清零接收计数变量9 G1 z0 d. z. X1 k
  25.                 printf("Update complete!\r\n");8 Z; o6 X7 T# t) W. |8 B
  26.             }
    % @, \  y4 w- `; J
  27.             else
    & H3 u9 e- U5 J
  28.             {
    - \, q6 P1 q; K, d$ P! l' G
  29.                 printf("Update error!\r\n");
    - X  T) _" y+ e! ?
  30.             }
    : u" v; y2 f3 n" M) J2 C
  31.             
    9 d! V# L2 ~/ H
  32.             // 准备跳转App执行7 O/ q  }& w+ E$ ^' C* s# Z# e( }
  33.             if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)   //判断是否为0X08XXXXXX.9 W% s2 p$ f$ y# Y# S/ s
  34.             {2 u4 @3 F8 z& b4 @: ^+ I  X- J1 a0 @
  35.                 printf("Enter App!\r\n");! q( k% Q# j& J2 d# T
  36.                 iap_load_app(FLASH_APP1_ADDR);   //执行FLASH APP代码: n1 _  [' {+ I0 k
  37.             }else - T* v+ Z' q1 O2 E. f. P  e
  38.             {/ m8 h) `5 ~2 k; f% l3 h( [
  39.                 printf("Enter error!\r\n");
    , p' K  c8 ]- P8 d/ V9 K  S
  40.             }
    - B9 h/ O" M" x" T6 f
  41.         }
    8 E) X4 |$ N: R1 a
  42.         else   // 未传输完成
    ' E4 M+ ~- \9 w) w) `0 q
  43.         {4 g) E4 n2 k: d3 c
  44.             curRecCunt = gReceCount;   // 更新当前接收数量8 v+ v* k7 Z8 E# K: s. g
  45.             // 延时一定要有,给串口中断留出时间" Z. d/ s& a% l) a9 r  l% b& m
  46.             // 如果没有会导致接收不完全
    1 M( X7 q! s0 U6 E/ {$ Z4 p
  47.             delay_ms(10);& E: E$ Z( i' U$ j  Y2 ]. ^
  48.         }1 L) E; `- d+ B% ~6 d3 v
  49.     }# A6 t8 |8 z2 h; z7 i$ [% X
  50. }
复制代码

; I  C3 W1 n8 ]3 g' @! S" A六、注意事项
1 q6 g# Q$ D) A6 @9 j需要注意的是,上面的程序中App程序是从0x8010000开始,需要配置好程序起始地址和中断向量表偏移。! }. T. ]9 w* }5 n# s

- R# v% J: K' |; e3 k
- H+ R) W9 w5 j9 m/ G8 s转载自:二土电子! `5 B2 \# k, T3 o8 S3 K: {
如有侵权请联系删除
/ b; v+ `; `- A. ^9 s, e" [/ C
# c4 [6 Z# W. |) @* P0 M
收藏 评论0 发布时间:2023-10-23 22:37

举报

0个回答

所属标签

相似分享

官网相关资源

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