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

STM32Cube下载与使用(三)

[复制链接]
STMCU小助手 发布时间:2022-12-24 17:40
意法半导体ST与ARM的关系
( I, q5 f! z: d- z4 q' |ST公司购买ARM公司的内核,在此基础上添加各种外设,组成STM32芯片
9 p& K' j6 h! V# B1 @$ M也就是说,在内核之上,所有的外设都是ST公司添加上去的
& y, s3 V" j  l5 Q) h- LST内核通过总线矩阵与外设连接
! s- G$ U6 R; m& U/ F; T( w
! H" `* x% |) S1 h& D% O# W
5a3f58651fc04072a671bbc917cfe770.png
1 _( W- A) O8 V/ v3 B& @8 ~9 J1 I
/ F: Z* a% D$ v. ^嵌入式C语言中对于地址的操作# S7 |# x8 |: L# x; ]1 M) w
/ z& u1 Y5 c: k$ O6 s+ @3 W
0e5314081e394a91936ece9f8df6fa23.png 8 e4 ^1 k$ V' s. E2 ^5 J
3 f( n% s. W4 [3 B, [$ {
此时指针pointer是指向寄存器的地址0x4002800' e1 O. J/ n9 }% @  C
6 X* d( a1 b! C8 ^6 X2 d
b889d5b2df7e44c0b955043c9c424150.png , r& Z8 M$ T/ I" u% ~2 ]
$ v. k4 E9 X. y+ U
4 q/ p( W2 `" P2 Z% A. V" b
按照这样我们可以完成对地址的访问
2 C% F4 k3 c$ X! j  d
- j& E5 m$ w: x* Y) j4 ^
这些基于指针的地址操作是非常常见的,基于寄存器的编程就是通过指针完成的& Q# Q+ h) ~; C( c% [
7 }0 u* @6 y- ~% ^# E
进一步地,考虑到定义变量会消耗内存,如果我们大量地定义地址变量,将会消耗大量的内存空间
! Z% ?, ~7 p1 B
& E: P1 I+ r) o- G- R) e因此可以使用宏定义,因为宏定义只是纯粹的文本替换. m% a7 w/ o& j$ I+ {$ n

* e& @2 R( l/ K/ w如下图所示7 g7 z$ }9 E% o
6 B/ e: r9 G+ y7 U/ \
e6fcb177f56b4de884f48696e60bf4ee.png   s/ K/ N" a( A
3 B6 s& ?. M0 L# [0 L
实际使用中,将GPIO外设的基地址,通过强制类型转换,将其转换成结构体指针的形式
' |1 Y6 T2 e1 q, s% v: r
8 w9 ^% N$ H+ O7 l' Q
可以简单理解为,建立了结构体与实际内存中的一种映射关系0 C7 k! b2 O! R! M; c
2 z9 O/ f, P" X. P, l
访问结构体成员(通过->),就等同于访问实际的一小块对应地址/ l/ n; z5 k" B7 l+ V

/ A0 @/ @( y' kHAL库中GPIO的配置
4 r, N$ y6 S. F% X* xHAL库中对于GPIO的配置如下2 K/ L+ n2 D- |4 b
8 C- a8 G6 G& [  v/ `8 q5 Z: y
c25a781693dc42729af3677f85f9864a.png
9 h- |$ Y+ C, J) a- d
8 @+ [/ `/ l7 w* {7 ~为了理解上述代码,我们可以先看以下代码
2 h0 Q) F6 j+ O1 N" `+ i' y0 K
' d  L1 T; Y& k+ v7 k- o
8239c9aeb64142a78b0179404c943d4d.png ( R7 F. ?8 h. I8 ~% Z

9 `; @) H3 J, x" @5 m5 g0 |0 q. @在上面的代码中,我们首先将1左移3位,然后将其以或运算的方式对寄存器中的内容进行操作,这样就打开了端口B时钟,对应的手册内容如下:
' E0 t- u; d& X. Z" s: y& u) p: p0 I
f34dffb1c069456daf0854e18891c939.png 0 r% I4 j- |, M  @6 V, F
. ~$ x- Z. Y' N- R- u$ @- V
这里对于寄存器地址是以基地址+偏移的形式进行定义的3 m* }: e% ^' m- K1 e
  u4 N3 M5 ]9 F0 R4 @  z8 ]$ F
62e3dba7a8614e3391e787577ad20a9c.png
) @6 y7 _/ G& D6 I  ]. ]

! `2 l. C7 K8 I* e6 d7 @! l6 ]& r2 a2 k
这个寄存器的低4位,即0 1 2 3位对应0端口,即第0位,对应于GPIO的0 端口, I  W; a  ?$ G" e* \$ @% o  E
1 O- f$ B& a/ D7 i1 k. }
假设我们的需求是:将最低2位配置为10 ,最低3,4位配置为00" ]/ c: w) C) W  c

" t  d" u: R, E0 u6 t5 B也就是说我们的需求是:将寄存器的最低4位配置为2
! h5 j2 C* p+ n; M: O$ Z% N4 c' N( N
一种做法是,我们直接将寄存器或上1,且左移0位,即:2 S- v' ^4 q0 F) {3 F
% Y' n" {) y# `
  1. GPIOB_CRL |= (2<<0);
复制代码
( Q, H9 Y5 \! p3 y( ?. L5 O2 Z
这样子,最低第2位就是1了  n4 c0 T5 `- Z: I; j

3 B8 b4 T* u) @但是这样的操作存在如下问题:因为假设最低4位本来就有数值的话,比如最低4位是1110,那么你直接进行这样的操作,其实只改了最低2位,而最低第3,4位仍然是11,并没有清0。
; d$ m) D, l7 J

4 Q" f& }1 l% V) K5 ]( q因此,在此之前,我们还需要对寄存器待配置的位进行清0操作$ q4 q2 t* D& l/ v
( k) a; G$ o! Y6 T2 f7 @0 l
  1. GPIOB_CRL &= ~(0xf<<(4*0)); // 清除低4位寄存器
复制代码
; i! d+ a2 P5 j# W3 t9 t
在这是,因为一次我们是操作4位,比如操作端口0时,我们用到的是第0,1,2,3位,操作端口1,我们用到的是4,5,6,7位,操作端口2,我们用到的是8,9,10,11位,6 d- l4 m5 K" V, g, x- X5 ^6 O

  Y0 N# z- }( j. P% a% G+ ^) `我们每次都是4位一起操作,所以左移了4*0,如果我们操作端口1,即4,5,6,7位,那么我们就可以左移4*1
9 O5 ]& W. P- G) \+ d" `
; d0 B: ~8 V/ |/ y4 P! Y1 I: l0xf是4个1,这个数是整型变量,因此会占32位宽度。也就是说此时除了低4位是1,其他的全部都是0,这时候我们将其取反,则32位中除了低4位是0,其他全部是1。0 F* x2 J+ S! |) v5 v+ i& o

7 l3 O$ n' k* j. ~/ j2 r此时,我们再将这个数整体与到寄存器中,此时这个数的最低4位全是0,与上去之后,相对于寄存器的最低4位被清0了,而其他位保持不变
# A- Z3 j. h* G. I% o2 w! `& K  O3 D9 S+ @$ t: V+ o2 G
清完0之后再将我们要的数据或上去,那么我们想要改变的位就会发生改变了,而且不影响其他的位# N3 ^7 Q  P) Z# k& F0 O3 U4 K

. t5 O( @$ E6 K4 ]# p) [
b1f1e4492b4d4bab987e568c6feccceb.png ! _4 m) {1 W+ t# ~( P
: L% J/ ~, |+ R. y; A7 b/ m0 Y

9 A& z7 f$ |' \* v9 ? ab33b67d75e3440cb21ba26f6083a767.png , ?, x! M) {" g/ S, t9 W  M# A. M" h
7 C9 a) I) ^& V. g# B. c# {
STM32的板级支持包
! d% r! A) s; \9 c板级支持包 (BSP) (Board Support Package) 是介于主板硬件和操作系统中驱动层程序之间的一层,一般认为它属于操作系统一部分,主要是实现对操作系统的支持,为上层的驱动程序提供访问硬件设备寄存器的函数包,使之能够更好的运行于硬件主板! `+ Q* U+ W) T
3 z' v2 w4 d1 w, ?! t
8dc89625449e4ee4bd8ffa6b89a60823.png ( U7 f" {/ s3 o) S  o- w

: B5 X0 }. Y2 p9 Y" u& i- D板级支持包:对板上的资源功能给出实现,并且提供用户应用程序的接口。以LED灯为例,用户应用程序不需要知道GPIO的硬件特点,他只需要知道调用这个函数,就可以点亮LED灯
8 Y: M7 l! _: `( Y6 }, e
/ d/ [2 D# k4 c. }  x
我们可以自己定义BSP中的宏
) U, u/ h0 t/ B. ?3 [) S  a7 I2 V0 `* @  r! c& L
0 b- J+ Q9 i; _8 ?
. B" p2 V( ~8 {
同样,我们可以在main函数中将板级支持包include进来7 R) r* @0 p2 x3 y

$ \; M, ]+ H  U注意:板级支持包的include位置
+ q0 q" {, t$ {. }
; ~2 o1 D% {. O6 S: X
04a7745eb8b543378d7481044dab3f65.png : }" X6 a: P2 R/ z* s6 s1 R) g
% e  [0 b6 t2 E  D' k
520ea9e74c9c45d3b2ced53836f748a6.png & K3 F3 J" e1 m6 S

( j3 H3 [; _* t' {ARM Cortex M3的哈佛架构
* W; ], n- l/ h$ y5 J2 [6 }$ x取指和数据分开% Y% }2 a# U) \) Z

4 S5 e* l' l; b% F
aa91e6afac7e4c6099b19a60a1a1adca.png
; i( W6 P1 Z2 l* F- k  [# {' P* N
& H+ ?0 }" c7 m; J
' ]. w, }* n/ E' t
对于处理器而言,可以将其理解为没有感情的代码执行机器。只要处理器开始工作,就会不断地获 取指令,并且执行
, L1 Y$ |" r- O  R9 U1 n# q
! k5 i4 S! m! ?Cortex M3对指令集的丰富
4 Q8 x1 y  W; m; c( c0 X2 |
4 k8 s! F8 O7 R1 ?7 I
2026aa0d0e614eb88778ae92bb35724b.png
# r0 `- c* v# a* n2 H9 _9 e- q7 s) I& p

7 ~( V' X$ e. v% C  \, NSTM32的启动过程8 p$ x4 P  d5 B9 f# N( ]1 @
STM32芯片上电以后会触发复位异常6 E/ T2 ~1 j) W4 Z8 M0 f+ d- I
. s& o' ~( {! y
在中断向量表中,处理器根据触发的异常,跳转到中断向量表的特定位置,获取内容并执行( D, x0 f2 k0 u+ f; i8 f* D

- ?/ O8 c5 T3 ^8 l' Y( z8 l" `/ ]即流程为:
) \% i$ X7 X: N1 @  Z  z! O/ N4 o: n) f( O8 P
触发异常->中断向量表->用户程序/ t5 D/ ~) G# X3 W- Z

4 b5 R6 H5 ]/ n! eSTM32的中断优先级如下:1 E+ o2 U% ~3 }3 {8 H3 U1 F( t1 |

+ ?/ m7 ?# Y' @, W
$~R~CW2W}}_V2C~TY~7ABZS.png
# s0 g7 m( _" \9 C: d

8 D. W8 S  g! l& i$ U/ [) pSTM32 Cortex M3中断向量表
6 H3 {5 t/ b) z' e  W! |3 N2 K  F( ]' Z, B! k3 s
3YQ7~FS({YDW(_8W5W~O`QG.png
' g" N! b; M. Z4 [& F

" R# l: K' D$ |( O  eSTM32的启动文件startup_stm32l432xx.s$ Q6 Q4 W! m$ h  p) K% _' k
以STML4为例,其启动文件为startup_stm32l432xx.s, ]! x( F7 b' Z' O% f& m

  R! q1 v' Y8 U" k可以查看其注释里的简介
* {" f# d% s. G7 V7 _
  1. ;*******************************************************************************1 t4 k! {% Y% E6 l+ V; G+ @! d
  2. ;* File Name          : startup_stm32l432xx.s
    + W! b; k( V" V+ y; A9 K1 L
  3. ;* Author             : MCD Application Team1 `2 v& j( y4 p' S  n
  4. ;* Description        : STM32L432xx Ultra Low Power devices vector table for MDK-ARM toolchain.
    5 `' S; Z% ^* X1 _( u0 }
  5. ;*                      This module performs:
    ' `3 x4 ?0 z  H. O
  6. ;*                      - Set the initial SP
    8 v. A. E* T) b/ D. R( l! {
  7. ;*                      - Set the initial PC == Reset_Handler; c$ i& i+ J; y+ O0 W5 u! j
  8. ;*                      - Set the vector table entries with the exceptions ISR address
    * o$ ?0 l9 ]9 t0 U# D$ t
  9. ;*                      - Branches to __main in the C library (which eventually
    3 G( P# U  S2 D9 y
  10. ;*                        calls main()).
    , G, ?7 O( H* ~/ d6 C& B4 W" P
  11. ;*                      After Reset the Cortex-M4 processor is in Thread mode,
    # l  @/ d, D5 l2 R  i' T! K8 N
  12. ;*                      priority is Privileged, and the Stack is set to Main.
    ; {+ j% A7 s: ]7 p  h( \  u/ p
  13. ;********************************************************************************
复制代码

9 f. A7 ], e$ g6 R' r) v! L第一步 设置堆栈的指针& q$ T6 I3 p0 u$ y% ]
& k. k; j4 \/ `5 B+ g8 W5 F  X5 h
第二步  设置PC指针的值,PC指针指向当前指令+8的位置
1 Z7 }5 I4 B7 d+ |
! Y8 l1 m& o; b$ J
第三步 设置中断向量表的入口2 ^0 m- @3 f# N( N, \8 z, @

  ]9 [0 ]! l1 ]; g. d- z第四步 配置系统时钟
7 f/ v1 m" E6 N# B' Z' L
7 b7 y4 u) x! o: g0 v4 U) K7 ?第五步 调main函数# V9 h3 \$ b7 f" p* v
" q! \, D) s0 `
首先,设置堆栈大小0 Q/ }. s0 s0 c

) S8 ^% M) M. w
261b5ddc4c134d508349c65dc2dee405.png
4 t) h7 T5 W% G6 V3 d6 ]# B& z* E5 W- D# X) f  i
然后,设置PC指针
8 e, O+ ]0 Q8 F. ^7 L
7 _0 @7 q: s1 ?# g, c注意:DCD指令会为指定的目标分配一块以字为单位的空间,一个字就是4个字节
) G% A2 e  y. @- Y

& l, o0 S* e& E4 i- k5 w9 M7 t首先为初始化堆栈分配1个字的空间,然后将对应的内容放进去 * q0 H9 h: x* y7 F. o/ ?
+ ~( ?. i: V; D8 R$ @- k) h
6ec9c6835db3456f8c4c5cb6bb765e8b.png - x- ^3 ?: v3 \$ A% ~, S9 R- _; G
& n) C8 l) b- {; s9 v$ @" B$ B. F

- ]; }& @4 ^4 J  Y2 M: _9 F( [9 G此即复位异常
7 v; t! G+ F& d9 q& a
3 f5 f- [# b9 y8 F% \) M
c81c3b31a8954847a61db60e15533b07.png
3 T7 S* o8 i8 M; l0 j( ]$ G+ M+ ~7 F( N' h
bf5bd7f29cc248ab8ac419f7e868ba68.png - a! M- m- b! B% B* }
1 P0 D' }% m& }

- C! P+ V$ x# y, D在这个Reset_Handler即复位中断的服务函数,其中IMPORT了两个函数,即__main和SystemInit, f7 P( D; u& w+ X+ ^7 L' z+ q, b! a
* \, t1 m/ f! r+ T" [; y2 }* v8 V& P
先将SystemInit函数的地址(即函数名)放到R0寄存器,然后BLX指令跳转到R0寄存器去执行
6 S3 P4 g+ w$ `4 Y' D5 \+ a

" J0 L6 n4 {! o" _/ }同样,将__main函数的地址(即函数名)放到R0寄存器,注意接下来使用的是BX跳转,跳过去就不会跳转回来了
" e; _* f. d. T  ?$ Z. Y+ n
4 P, w% b+ ?, t% `9 b' P, C7 N
6 l# Y% x& s( c
02e004ac51c1477abf48bf28e28b3359.png 9 N  |, ^& U/ Y( j$ O7 v

. w; H& ~4 r# T( W5 N; K' B) o! r
9 z, J+ b% c9 A% b* k3 ~8 N
注意,汇编语言中的[WEAK ]表示弱定义,即我们可以自己去实现的 ' i) y: v( |% H; A  s& E  R& H
————————————————" g$ q7 {, Z: B1 I
版权声明:CodeForCoffee
. Y$ ]3 v1 O) a1 J7 c
$ w9 B) q5 G& o  d5 E1 k6 k& t* r2 K: H; @+ q9 U
赞 收藏 评论0 发布时间:2022-12-24 17:40

举报

0个回答

所属标签

相似技术帖

官网相关资源

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