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

STM32F0系列之使用汇编点亮LED灯

[复制链接]
STMCU小助手 发布时间:2022-8-25 21:30
一、启动流程& P1 I0 ^) s. F4 r8 B1 i
对于STM32F103从flash的启动流程如下:. {$ ]$ U7 _5 y; u# M
首先设置栈:CPU会从0x08000000读取值,用来设置SP(不使用C语言可以不设置,或者在程序里设置SP)* Z8 T3 ]8 C5 \' ~" T% z: x  S; U
然后跳转:CPU从0x08000004得到地址值,根据它的BIT0切换为ARM状态或Thumb状态,然后跳转
  N! F( e; S1 B$ o          对于cortex M3/M4,它只支持Thumb状态,所以0x08000004上的值bit0必定是1* y/ R2 N& C# D' M3 w$ I- i
           0x08000004上的值 = Reset_Handler + 1
4 y( A& p4 k: R) v5 ?9 |& ]6 i+ @接着就从Reset_Handler继续执行, z' l' b! B; p/ S0 G
二、在keil-MDK下编写程序/ L/ s' x! z, o4 D
打开keil,新建工程,选择STM32F103ZE
4 o# t' Z% P+ N) e# H6 e* \( f: I( e! t1 O2 v& ]5 A
20210101185822103.png
7 z6 R3 p8 `$ Z% m7 T' H2 d/ Y4 i6 O- ~" x7 F* l
新建start.s文件,编写如下代码;
6 d8 b/ X4 ~8 J
  1. / v* A3 X7 n! D) n! _% A
  2. 2 o0 l. A! @& }" V* f7 n- t. q
  3. ) J% h1 G, }  U' _# g7 h$ A3 f
  4.                 PRESERVE8                                                        ;指示编译器8字节对齐
    6 C7 o, s3 Y4 |7 T4 j
  5.                 THUMB                                                                ;指示编译器以后的指令为THUMB指令                                                               
    2 S6 f" S% h( \! o& g1 ~

  6. 1 [7 D4 M( _$ K0 t, }% v% G
  7. ! J+ G+ B! I, r1 I
  8. ; Vector Table Mapped to Address 0 at Reset
    1 ~8 ?: d' K* H5 f/ F$ B
  9.                                 AREA    RESET, CODE, READONLY                ;定义只读数据段,标记为RESET,其实放在CODE区,位于0地址
    * C& _! `+ w" t) t, X
  10.                                 EXPORT  __Vectors                                        ;在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用2 D, M0 Y& B) @  T
  11.                                        
    4 @* i3 r9 i% G8 ~5 d
  12. __Vectors       DCD     0                                        ;当前地址写入一个字(32bit)数据,值为0x00000000,实际是应该填入栈顶地址 , `; [7 C( N- v' ~2 D5 n
  13.                 DCD     Reset_Handler                      ;当前地址写入一个字(32bit)数据,值为Reset_Handler的值,即程序入口地址* C# C/ S9 D6 K
  14. * O3 i& Y7 A0 {7 y# l$ ?$ _
  15.                                 AREA    |.text|, CODE, READONLY                ;定义代码段,标记为.text
    7 c; T* D# j2 ~, y5 a% r' I  }& x- a
  16. # K+ p! v/ Z! _, _3 D  z; V
  17. ; Reset handler        ;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰1 q4 z4 @  y* c& E9 }& I5 i
  18. Reset_Handler   PROC                                                                ;过程的开始 # @* m2 z7 v% `& G( I7 t) L
  19.                                 EXPORT  Reset_Handler        [WEAK]                ;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。
    * s1 ^4 I- v; |% L, b9 I  i6 ?' Q

  20. " O1 Y; W; \. t$ C" }. ]* \. y0 K
  21.                                 ; 1、使能 GPIOB 7 u+ i" k( U/ G4 A0 I$ u3 ~
  22.                                 LDR R0, =(0x40021000 + 0x18)
    3 Q# u3 G) q; j+ h
  23.                                 LDR R1, [R0]: Z9 k' H+ ?$ W: @7 M- j
  24.                                 ORR R1, R1, #(1<<3)" V# m; Z# @5 _2 }
  25.                                 STR R1, [R0]
    4 Z. d/ k8 F0 t+ A! l% h5 l9 E
  26.                                 ( }1 W5 U' O' f$ [/ N- ?
  27.                                 ; 2、把GPIOB5设置为输出引脚: ^: [5 q0 T$ L, k
  28.                                 LDR R0, =(0x40010C00 + 0x00)( _( z. @4 l" p( I4 Q. U
  29.                                 LDR R1, [R0]
    - C( s* l9 ^# z* x6 w, ]
  30.                                 ORR R1, R1, #(1<<20)  ?  K$ P4 j2 f1 q- a! J
  31.                                 STR R1, [R0]
    / o, P; p. N4 u; [- a
  32.                                 3 Q. i1 v2 n  F' x2 X$ ^- t: W  D
  33.                                 ; 3、设置GPIOB5的输出寄存器' v1 ^1 i2 J& q1 b2 K
  34.                                 LDR R2, =(0x40010C00 + 0x0C)
    " B& y; ]7 I; N6 D# b
  35.                                 
    8 p; J+ N  y& u" c- s9 h* T
  36.                                 ;4、loop循环- W( _* q& D1 E
  37. Loop                                
    . Q$ U0 x9 z1 g% i! h1 t1 k; D) E
  38.                                 ; 5、设置GPIOB5输出高5 k* h+ I" Z- i
  39.                                 LDR R1, [R2]" |; T1 O% [! d: m6 S
  40.                                 ORR R1, R1, #(1<<5)" e! a2 g1 U0 P) f( P+ O
  41.                                 STR R1, [R2]
    - r0 m% l( ?' L" k3 ?1 F5 p
  42.                                 3 J' Q/ x) i1 Z; n2 y. J0 o
  43.                                 LDR R0, =1000000
    1 a0 b; S5 S' f5 D$ _
  44.                                 BL delay
    . r: \; j" p# S
  45.                                 
    - p' ~' q4 ?$ V5 J
  46.                                 ; 6、设置GPIOB5输出低6 a- Z4 h3 q. N" E
  47.                                 LDR R1, [R2]
    " _8 q) A3 s$ P& y
  48.                                 BIC R1, R1, #(1<<5), _! ]* _% B8 ^4 N: B
  49.                                 STR R1, [R2]
    % d, v7 u4 b6 s  e7 b( v% ^' t7 L' f) `
  50.                                 ! p" I. a; y6 u5 H# s0 o: W* }
  51.                                 LDR R0, =1000000
    8 Q% ^- A! P1 ~+ m
  52.                                 BL delay
    7 T6 k7 [: @! |; `* b
  53.                                 
    4 X9 ^: K6 _' ?1 k- R
  54.                                 B Loop
    9 L( X" L; b& B: ^; T  }! l2 d( @
  55.                 ENDP                                                                ;过程的结束
    & l* g& u/ \1 z$ f
  56. ! U; F, }+ s5 P' e
  57. delay. \! a' i" a6 {
  58.                                 SUBS R0, R0, #1
    # q3 G1 ~  a# T9 M2 r; o. N
  59.                                 BNE delay1 F+ u; f6 }9 V' A/ V, T
  60.                                 BX LR9 o0 A( K$ Q( i; x4 K$ s
  61. ' a, |6 ]* _1 y4 @
  62.                 ALIGN                                                                 ;填充字节使地址对齐
    1 ~) j1 _9 L2 `8 q) G  E1 u6 m. @* V
  63.                 END                                                                        ;整个汇编文件结束
复制代码

. E) c7 A) m; l# t* r' {然后添加如下两行在编译中执行的命令:
  1. fromelf  --bin  --output=led.bin  Objects\led.axf9 {1 \4 E# Q! ]; W3 Y# N  H
  2. fromelf  --text  -a -c  --output=led.dis  Objects\led.axf
复制代码
8 j$ y! `1 Q) A; L/ N
或者如下通用命令,和上面的命令是等效的
  1. fromelf --bin -o "[        DISCUZ_CODE_2        ]lt;a href="mailto:L@L.bin">L@L.bin</a>" "#L"3 }3 }2 A& m, ^  ?
  2. fromelf --text -a -c --output="[        DISCUZ_CODE_2        ]lt;a href="mailto:L@L.dis">L@L.dis</a>" "#L"
复制代码
1 H, m/ f; S/ |$ P* X
第一行生成.bin文件,第二行生成反汇编.dis文件
7 `: L& ]/ U& T* B7 s% h
( }- \9 F7 w3 H/ t( J 20210101203833562.png
# t) \$ d1 y/ U6 y! y6 y
- q" D0 N& b+ j4 G) C然后点击构建,可以看到,已经生成了led.bin文件和反汇编led.dis文件9 W& }9 u3 w( i! ?0 X3 q" ~$ @7 E/ ]
0 f) ]  P) D$ n) C0 H, e
20210101204003119.png / b+ k* P) p9 J
( V9 l( M" f0 u! e
打开led.dis和led.bin文件,可以看到,因为stm32f103的cotex-m3内核使用的是Thumb指令集,所以其指令长度既有16位又有32位;按stm32在flash模式下的启动顺序,先在0x0800000地址下获取栈地址,在0x08000000地址下获取程序入口地址+1的值,其中最后一位1表示thumb指令。  `& `. |; N: X0 m  g+ t  Y

. T! m3 Q6 U! @0 p 20210101213108321.png
6 n& W  \7 }& p4 Q* p  Z$ L8 J" o! H: [" b* g2 f
将其烧写到开发板,可以看到其LED灯闪烁
: j2 |: N6 t( T7 N& c" u# x7 U
. l" ?: i8 p, m' v: y 1.gif
$ u5 ^+ p% i- |; W( z) }6 x% T* s
: `6 g2 k# [1 U* w& k0 e1 `三、使用gcc编译程序

4 D+ [2 M" V  n7 f  @& N5 t编写在gcc下使用的start.s汇编代码
- X7 S2 U) Q0 p0 F" D
$ r. |; G* z7 m( Q- X* Z6 t7 J

  1. & S% ]+ ?) u7 H9 P4 q
  2.   .syntax unified                   /* 指明当前汇编文件的指令是ARM和THUMB通用格式 */0 c5 [; m  K- y3 p: o. B
  3.   .cpu cortex-m3                    /* 指明cpu核为cortex-m3 */
    1 D/ S+ T& P: s+ Q
  4.   .fpu softvfp                      /* 软浮点 */: B3 g( K1 T" ^/ f
  5.   .thumb                            /* thumb指令 */
    , ?' |5 O" ~' P$ H( A3 J

  6. ; Q' B6 _6 z4 F+ E  B
  7. .global  _reset                     /* .global表示_start是一个全局符号 */3 y- a3 `( F3 }; J4 ]5 F
  8. : i) Z: w8 o/ q" h
  9. .word 0x00000000                    /* 当前地址写入一个字(32bit)数据,值为0x00000000,实际上应为栈顶地址 */
    9 \, S$ j3 ]" a8 M4 d$ y
  10. .word _reset+1                      /* 当前地址写入一个字(32bit)数据, 值为_reset标号代表的地址+1,即程序入口地址*/
    5 _4 o' a, t) e6 Q

  11. % I; s1 w; b4 @
  12. _reset:                             /* 标签_start,汇编程序的默认入口是_start */
    / P7 ?) |. Q. g  o; s0 j1 @2 R
  13.     /* 1、使能 GPIOB */
    : \' w+ a& f  G! u
  14.     LDR R0, = (0x40021000 + 0x18)   /* 将APB2外设时钟使能寄存器的地址值写入R0 */
      g! x# D$ a9 Z
  15.     LDR R1, [R0]                    /* 读取该寄存器的值 */      
      v3 w  O: T# {( J9 d6 l
  16.     ORR.W R1, R1, #(1<<20)             /* 修改读出的值 */
    # p$ `1 `; U5 @$ ^
  17.     STR R1, [R0]                    /* 写入修改后的值到该寄存器 */
    $ `, b' S% _: ]- P4 t' b

  18. , z: D) N( o$ `& |( b  a
  19.     /* 2、把GPIOB5设置为输出引脚 */+ s. y: z( ?" ?  x0 Y  E
  20.     LDR R0, = (0x40010c00 + 0x00)
    0 R! E! U7 F( E8 G' c" x1 L
  21.     LDR R1, [R0]9 l+ M8 ?: m9 @
  22.     ORR.W R1, R1, #(1<<20)# N5 w  {# x& l
  23.     STR R1, [R0]
    % e2 d2 p& }" ?+ t; S9 n! n$ Z
  24. 0 a5 I" V+ J8 ]1 T
  25.     /* 3、设置GPIOB5的输出寄存器 */4 G, l: p* b4 ~6 A% w& ^  c+ ~5 ^
  26.     LDR R2, = (0x40010c00 + 0x0c)6 A( W4 k" n$ I" T' m
  27. 8 `  Y  K) v( B; v/ Z+ ^: ~- o
  28.     /* 4、loop循环 */
    8 Z: ~" U, u6 X. Y
  29. loop:/ I4 ~) @) S" @/ Z/ ?) T
  30.     /* 5、设置GPIOB5输出高 */
    9 ?' A, I+ L7 j1 S+ t
  31.     LDR R1, [R2]& L7 [% Z3 {9 Z; v5 t5 E
  32.     ORR.W R1, R1, #(1<<5)' v% f  [  V3 n6 ~
  33.     STR R1, [R2]
    4 F. O0 u4 k6 [) i4 ], M: U
  34. ! Q+ j( u8 Z& K- s7 L
  35.     LDR R0, =1000000
    + o" \5 `, z7 N  A- O0 S
  36.     BL delay
    5 Y8 g9 ~. n- I
  37. : S  V  ^6 ?  i* _, v, l9 j
  38.     /* 6、设置GPIOB5输出低 */; w8 h2 h3 h5 g& H  B9 g9 c% i" U
  39.     LDR R1, [R2]8 `: `( d/ i# s+ o& H
  40.     BIC.W R1, R1, #(1<<5)
    $ d9 {& K' h4 V9 {9 y1 J
  41.     STR R1, [R2]- O; m' {% A- L

  42. # C9 V% i6 Y3 Y( L* S- ?
  43.     LDR R0, =1000000
    ' u$ d# n5 A6 i) Q9 I0 T- j  i  r
  44.     BL delay
    1 w7 s0 _4 _+ g4 f) r
  45. $ G( L) H6 u+ n$ Z: Y! u: z- S
  46.     b loop
    4 Y# c1 H8 a3 ]9 J
  47. 3 E, [& i1 d. t9 Q, X- b
  48. delay:) a6 b  I6 Z7 @- |
  49.     SUBS R0,R0,#1
    ; |9 z: T9 X& X- @0 K9 A4 P
  50.     BNE delay& s3 s% _, a7 w: @5 f/ H1 S8 X
  51.     BX LR
复制代码

0 C. r3 F7 q  f3 x" N8 ]2 n以及Makefile文件如下
0 x& D  p8 A# U9 P$ r+ v5 W" ~2 H6 R3 t: W9 n5 D

  1. ! T7 w5 Q" ^" m3 h% G
  2. led.bin:start.S
    2 _2 D, o3 n& T' ?  |
  3.         arm-none-eabi-gcc -c start.s -o led.o/ x! y8 U; v2 f. v$ M3 [# e" Z
  4.         arm-none-eabi-ld led.o -Ttext 0X80000000 -o led.elf# N1 R" Z% G4 j" a/ M+ w# |
  5.         arm-none-eabi-objcopy led.elf -O ihex led.hex
    ! \" g0 R4 Y9 o2 l% f8 O' C6 l
  6.         arm-none-eabi-objcopy led.elf -O binary -S led.bin
    ; a+ v) C) I' H/ l, `
  7.         arm-none-eabi-objdump -D -m cortex-m3 led.elf > led.dis
    " |& O4 L; c! O$ g! b

  8. . w/ V0 D" k! |6 e% f
  9. clean:
    + x+ y' c7 ^7 L: _
  10.         rm -rf *.o led.elf led.hex led.bin led.dis
复制代码

; {: y! Z& g% }1 a/ l; W然后执行make命令,如下所示,编译成功," v' V7 L) m2 k+ ~8 ^1 _) I
' J" r. j9 X5 h& D% J
20210102013954949.png 1 w! w. s) u( T! t' g, p% E
1 K4 S4 f; Q7 E- H! Y
将其烧写到开发板,可以看到其LED灯闪烁
6 z. T7 D4 E6 [8 |$ v, E
4 A$ M! S  M/ u. O4 ]1 v; R/ B 2021010201402213.gif ( J! s( Y' V  G0 K! X) {& A; ?5 Z

0 y$ L# e$ G0 D$ L( A1 Y( D2 \四、比较MDK和GCC编译差别
0 w4 ]8 m5 t3 g, O分别比较两个.dis文件,如下所示,按stm32在flash模式下的启动顺序,先在0x0800000地址下获取栈地址,在0x08000000地址下获取程序入口地址+1的值,其中最后一位1表示thumb指令。
  s8 J  q; K2 z$ @( F9 @& G另外发现其中有一个位置不同,但在汇编文件中使用的指令是相同的,这是因为gcc编译器会强制将SUBS有两个相同寄存器指令转换为单个寄存器操作。: f/ H0 q# w0 h+ w- _

& j; [% F( X# P6 U+ j6 K% ^' h 20210102015028523.png ( T6 K5 U9 N$ R$ s/ f
+ _. X! O9 D# v& ?/ h2 @. P
————————————————0 Y/ {8 V3 l3 a$ S1 `
转载:Willliam_william$ Z! ^, l. G  i( [+ y( B, a' r
1 W8 H/ j! L( B: B" \4 b

' i+ e( }. m0 d" S" r# v! f% t
赞 收藏 评论0 发布时间:2022-8-25 21:30

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版