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

【经验分享】STM32 位段详解

[复制链接]
STMCU小助手 发布时间:2022-4-18 10:58
1 定义! l8 {  d/ A# K+ Q
首先需要明确下,位段,位带和别名区这三个名词, B! E7 }. e( ?6 j
$ j" v; T1 \9 g/ L1 w% d
位段:STM32用户参考手册使用的名字8 R9 a, s2 @  \  z: }: U  A
  t, N* Y/ T8 |. u$ |* V
位带:CortexM3参考手册使用的
8 @! V6 v/ V% V6 |/ q. }' P# b6 H; m* v# ]2 T! Q& U+ Y
别名区:地址总线上用来位访问地址区域,% k5 S7 O# [. R5 u3 M

0 b) V) e) H3 I6 {所以说,位段和位带是一个意思,是不同手册的不同叫法。, I7 N5 J% x) J, ]2 M

' O9 E3 u* \/ Y& P由上述的名词解释得知,位带功能并不是STM32独有的,是CortexM3的功能(CortexM4也有这样的功能)。MCS51有位操作,以一位(bit)为数据对象的操作,MCS51可以简单的将P1口的第2位独立操作:P1.2=0;P1.2=1 ;这样就把P1口的第三个脚(bit2)置0置1。而STM32的位段、位带别名区最重要的就为了实现这样的功能。1 X9 ]& Z5 ?5 N* y
# m5 L9 `; j  ^3 {2 c+ L; \, V
2 位带操作/ q* o+ e, K* ]3 g# N
2.1 范围

7 _- }: S% y$ j2 n- f5 z* g. H  A位带是有范围的,并不是CortexM3全部地址空间都支持的。在 CM3中,有两个区中实现了位带。其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB 范围。这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。) }* v) O8 d4 `/ i$ m
3 ~# d: P- o; C
支持位带操作的两个内存区的范围是:
+ ~: D, L" \% H: ^8 ~0 y  K/ G$ F& w. s% ~" ^, J; E
0x2000_0000‐0x200F_FFFF(SRAM 区中最低1MB区域)1 M2 r/ T3 w# j: y

! X! G% C* u' ^( K; [0x4000_0000‐0x400F_FFFF(片上外设区中的最低 1MB)9 K/ o5 r* f! |

# F1 U% b& [! q" C/ M/ [5 _2.2 位带操作& @$ z3 T* _0 ~: e! U
对 SRAM 位带区的某个比特,记该比特所在字节的地址为A,位序号为 n (0<=n<=7),则它在别名区的地址为:
; z8 w4 j3 r, |" b. {; D" o- w0 a6 @5 i! G7 G* Q7 {
  1. AliasAddr = 0x22000000 + ((A‐0x20000000)*8+n)*4 =0x22000000 + (A‐0x20000000)*32 + n*4
复制代码
, f$ `$ A9 p, q( R% Q
对于片上外设位带区的某个比特,记该比特所在字节的地址为A,位序号为 n (0<=n<=7),则该比特在别名区的地址为:* Z& E) o! [6 |) Y& b2 I, G# @* W
2 I) d1 O) W1 I6 E* ?; ]% v) {
  1. AliasAddr = 0x42000000 + ((A‐0x40000000)*8+n)*4 = 0x42000000 + (A‐0x40000000)*32 + n*4
复制代码

( K: W8 C4 T$ B, X6 S: E1 E4 J# G上式中,“*4”表示一个字为 4 个字节,“*8”表示一个字节中有 8 个比特。
; B/ F) Z7 v$ w1 C. n; m/ Z2 ]3 m/ B3 S
aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8wUmc4RGRwQWliZlVrdURUTmJ4ekplNzdvaWJp.png 3 r. P0 C7 s' R- L3 P* c+ m4 V

& d2 D: k9 e) r  {4 l% D9 n2.3代码实现
- [) W- H) p1 |# a5 H0 @/ @把“位带地址+位序号”转换别名地址宏为:) ]% b8 n+ k5 z3 i- T

5 @9 O5 E7 M  A+ N/ t2 N. E0 @
  1. #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000 + ((addr &0xFF FFF)<<5) + (bitnum<<2))
复制代码
. b9 T# r  Z* ~. c( z
把该地址转换成一个指针:
" r9 s  i* ]( J( j/ P; o/ o; a
7 |$ ^. U* H% J' X  |
  1. #define MEM_ADDR(addr, bitnum) *((volatile unsigned long *)((addr & 0xF0000000)+0x2000000 + ((addr &0xFF FFF)<<5) + (bitnum<<2)))
复制代码
& ]# }8 ], N; c
其中
: O- q9 }1 [" w5 ]7 N- ?  `* J9 t& c: [7 s) l, o% k
addr的取值范围:9 W% D" `  H: ]7 l# r
7 d  q$ K1 H  u# R- F6 D9 A
0x2000_0000‐0x200F_FFFF
- @" ^+ d3 t( G. o: }7 Q+ f* N) ?' Y
0x4000_0000‐0x400F_FFFF& u: h' d8 a" e/ H, Q/ K

8 A$ e2 `  x* b- M注意:addr取值要32位对齐+ B" K1 P0 ^+ k  w$ Q$ D

) v1 p. z. U1 \; \. n% b4 b3 @bitnum的取值范围:
( Y3 W% }: i& y; J# c
! H7 G7 E% C! w* O# p. E0-31
/ j* M  w1 r: D- {+ m$ E6 g, _, m0 c% C) i+ c
解析:
( C2 }' K  e9 n7 S6 z. P. C
) N: S6 I! [9 s(addr & 0xf0000000) + 0x02000000:
2 Y) b" f0 S- V' W# r) o1 Z+ Q0 w7 v9 n5 E: Y/ @
区分SRAM还是外设,如果是外设,结果为4,再加0x2000000就等于0x4200000,0x42000000就是外设别名位带区。如果是SRAM,结果为2,再加上0x2000000就等于0x22000000,0x22000000就是SRAM别名位带区。
2 s/ ]1 Q2 u! _& |: O7 \9 |! U6 t% g$ Q5 U5 d4 Y$ j, x1 W! p* }
addr & 0x00ffffff:
: r4 {5 E8 u' ]
. y" N; e5 ]1 z& ]: F屏蔽了最高2位,相当于减去0x20000000或者0x40000000。因为位带区的有效范围是1M,即0x100000,这样子就做到了低6位有效。, L% o* Z' B$ K% C

5 p* N3 ~; Z- M  T. Z3 r<< 5:( g+ F6 f2 X0 [  v6 d  ~) R
  c# T4 `8 x+ h5 p
等价于乘以32
9 y; P! k! H2 f* _: B4 c0 R$ w1 g$ P9 Y5 t2 a/ j) U2 }5 E! t
<< 2:
! O/ M2 I& q" U( f9 w8 x# _) H" ~- h+ [
/ O" `& q) Q. O! b1 D/ q
等价于乘以4
* T; k& W: f1 f+ i/ [% v* V; j0 g- A
特别提醒:
* V! `/ {4 ?& i  T  ~$ j) _0 d; c! Y- K7 I& F7 y, C
当你使用位带功能时,要访问的变量必须用 volatile 来定义。因为 C 编译器并不知道同一个比特可以有两个地址。所以就要通过 volatile,使得编译器每次都如实地把新数值写入存储器,而不再会出于优化的考虑。
3 H! @/ v% \+ W+ u3 I
7 P# N  u# A$ Z2 |4 H3 位段的优点8 L5 x' s# F5 u5 R4 S* n+ F0 c" s
最容易想到的就是通过 GPIO 的管脚来单独控制每盏 LED 的点亮与熄灭。另一方面,也对操作串行接口器件提供了很大的方便(典型如 74HC165,CD4094)。位带操作可以把代码缩小, 速度更快,效率更高,更安全。总之位带操作对于硬件 I/O 密集型的底层程序最有用处了
# D) p; U0 P9 }. x& ^5 f% d5 _" T( q! n. X) V+ d, Q+ J
位带操作还能用来化简跳转的判断。
+ N- {2 {, C1 g: I) K5 u0 N8 X1 K; L* e2 [8 E0 c6 V: {
当跳转依据是某个位时,以前必须这样做1 R9 e; p: N; G# k' y( t! l

) j0 d; E& E( S4 n; U7 f1、读取整个寄存器, F( X0 F% _0 ^. v' C

$ n; V, Y- l$ k8 ~2 Y, g/ H2、掩蔽不需要的位
% Q/ `& ^. Q2 E5 u* e% j
' N4 d; d% ]/ w1 P& j) g' e9 V' r3、比较并跳转
+ I. Q8 J5 W2 `5 g8 ^' {( K0 ^
& z9 |6 ]. E1 I- }* f使用位带操作后/ f. `# [$ b% O7 H; E

  a9 {& j/ O( H% l# v0 d) t) D) x& q. V1、从未带别名区读取状态位, J2 Q! l) M, d( [* m; a

& ?4 x2 d0 {" o& }2、比较并跳转  i3 k  D9 l& n& B# @

) Q, |2 f: C# h: \; e: r8 C aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8wUmc4RGRwQWliZlVrdURUTmJ4ekplNzdvaWJp.png
5 e) {" i- F  I. H0 |5 ~$ M& W( t4 o0 A& c0 @/ ]9 `
当然,对于写入操作也从4步精简到3步9 B. L1 ^, @1 r2 d
+ s" M1 e9 C( J$ U4 H
aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy8wUmc4RGRwQWliZlVrdURUTmJ4ekplNzdvaWJp.png
! V8 P! C+ f, h6 K7 ?4 ]0 C/ q. q8 |8 S
4 STM32的3种不同GPIO驱动# U! h5 j9 R. k8 K! S
4.1 库函数版
2 r# S- n% ]! O0 ~5 O( o. E! y
最常用的版本,使用ST标准外设库
' v4 H/ M, i; S. s9 B( Y
. C4 C; i* n7 k7 \) h3 c& T! N" i
  1. void  Led_Key_Init(void)
    0 Y6 J5 t: r4 R- J; y
  2. {  
    7 I% Y$ }- O1 f0 e& q" e
  3.   GPIO_InitTypeDef GPIO_Init_s;
    $ l6 ~0 T1 c8 u& N7 Q; u

  4. 8 |+ S7 f9 K" g; j5 b5 \
  5.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);//使能时钟  " I5 T+ w0 I: t) Y
  6.   GPIO_Init_s.GPIO_Pin = GPIO_Pin_4;5 s  R, J5 [3 ~3 @, Z% X
  7.   GPIO_Init_s.GPIO_Mode = GPIO_Mode_OUT;
    " b1 D$ f3 l1 ~  v- V
  8.   GPIO_Init_s.GPIO_OType = GPIO_OType_PP;
    ( x  B9 x% ^$ f$ Z3 b
  9.   GPIO_Init_s.GPIO_Speed = GPIO_Speed_50MHz;
    : p' i# S$ M, C
  10.   GPIO_Init_s.GPIO_PuPd = GPIO_PuPd_UP;
    ' q+ ]% \9 W, R4 m. H+ Z, L# b
  11.   GPIO_Init(GPIOE, &GPIO_Init_s);" a' c/ C; T* r4 v0 k6 l2 P# {
  12. }
复制代码

. h& N4 x2 V6 I( d, j# `4.2 寄存器版& ~3 k3 ^! P2 G2 }& H; F9 {* l
寄存器版其实就是把ST标准外设库拷贝出来,可以简化一些操作8 \1 }# Z4 H9 ?# l7 Q7 t
: V$ r' @1 q. z* O% |  X6 `

  1.   T# h* M9 w" [( E0 [* O
  2. /********使用寄存器---start********/
    3 i- E3 r0 i1 H9 v+ p
  3. //TP---PA15
    * N6 r3 E8 b6 a6 o! D

  4. 2 |2 C# T$ V4 S
  5. #define TP_PORT      GPIOA      2 S9 `$ d9 I! Y' y. n3 N
  6. #define TP_PIN      GPIO_Pin_15
      N1 r% C1 T! }; _1 i2 X% I

  7. 0 ^$ o! i# w! _2 f/ X
  8. #define TP_OUT()   TP_PORT->CRH&=0X0FFFFFFF; TP_PORT->CRH|=0X50000000;   //!IO输出* g- ^1 D% x7 d3 d7 c
  9. #define TP_IN()    TP_PORT->CRH&=0X0FFFFFFF; TP_PORT->CRH|=0X40000000;   //!IO输入! I. r. Y- g) e6 j# g7 {* }
  10. #define TP_READ()  (((TP_PORT->IDR)>>15)?1:0)   //!<IO读入: G) Y+ C9 i% D

  11. 9 }: h; i/ |, L0 z! i" a+ [
  12. #define TP_SET()    TP_PORT->BSRR = TP_PIN;//写1
    : l' M5 K  B: ~: n
  13. #define TP_CLR()    TP_PORT->BRR  = TP_PIN;//写0* x. q5 i$ S, O* W+ j) h
  14. ' U/ g8 G3 J3 Q: ]
  15. #define TP_DATA_SET()   TP_SET() //IO写1
    " y7 F- j7 x# `' K0 {" V, q
  16. #define TP_DATA_CLR()   TP_CLR() //IO写0
    0 S& u) U8 W: Z& N+ ^9 A
  17. #define TP_DATA_OUT()   TP_OUT() //将IO设为输出9 D  {6 y8 u  V# [9 X1 B& [2 J
  18. #define TP_DATA_IN()    TP_IN()  //将IO设为输入, C5 I+ F3 N, s" U
  19. #define TP_DATA_READ()  TP_READ()//读取IO的电平
    ) U0 ~' N  b9 a- I& _: B

  20. 2 r- |. V) a8 g2 }
  21. /********使用寄存器---end********/
复制代码
' |/ C7 w9 M1 `& X5 e
4.3 位段版; d. \$ K8 X5 q8 C& C$ ]5 X
基于位段的,操作方便
6 W4 B( B' C1 t9 S) f0 r2 Z, u" f4 y7 [
  1. /*LED配置---PE4*/
    ) ?1 b' H8 A6 U0 S% o/ S! E
  2. #define  GPIO_IDR_OFFSET  (GPIOE_BASE+0x10 - PERIPH_BASE)$ G" [5 W) p3 J! ]! E: J: r
  3. #define  GPIO_ODR_OFFSET  (GPIOE_BASE+0x14 - PERIPH_BASE)
    2 Q3 ^- T* F$ |$ u, d
  4. ; ?* T2 N4 y8 J
  5. #define  GPIO_BitNumber   4! l2 q- |  L, ]! ^/ m
  6. #define  GPIO_OUT_BB     (PERIPH_BB_BASE + (GPIO_ODR_OFFSET * 32) + (GPIO_BitNumber * 4))   
    8 H2 d! y# `4 B
  7. #define  GPIO_OUT_DATA           *(__IO uint32_t *)GPIO_OUT_BB
    , e( ]# ~2 b, \3 O7 {

  8. ) i7 b& d. {+ ^& [) ?  n7 J
  9. #define  GPIO_IN_BB     (PERIPH_BB_BASE + (GPIO_IDR_OFFSET * 32) + (GPIO_BitNumber * 4))   
    - @- W. _7 F+ w+ }
  10. #define  GPIO_IN_DATA           *(__IO uint32_t *)GPIO_IN_BB
    : N) e, b% w% D
  11. 7 V; x' \4 ~% g; s: o& o
  12. #define  GPIO_DIR_REG     *(__IO uint32_t *)(GPIOE_BASE+0X00)
    1 f3 f+ a5 @+ w' O- [6 O; F

  13. # e! }! ~  F& O2 Q7 y' g2 m7 W1 P8 ?
  14. 3 r% {7 W/ T1 l  r7 a
  15. #define PE4_SET()  GPIO_OUT_DATA = 1   //!< IO写13 J$ h/ ?! m) \
  16. #define PE4_CLR()  GPIO_OUT_DATA = 0   //!< IO写0) P+ a8 k  U% d( ^3 I- I: q
  17. #define PE4_OUT()  GPIO_DIR_REG = (((GPIO_DIR_REG) & 0xFFFFFCFF) | 0x00000100)  //!IO输出# ~) p3 W0 Z9 }# t; ^% B- J- Q
  18. #define PE4_IN()   GPIO_DIR_REG = ((GPIO_DIR_REG) & 0xFFFFFCFF)  //!IO输入* @8 v0 N  b3 L( u! J
  19. #define PE4_READ() GPIO_IN_DATA   //!<IO读入
复制代码
. y; `0 \7 J' X, p
将上述整理为通用GPIO驱动代码  X" J& E% m$ g( Y; u, k+ D
9 R! O& l# n! R/ N6 `( `
  1. *GPIO配置---PXX*/0 @  Y% u% a  M+ ^/ u/ n
  2. #define GPIO_SET(GPIOx_BASE,GPIO_BitNumber)   *(__IO uint32_t *)((PERIPH_BB_BASE + ((GPIOx_BASE+0x14 - PERIPH_BASE) * 32) + (GPIO_BitNumber * 4))) = 1   //!< IO写1; b& r: T! h8 f( o- g2 D5 B2 A
  3. #define GPIO_CLR(GPIOx_BASE,GPIO_BitNumber)   *(__IO uint32_t *)((PERIPH_BB_BASE + ((GPIOx_BASE+0x14 - PERIPH_BASE) * 32) + (GPIO_BitNumber * 4))) = 0   //!< IO写0
    : Y& m+ z/ h+ X, \* a' F

  4. 1 s: \1 I. v- \' L2 {- k
  5. #define GPIO_OUT(GPIOx_BASE,GPIO_BitNumber)   *(__IO uint32_t *)(GPIOx_BASE+0X00)  &= ~(GPIO_MODER_MODER0 << (GPIO_BitNumber * 2));\
    $ E4 Q: G6 a0 K
  6.                                               *(__IO uint32_t *)(GPIOx_BASE+0X00) |= ((GPIO_Mode_OUT) << (GPIO_BitNumber * 2)); //!IO输出
    8 h: b! i1 _) U$ @0 @

  7. - t1 @8 r% j8 L, X. M! k
  8. #define GPIO_IN(GPIOx_BASE,GPIO_BitNumber)    *(__IO uint32_t *)(GPIOx_BASE+0X00)  &= ~(GPIO_MODER_MODER0 << (GPIO_BitNumber * 2));\
    - F* a. n: x1 I: R( E( V
  9.                                               *(__IO uint32_t *)(GPIOx_BASE+0X00) |= ((GPIO_Mode_IN) << (GPIO_BitNumber * 2));  //!IO输入7 K  W- ]) l/ P
  10. 9 |' |( g- G- Q% c# @
  11. #define GPIO_READ(GPIOx_BASE,GPIO_BitNumber)  *(__IO uint32_t *)((PERIPH_BB_BASE + ((GPIOx_BASE+0x10 - PERIPH_BASE) * 32) + (GPIO_BitNumber * 4)))   //!<IO读入
复制代码
! M+ z5 L/ [3 d9 z$ I/ ?
调用方法8 i  ~3 i: y2 X2 k

6 f8 V4 l8 v2 y9 G$ f: NGPIOx_BASE:GPIOE_BASE基地址,而不是GPIOE; U. U6 C) R/ C% _+ ]5 ?' h' q
1 N4 o" A0 V2 \) U4 Y4 E
GPIO_BitNumber:4,而不是GPIO_Pin_42 H3 G  U- \: W. W* d
6 z0 D4 `: |/ Y5 T+ E* W
  1. GPIO_OUT(GPIOE_BASE,4);
复制代码

( f" l5 `/ X% d) f: q7 s; ?1 x  _' f

' U1 U8 N# T0 V* J, w* @
9 ?/ l: b' r' d4 l* ~/ ~
赞 收藏 评论0 发布时间:2022-4-18 10:58

举报

0个回答

所属标签

相似技术帖

官网相关资源

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