对于我们学的STM32都是习惯性的调用库函数,操作GPIO直接对结构体成员直接赋值即可。学到I2C通信时,看到以上代码中突如其来的寄存器操作,对于我这个新手小白来说是一脸懵逼状态,经过一个下午的学习,也算是搞懂了GPIO寄存器操作。分享一下个人学结,有错误的地方欢迎各路大佬指正。$ _7 v& |) D1 R1 p) t 1、所需基础知识回顾0 J+ [) O/ V' \3 L8 b4 W: g- Y9 N 由参考手册可知,GPIO端口每个位可以由软件分别配置为: 输入浮空 输入上拉; x; o$ D: c l+ F9 Y 输入下拉' Z- x( \% k" y$ w: a 模拟输入4 V5 V2 E+ B3 h 推挽式输出 推挽式复用功能 开漏复用功能 每个GPIO端口由七个寄存器来控制,分别为:, C. s( w( _4 V- q* b' B 端口配置低寄存器(GPIOx_CRL) 端口配置高寄存器(GPIOx_CRH) 端口输入数据寄存器(GPIOx_IDR)6 E0 E$ J! a' R( e7 E. k, H" J 端口输出数据寄存器(GPIOx_ODR) 端口位设置/清除寄存器(GPIOx_BSRR) 端口位清除数据寄存器(GPIOx_BRR) 端口配置锁定寄存器(GPIOx_LCKR)$ ~( _, I$ U, v; G: n) F 注:其中GPIOx_CRL配置GPIOx_Pin0——GPIOx_Pin7引脚,GPIOx_CRH配置GPIOx_Pin8——GPIOx_Pin15引脚(这个必须要明白) 逻辑运算' e* }( @( v1 }- J$ U 左移运算 Value << num8 I& {9 z/ x, Y: L, K2 N) h num:指定要移位值Value移动的位数1 C) \5 y& k) D& a* u2 Z+ D 丢弃最高位,0补在最低位 左移一位相当于乘以2的1次方,左移n位相当于乘以2的n次方 例如:8<<28,即:8向左移动28位 与运算:1 & A = A ;可得出1跟任意数进行”与“运算不会改变其值 STM32寄存器描述 GPIOx_CRL寄存器,也就是低位寄存器。配置:Pin0—Pin7。图一如下: 8 q/ o8 m' i) o9 \
GPIOx_CRH寄存器,也就是高位寄存器.配置:Pin8—Pin15。图二如下:* B5 T3 J, C1 W $ A* y9 t; V) y8 `2 A* r, e# S
6 w! [8 ]- i1 F 也就是一个GPIOx端口分为GPIOx_CRL寄存器和GPIOx_CRH寄存器,其中MODE0[1:0]和CNF7[1:0]一起表示Pin0的配置值,以下依此类推,MODE7[1:0]和CNF7[1:0]一起表示Pin7的配置值。( x( s4 ^/ A! B: C3 \8 _" |+ P 2、代码详解 对于文章开头提到的两个宏定义分开两条解释,SDA_IN()如下:$ F2 A+ `2 [2 A 6 r4 n/ u7 K. L3 |. h #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}9 E6 z& s, [& [' w+ P 其中GPIOB->CRL&=0X0FFFFFFF 等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF0 ^+ f% D; [" [# H. i2 l0 m 含义为:由于上述提到的CRL为低位寄存器,意思是把GPIOB端口的CRL寄存器的28、29、30、31位清0,即将PB7中CNF7[1:0]和MODE[1:0]四位都置零。(这里应该还是蛮好理解的)! P0 j( C; P. R4 L / g0 s c/ u# |# [" l 其中GPIOB->CRL|=(u32)8<<28等价于GPIOB->CRL = GPIOB->CRL | (u32)(8<<28) 由于u32强制类型转换为32位,则8的二进制为:0000 0000 0000 0000 0000 0000 0000 1000" p' n5 _3 f8 z5 l# l/ C1 Y 将8左移28位得:1000 0000 0000 0000 0000 0000 0000 0000 由左移之后的数据可知:将31、30、29、28位设置为1000。下面对上图中的CNF和MODE解析- X0 C# W& t. B3 h% x5 q- Z1 v 即将PB的CRL寄存器中29和28位设置为00,由图一可知,其中29位MODE7[1]的值=0;28位MODE7[0]的值为0;图三如下: 8 X1 G- E1 ]# B0 b7 r : r( x8 d! V8 M( U" X6 x0 c
; |0 `) `+ e8 N7 C 由上图得知:29:28位配置为00,由下表得知,MODE1和MODE0为00时为输入模式。图四如下:5 h" C6 w3 w3 X. ? ( {; }/ |& x0 Z" Q+ \
' C. O- ^4 Q, l) s/ G 接下来分析31:30位的值:10,由图一可知,其中31位CNF7[1]的值=1;30位CNF7[0]的值为0。图五如下:" }/ R1 K& O+ b7 v0 F
由29:28位得知:为输入模式,且31:30位的值为10,对照图四亦可得出,即为上下拉输入模式: x! ~2 W# s' j SDA_OUT()如下: #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;} 3 k! I, `* \9 @; A 其中(GPIOB->CRL) &= 0X0FFFFFFF等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF 其含义为:把PB端口的CRL寄存器的第28、29、30和31位清空,把GPIOB端口中的CNF7[1:0]和MODE7[1:0]置零( N7 R" _/ |6 q) [5 s1 [6 T % ^$ f# g) [8 Q. P- y4 O 其中GPIOB->CRL |= (u32)3<<28 等价于 GPIOB->CRL = GPIOB->CRL | (u32)(3<<12) 3转换为2进制为:0000 0000 0000 0000 0000 0000 0000 0011 将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。" r1 t- o4 B# O; v
4 [% V( e1 b; W. z( l+ w: U/ D* G! c PB的CRL寄存器中31和30位设置为00,其中31位CNF7[1]的值=0;30位CNF7[0]的值为0;由图五可知:通用推挽式输出模式。 ————————————————6 w9 y" j3 [, e3 t. c" _ 版权声明:枫华雪悦v" |' T/ {) e6 R2 u 如有侵权请联系删除 ! G, o5 u4 W/ ^' m, y7 m4 s |
DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【福利三:雨露均沾·逢7狂欢】之五:STM32系统时钟PLL故障排除1例
【福利三:雨露均沾·逢7狂欢】之三:AI帮我查BUG
福利三:雨露均沾·逢7狂欢】之二:STM32F407VG串口通信乱码故障的排除
基于STM32F103的I2C主从机通信
OpenBLT移植到STM32F103战舰开发板上适用于所有STM32F103系列的Bootloader
TFT LCD 与 FSMC 的硬件连接大容量 STM32F10xxx FSMC 接口
单片机:初学者该了解的STM32F103基础知识
2025软件工具兔哥知道
STM32之继电器模块
微信公众号
手机版