|

xzlo2uopc3k6409610.gif
6 r+ i6 w; I2 u4 z. \) m
点击上方蓝色字体,关注我们- T$ ^ I( q% ~$ O0 `( \! Q& I' x
2 L7 C( [& H7 C: v4 b/ i0 y
& c9 f4 J, c' }如果你的项目对效率要求高,推荐使用位掩码操作;如果需要可读性和维护性,推荐结构体映射寄存器;如果使用Cortex-M架构,位带操作是个不错的选择。
5 m' u& R+ Q' w$ }3 K v
: C8 t8 E- U7 y! d) n
xbfkslgnyvq6409710.png
" q' ~: d) [! e, @! [4 x! t
15 w: X7 w" N' m9 V' {/ x# n
使用位掩码+ S9 |' Q; d0 l( [
位掩码用于选择寄存器中的特定位,而不影响其他位。常见的位操作包括 置位、清位、翻转、读取。0 B% a7 D" ~8 M* @2 h3 R: [0 q
& ~$ m; J, a' Z% ^7 H; t
svnf5rrebrk6409810.png
2 \+ s* X' H5 J
8 m- L: e9 j; ?3 d0 |, A
示例(配置GPIO输出高电平):: B4 F$ v: a. W% s5 W8 y
4 K( H P( n+ u: ~#define GPIOA_ODR (*(volatile uint32_t*)0x48000014) // GPIOA 输出数据寄存器地址#define PIN_5 5// 置位PA5引脚(输出高)GPIOA_ODR |= (1
/ } \, b9 M3 |2
; ]* @/ j+ a/ N2 N使用宏定义2 ?, j2 p) R/ n
使用宏定义可以提高代码的可移植性和可读性,减少硬编码。0 i5 N2 J4 w4 b; h! u( f1 n- W
( g* ^6 T+ g; k- A9 |7 s5 a
#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):5 g" [ Z4 t, t# `9 O r5 a
8 S1 y) u$ P( S+ D" _; b
SET_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); // 读取状态* i: i" b" d8 R$ u$ o
3: x1 H- c' a u" V8 K- B: b5 ^
使用结构体映射寄存器, {( c9 r3 S0 Y2 G$ u
单片机的寄存器通常是地址连续的,可以使用结构体映射寄存器,提高代码的可读性和可维护性。
/ ]1 v5 ^4 d* h9 ?5 T" ~
5 [, K5 r. M T6 _. T0 w _6 _* Mtypedef 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;# R, Z: u6 ^/ @3 l6 |
#define GPIOA ((GPIO_TypeDef*) 0x48000000) // GPIOA基地址. q' u3 j* V' M7 [ K) s
// 设置PA5为输出模式GPIOA->MODER |= (1 5 * 2));
0 P( O! z; R1 F: } V4' Y* v; T2 @1 X5 `* s1 Q: }$ F
使用位带操作(Bit-Banding,适用于Cortex-M)
9 A5 f1 c' W5 T# H! q对于Cortex-M架构,位带操作允许对单个位进行原子读写,不影响寄存器的其他位。
& t3 u$ d) E8 r0 J, \* d2 r# k& L! O. S4 _) j
位带区地址计算公式:7 i. g; V, F( K3 A
. R& @: S! s0 S& r/ v5 f
位地址 = 位带基地址 + (字偏移 × 32) + (位号 × 4)示例(Cortex-M4,使用位带操作置位/清零): N, R3 Y2 }( n5 V$ ~, Q
# g$ x+ {% f$ w/ o#define BITBAND(addr, bit) ((volatile uint32_t*)(0x42000000 + ((addr - 0x40000000) * 32) + (bit * 4)))#define GPIOA_ODR 0x48000014#define PA5 5* s9 X* s2 y* y3 p' H; T" m) B
// 置位PA5*BITBAND(GPIOA_ODR, PA5) = 1;
3 n( p9 j! ]0 U1 D8 x7 H0 X// 清零PA5*BITBAND(GPIOA_ODR, PA5) = 0;
$ F- b1 S4 O; H5
% j. S. Q7 }. | T) V e6 x使用位字段优化位操作
, h" z& d F, @C语言提供了位字段(Bit Fields)功能,可以定义结构体,并指定每个字段占用的位数,适用于某些特殊寄存器操作。
8 x e1 y0 ?3 @$ f' x* T5 j: h
8 @' C; q) ]. N; e5 ~1 V4 q2 Ptypedef 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;
/ s2 K( V4 _! v3 q. O2 ^4 y6
/ g1 N9 ~9 o- Q2 I1 r+ P, y+ T避免常见错误
. I4 S* j; B. h7 E- \0 \4 i避免误清其他位:2 U9 K3 s( o. i7 m7 z
3 a* v% K7 |1 E* X! j
REG &= ~(1 BIT_POS); // 正确:仅清除 BIT_POS 位REG &= 0; // 错误:清零整个寄存器注意寄存器的读写顺序:1 k- I& ^7 A1 ?0 K' E8 L; l
/ q2 q+ a, i- }& x
REG |= (1 BIT_POS); // 先读取 REG,然后置位可能导致竞态问题,可使用:8 ?& ]# S/ j( W8 j6 |0 s) x% m( _
1 p0 L: b8 B) c+ g- T. Z
__disable_irq();REG |= (1 __enable_irq();
: M! ? j4 f! x5 d* Y/ ~
0ghxehihxmy6409910.jpg
0 n! z4 \: O+ p" D
3wujqwc420o64010010.gif
* O, [- {7 \0 i! }1 [% A点击阅读原文,更精彩~ |
|