
对于我们学的STM32都是习惯性的调用库函数,操作GPIO直接对结构体成员直接赋值即可。学到I2C通信时,看到以上代码中突如其来的寄存器操作,对于我这个新手小白来说是一脸懵逼状态,经过一个下午的学习,也算是搞懂了GPIO寄存器操作。分享一下个人学结,有错误的地方欢迎各路大佬指正。 `$ m4 l( n/ e6 c 1 M# A) {9 m9 R) Y 1、所需基础知识回顾 由参考手册可知,GPIO端口每个位可以由软件分别配置为:4 V3 r. W& ^9 h5 ` 输入浮空5 J% V0 V3 H! I 输入上拉2 g5 g% a. W$ p' G0 R' L* } 输入下拉9 d6 M# M% p' Y/ ~ 模拟输入& e. k4 ~' c r4 ^" D 推挽式输出* [" i( D: I r# U/ Y5 [ 推挽式复用功能; Y0 x! a' X: q6 w 开漏复用功能$ \* S' E3 e$ w, Z 每个GPIO端口由七个寄存器来控制,分别为:8 |0 l: X4 N6 }( h5 w 端口配置低寄存器(GPIOx_CRL)& T+ l/ l5 L4 a6 A( K; q 端口配置高寄存器(GPIOx_CRH) 端口输入数据寄存器(GPIOx_IDR)# Z6 o5 s- g# j- i& F 端口输出数据寄存器(GPIOx_ODR)9 s/ Z; @, a i. G- j 端口位设置/清除寄存器(GPIOx_BSRR) 端口位清除数据寄存器(GPIOx_BRR) 端口配置锁定寄存器(GPIOx_LCKR)6 t* U( o4 `9 y" z! g6 \ 注:其中GPIOx_CRL配置GPIOx_Pin0——GPIOx_Pin7引脚,GPIOx_CRH配置GPIOx_Pin8——GPIOx_Pin15引脚(这个必须要明白)6 I% h9 T6 n) S6 O3 g1 U 逻辑运算5 W, F8 Q( v5 j; j5 } 左移运算 Value << num num:指定要移位值Value移动的位数$ h6 K* X% u1 o& j# U! |) P, @ 丢弃最高位,0补在最低位 左移一位相当于乘以2的1次方,左移n位相当于乘以2的n次方: c6 S* S4 `0 u4 Y 例如:8<<28,即:8向左移动28位3 D) T% ]/ b: q' E 与运算:1 & A = A ;可得出1跟任意数进行”与“运算不会改变其值 STM32寄存器描述 GPIOx_CRL寄存器,也就是低位寄存器。配置:Pin0—Pin7。图一如下: ![]() ) M3 f p8 v; O! x8 ?# J( V GPIOx_CRH寄存器,也就是高位寄存器.配置:Pin8—Pin15。图二如下:* D% \ E+ D* T8 r; R ; `1 `/ ^9 b3 E" k! i- H# Q ![]() 也就是一个GPIOx端口分为GPIOx_CRL寄存器和GPIOx_CRH寄存器,其中MODE0[1:0]和CNF7[1:0]一起表示Pin0的配置值,以下依此类推,MODE7[1:0]和CNF7[1:0]一起表示Pin7的配置值。 2、代码详解- A1 Q: I+ u9 g. Q( ? 对于文章开头提到的两个宏定义分开两条解释,SDA_IN()如下:2 I- ^" m+ D. W: n5 J& M% R7 o0 f / [2 S% Z9 g8 @9 C #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}3 Q2 m$ R' _5 q& ^( V 5 ^4 C9 b; U! j& d# [- | 其中GPIOB->CRL&=0X0FFFFFFF 等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF 含义为:由于上述提到的CRL为低位寄存器,意思是把GPIOB端口的CRL寄存器的28、29、30、31位清0,即将PB7中CNF7[1:0]和MODE[1:0]四位都置零。(这里应该还是蛮好理解的)1 a! d9 D# f: [( u 其中GPIOB->CRL|=(u32)8<<28等价于GPIOB->CRL = GPIOB->CRL | (u32)(8<<28)& H% S6 S: R0 [: u; B8 L 由于u32强制类型转换为32位,则8的二进制为:0000 0000 0000 0000 0000 0000 0000 1000 将8左移28位得:1000 0000 0000 0000 0000 0000 0000 0000 由左移之后的数据可知:将31、30、29、28位设置为1000。下面对上图中的CNF和MODE解析 即将PB的CRL寄存器中29和28位设置为00,由图一可知,其中29位MODE7[1]的值=0;28位MODE7[0]的值为0;图三如下: ( Q. |- w1 m$ ^ ![]() " X/ O' F4 Q$ l) e0 x, t, y 由上图得知:29:28位配置为00,由下表得知,MODE1和MODE0为00时为输入模式。图四如下:1 t2 C7 L" @, A- b0 x. h { 4 h/ x9 y8 V8 ?" X' O ![]() 接下来分析31:30位的值:10,由图一可知,其中31位CNF7[1]的值=1;30位CNF7[0]的值为0。图五如下: , o6 ~5 } Z5 G3 I! D) i f ![]() C' x: i3 i7 i2 F( d; j8 ] 由29:28位得知:为输入模式,且31:30位的值为10,对照图四亦可得出,即为上下拉输入模式 SDA_OUT()如下:. q$ w2 K4 }# L3 Q) k) L Y #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}+ N3 N% u2 J: \. d( d! s' c * ?6 R' f& }5 {& W4 c 其中(GPIOB->CRL) &= 0X0FFFFFFF等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF7 g; ]* P! x2 i1 D 其含义为:把PB端口的CRL寄存器的第28、29、30和31位清空,把GPIOB端口中的CNF7[1:0]和MODE7[1:0]置零" d, V C% N5 \: d 6 Y, p5 T, [. G, A1 k9 |: F 其中GPIOB->CRL |= (u32)3<<28 等价于 GPIOB->CRL = GPIOB->CRL | (u32)(3<<12) 3转换为2进制为:0000 0000 0000 0000 0000 0000 0000 0011' u3 R. v0 d& U7 s 将3左移28位得:0011 0000 0000 0000 0000 0000 0000 0000 分析左移之后的数据为,将31、30、29、28位设置为00 11 PB的CRL寄存器中29和28位设置为11,其中29位MODE7[1]的值=1;28位MODE7[0]的值为1。即为输出模式,最大速度为50Mhz。 ![]() ) h0 a: K& O2 l0 [; { PB的CRL寄存器中31和30位设置为00,其中31位CNF7[1]的值=0;30位CNF7[0]的值为0;由图五可知:通用推挽式输出模式。. X8 y w7 Y. R ———————————————— 版权声明:枫华雪悦v/ r" y! ^* g+ v- c5 @' f1 f8 M5 } 如有侵权请联系删除' |2 A$ G6 D4 x" T ; z" m# `0 `/ K+ ^! N7 S- b0 M( o) F 4 H% c6 N5 g( @( q, x |