- e+ \" t1 n1 H0 `$ ` 对于我们学的STM32都是习惯性的调用库函数,操作GPIO直接对结构体成员直接赋值即可。学到I2C通信时,看到以上代码中突如其来的寄存器操作,对于我这个新手小白来说是一脸懵逼状态,经过一个下午的学习,也算是搞懂了GPIO寄存器操作。分享一下个人学结,有错误的地方欢迎各路大佬指正。 , P5 i# O$ ~. Q: [# q 6 N7 y( z8 b# z 1、所需基础知识回顾$ ~' D% Y/ t* T5 a 由参考手册可知,GPIO端口每个位可以由软件分别配置为: 输入浮空 输入上拉 输入下拉4 p; ~# f, X* f& d0 F& B1 F! Z2 y 模拟输入) V$ ]) N' ?9 {3 x 推挽式输出 推挽式复用功能 开漏复用功能 每个GPIO端口由七个寄存器来控制,分别为: 端口配置低寄存器(GPIOx_CRL) 端口配置高寄存器(GPIOx_CRH) 端口输入数据寄存器(GPIOx_IDR)9 d" l3 D8 `. I! j1 Y 端口输出数据寄存器(GPIOx_ODR) 端口位设置/清除寄存器(GPIOx_BSRR) 端口位清除数据寄存器(GPIOx_BRR)2 e* g! [( e# c/ t* w 端口配置锁定寄存器(GPIOx_LCKR) 注:其中GPIOx_CRL配置GPIOx_Pin0——GPIOx_Pin7引脚,GPIOx_CRH配置GPIOx_Pin8——GPIOx_Pin15引脚(这个必须要明白) 3 `1 _8 s; {- x8 E1 H 逻辑运算 左移运算1 _, R8 A, N% u( O/ J- v Value << num num:指定要移位值Value移动的位数* y8 I' R. { ]& ? 丢弃最高位,0补在最低位 左移一位相当于乘以2的1次方,左移n位相当于乘以2的n次方5 u) Q4 C; ?' \) J% C) C 例如:8<<28,即:8向左移动28位" ~, w' [# |% Y. v+ g 与运算:1 & A = A ;可得出1跟任意数进行”与“运算不会改变其值7 _( q) k4 `, [ g3 b, o1 V5 f STM32寄存器描述0 _' h/ K0 p( _% i0 s" ] GPIOx_CRL寄存器,也就是低位寄存器。配置:Pin0—Pin7。图一如下:3 P) g4 d. T$ c3 y" K( q5 U
GPIOx_CRH寄存器,也就是高位寄存器.配置:Pin8—Pin15。图二如下:' i; r7 Y, o O1 ?6 U7 `8 Z
& _9 p) E3 M# i: Z 也就是一个GPIOx端口分为GPIOx_CRL寄存器和GPIOx_CRH寄存器,其中MODE0[1:0]和CNF7[1:0]一起表示Pin0的配置值,以下依此类推,MODE7[1:0]和CNF7[1:0]一起表示Pin7的配置值。 / v( E9 @6 C1 p9 L: I, B p3 u 2、代码详解 对于文章开头提到的两个宏定义分开两条解释,SDA_IN()如下:( O- V& E+ Q i7 ~; c" S8 Q! J) T #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;} ! E3 y' M) t8 r' n 其中GPIOB->CRL&=0X0FFFFFFF 等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF! u5 R$ S, E7 Z) H+ k8 o 含义为:由于上述提到的CRL为低位寄存器,意思是把GPIOB端口的CRL寄存器的28、29、30、31位清0,即将PB7中CNF7[1:0]和MODE[1:0]四位都置零。(这里应该还是蛮好理解的)% W* x/ D, w) G9 U3 i 6 H% y: _: E/ L# \& f 其中GPIOB->CRL|=(u32)8<<28等价于GPIOB->CRL = GPIOB->CRL | (u32)(8<<28)$ c0 m/ Y+ b$ J$ E( G0 I' m3 }0 f 由于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解析( D+ ]5 v. l4 U q 2 Q( n9 I: f4 n2 \; z. w" } 即将PB的CRL寄存器中29和28位设置为00,由图一可知,其中29位MODE7[1]的值=0;28位MODE7[0]的值为0;图三如下:0 o" ~9 b! C% U4 z
由上图得知:29:28位配置为00,由下表得知,MODE1和MODE0为00时为输入模式。图四如下: ( p% p. Q$ Z+ G5 D* j9 _; f d& [% {4 I
3 R/ _* Y ^ d4 \8 R# v! g 接下来分析31:30位的值:10,由图一可知,其中31位CNF7[1]的值=1;30位CNF7[0]的值为0。图五如下: " n* I0 W8 E! }6 N/ ]
; k) w/ Y' G% a- _( Z 由29:28位得知:为输入模式,且31:30位的值为10,对照图四亦可得出,即为上下拉输入模式 SDA_OUT()如下:8 w5 j4 f3 g( S) C #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}0 b- @' {% `4 M+ B1 C( \2 d5 [ 0 G8 E0 D$ R) ~: h2 O 其中(GPIOB->CRL) &= 0X0FFFFFFF等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF7 n' w9 J5 T& }+ }2 z 其含义为:把PB端口的CRL寄存器的第28、29、30和31位清空,把GPIOB端口中的CNF7[1:0]和MODE7[1:0]置零 其中GPIOB->CRL |= (u32)3<<28 等价于 GPIOB->CRL = GPIOB->CRL | (u32)(3<<12)( v e$ D. S6 y2 ~7 M& N 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- a4 k( J1 M2 `) A PB的CRL寄存器中29和28位设置为11,其中29位MODE7[1]的值=1;28位MODE7[0]的值为1。即为输出模式,最大速度为50Mhz。4 I) i- u' G: z( ^9 @ 6 R8 Z, { U: T* P
6 U4 d3 q. n0 y4 ?6 V+ U, F PB的CRL寄存器中31和30位设置为00,其中31位CNF7[1]的值=0;30位CNF7[0]的值为0;由图五可知:通用推挽式输出模式。# B4 @& p* [: w+ ~# d ————————————————; n' s" N1 m8 U1 d! H+ { 版权声明:枫华雪悦v 如有侵权请联系删除1 e/ ~! a$ t/ D2 c' Y, y& b( x% S" A ; { ~1 h+ z$ f. A / J" K- x, s7 N* M |
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之继电器模块
微信公众号
手机版