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

STM32内部的BootLoader

[复制链接]
gaosmile 发布时间:2021-1-15 11:15
前段时间研究了一下STM32内部的BootLoader  ,也就是STM32内嵌的一段程序,搞明白原理后,使用Qt编写的一个测试小工具,展示效果如下:https://mp.weixin.qq.com/s/8dXPqM0e7_VJXrFh2PdWGQ; c1 S9 E0 u) L# t8 O
可能你还不知道:STM32中内嵌了一段自举程序,可能很多人不知道。那段自举程序存放在System memory(系统存储器)中,我们通过配置启动,选择启动系统存储器就可以运行这段程序。% w, u" m. P1 x! _7 y
STM32有三种启动模式:01.Main Flash memory:主FLASH
; Z7 F: V: |+ W02.System memory:系统存储器4 V- e+ `" A: T7 f# s
03.Embedded SRAM:内置SRAM
# a4 t9 T& f# U9 D3 K# {) S1 v三种模式是通过不同配置来实现,一般通过BOOT引脚和BOOT位来配置启动模式。不同的芯片配置有差异,有些芯片没有BOOT1引脚,会结合BOOT位来实现。
! E* ?# @- @4 _# s) E2 f
内嵌的自举程序, I" Y1 S( L/ @; _3 ~! A+ ~, G
STM32内嵌的自举程序存放在系统存储区,由ST在生产线上写入,用户不能修改。我们选择System memory(系统存储器)启动模式,就会进入系统存储区执行自举程序,内嵌的自举程序主要用于通过串行、IIC、SPI、USB、CAN等接口与外部通讯。不同型号MCU支持的串行接口不一样,具体请参看应用笔记 AN2606。

' u6 g4 o  A0 `& y) P1 Y0 \自举程序中UART协议' s  k* |6 X# s1 m- f
通信就会牵涉到协议,这里也说一下自举程序中 USART 协议。
4 m; I$ c+ j% I. ^* W; `$ ~4 K
USART 自举程序命令集:(具体参考官方文档AN3155)
- O* y8 J  k4 [
微信图片_20210115111120.png
2 g0 b6 \9 t6 f! c2 R# h
4.介绍几个简单的命令流程$ }3 `4 ^; C. b/ k, U4 v
4.1 Get 命令
( c5 t+ {/ y+ X$ g用户通过 Get 命令可获取自举程序的版本及支持的命令。自举程序接收到 Get 命令后,会将自举程序版本和支持命令的代码发送给主机。

: e" h6 N$ b/ O  E, X
微信图片_20210115111126.png # m$ H+ A8 D' j* M! g3 a; o' J
具体如下:
! P2 d2 W9 R- [: n- n
微信图片_20210115111129.png 5 j# Z- J5 Y% M; I

" j! u9 K. A/ i# {+ b; Z3 o
4.2Get Version & Read Protection Status 命令GetVersion & Read Protection Status 命令用于获取自举程序版本及读保护状态。自举程序接收到此命令后,会将如下信息(版本、使能和禁止读保护的次数)发送给主机。
4 L0 W% N/ H/ F8 A, F$ ~: E5 C
微信图片_20210115111137.png ' \5 d% }5 _/ U; z9 B1 |
具体如下:+ i- ~, T1 h4 \5 l6 ]2 e/ F( L
微信图片_20210115111140.png
( O( E% H7 i  h# k& H

! n( C0 X! G0 ?/ ^: y; Z# s
4.3Get ID 命令Get ID命令用于获取芯片 ID(标识)的版本。自举程序接收到此命令后,会将产品 ID 发送给主机。 微信图片_20210115111145.png - T( D4 J( l* X
具体如下:- e7 P, G: t/ i
微信图片_20210115111148.png + [! F5 u# j9 g+ s' ^* v# f

, ]1 u. H8 s% b, p2 T
4.4 Read Memory 命令ReadMemory 命令用于从RAM、 Flash 和信息块(系统存储器或选项字节区域)中的任何有效存储器地址(参见注释)读取数据。(读的前提是Flash没有加读保护操作)
* S. M1 S8 G  m5 P 微信图片_20210115111153.png ) h/ D2 q( R: T6 j
具体如下:(读取flash 0x 08000000 开始的16个字节的数据)
+ R2 e) k) M- _9 S, \2 j; _1 S 微信图片_20210115111156.png 0 z. U" @( a& |3 G. J. }6 b- k3 j
: Z9 E" h" r1 l# k' X
4.5 Go 命令Go 命令用于从应用程序指定的地址开始执行已下载的代码或其它任何代码。自举程序接收到 Go 命令后,会将 ACK 字节发送到应用程序。发送 ACK 字节后,自举程序会等待一个地址(4 个字节,字节 1 表示地址 MSB,字节 4 表示 LSB)和一个校验和字节,然后检查接收到的地址。如果接收到的地址有效且校验和正确,则自举程序将发送一个ACK字节,否则将发送一个 NACK 字节并中止此命令。
" w/ I6 {9 D4 i/ w, a
微信图片_20210115111206.png
8 K# F/ K3 y' e7 u4 f
具体如下:使用go 跳转到 flash地址处执行(0x08000000),此时LED已经闪烁
! V6 @9 B: P; O2 ^, q 微信图片_20210115111209.png ' e6 t: F: c& _& `
命令太多,其它的就不再介绍了!
" L3 p, c* L9 o, C9 |  i1 b5.自举程序上位机(自己使用Qt编写的,主要用于测试)01. 在自举模式下可以简单实现对内部flash的数据的读出(flash不加锁)02. 在自举模式下可以简单实现对内部sram的数据的读出(flash不加锁)03.可以生成对应的.hex文件,使用Qt自己设计算法实现。04.下载部分暂且没有做。5 y5 N0 W( V; m! i
微信图片_20210115111214.png ( f4 F5 Q% r$ }3 M/ Z
微信图片_20210115111217.png 0 c. c+ J1 F7 R: m3 c$ U( b, K

, f4 ]! x& G$ U6 P1 m- C, E2 w1 \7 F% ]
读取Hex文件  和拼Hex文件代码开源给大家
. d8 C3 s5 x0 z$ ]+ Z5 j7 h8 l
  •    // 读取芯片内部flash数据
    , p  k1 m1 b- f/ }! m
  •    // 地址范围:0x0800 0000 ~ 0x0801 ffff   128K Byte
    4 A/ m$ Q! s" s
  •    // 循环读取,每次读取16个字节,读取128K Byte /16Byte = 8000 次
      _2 t4 w2 t. }  G- p& Y+ H
  •     int base = 0x08000000;
    ) k& _( @! ]# ^) B
  •     for(int i =0;i<8000;i++)
    9 ]+ e9 Z7 d' u. z. k" H$ K# U
  •     {
    % i1 B9 L/ y7 m( Z7 y, r1 B; M
  •         //1.发送读内存命令0 b  z. d9 J1 n& T
  •         UartSendHex("11ee");
    , A- y3 U* |" j' m; R1 D0 I
  •         Sleep(5);  A% x# O/ G, S9 m7 r4 B( Z9 Q4 B
  •         //2.发送地址和校验和" T3 \1 x' i+ R: r
  •         int offer = base + i*16;
    , Q  {1 D- h+ v  y
  •         QString offer_str = QString::number(offer,16);
    ' N' Z+ X2 C/ w* R9 Z4 J
  •         if(offer_str.size()<8)* `! [: _: t  [9 D: B& R4 l
  •             offer_str.insert(0,"0");2 t6 ?0 S9 z# ]$ _
  •         QString check_str;0 b. C. g8 h+ {7 s
  •         check_str = getXORresult(offer_str.mid(0,2), offer_str.mid(2,2));
    + F3 w% I: }6 P& u. Q5 n: r& I
  •         check_str = getXORresult(check_str, offer_str.mid(4,2));
    " Z2 T% |# u; v; w* H
  •         check_str = getXORresult(check_str, offer_str.mid(6,2));
    1 i" Y. ]* z5 c3 T
  • 0 n, X* z# ^3 ^! v' @3 z. k" F
  •         UartSendHex(offer_str+check_str);
    ) Y  i2 \! B3 u* p( H& V
  •         Sleep(5);7 t1 e7 @3 g4 w- s
  •         //3.发送要读取的字节数和效验和
    * v0 l6 v, @6 k
  •         QByteArray ByteNum = "0ff0";4 x/ e* V5 L$ A, m* O
  •         UartSendHex(ByteNum);
    * C5 W) F: D0 ]/ k% j: n. F* I
  •         Sleep(5);! S) P2 A( c' v" R: `$ R" S! Z; Y
  •         ui->progressBar->setFormat(QString::fromLocal8Bit("%1%").arg(QString::number((i+1)/10, 'f', 1)));4 E; c" I+ z; l' C9 C9 V
  •         ui->progressBar->setValue((i+1)/10);* U+ R: m8 l5 e2 n
  •     }0 G* \( ]" `  I* `* I  U/ |0 Y1 W
  •    
    . k8 ?4 \  a% d5 M9 {9 }9 ]
  •    
    2 E1 G; V1 t2 P6 R4 g- ^& `% D
  • // Qt flash数据 转 hex文件算法* {6 r2 d' O, p; E6 @

  • & K5 A1 `1 B% y3 h  B+ M
  •     int t = 0,g = 0;9 Y% u) [) I* @- D' j2 {" p
  •     QString text = ui->textEdit_Recv->toPlainText();
    : i; \$ A' Y: |% i1 I+ A) L
  •     QStringList number_list = text.split("\n");( Z- E+ _2 n' X% |# w9 X
  •     QStringList result_list;
    1 G1 g$ j! a/ ~7 E, `
  •      // 遍历字符串列表,滤除全ffff.........结尾,记录行数
    6 d! n8 R5 P5 ~  X( A7 W
  •      for(int i = 0; i < number_list.size(); ++i)" U2 m- b' M) ^2 P- b* n
  •      {
    9 ^0 d  r6 E7 q
  •         if(number_list.at(i) == "ffffffffffffffffffffffffffffffff")
    4 |: ~/ g( ]# Z! @, ~- s+ g
  •         {# L$ O1 X" ^/ z" B* n$ I: `: \
  •             t = i;, W$ ^/ O$ Z1 f/ H2 i: L- q
  •             break;. x# I; A& W+ c9 ]
  •         }
    2 m" [. ]/ T, O6 b
  •      }
    ! D$ y3 N8 q8 ~, t: A  c' S9 e
  •      // 遍历字符串列表斩断行,滤除改行的 ffff.........: `) t$ v  V' a) V* a
  •      text.clear();
    ; R* ~) Q/ e/ f4 y
  •      text = number_list.at(t-1);
    " Y. g# O/ X: C% w' Q
  •      for(int i =0;i<text.size();i++)1 W* i7 [& z) G! B- D! a
  •      {
    4 j. a$ c* y& t* x! u! G4 A% @
  •          if((text.at(i) == 'f')&&(text.at(i+1) == 'f')&&(i%2 == 0))6 A  ]* M9 v$ d. d
  •          {3 x- _2 A* N1 w# J+ P6 X. N7 w
  •             g = i;
    / X7 J8 D4 }" G! m# e9 q  N0 g
  •             break;# n, i4 j9 M! _# X! y1 I
  •          }
    + P& D0 H5 Q4 L9 p2 B, |) l/ J2 h2 {
  •      }8 s# g' Y, D2 b9 z4 k  o
  •      // 拼接新的全数据的字符串列表
    $ ]+ ?  Q& I6 D$ x6 N
  •      for(int i =0;i<t-1;i++)
    $ Z5 e) H' F* F% G. I/ k. q
  •      {5 i! M0 `% I: S- m0 H, Z) Q
  •         result_list<<number_list.at(i);) J. {" l/ y% M( D5 X) a' b
  •      }
    4 n; w2 I5 B$ n, d& S
  •      if(g!=0)5 Y# \3 F$ ~8 }: ^; }
  •      {
    . ?3 o5 v/ {: V! N# c" q# k! K
  •          text = text.remove(g,32-g);8 {! c+ F% v7 c3 {3 Q8 S
  • - _3 `' d- C! ~/ X3 \
  •          result_list<<text;
    3 o' a, V5 C5 C
  •      }
    # ]5 K7 R5 I, ~: }! Y* X" ~) f
  •      //生成HEX文件
    1 _2 Z1 t- u! D/ P5 U; Y5 G# D- J# ]
  •      //1.添加 10 字节数0 c  T9 \7 f1 j, j& V8 H$ K
  •      text.clear();
    . W: E$ k0 q8 n3 `  K: @9 P5 D3 _
  •      QStringList result_list1;
    . D8 \. k! u; p- H& z% m9 D
  •      for(int i = 0; i < t; ++i)! x' d. Y, {; _3 J& a7 O4 f
  •      {
    & E2 C% \& b  u) Z
  •         text = result_list.at(i);
    6 ~8 r( T% z' N: F. }( T
  •         if(i!=t-1)
    . _' A0 m* w+ O3 h/ I5 i4 C
  •             text = text.insert(0,"10");; t+ @5 w$ B+ n* h# ?5 M
  •         else
    ; b+ |. D. U: S- B
  •         {6 y4 i+ n* H, z7 t0 U9 ?1 A
  •            QString str = QString::number(text.size()/2,16);
    9 H& K: ~# n& t/ E" L- b
  •            if(str.size()<2)
    " X2 S2 ~% f, r1 q
  •                str.insert(0,"0");
    ) R/ P3 ]( D* W/ _  E! S
  •            text = text.insert(0,str);! V) D, l% x2 O
  •         }
    $ U& Z1 N* I9 E. j. `9 j
  •         result_list1 << text;
    # w! S4 N9 U/ N# _1 `( n
  •      }
    & T/ V2 @; O  H$ P0 N3 E
  •      //2.添加地址偏移9 Z4 D3 |" ]0 z
  •      QStringList result_list2;5 B3 o5 B  P: {2 h3 |& A2 w% O* r3 W' h
  •      int offer = 0x0000;
    2 x$ N+ l% k4 y/ p% G$ k
  •      QString offer_str;
    2 b% Y# U7 n3 k6 U1 ?0 n( B; |; q
  •      text.clear();
    : u7 X$ x; \* m
  •      for(int i = 0; i < t; ++i)) D5 I/ t, L! E8 L- T
  •      {
    , P, i% N7 W8 d6 X& h
  •         offer = i*16;" S8 c5 B, R5 c) @4 p: C; _+ ~
  •         offer_str = QString::number(offer,16);7 ?$ R" g, J# v
  •         if(offer_str.size()<4)
    & ]# n8 r! z8 f1 |& q
  •         {
    7 ^6 s* e6 {8 ^
  •           if(offer_str.size() == 1); t, C" D; l" }" P
  •              offer_str.insert(0,"000");
    3 \* A/ l/ C, j3 ~; ~
  •           if(offer_str.size() == 2)* @0 F3 C6 m, f0 b, A
  •              offer_str.insert(0,"00");
    , d5 A* N7 a7 C
  •           if(offer_str.size() == 3)6 f/ {" D8 s* C* G6 h" n' i
  •              offer_str.insert(0,"0");
    % }3 a6 S7 V* D" k+ r# P& z$ U
  •         }
    ' E( ~, B5 q' b- J
  •         text = result_list1.at(i);
    + L4 d1 ?3 j2 Y  D' ^
  •         text = text.insert(2,offer_str);
    1 \: E& _3 n8 {5 R. G* t& L
  •         result_list2 << text;  F6 ^3 }* g+ {2 l1 ~
  •      }
    5 h' A: K* i# w
  •      //3.添加效验和7 e0 A' L4 x7 `9 p
  •      // 以020000040003F7为例子
    ) i0 [+ ]& ^* O" n1 V
  •      //具体算法为 0x100-((0x02+0x00+0x00+0x04+0x00+0x03)%256)= 0xF7
    : v7 G. ^, Q  B4 k& L, R
  •      text.clear();
      i2 ]4 W* D: q5 l- r; F, \
  •      bool ok;
    # v7 e' U# k3 |) p
  •      int check;
    ) i# C5 N7 f+ K+ M8 t# Y
  •      QString check_str;4 ]# w# _( R1 d+ Y; g# m% X1 p: q
  •      QStringList result_list3;4 U; d! J* V' M* v
  • ( U" \& `, p% L0 f+ a, d
  •      for(int i = 0; i < t; ++i). K: h$ P9 b2 \  p3 A- E
  •      {
    9 a) l1 R: k' x4 X) U
  •         text = result_list2.at(i);% Q9 n7 F' b1 \& x" T: ^
  •         check = 0;' F; _. Q: T8 e8 r& Z
  •         for(int j=0;j<text.size();j++)
    $ Q$ ?$ q* y; N- F8 B0 D" @" l; I
  •         {+ {/ U) _! C. _
  •            check += text.mid(j*2,2).toInt(&ok,16);
    ( ~; x4 A3 u) U$ m0 W9 {, j* ^
  •         }
    2 }9 H7 S: F6 ]0 ^$ w. F) y- V
  •          check = (0x100 - check%256);# n1 h, w  M) M' B. H) @6 P0 F
  •          check_str = QString::number(check,16);
    / Z8 R2 V+ V5 O. X" q' o
  •          if(check_str.size()<2)5 z( y8 K7 v5 g; e0 u0 {* X6 n/ v
  •              check_str = check_str.insert(0,"0");
    0 h  Q/ ~0 M$ }( Y! @0 L3 N' ]
  •          text = text.insert(text.size(),check_str);/ m- t# l) p. r
  •          result_list3<<text;
    ) \+ t8 M$ Y) Q7 G, W
  •      }
    / E1 \0 w' ?- {, p" R2 {' {
  •      //4.添加冒号 :
    & C+ {! ?5 P" M" A! }* D* |' q+ B
  •      QStringList result_list4;1 {/ |$ i- i5 V- h" T& J
  •      text.clear();
    - e- z2 l, z, Z% U8 g* `
  •      for(int i = 0; i < t; ++i)& y  b2 O, g, w" T* V
  •      {
    ) w. E( S6 r* i$ }& i  c
  •       text = result_list3.at(i);
    : a& X7 z+ ~* |% N5 Y& U8 ~% t
  •       text = text.insert(0,":");
    8 m" Y0 ]& ?" q3 J8 S% g
  •       result_list4<<text;/ l! g: Q5 d* W2 l
  •      }
    / o2 Z: M" F$ f3 }1 B; |7 ?
  •      //5.添加HEX头部. Z& H9 O+ e, j
  •      QString hear=":020000040800f2";
    . a# U' q  w2 |, A- c8 Y) c
  •      //QStringList result_list5;
    & c0 t6 A6 T( g: ?! j% A% o
  •      result_list4.insert(0,hear);
    , ]( P1 ]! s, F. z9 V
  •      //6.添加HEX尾部
      n9 Q4 A5 W& n+ o
  •     QString tail1=":04000005080001cd21";
    ! O; N0 [' E9 @" |) [9 j9 ]: P8 l
  •     QString tail2=":00000001ff";/ }9 j& f. n' b/ i+ n' e1 R0 H8 f
  •     result_list4.insert(t+2,tail1);+ m  H' D% V' Q8 r: Z& _9 m1 Z
  •     result_list4.insert(t+3,tail2);
    % }7 ]% x- A  B: m- k4 o/ Z$ _
  •     //7.保存数据为.hex文件" x* u% w3 w3 |  m
  •     ...............
      U/ s% ?& I9 U$ U/ i

0 c( ]" h, w+ V: ]; J
0 ?! i5 W1 \4 s% V/ y. r
收藏 评论3 发布时间:2021-1-15 11:15

举报

3个回答
goyhuan 回答时间:2021-1-15 11:58:36
是不是相当于可以不用写BootLoader代码直接更新了
freeelectron 回答时间:2021-1-15 12:16:16
其实就是ISP功能,利用串口下载程序
mr_la 回答时间:2021-1-15 15:20:26
好样的,,一直知道有多种下载方式,但是没有想到要去研究一下它的bl..

所属标签

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