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

STM32F0系列之编写C语言点亮LED灯

[复制链接]
STMCU小助手 发布时间:2022-8-25 22:07
一、前言4 U6 F1 V: B6 v: `3 w! m
使用c语言时,要用到的一个很重要的就是栈,所以我们要在汇编中就设置好栈;5 S$ P! M. K* F
而对于当下的stm32f103来说,我们可以在0x08000000处写入栈顶地址,也可以在汇编程序中使用指令设置SP
6 s* u( w9 l0 T8 G. \
0 f. \7 e* ^) [0 I3 A( m二、在MDK中实现
  f0 a# d: ~% m! N9 S7 _修改start.s,在0x08000000处写入栈顶地址,如下所示:
% p& U% \& U9 E1 U2 m' _. p5 X
$ O* M) [. Z' m, Y6 n* _- {0 O: `. c4 S
  1. Stack_Size      EQU     0x00000400                                  ;定义堆栈大小为1024byte
    4 P7 B8 ^- i5 C& L6 r1 T2 E
  2.                                 AREA    STACK, NOINIT, READWRITE, ALIGN=3  ;定义一个数据段,标记为STACK,即栈,不写入初始值初,对RAM来说,即初始化为0,8字节对齐
      M# p0 g0 M) b( w7 X- }. W
  3. Stack_Mem                SPACE        Stack_Size                                    ;保留Stack_Size大小的栈空间3 m0 b* l: O: D/ Z2 ^9 y3 j
  4. __initial_sp                                                                                  ;标号,代表堆栈顶部地址,后面有用
    9 F" ?3 x( _8 K
  5. + B/ J; A/ I* h  }( D# B
  6.                 PRESERVE8                                                        ;指示编译器8字节对齐
    8 Y) {3 t% Q4 I
  7.                 THUMB                                                                ;指示编译器以后的指令为THUMB指令                                                               
    8 j; w7 p$ {2 V( m: N

  8. 3 q* z6 t) C4 J* L

  9. & M  i* `6 X/ Y5 B
  10. ; Vector Table Mapped to Address 0 at Reset
    4 o  w  @/ A, G- ]* ~8 o# v8 q
  11.                                 AREA    RESET, CODE, READONLY                ;定义只读数据段,标记为RESET,其实放在CODE区,位于0地址% D0 ]5 f5 y# h; M) u
  12.                                 EXPORT  __Vectors                                        ;在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用! G8 f, [2 _, H4 j* N" G. t
  13.                                         8 ?& o* }, d+ ?. T9 ~5 E
  14. __Vectors       DCD     __initial_sp                            ;当前地址写入一个字(32bit)数据,值为__initial_sp指向的地址值,即栈顶地址
    % W9 [: u0 A1 U, ?4 }8 @0 C
  15.                 DCD     Reset_Handler                      ;当前地址写入一个字(32bit)数据,值为Reset_Handler指向的地址值,即程序入口地址2 l; q# R3 J! B5 J' d' R8 H
  16. ) J) j* X/ z' I
  17.                                 AREA    |.text|, CODE, READONLY                ;定义代码段,标记为.text
    % |+ u  ?8 X3 i2 z

  18. 9 ?/ A0 o1 a( t4 b
  19. ; Reset handler        ;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰8 \5 v  Z+ ]; Y; e- W9 U- l+ }
  20. Reset_Handler   PROC                                                                ;过程的开始 - E5 i1 k' v9 |6 a: W
  21.                                 EXPORT  Reset_Handler        [WEAK]                ;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。9 t. ]$ o# ?/ e1 ^( c4 F8 a7 E
  22.                                 IMPORT  led                                                        ;通知编译器要使用的标号在其他文件* [. Q' s  y0 P4 b8 {8 j+ S
  23.                                 BL                led                                                 ;跳转去执行led函数8 x$ I6 N& `0 ]( D- Q
  24.                                 B                .                                                        ;原地跳转,即处于循环状态2 z8 c0 B! T. m5 F; w8 p
  25.                                 ENDP
    / x7 h  \+ l, S8 }

  26. 2 u/ p2 Y  L) G: F
  27.                 ALIGN                                                                 ;填充字节使地址对齐
    2 e# W: N  c1 t
  28.                 END                                                                        ;整个汇编文件结束
复制代码

/ R' Q0 G4 {0 B  `/ D在汇编程序中使用指令设置SP,如下所示:
# `+ H7 }% e6 c4 P
) k1 |' E3 n$ e
  1.                PRESERVE8                                                        ;指示编译器8字节对齐
    2 E* R( Y# m) M. a
  2.                 THUMB                                                                ;指示编译器以后的指令为THUMB指令                                                                7 c- Q) q8 \* L. _# w& D
  3. 9 r5 R' m1 X$ I- `' G. o
  4. $ m0 D! b$ N& t
  5. ; Vector Table Mapped to Address 0 at Reset' j7 D4 q8 Z2 j1 _8 ^
  6.                                 AREA    RESET, CODE, READONLY                ;定义只读数据段,标记为RESET,其实放在CODE区,位于0地址
    6 T9 o" I' l6 ]$ ~, f
  7.                                 EXPORT  __Vectors                                        ;在程序中声明一个全局的标号__Vectors,该标号可在其他的文件中引用% n" u1 U% A3 A
  8.                                         . y& b! R, r, w* {
  9. __Vectors       DCD     0                                                    ;当前地址写入一个字(32bit)数据,值应该为栈顶地址
    7 g$ ^( r3 L& g# N. j: n5 |! [6 {  h
  10.                 DCD     Reset_Handler                      ;当前地址写入一个字(32bit)数据,值为Reset_Handler指向的地址值,即程序入口地址
    . z) o% O  {3 p4 g
  11. 4 c, u( z/ H6 x  O9 d
  12.                                 AREA    |.text|, CODE, READONLY                ;定义代码段,标记为.text; u5 b- o. E7 y- x! X

  13. , K, I3 x2 N9 ?0 h* k, j! {! ?
  14. ; Reset handler        ;利用PROC、ENDP这一对伪指令把程序段分为若干个过程,使程序的结构加清晰
    ' U5 z5 P8 l$ R& g& I! K! r, u
  15. Reset_Handler   PROC                                                                ;过程的开始
    , N- `4 U, V8 h% W$ ^, |
  16.                                 EXPORT  Reset_Handler        [WEAK]                ;[WEAK] 弱定义,意思是如果在别处也定义该标号(函数),在链接时用别处的地址。
    1 ~, ^3 m( O! r% X
  17.                                 IMPORT  led                                                        ;通知编译器要使用的标号在其他文件
    $ a" p5 G7 Y' B, p
  18.                                 LDR SP, =(0x20000000+0x400)                        ;设置栈顶地址为0x20000400
    / g( J# f9 Q+ d5 i2 X/ I% n
  19.                                 BL                led                                                 ;跳转去执行led函数$ H5 X  k: z3 T: c9 N( [- X& u
  20.                                 B                .                                                        ;原地跳转,即处于循环状态  g$ Y( l3 k) C9 k( a) k2 s8 C: S
  21.                                 ENDP% M) C6 G; R2 h0 B

  22. 0 M& ~6 M- i, u" T4 S* e" t) P
  23.                 ALIGN                                                                 ;填充字节使地址对齐
    3 S0 x+ R9 F* `5 D2 B' }
  24.                 END                                                                        ;整个汇编文件结束
复制代码
  s' ~" g& K! v% o: c# m
然后新建led.c文件,如下所示
$ x: {2 _/ p( s1 Y! U6 C
! R! I+ B; k1 h; d  }7 n
  1. int delay(int ndelay)
    8 ]  Z" E: l7 v- d5 P
  2. {; \% ~, A0 d9 n$ S4 `
  3.         volatile int n = ndelay;
    & c- l- a. R, X8 U, Q
  4.         while(n--);2 ?, p* F" P5 x  h6 ^0 h, R
  5.         ( r' M9 R% @" H/ G$ [
  6.         return 0;; L0 K) j2 s/ M% v: H
  7. }
    ' I, j8 ]& s* P

  8. " q9 E! a/ ]& Z: j. a8 C
  9. int led(void)
    6 n3 ~3 [) P8 v, @6 K
  10. {
    9 O+ U; F% q. P( E0 y  `6 U7 h
  11.         unsigned int *pReg;
    # a) U, U0 v* ^( r7 G
  12.         
    7 V7 Y) F) D4 h) |2 j4 p
  13.         /* 1、使能GPIOB */' m, q' e' e6 P7 {
  14.         pReg = (unsigned int *)(0x40021000 + 0x18);& s  A( W1 n6 U6 f3 h7 i6 m( \
  15.         *pReg |= (1<<3);5 S) `9 Y, P  m! V
  16.         ) Y- _2 Z" V& T, H, c% n
  17.         /* 2、设置GPIOB5为输出引脚 */
    / a% S( S. {& \9 c. O! J
  18.         pReg = (unsigned int *)(0x40010C00 + 0x00);
    5 Y  v& t1 O8 ~- i2 B0 N5 P9 H* u
  19.         *pReg |= (1<<20);
    6 F! T8 G: s, \/ ?9 ]2 ?8 W

  20. 2 S  \4 Y# N% ^2 H; Q1 Q
  21.         pReg = (unsigned int *)(0x40010C00 + 0x0C);' T+ a7 k! U! N9 s) u4 ~: Z; Z
  22.         $ y  v/ g( U; d
  23.         while (1)5 x; `$ L. ~- d; @) O: t) T6 O! B
  24.         {, A' t2 M& m* }8 K9 P
  25.                 /* 3、设置GPIOB5输出1 */9 P1 v( _2 ~, _5 y. S6 ?) O5 F% p% m+ E- p
  26.                 *pReg |= (1<<5);
    ' F/ l1 M! s+ H4 }5 K3 v
  27.                 - S/ b8 J5 f' a
  28.                 delay(1000000);6 f5 W: U: u, g
  29. ' a/ p+ U0 c' |
  30.                 /* 4、设置GPIOB5输出0 */
      L' P8 D/ F. f2 j
  31.                 *pReg &= ~(1<<5);
    ! w7 D; W% K  l5 O6 ^9 R0 N
  32.                 , s7 c; H# U$ g2 V8 ?
  33.                 delay(1000000);                : J# m; q+ @; ?6 f1 L2 S, `
  34.         }
    / l7 }, R, E$ q5 l2 |* R5 ^
  35. }
复制代码
$ }6 @% v2 y" `5 k; R5 K8 [" s
编译链接烧写到开发板,可以看到LED闪烁
% y+ f" S" c9 s8 F+ C: _$ s+ S* m& l7 a& T
20210102162522474.gif
0 m2 C( b& a4 z$ E( U! @
% k! v5 \7 V5 l! x. m三、在GCC中实现

3 R. i4 e6 ?  S- b+ n5 p修改start.s如下所示:' i" b4 U2 b5 U8 D% n4 q

' Z: t9 X5 q/ P+ g* }: ?
  1.   .syntax unified                   /* 指明当前汇编文件的指令是ARM和THUMB通用格式 */$ Y1 K% \# n. c$ V9 j# h9 `! y3 y
  2.   .cpu cortex-m3                    /* 指明cpu核为cortex-m3 */" j, M9 N8 P/ f* T/ B. F' E6 X9 l
  3.   .fpu softvfp                      /* 软浮点 */- B' k" K0 A! ~8 @5 L( C! A
  4.   .thumb                            /* thumb指令 */
    3 d- ?% W% n. u9 o" ^9 e6 j9 e4 S: D
  5. * i( N& T) [4 v- X& a6 f
  6. .global  _start                     /* .global表示Reset_Handler是一个全局符号 */
    * v& m  |2 ?' J; t- z7 W
  7. , [6 |4 `/ j4 z$ B, @, f: e
  8. .word 0x00000000                    /* 当前地址写入一个字(32bit)数据,值为0x00000000,实际上应为栈顶地址 */* \0 }. X" L$ E( [
  9. .word _start+1                      /* 当前地址写入一个字(32bit)数据, 值为_reset标号代表的地址+1,即程序入口地址*/8 n) B9 F/ {# S
  10. 7 R) G2 L: j8 \9 Y) H
  11. _start:                             /* 标签_start,汇编程序的默认入口是_start */
    9 I8 q' w. P1 t  Y
  12.     /* 1、设置栈 */
      h% ?# D9 @; g) `- G
  13.     LDR SP, =(0x20000000+0x400)5 U  w# W* T0 f4 @, o
  14.     /* 2、跳转到led函数 */
    " S% E& P% s' q5 b
  15.     BL led& s* J/ s' R+ h
  16.     /* 3、原地循环 */
    1 E7 F$ t8 w& I  X; ?4 ?. \1 {$ c3 D
  17.     B .
复制代码
7 R8 _; f/ c/ y3 |) P: Y! `5 \
使用同样的led.c文件,如下所示
; }; B2 h. d; |4 e
9 O' R+ f* R7 T' E) {% ?6 I
  1. int delay(int ndelay)
    - d) G. @% {& y8 x  I& i
  2. {
    " c5 \, o* l$ k- s6 T4 _% o
  3.         volatile int n = ndelay;
    ) x3 G7 x+ x; V) K- t$ j) o
  4.         while(n--);- d8 G, {6 m" T
  5.         - t1 s# y+ G. }4 {9 M3 u% }8 n( A
  6.         return 0;; I& l. B/ A1 c5 [
  7. }  ~  q: j5 e+ E' Q. g

  8. 0 H- j3 \! u+ R4 ?; e
  9. int led(void)
    8 d$ k7 v& {9 x1 n
  10. {
    1 D9 B, |3 s0 D% n
  11.         unsigned int *pReg;& U% v7 ?; A% D' B, n
  12.         6 o# H5 Y" z7 g
  13.         /* 1、使能GPIOB */$ X4 g2 o6 Z) i' _1 ?; p
  14.         pReg = (unsigned int *)(0x40021000 + 0x18);
    ; n' U9 @1 w" Z9 q6 ~
  15.         *pReg |= (1<<3);/ F+ M2 T9 P, o* g" N9 \, i: P
  16.         % f8 F- e$ z$ W; i. Q$ {
  17.         /* 2、设置GPIOB5为输出引脚 */
    * M0 y  V2 p1 q, a% Z0 t
  18.         pReg = (unsigned int *)(0x40010C00 + 0x00);6 ~; m$ x4 R+ u
  19.         *pReg |= (1<<20);
      a! X' V8 P/ ?% W$ Q4 }5 V2 O, i
  20. % O0 |: O" T2 i' w9 a% v
  21.         pReg = (unsigned int *)(0x40010C00 + 0x0C);
    0 e. p# d" C1 T/ |2 L0 m
  22.         
    6 H" `/ Q) ?- y) X* u' G
  23.         while (1)
    ( _* t: r, q% w# p' ~
  24.         {3 [9 F4 J! c6 ~7 _( \
  25.                 /* 3、设置GPIOB5输出1 */) B% b/ C  {% C
  26.                 *pReg |= (1<<5);$ x/ }( v- q1 {/ ^
  27.                 2 T8 P, M! d& @3 o3 j$ G, r* V
  28.                 delay(1000000);
    ' z" e& B' v' ~; G+ a

  29. ( `& H* f6 N+ y
  30.                 /* 4、设置GPIOB5输出0 */$ |9 f- O% {: W& O* `7 u& A
  31.                 *pReg &= ~(1<<5);1 L/ Q, j8 D" c+ X$ U+ y
  32.                 3 k& `% G5 H1 Y- ?9 ]8 o4 u
  33.                 delay(1000000);               
    ! h, [5 h; s& i4 o9 _5 J) d
  34.         }& m3 M4 ]1 Z7 U+ I
  35. }
复制代码

, I" M+ |: o; ^' e/ k再修改Makefile如下,注意要有-mcpu=cortex-m3 -mthumb参数,这个很重要
+ u) {- J% g* ^/ d* C( P" k, `; B9 ?2 e
3 h3 e% J6 a7 s3 f" l7 b0 s' T" }
  1. all : start.S led.c
    4 D" O& s  a( |; ?1 \
  2.         arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c start.s -o start.o3 Q( u" I4 f# h
  3.         arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c led.c -o led.o
    4 B' z% g3 n& S- Y
  4.         arm-none-eabi-ld  start.o led.o -Ttext 0X8000000 -o led.elf
    ; O9 _5 e( F$ Y, W. M
  5.         arm-none-eabi-objcopy led.elf -O ihex led.hex) o7 R! c5 G0 |
  6.         arm-none-eabi-objcopy led.elf -O binary -S led.bin+ o+ B& J* P# y2 \
  7.         arm-none-eabi-objdump -D -m cortex-m3 led.elf > led.dis
    ; z6 }1 R; j# }8 G. @. d. }  y4 \

  8. - H( m' \3 r; K/ d* I
  9. clean:
    1 ]) S8 V+ u& S! X; D( I
  10.         rm -rf *.o led.elf led.hex led.bin led.dis
复制代码
+ ]1 A) U8 ^( u" {4 G
使用make执行后如下4 I% s) k+ k$ v8 l0 f( @5 h& O9 z
20210102175418537.png
: H5 P: m2 c( z" ]$ D9 M% j- {/ d+ U& Q( a2 |4 `9 f% x
编译烧录后可以看到LED闪烁+ N- R+ a# F, W3 r% O4 }
7 a% |, m- i* o9 S, L0 W) j3 G% k
20210102175449422.gif
, e7 `' t$ r2 ~6 c$ @
$ C* Y. t% @/ p/ o; l四、优化Makefile: R8 {$ F" d1 t/ H1 N7 q
可以将Makefile优化为如下所示
0 y% R: X3 s# Z+ g8 Y( R) M
$ ]* e: l- A# Y% [5 H+ H& f
  1. TARGET = led, c! n; t: w# T% r
  2. : e. n+ U) o  e" G
  3. OBJECTS = start.o led.o
    , m" a# o  K& ^: h: |

  4. 3 t( t( p9 U* ], j! v
  5. CPU = -mcpu=cortex-m3
    ) W4 ?+ [" t1 m  b1 A
  6. MCU = $(CPU) -mthumb( w$ H8 G1 D/ Q4 g8 h5 o, I

  7. 0 Z& u( i0 s& ?+ t6 a
  8. CFLAGS = $(MCU) -Wall
    / X0 \$ F, c, B$ P+ n2 m9 X

  9. 8 I" X5 C' s8 z# c: G7 |
  10. all : $(TARGET).elf $(TARGET).hex $(TARGET).bin $(TARGET).dis+ T4 q; m  o& R0 x2 ~4 J
  11.         arm-none-eabi-size [        DISCUZ_CODE_6        ]lt;' z% m: p" g" m+ m4 f+ V  C1 A% G6 N

  12. . k0 c: ^* M" T$ O; {9 u& X0 G8 w0 _
  13. %.o: %.c) t* @4 A# e( k3 N
  14.         arm-none-eabi-gcc -c $(CFLAGS)  [        DISCUZ_CODE_6        ]lt; -o $@) j; A$ p( @% |/ E7 J; u2 q

  15. # O. q: a6 |' \, ]4 A3 F5 |8 f: [
  16. %.o: %.s+ r* |) W5 N0 G% O4 A. K. w
  17.         arm-none-eabi-gcc -c $(CFLAGS)  [        DISCUZ_CODE_6        ]lt; -o $@
    ' _' X% T1 o5 |
  18. 5 b9 ^: S5 k/ Y6 Y; g  m3 w
  19. $(TARGET).elf: $(OBJECTS)
    + ~& K4 c/ N* Q7 d8 O$ T
  20.         arm-none-eabi-ld  $(OBJECTS) -Ttext 0X8000000 -o $@  B- Q) B# p/ a, ]/ B, n7 k

  21. 5 \# B* X& a2 p& }6 w+ X! e9 L$ j
  22. %.hex: %.elf / b# K$ L: Z) T% C' P, i
  23.         arm-none-eabi-objcopy -O ihex [        DISCUZ_CODE_6        ]lt; $@0 _6 I5 h% Q4 ]) t
  24. . ~2 |1 o+ V6 r: D$ T
  25. %.bin: %.elf: H8 |3 Q, k$ w: M
  26.         arm-none-eabi-objcopy -O binary -S [        DISCUZ_CODE_6        ]lt; $@5 E% x1 j" ^0 }6 H% t+ t! t
  27. 4 j& I7 Y* \9 O) T- C
  28. %.dis: %.elf
    . X/ B. @3 u& k: D3 a+ ^
  29.         arm-none-eabi-objdump -D -m cortex-m3 [        DISCUZ_CODE_6        ]lt; > $@
    + n; Q2 y+ l# m! h& }9 f

  30.   Z# y) A& B" @6 j  z
  31. clean:
    ' E9 ^) d' s5 v0 S: b! ]+ A
  32.         rm -rf *.o led.elf led.hex led.bin led.dis
复制代码
9 @1 t2 H: w" l, e% G
————————————————- U* r' m- c( U# {% o
转载:Willliam_william
* F* m" H. }! F4 {
$ H' G' K6 K* R# w- X! m3 {3 z- A7 _4 S
赞 收藏 评论0 发布时间:2022-8-25 22:07

举报

0个回答

所属标签

相似技术帖

官网相关资源

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