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

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

[复制链接]
攻城狮Melo 发布时间:2023-10-23 22:37
一、串口IAP简介. Z9 _, w/ r8 K2 x& p+ ?0 Z2 h1 F
1.1 什么是IAP
  v6 h; @  F5 e) v4 Y& f
IAP,英文全称In Application Programming,在应用中编程。很好理解,就是在程序运行过程中我们进行程序的烧写,或者叫升级。1 f+ e  S0 c5 T- }
8 h9 h+ M) Z2 T$ u
1.2 STM32下载程序- T) @1 S9 u* G! O8 W  L
我们都知道,STM32可以利用串口下载程序,这是因为ST公司在产线上就在产品中内嵌了自举程序。所谓的自举程序,实际就是支持我们通过串口下载程序的代码。自举程序被存放在系统存储区,因此如果我们需要通过串口下载程序,需要将Boot0接高电平,Boot1接低电平,让程序从系统存储器开始运行,运行自举程序。下载完成后我们再将Boot0接地,让程序从主闪存存储器开始运行。自举程序是我们用户无法修改的。
7 Z& t: v% A/ r1 u+ M1 y1 q9 Q
$ Z3 ]% u! R+ Q3 H, a

# u/ L3 O9 g# w$ `- x' B二、串口IAP有什么作用
& g  G  Z- Q  q0 r4 Y9 L3 R) H- G上面我们介绍了什么是IAP,那么这个IAP到底有什么作用呢?6 [6 }) M; M( d  E+ i" ~

+ J* F3 p. H; w5 W7 F" W首先介绍两个词——Bootloader和Application。Bootloader实际就是一段引导程序,单片机上电后先执行Bootloader程序,然后再执行用户编写的应用程序Application。介绍完这两个词,我们来介绍一下IAP有什么作用。比如我们生产了A,B两款产品。A产品是某个精密器件的一部分,B产品是一款物联网产品。我们的产品销售范围很广,远销海外。: E7 ^+ x* [2 c  I

6 R( X; i1 f. P2 t某天A产品的某个客户反映了一个Bug,我们编写好了程序,需要进行程序更新,或者叫升级。利用IAP,我们可以在程序运行时,通过预留的通信接口直接烧写程序。而不需要再把整个设备拆开,像我们调试时那样下载程序。甚至我们可以直接给客户邮寄一个小设备,客户直接进行傻瓜式升级。
( L9 L* p) Q  u, R0 @% y# h3 N
5 Z9 l5 K- ^9 \/ z3 w7 F9 P; d- d
又过了一段时间,B产品的程序出现了一个Bug,风险等级比较低,但是依旧需要全体升级程序。我们总不能挨个产品派人去升级,成本极大。这时候又轮到我们的IAP出场了。它可以在所有设备在线运行的情况下,直接通过网络下发升级程序,实现在线升级,节约了大量的人力成本。
& m9 t8 L$ J5 Z( r) N0 P7 S9 p/ a0 \$ G
- C& y2 o7 M9 ^+ f, p+ t& k通过上面这两个例子,大家应该能够基本了解IAP的用处,使用IAP让我们不需要再使用调试器进行下载,甚至实现设备的在线升级。. K; Q% s3 x, e# ~' u9 N5 h5 ?+ L, Y
5 Y0 l* W5 I  g& I
7 x8 F# \& g2 f4 h7 {8 b
三、启动流程
, F4 c; h) z4 L' p$ H在介绍如何实现IAP之前,我们先来简单了解以下STM32的启动流程。1 w+ I6 E+ B6 V7 C. A+ {$ ?* F9 G

: O( e* j( H$ N  N0 K- M# |
3.1 正常启动流程6 m( Q0 a- R/ v% Q9 U+ {' X
这里的正常启动流程指的是,没有添加IAP的流程。$ E% F7 \7 F  m# I5 _) X! b
( K: n8 v1 z8 r
  z6 Z# h, e' |$ _" v' D9 C
微信图片_20231023223616.png
1 }3 L. q+ u8 ?7 ^1 W1 u& }# o) R
正常启动流程

" T* O' c( }, ?! f8 C# \9 ~
+ e: j3 D0 `- R. B% {2 P1 N! b% }
程序启动时首先开辟栈空间,配置栈顶指针。然后配置堆空间。配置完成后,建立中断向量表,在中断向量表中找到复位中断,开始执行复位中断服务函数,然后跳转到main函数中,执行用户代码。当用户代码中有中断请求时,会回到中断向量表,根据中断源执行相应的中断服务函数。" P7 c. o0 w$ \6 |% i0 @+ o

0 k  s4 X5 d3 U8 h9 W3.2 加入IAP后的启动流程- L* ^3 T' S9 B7 W0 p
下面是加入IAP之后的启动流程。
3 f5 d  t) Q. W  D, m# d9 E( }) o4 u6 h1 H  u
微信图片_20231023223619.png

. q6 `8 d6 }$ a. B  s
加入IAP启动流程

( z/ t9 r3 y3 u& L. n

" m1 `5 |; q9 W' i8 U6 @' f/ q) E# R7 p. E
可以看到,与上面不同的是,加入IAP后,执行完复位中断服务函数后直接进入IAP的main函数。在执行完IAP之后,跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main 函数。. }4 G- K% |0 s2 v
) i7 C3 ~; C, ?& v
由上面的两个启动过程我们可以看出
. l7 t5 [. g8 e4 I# W• 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始。2 j: M7 n- A% W& _! F
• 必须将新程序的中断向量表相应的移动,移动的偏移量为 x。
) Y9 T6 M- _" r; |% R0 b5 ?8 L
) C2 D  L! h. ]

$ ^" A- C. n6 [: P+ [# y2 g8 m四、必备知识
! y0 y- V* l8 H! C& P; m44.1 修改程序运行起始地址

1 Y  P1 Y. M! b5 j$ z1 R# j  f点击魔术棒,选择“Target”,修改运行起始地址和代码大小。
- b8 x0 J) ^4 [' Q" C/ |1 n  p7 q3 x+ Z. |0 G
微信图片_20231023223624.png

. T; o. Z% s8 v6 ^# a& M
修改App运行起始地址

4 q5 N3 a. [6 q1 Y- V. Z
: x' S. J6 w; g! k# |% A7 j
' \6 x# `' q+ Z5 F9 S9 S
4.2 设置中断向量表偏移. f/ l# M3 j1 W( ]/ F
VTOR 寄存器存放的是中断向量表的起始地址。如果要设置中断向量表偏移,只需要在main函数最开始添加如下语句即可! t) }3 `  m/ N+ N7 x9 v
  1. SCB->VTOR = FLASH BASE | 偏移量:
复制代码
' G) N. j6 [& U
4.3 生成.bin文件
) ?, t8 y- m2 W8 \- k点击魔术棒,选择“User”,按照如下配置,输入下面的内容
9 j% ~* o/ G# p+ A" }7 o
  1. fromelf --bin -o "$L@L.bin" "#L"
复制代码

# T2 Z4 s2 Z3 `( D( h: K/ w4 u$ k
微信图片_20231023223706.png

1 F, e1 s3 P! W- ~  J
生成.bin文件配置

2 H6 `2 S, [6 E. U1 ?$ L7 l
- o% z* n5 `) p5 p- Z! f9 H/ u

+ ~( M# M# v, M, e" k
点击编译,不报错就可以,去设置的输出文件夹中就可以找到对应的.bin文件。
, E& O% W$ W8 g+ X5 C

, t, W) Z" j' X# c; \% i4 h
微信图片_20231023223710.png

9 d  C0 f$ I! c7 h: ?
编译提示

- d+ h9 Q; @. G* o

1 ]! R+ z5 k. I2 c) n' S$ @) S
4 B; ~% Q0 L% j" d/ {; T. W
五、串口IAP实现
% r7 n# U- Q* T4 i' q本次的目标是实现一个串口IAP,也就是编写Bootloader,在程序运行过程中实现程序的下载。Bootloader程序应该可以通过串口接收上位机发来的.bin文件(App程序),检查后将.bin文件写入到Flash特定位置,然后跳转到App程序运行。- b# i( C6 B2 ^/ R, s1 k
4 ?0 |( G3 G( ~. V4 O
5.1 串口中断服务函数
$ `% [' h# E, E5 ]+ J' ]0 {本次的串口中断服务函数与之前不同,这里单独贴出来。需要定义一个接收数组,接收数组的起始地址限制为0X20001000。接收数组最多可以接收55K字节,可以根据需要调整。但是需要注意的是,数组的大小需要比App程序要大,而且不能超过芯片的SRAM空间大小。1 b, B2 Z6 g! n& x0 `( \+ }
  1. /*. g$ e; r. ~% E$ X* b
  2. *==============================================================================( y1 L! i6 w3 T' b' k, O% c
  3. *函数名称:USART1_IRQHandler
    $ F$ L7 g0 o' A7 N  p( Z: r  S
  4. *函数功能:USART1中断服务函数* D* w) ]; b# Y# N% p
  5. *输入参数:无
    : Z) L/ y, ^( c, D* ], ~4 C
  6. *返回值:无
    0 e4 ~- S. [) `% D- J2 {/ r
  7. *备  注:无/ j7 ?3 i3 d0 U1 O7 F
  8. *==============================================================================  O' Q6 ?: m! K
  9. */! d8 {4 P  s4 E( N
  10. u32 gReceCount = 0;   // 接收计数变量
    6 E" ^0 E7 [$ R% E+ W
  11. // 接收数组+ j6 }1 G9 U9 W( g8 u) F
  12. // 限制起始地址为0X20001000
    , a, Q% @, Q4 v0 N# v- X7 [! g$ [6 ~2 {% I
  13. // 保证偏移量为 0X200的倍数
    $ i/ I0 x" ^) f
  14. // 是为了给App留SRAM空间$ V$ t$ t" h( O( ~
  15. u8 gReceFifo[USART_RECE_MAX_LEN]__attribute__ ((at(0X20001000)));& J$ t  O' t1 W: H. {0 a

  16. $ V/ O% p9 c& k! L( t' t
  17. void USART1_IRQHandler(void)  
    ' i$ v3 x. X1 z0 S- M
  18. {  }' V7 [4 c7 p! a( u* m
  19.     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   //接收到一个字节  6 g; \# p* a# E8 d
  20.     {, C& @% G% v4 E) [# i7 b2 l3 Y: i
  21.         if (gReceCount < USART_RECE_MAX_LEN)/ a3 Z: Y5 N& A9 ~4 l3 k
  22.         {4 D2 p& C0 F4 [( i' h7 H) `* S
  23.             gReceFifo[gReceCount++] = USART1->DR;7 N* c, W0 B  {7 |8 |2 ~$ N
  24.         }
    1 a' c+ ]' v1 p9 K1 Y; Q$ n
  25.         else
    ' J- ~9 |# f  @* B
  26.         {6 I9 D0 n- L) \
  27.             printf ("APP code out of memory!\r\n");
    & ~1 ~/ C& e/ _/ c' t
  28.         }, Q7 U/ ~, p+ I7 O, P' L: M
  29.     }
    " ?! p! f% ~' g& e$ P9 S
  30. }
复制代码
" ~  ?; T9 U. E
5.2 Flash写入程序
% q6 U7 j- j# |关于Flash程序,这里就不在赘述,只是贴一下带检查的写入程序。其他具体内容可以到博主STM32速成笔记专栏查看。
9 q) |; O7 P( R% _, ]& m
  1. /*
    . `7 ^6 C5 C4 a: ~' }$ R. Y# Q; m) A
  2. *==============================================================================4 N6 x3 u8 c% D# {( i/ ?
  3. *函数名称:Med_Flash_Write
    : B8 W- E3 h  k/ b- B  b& H& g: w6 R
  4. *函数功能:从指定地址开始写入指定长度的数据
    9 o. p2 o9 Y* n7 @( E( j5 h7 ~
  5. *输入参数:WriteAddr:写入起始地址;pBuffer:数据指针;, c6 D  M. c4 V6 o
  6.                         NumToRead:写入(半字)数
    0 M) M! s6 |3 x# b- X  v' K) B
  7. *返回值:无
    9 n( V& @9 h. ?$ {9 q5 |/ C1 R
  8. *备  注:对内部Flash的操作是以半字为单位,所以读写地址必须是2的倍数
    ! F- _9 t4 w* _( {- Y, A" x
  9. *==============================================================================
    ! B9 f! o; B0 Z8 M
  10. */
    7 b; K/ L5 V  e% N6 r0 k1 \4 K

  11. & d& _9 P! |( l& n6 M* B
  12. // 根据中文参考手册,大容量产品的每一页是2K字节
    - m/ m2 ^9 l! ?
  13. #if STM32_FLASH_SIZE < 256' z2 @2 p1 e: P/ D
  14.     #define STM32_SECTOR_SIZE   1024   // 字节3 X5 S% D' Q/ \! S
  15. #else
    7 ~  D4 B+ e7 [) o& p
  16.     #define STM32_SECTOR_SIZE   20486 K5 C3 l0 o' {  N3 v
  17. #endif3 C6 m! a% k+ p& v; l3 i

  18. 4 |7 d) M8 ]0 n5 s& {
  19. // 一个扇区的内存
    / N1 T2 A1 i0 }, S
  20. u16 STM32_FLASH_BUF[STM32_SECTOR_SIZE / 2];
    5 A+ K! {+ n/ `/ a. j5 m
  21. ; h6 A- ]. R- I. p' E8 L1 J
  22. void Med_Flash_Write (u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
    ) Q7 H* I/ ^. i2 |4 A
  23. {. Q% F, v. ~0 C, D/ G+ O" n& J
  24.     u32 secpos;   // 扇区地址' M! c4 P1 G, K! {! m( I
  25.     u16 secoff;   // 扇区内偏移地址(16位字计算)
    , O4 ^1 U4 K1 _; J: F# S
  26.     u16 secremain;   // 扇区内剩余地址(16位计算)    9 H) q: |6 D$ y. ^# x; B! ?2 t: K  o$ Q
  27.      u16 i;   
    7 G2 q2 a7 H3 P! F4 B3 N" p# J+ I/ i
  28.     u32 offaddr;   // 去掉0X08000000后的地址
    7 [1 `, s" u& ~0 c
  29.    
    : t2 r" X0 J5 l1 U, h$ u4 U' y0 p
  30.     // 判断写入地址是否在合法范围内
    , r% P* }* }( s2 I2 K
  31.     if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))0 e5 F$ Z4 h% I2 I" P% G* g* c5 w
  32.     {$ }6 {. J; T) h. ]! W9 ]7 @
  33.         return;   // 非法地址# z+ [* ?- Q; L( A  }$ B. ^6 w8 C
  34.     }
    6 N' A; t$ w% p, L  }, `& K
  35.     - @* ~( q+ v. u& F0 n
  36.     FLASH_Unlock();   // 解锁
    / V" D6 c. P3 H1 c8 y/ d% Q! D8 z
  37.     offaddr = WriteAddr - STM32_FLASH_BASE;   // 实际偏移地址
    - Q  O( D$ J# c! c. t& _- Z% a$ W
  38.     secpos = offaddr / STM32_SECTOR_SIZE;   // 扇区地址
    ' z/ `* X$ R  r
  39.     secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 在扇区内的偏移(2个字节为基本单位), x2 i( ?, L& q3 y
  40.     secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇区剩余空间大小! o2 g& y8 |7 ?: W2 y: @4 m9 e
  41.    
    & A" l) }7 V& Y" q8 g0 H3 ?
  42.     if (NumToWrite <= secremain)# u' ^; _+ N# c
  43.     {
      f' v' X5 P/ q1 |9 w7 c
  44.         secremain = NumToWrite;   // 不大于该扇区范围
    ! S0 z+ v( |& I
  45.     }
    * a' c3 X" x/ L' h
  46.    
    - T- d) `" y! a7 \2 s9 z3 e& {
  47.     while (1) $ c! F( o- Q* k! J* e
  48.     {4 D: B" i4 n4 c1 ]* @' K. A
  49.         // 读出整个扇区的内容0 a" T$ D+ S& T0 C
  50.         Med_Flash_Read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
    ' Z) H1 {1 q" o: g
  51.         
    , G$ T+ x$ Z. K) ~9 F6 M
  52.         // 校验数据  E' @* q% U$ R. O
  53.         for (i = 0;i < secremain;i ++)
    ; v, k. `: H, O
  54.         {7 x+ P$ C8 S' a# \
  55.             // 需要擦除
    2 P0 a7 Y  C$ R
  56.             if (STM32_FLASH_BUF[secoff + i] != 0XFFFF)
    6 _0 D& t: C) P2 q# {$ x# W- T
  57.             {
    0 ~( K* d$ A1 u- G, p, M
  58.                 break;
    , t" e) W. V2 |& z% g0 r6 C
  59.             }   
    + k  J4 q1 g0 H$ h
  60.         }7 {7 [2 L; @6 `7 U4 k
  61.         // 需要擦除& W/ a3 n" r* \$ z
  62.         if (i < secremain)/ ~( L) F9 G9 I5 D. u3 ~& u
  63.         {; e6 p& P. r2 @( H9 D
  64.             FLASH_ErasePage(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE);   // 擦除这个扇区4 ~! ]; J% d. Z9 i
  65.             9 Y# o& V) m7 t& F5 b! r' ]3 p
  66.             // 复制
    % L4 f- e, ~! T% f
  67.             for (i = 0;i < secremain;i ++)
    8 s1 ^9 H% O7 n! A' s7 d
  68.             {9 O, F1 r/ k( h8 C) H
  69.                 STM32_FLASH_BUF[i + secoff] = pBuffer[i];   3 X& d  g4 \/ s
  70.             }
    2 G) O8 M1 H! C9 H. ?
  71.             % L& ~4 Z) _& T) F
  72.             // 写入整个扇区3 H+ x% G) D3 b! W
  73.             Med_Flash_Write_NoCheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE,STM32_FLASH_BUF,STM32_SECTOR_SIZE / 2);
    . d* V3 D1 p  {% u# p9 R1 |
  74.         }
    6 C# b  J, \. m& ]& c( `
  75.         else3 u+ f) @$ S* M  t0 h" M7 m! _
  76.         {2 {, E1 C4 Y3 ?- M1 k! z
  77.             // 写已经擦除了的,直接写入扇区剩余区间
    ' `: \/ D5 I" n' V( `8 ~; \! o7 F+ x
  78.             Med_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);. r7 e$ @# V, O  D+ I
  79.         }
    - C! Y1 G6 c, H' O
  80.         4 o1 E0 {' `2 E
  81.         if (NumToWrite == secremain)3 v7 K- V1 Q2 m
  82.         {5 @/ o9 B$ o0 |( Y3 q
  83.             break;   // 写入结束了/ W6 N* ~1 m% L( @; ]' v
  84.         }
    $ G  X: ?+ O9 @4 c
  85.         // 写入未结束
    % }2 R- ]( N) ~2 u* U
  86.         else
    1 ^3 ]/ {/ g3 M* B6 b
  87.         {
    ! ?& Y$ Z! M- ?7 `$ y2 b1 M
  88.             secpos ++;   // 扇区地址增1! U, b4 W. Y  W" C2 i( i# H
  89.             secoff = 0;   // 偏移位置为0   $ L) @/ G! ^" g7 S) O
  90.             pBuffer += secremain;   // 指针偏移
    , a0 T" q; y  s1 X0 Z2 A# R
  91.             WriteAddr += secremain;   // 写地址偏移    3 h7 p( K, ]/ o% X
  92.             NumToWrite -= secremain;   // 字节(16位)数递减  D/ R+ @$ Y2 B
  93.             if (NumToWrite > (STM32_SECTOR_SIZE / 2)); d% ], H6 E( Y
  94.             {2 Z! H8 \/ G+ b9 {2 A& y2 X) O7 M  r2 W
  95.                 secremain = STM32_SECTOR_SIZE / 2;   // 下一个扇区还是写不完* }: O2 W6 _+ x' b1 N8 q
  96.             }% J  u5 F2 ~& v9 R. W
  97.             else
    - \& [/ V- G9 o3 k3 A+ v
  98.             {
    - p% I# T% H$ \* K3 g  ~- D9 X( R& o
  99.                 secremain = NumToWrite;   // 下一个扇区可以写完了
    1 w4 M3 }3 d* U! x2 l9 Q. d6 p
  100.             }3 V" @/ t: z8 c) d0 R) Y; j( m
  101.         }  
    7 V4 p8 r& Y: `% f( Q
  102.     } 3 R: ^, J/ W" R, i& I
  103.     FLASH_Lock();   // 上锁2 ^6 }8 O( a2 Q8 Y5 [8 i- u- o3 V
  104. }
复制代码

; n) _" A) V' {+ a; K. Z1 Q$ X5.3 IAP程序
) v0 }& \" B% u% a* S  ^+ f* `IAP程序包含两部分,一部分是将接收到的.bin文件写入Flash,另一部分是跳转到App执行。$ C8 Z+ V4 @: _
  1. /*9 V# E, c+ P7 N! l' J
  2. *==============================================================================9 p6 B8 g+ A1 Q/ p8 |7 M
  3. *函数名称:iap_write_appbin* [% G' f. \  e! j, G; N
  4. *函数功能:写入.bin文件% }; B1 Y4 f2 n: s
  5. *输入参数:appxaddr:App程序起始地址;appbuf:缓存App程序的数组;4 B1 o/ ~( j9 n! c+ R$ s
  6.                         appsize:App程序大小2 F4 {. d2 D+ S0 r4 u% _
  7. *返回值:无/ A) O) |' Q% b! }0 e- Q
  8. *备  注:无3 v$ e+ M  ?2 Z: R* A
  9. *==============================================================================
    / N8 i  Y! M1 e# [% p
  10. */8 g" _9 L- y" @1 a. U: l" t
  11. // 对Flash操作的最小单位是16位
    ( c  x/ _# {1 m5 p
  12. u16 iapbuf[1024];   // 要写入Flash的内容( V' j" W0 H1 Y& p, r
  13. % M2 X/ m% ~3 c* G( F
  14. void iap_write_appbin (u32 appxaddr,u8 *appbuf,u32 appsize)
    , [: \% J* ~( l0 m
  15. {
    ( s7 Z, S- O" z4 C7 r
  16.     u16 t;   // 临时循环变量" [$ E& }, V' l% {! O, Y) t
  17.     u16 i = 0;   // 自增索引1 ?, c1 \' I2 M5 {* W
  18.     u16 temp;   // 临时计算变量' O0 y% o/ z7 @+ ]" G, Y
  19.     ; e: a! K. \3 l9 v2 Z, ]
  20.     u32 fwaddr = appxaddr;   // 当前写入的地址
    ! U) E: G9 u& d! A* K; ^
  21.     u8 *dfu = appbuf;   // 指向App代码数组首地址的指针& e6 u# X, t3 X, w( h) o! A
  22.       b9 i) ?( j% n' T
  23.     // for循环,2K为单位进行写入, i3 b0 [9 U) |& I& L! ]7 W
  24.     for(t = 0;t < appsize;t += 2)
    1 a' Q9 C# g- e
  25.     {  
    3 d8 o" V& K9 Q
  26.         temp = (u16)dfu[1] << 8;
    ( |7 |/ R7 q6 j! s
  27.         temp += (u16)dfu[0];   : W9 q, Q' w! D3 P* b- X& c/ P
  28.         dfu += 2;   // 偏移2个字节
    + `/ [& t- @( b6 E: n: x
  29.         iapbuf[i++] = temp;     + E+ f6 ^7 h% v* z
  30.         if(i == 1024)
    . j3 _* @! j  p
  31.         {
    " H# q1 E: [0 ]* Q- J+ B* Q5 I
  32.             i = 0;  C. f, q0 p, H4 [" a9 W
  33.             Med_Flash_Write (fwaddr,iapbuf,1024); 5 B- K+ Y7 @3 n- c8 l
  34.             fwaddr += 2048;   // 偏移2048  16=2*8所以要乘以2
      X; p, a* V% R2 i
  35.         }
    & c' ^+ j" Q  N, e0 r9 F' O5 j" Z
  36.     }
    + [' Q2 q/ u% A& \# V- Y$ v# d1 h8 }
  37.     if(i). f9 ]. F9 ~+ Y5 d& f; M
  38.     {# t6 z! s: L6 n2 R0 S) ~) G
  39.         Med_Flash_Write (fwaddr,iapbuf,i);   // 将最后的一些内容字节写进去% d0 K  \* h; {# G( o2 j$ R; F
  40.     }) R  o3 E, Z9 h( c
  41. }
    9 @* v% d& g6 \. |: Z
  42. /*: h: k; k- `0 h4 _. y" m( _/ ?  A! |
  43. *==============================================================================- U; `$ J0 h$ G# |; B3 J8 K
  44. *函数名称:iap_load_app, g; N" M6 }( O7 f
  45. *函数功能:跳转到App. b4 [5 Q1 u  ^/ e* A
  46. *输入参数:appxaddr:App程序起始地址
    6 P2 d& _, A: U# Z+ L- D$ Y4 {1 O
  47. *返回值:无  o9 T& m8 H& x  ]; @, U
  48. *备  注:无7 h, |- N' X8 c* H5 z
  49. *==============================================================================1 U, a& u1 z* j; U7 U
  50. */
    1 f# ?9 i  J- j
  51. iapfun jump2app;0 q6 N6 g* ~0 R1 p% o

  52. , K7 ~" }. M) v, y! W
  53. void iap_load_app (u32 appxaddr)" R( g, P* j0 q
  54. {0 p8 u% g/ H4 Z" l
  55.     if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)   // 检查栈顶地址是否合法
    9 F8 X/ i+ L3 H# q" V0 D
  56.     {
    + `. F! N8 B/ {% E3 u
  57.         jump2app=(iapfun)*(vu32*)(appxaddr+4);   // 用户代码区第二个字为程序开始地址(复位地址)  
    " V) x: }3 Q8 U+ x) S# H2 [9 z2 h
  58.         MSR_MSP(*(vu32*)appxaddr);   // 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)6 l- i! K& y4 \4 q2 h
  59.         jump2app();   // 跳转到APP
    & V! r$ P5 X6 p( L7 G/ s9 @" l
  60.     }
    % A* M- w0 f2 Y
  61. }
复制代码

1 G: U% F7 Y- i" u5.4 main函数
3 R( S/ t8 Q" Y  Rmain函数设计如下- W* o& B# S1 K- s1 \
  1. int main(void)
    * |" Z# Q, c8 W) q" ?7 ~
  2. {/ g$ U0 t% w9 V8 n
  3.     u32 curRecCunt = 0;   // 当前接收数量, b; r* @7 a. S, j; ^9 z/ f
  4.    
    % ?& f) F& M, R
  5.     // 设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    * B* S7 X4 K6 j$ j
  6.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);2 x9 V0 M: E: P. s% ]
  7.     delay_init();   // 延时初始化9 r4 d( c. r4 [- H# P* o0 Q
  8.     uart_init(115200);   // 串口初始化
    6 M: C$ g& z, Y8 c
  9.     # o0 f# f  {2 H1 H
  10.     printf ("Enter the Bootloader Code!\r\n");
    1 c4 z. _# ]3 k: s( J+ d: }& I
  11.    
      B4 n% u$ y( x0 n; N
  12.     while(1)
    0 M+ A$ b+ f, }7 j  Z* g% J* x1 c
  13.   {
    $ F- E- `" S7 p/ V! h8 R' {/ m
  14.         // 如果接收到内容且传输完成4 o2 `( R* t! b# J0 F2 N  b
  15.         if (curRecCunt == gReceCount && gReceCount != 0)
    ' J# W! @$ A# t+ d
  16.         {
    7 @0 _: o* k/ I1 O. O. @  m7 a
  17.             printf ("App Code reception complete!\r\n");
    1 D# |' Z7 v( O% Y  E5 N7 [
  18.             printf ("The length of the received App code is %d!\r\n",gReceCount);0 }" ~$ ]; I1 _. y: `# R: h! W
  19.             
    + Q8 w; X* H7 g4 p8 L" \3 T  Z
  20.             // 开始准备写入Flash0 f, l8 v5 y' j7 ~
  21.             if(((*(vu32*)(0X20001000+4)) & 0xFF000000) == 0x08000000)   // 判断是否为0X08XXXXXX.
    2 H& R/ y0 \5 Q# ~2 I2 Q  t1 B
  22.             {  U: p' y) Y) y
  23.                 iap_write_appbin(FLASH_APP1_ADDR,gReceFifo,gReceCount);   // 更新FLASH代码  
    4 t" [5 t0 k/ U# q3 b
  24.                 gReceCount = 0;   // 清零接收计数变量$ J# B8 j& }+ ^7 }: q" T  Z4 t
  25.                 printf("Update complete!\r\n");& b" M2 C5 W$ Q7 n
  26.             }
    ) V  J6 [# k) C- U  N
  27.             else
    0 P+ y4 R8 G5 M& e4 w/ s1 H: I
  28.             {
    6 D; x( M) a4 Y! c
  29.                 printf("Update error!\r\n");
    8 {, }2 V$ K  @& Z
  30.             }# M! |- ?* V, a+ _" E$ E
  31.             
    # w$ c" S: z/ L7 B1 w& E4 v
  32.             // 准备跳转App执行
    - l# l. F' |% I. Z) v8 M# u
  33.             if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)   //判断是否为0X08XXXXXX.! e' w8 G- g' @: _! z
  34.             {
    & S4 \" t* [( t
  35.                 printf("Enter App!\r\n");! @& A2 @9 L7 |* `+ ~( G
  36.                 iap_load_app(FLASH_APP1_ADDR);   //执行FLASH APP代码
      g- p- D& }7 Q, m: U) `& p- u/ O
  37.             }else
    + d2 s; Z' |) f/ o
  38.             {
    7 J* b0 B  b4 f9 X# l7 {# |; |; S
  39.                 printf("Enter error!\r\n");
    2 f( |- M+ a/ H; n' U5 Y
  40.             }
    . g5 t5 p; g/ ^, s9 J
  41.         }
    7 s$ B7 F1 c: t4 }6 I; I
  42.         else   // 未传输完成
    % o1 D) L$ ~3 J: e
  43.         {0 `( g8 Y: r9 b
  44.             curRecCunt = gReceCount;   // 更新当前接收数量
    : D+ U+ J2 U4 y0 d6 U6 J
  45.             // 延时一定要有,给串口中断留出时间# h; E; ]7 r5 G) v# ?" V
  46.             // 如果没有会导致接收不完全
    4 B" x: U: ^1 E. R$ c2 h  h
  47.             delay_ms(10);
    8 L) |4 k1 ~% a% a, p: Z' S- t/ D
  48.         }
    8 P& X, o6 ^6 d0 A! Y9 K( J! d
  49.     }
    - b& \- o1 x: O, l; K% u
  50. }
复制代码

6 ~/ ?+ S: t7 e六、注意事项
1 t1 k! J& U1 ^需要注意的是,上面的程序中App程序是从0x8010000开始,需要配置好程序起始地址和中断向量表偏移。7 a, Z6 \3 G7 F- \/ H+ ^9 D& g

! C$ r! G3 U" ~* V1 A, H  K$ i/ n. M
转载自:二土电子
. C. p% t8 S, A如有侵权请联系删除" b, j6 R* i' O: a6 b1 U) T

4 P/ `. _0 q( r: U  N
收藏 评论0 发布时间:2023-10-23 22:37

举报

0个回答

所属标签

相似技术帖

官网相关资源

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