|
xzlo2uopc3k6409610.gif
) H. [) B0 T% V; O" A0 y* T. H
点击上方蓝色字体,关注我们5 q$ D( E1 [$ M( ?% v* A
$ l' u& N# Z8 ^( E3 V% G+ a
3 K$ n9 |) W% K如果你的项目对效率要求高,推荐使用位掩码操作;如果需要可读性和维护性,推荐结构体映射寄存器;如果使用Cortex-M架构,位带操作是个不错的选择。. q4 w2 k8 W$ y( i$ P
( G$ ^' s# X1 b# K) j
xbfkslgnyvq6409710.png
1 e0 ]' j; T6 r2 l1
! d7 F" C! z5 n$ g- s' Y使用位掩码
9 |- z% L7 T" p: u; ]0 Z# Z位掩码用于选择寄存器中的特定位,而不影响其他位。常见的位操作包括 置位、清位、翻转、读取。6 l% q) M/ q* `& v; `2 I+ I
7 g# H! u; _4 N+ f/ L3 P
svnf5rrebrk6409810.png
, [1 g, o9 D2 ^6 w# ?" A. h; r" @6 |4 Q. B0 |8 l
示例(配置GPIO输出高电平):( V% ^1 P7 I6 U! ~- V
2 A) |: H2 J$ W9 p#define GPIOA_ODR (*(volatile uint32_t*)0x48000014) // GPIOA 输出数据寄存器地址#define PIN_5 5// 置位PA5引脚(输出高)GPIOA_ODR |= (1 ! q# y- A" N2 N. v0 g4 ?, a
2* |9 t, N' ]; W/ ?- m: W& Q3 g
使用宏定义
9 P! O2 P# _- |4 i- ~% ~4 ?使用宏定义可以提高代码的可移植性和可读性,减少硬编码。; q8 w& l! w4 C$ h @* c# d6 {
" `; ?2 l9 x* i R
#define SET_BIT(REG, BIT) ((REG) |= (1 #define CLEAR_BIT(REG, BIT) ((REG) &= ~(1 #define TOGGLE_BIT(REG, BIT) ((REG) ^= (1 #define READ_BIT(REG, BIT) (((REG) & (1 示例(使用宏定义控制GPIO):
) x0 u) D" g' ^ n; |4 O! T- Q
0 x8 C' \* V5 `5 i s* m% Y4 nSET_BIT(GPIOA_ODR, PIN_5); // 置位CLEAR_BIT(GPIOA_ODR, PIN_5); // 清零TOGGLE_BIT(GPIOA_ODR, PIN_5); // 翻转int state = READ_BIT(GPIOA_ODR, PIN_5); // 读取状态
+ F+ X: |2 R. x2 Z8 ]; T3
+ Z2 Y3 i' v" W5 [$ J使用结构体映射寄存器3 ]6 X$ N$ r% P' G& J4 f
单片机的寄存器通常是地址连续的,可以使用结构体映射寄存器,提高代码的可读性和可维护性。
9 |& k" v; Z* ]- V! T8 B8 s+ p" {# k4 D( E
typedef struct { volatile uint32_t MODER; // 模式寄存器 volatile uint32_t OTYPER; // 输出类型寄存器 volatile uint32_t OSPEEDR; // 输出速度寄存器 volatile uint32_t PUPDR; // 上拉/下拉寄存器 volatile uint32_t IDR; // 输入数据寄存器 volatile uint32_t ODR; // 输出数据寄存器} GPIO_TypeDef;
- F$ p3 O/ n5 Z" Q( w) [1 Z! ]#define GPIOA ((GPIO_TypeDef*) 0x48000000) // GPIOA基地址$ a `3 R$ O& c/ F0 s& ^5 u
// 设置PA5为输出模式GPIOA->MODER |= (1 5 * 2));5 h- ?7 ~2 |5 _' ] a% n
4
5 v3 ~) \" a9 W& ~' n使用位带操作(Bit-Banding,适用于Cortex-M); J- r6 ^; ]/ q+ o& }: |1 ^
对于Cortex-M架构,位带操作允许对单个位进行原子读写,不影响寄存器的其他位。: j% u, C& g" @7 a. V$ m2 A
7 A* m$ J: z* j/ [$ }, a) j
位带区地址计算公式:
" S5 v$ x" ^& b X g1 h8 J8 M; d) l' ^5 I4 r% x8 ]
位地址 = 位带基地址 + (字偏移 × 32) + (位号 × 4)示例(Cortex-M4,使用位带操作置位/清零):2 j% E+ S8 p1 Z8 W) ~- N$ ~* w
, O( h. r# v& j& v! y+ i" Y
#define BITBAND(addr, bit) ((volatile uint32_t*)(0x42000000 + ((addr - 0x40000000) * 32) + (bit * 4)))#define GPIOA_ODR 0x48000014#define PA5 5
8 `, W! R0 P z1 o( Y' x) W// 置位PA5*BITBAND(GPIOA_ODR, PA5) = 1;) ~+ w7 a$ n! |* M5 f1 l3 X, \
// 清零PA5*BITBAND(GPIOA_ODR, PA5) = 0;% d! F' S* ?2 ^7 Y, Q: m
57 m- h7 i; b% J6 S
使用位字段优化位操作
3 U+ r0 o$ v) MC语言提供了位字段(Bit Fields)功能,可以定义结构体,并指定每个字段占用的位数,适用于某些特殊寄存器操作。' D7 q8 H" |' Y# p/ p3 H& C# Y
* ?* `' u! U7 K( R3 Vtypedef struct { uint32_t BIT0 : 1; uint32_t BIT1 : 1; uint32_t BIT2 : 1; uint32_t BIT3 : 1; uint32_t BIT4 : 1; uint32_t BIT5 : 1; uint32_t BIT6 : 1; uint32_t BIT7 : 1; uint32_t RESERVED : 24;} GPIO_ODR_Bits;#define GPIOA_ODR_BB (*(volatile GPIO_ODR_Bits*)0x48000014)// 置位PA5GPIOA_ODR_BB.BIT5 = 1;// 清零PA5GPIOA_ODR_BB.BIT5 = 0;
: A/ M$ C3 P6 F0 ~60 M6 L! O z) V/ |4 n, X2 k
避免常见错误5 j9 l. d! a" Z8 ]' }& U( w
避免误清其他位:1 f) V& @; V" E' o& x
3 ^& _* A4 Q1 Y' @8 ]4 ^$ x; }
REG &= ~(1 BIT_POS); // 正确:仅清除 BIT_POS 位REG &= 0; // 错误:清零整个寄存器注意寄存器的读写顺序:
& f* U& O7 b ?# G$ [& h2 @& |0 X: f: R4 C1 d7 |% W
REG |= (1 BIT_POS); // 先读取 REG,然后置位可能导致竞态问题,可使用:( V1 y1 p& S$ R i5 [- _% E- P
5 M |, T# ^: w# Y4 |, W
__disable_irq();REG |= (1 __enable_irq();7 U z) U- X4 s2 K3 T$ g T0 P# B
0ghxehihxmy6409910.jpg
: x6 T, B( ^9 v. m D$ w4 _
3wujqwc420o64010010.gif
2 r- ]5 A$ q$ i, B+ l4 r# c点击阅读原文,更精彩~ |
|