对于我们学的STM32都是习惯性的调用库函数,操作GPIO直接对结构体成员直接赋值即可。学到I2C通信时,看到以上代码中突如其来的寄存器操作,对于我这个新手小白来说是一脸懵逼状态,经过一个下午的学习,也算是搞懂了GPIO寄存器操作。分享一下个人学结,有错误的地方欢迎各路大佬指正。 0 o2 I P9 _6 v1 r 1、所需基础知识回顾 由参考手册可知,GPIO端口每个位可以由软件分别配置为:) `; @$ U) w# n% j! X9 R k 输入浮空 输入上拉) ~; z& z& Y, M& ]* l4 ~7 i 输入下拉: K5 z6 Q& q% V9 A( e 模拟输入 推挽式输出" ~3 _+ e9 k3 {3 y' f 推挽式复用功能 开漏复用功能 每个GPIO端口由七个寄存器来控制,分别为:) w# j5 k* _0 A- C' U! Y& X 端口配置低寄存器(GPIOx_CRL) 端口配置高寄存器(GPIOx_CRH) 端口输入数据寄存器(GPIOx_IDR) 端口输出数据寄存器(GPIOx_ODR)6 }% i8 }. Z4 `8 J; s* I- v6 Q+ n 端口位设置/清除寄存器(GPIOx_BSRR)2 N: v1 Y, d; ^' _& l+ @ 端口位清除数据寄存器(GPIOx_BRR) 端口配置锁定寄存器(GPIOx_LCKR) 注:其中GPIOx_CRL配置GPIOx_Pin0——GPIOx_Pin7引脚,GPIOx_CRH配置GPIOx_Pin8——GPIOx_Pin15引脚(这个必须要明白)! \( V- X* k( h, {, @ & P$ e/ i" `( Q/ \6 k) X& [ 逻辑运算! J3 W4 h/ {0 ~7 t* k- \ 左移运算( t0 E: u2 _3 o J' H+ @ Value << num num:指定要移位值Value移动的位数/ t9 s8 s' K/ b. W- d 丢弃最高位,0补在最低位- D- U/ ?/ S$ R" c2 E# ?: o: S 左移一位相当于乘以2的1次方,左移n位相当于乘以2的n次方 例如:8<<28,即:8向左移动28位5 C& ^7 ?+ e0 B5 B8 L# o2 q 与运算:1 & A = A ;可得出1跟任意数进行”与“运算不会改变其值8 k; |) t- x1 {* L4 Z0 @* } STM32寄存器描述( s) K3 @! q3 z F% B GPIOx_CRL寄存器,也就是低位寄存器。配置:Pin0—Pin7。图一如下:( q$ V+ ~ j! v5 t+ ?7 h* _4 h # i; \0 R" X' l7 O- i
8 w2 P* X! S1 w8 p6 |* \- {. X GPIOx_CRH寄存器,也就是高位寄存器.配置:Pin8—Pin15。图二如下:2 Z: V8 B9 @& ~& b1 o) y" v : u9 N T$ f9 y+ h
也就是一个GPIOx端口分为GPIOx_CRL寄存器和GPIOx_CRH寄存器,其中MODE0[1:0]和CNF7[1:0]一起表示Pin0的配置值,以下依此类推,MODE7[1:0]和CNF7[1:0]一起表示Pin7的配置值。 / a j1 K. x1 Z1 b( d0 i1 }1 M 2、代码详解 对于文章开头提到的两个宏定义分开两条解释,SDA_IN()如下: * w: W8 {8 z( R# o" f0 Z #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;} ; Y* E- W3 T2 e0 d% z 其中GPIOB->CRL&=0X0FFFFFFF 等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF7 K1 z% ?% ^& }; e4 u 含义为:由于上述提到的CRL为低位寄存器,意思是把GPIOB端口的CRL寄存器的28、29、30、31位清0,即将PB7中CNF7[1:0]和MODE[1:0]四位都置零。(这里应该还是蛮好理解的) 其中GPIOB->CRL|=(u32)8<<28等价于GPIOB->CRL = GPIOB->CRL | (u32)(8<<28)1 @# e- ?# E9 n7 n$ o 由于u32强制类型转换为32位,则8的二进制为:0000 0000 0000 0000 0000 0000 0000 1000! J& ]1 B7 T5 U- m, ]' d6 T. {/ O 将8左移28位得:1000 0000 0000 0000 0000 0000 0000 0000 由左移之后的数据可知:将31、30、29、28位设置为1000。下面对上图中的CNF和MODE解析8 R/ J$ H& z) Q* W 即将PB的CRL寄存器中29和28位设置为00,由图一可知,其中29位MODE7[1]的值=0;28位MODE7[0]的值为0;图三如下: , `& B4 [! E. f; m' ~ % X* p. f! D# h. z: q$ @; Z8 c
1 E6 k0 B* S4 r4 @0 k 由上图得知:29:28位配置为00,由下表得知,MODE1和MODE0为00时为输入模式。图四如下: ; F# l5 t( O* t" Z
接下来分析31:30位的值:10,由图一可知,其中31位CNF7[1]的值=1;30位CNF7[0]的值为0。图五如下:- H1 m/ `7 U- W
' B" p ~$ r# m7 P3 @9 g 由29:28位得知:为输入模式,且31:30位的值为10,对照图四亦可得出,即为上下拉输入模式 SDA_OUT()如下: #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}( b! Q8 i3 ~& e: N 其中(GPIOB->CRL) &= 0X0FFFFFFF等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF 其含义为:把PB端口的CRL寄存器的第28、29、30和31位清空,把GPIOB端口中的CNF7[1:0]和MODE7[1:0]置零 ; C4 H8 A. R, J S& r# u: q: h! E 其中GPIOB->CRL |= (u32)3<<28 等价于 GPIOB->CRL = GPIOB->CRL | (u32)(3<<12)5 D# G+ s( k$ h, O7 H. C. \ 3转换为2进制为:0000 0000 0000 0000 0000 0000 0000 00114 o" t* i t5 n$ b0 Y6 ]* C 将3左移28位得:0011 0000 0000 0000 0000 0000 0000 0000; b0 n1 {' c5 |$ H4 p 分析左移之后的数据为,将31、30、29、28位设置为00 11; B& \$ c6 H$ w* D, f P- ] PB的CRL寄存器中29和28位设置为11,其中29位MODE7[1]的值=1;28位MODE7[0]的值为1。即为输出模式,最大速度为50Mhz。
9 u) `2 s/ L4 r+ T/ R PB的CRL寄存器中31和30位设置为00,其中31位CNF7[1]的值=0;30位CNF7[0]的值为0;由图五可知:通用推挽式输出模式。 ————————————————) ], x0 v# C; ]7 N: u# R& F% j 版权声明:枫华雪悦v7 X! `5 z4 f) t7 {$ F/ g0 b 如有侵权请联系删除+ H' M: c( G- v; _ |
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之继电器模块
微信公众号
手机版