对于我们学的STM32都是习惯性的调用库函数,操作GPIO直接对结构体成员直接赋值即可。学到I2C通信时,看到以上代码中突如其来的寄存器操作,对于我这个新手小白来说是一脸懵逼状态,经过一个下午的学习,也算是搞懂了GPIO寄存器操作。分享一下个人学结,有错误的地方欢迎各路大佬指正。 4 E7 ^/ k- O- a* ?" D# n 1、所需基础知识回顾 由参考手册可知,GPIO端口每个位可以由软件分别配置为:1 E+ Q9 n. T, U. b1 E1 p2 r" y# _( b* u 输入浮空 输入上拉" U- W# W t" t# P( G 输入下拉3 x. R6 K- U* s. ^: h 模拟输入 推挽式输出 推挽式复用功能 开漏复用功能# E- y7 `6 u# z9 v, A 每个GPIO端口由七个寄存器来控制,分别为:+ _# u& V) T& y2 ]' R) F: g 端口配置低寄存器(GPIOx_CRL), n1 K) O2 m$ V( N 端口配置高寄存器(GPIOx_CRH) 端口输入数据寄存器(GPIOx_IDR) 端口输出数据寄存器(GPIOx_ODR) 端口位设置/清除寄存器(GPIOx_BSRR)1 O2 H. ]6 C6 T+ l' b 端口位清除数据寄存器(GPIOx_BRR) 端口配置锁定寄存器(GPIOx_LCKR)# a, s0 `. b" g2 u+ \3 L* d2 Q( e 注:其中GPIOx_CRL配置GPIOx_Pin0——GPIOx_Pin7引脚,GPIOx_CRH配置GPIOx_Pin8——GPIOx_Pin15引脚(这个必须要明白) ! F4 C( D% ]( a9 Z5 ]8 k j2 L8 r 8 o6 b; R/ X! J" R c 逻辑运算6 ?9 r S8 q* K 左移运算/ m' f8 E; Y/ I7 R: P# N9 {; r4 ? Value << num8 i+ R$ y+ ?6 ~. M0 q5 q' U num:指定要移位值Value移动的位数) y$ X! w5 M, m- Y/ ]& J- } 丢弃最高位,0补在最低位 左移一位相当于乘以2的1次方,左移n位相当于乘以2的n次方/ N" H! D& ?' G 例如:8<<28,即:8向左移动28位 与运算:1 & A = A ;可得出1跟任意数进行”与“运算不会改变其值5 V- _2 {* r& @% G) C0 K+ _ STM32寄存器描述 GPIOx_CRL寄存器,也就是低位寄存器。配置:Pin0—Pin7。图一如下: 3 z4 e5 ]+ p+ h( Z# J
# `6 a% B& `# A; _4 ~) V. x6 q GPIOx_CRH寄存器,也就是高位寄存器.配置:Pin8—Pin15。图二如下: " d/ T% v' n! n
; y0 L4 J0 O2 C0 |( B0 _ 也就是一个GPIOx端口分为GPIOx_CRL寄存器和GPIOx_CRH寄存器,其中MODE0[1:0]和CNF7[1:0]一起表示Pin0的配置值,以下依此类推,MODE7[1:0]和CNF7[1:0]一起表示Pin7的配置值。6 W7 Z/ `# G7 _6 |0 M ( i6 l) ^2 m# \ 2、代码详解 对于文章开头提到的两个宏定义分开两条解释,SDA_IN()如下: 1 {+ G' C( w( ? #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}" F& J) M/ |6 s% x& d& V. Y 其中GPIOB->CRL&=0X0FFFFFFF 等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF 含义为:由于上述提到的CRL为低位寄存器,意思是把GPIOB端口的CRL寄存器的28、29、30、31位清0,即将PB7中CNF7[1:0]和MODE[1:0]四位都置零。(这里应该还是蛮好理解的)) m& U+ D: }7 Y" u7 n: S6 a ( |# j- y& r" \& Z 其中GPIOB->CRL|=(u32)8<<28等价于GPIOB->CRL = GPIOB->CRL | (u32)(8<<28)% x0 P1 ]) k& ?4 B2 R 由于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解析 / l/ T2 i' T& Z+ D7 O4 } 即将PB的CRL寄存器中29和28位设置为00,由图一可知,其中29位MODE7[1]的值=0;28位MODE7[0]的值为0;图三如下:6 c; l0 I' V! k# ^ $ O% v* Q. _6 S( G3 I* r$ q) W
由上图得知:29:28位配置为00,由下表得知,MODE1和MODE0为00时为输入模式。图四如下:
8 q8 j& m' `7 T9 ^/ n0 [: G 接下来分析31:30位的值:10,由图一可知,其中31位CNF7[1]的值=1;30位CNF7[0]的值为0。图五如下:" |( q) G+ ^( c$ C
由29:28位得知:为输入模式,且31:30位的值为10,对照图四亦可得出,即为上下拉输入模式6 R2 \2 R4 B% b N4 U0 j SDA_OUT()如下:0 W1 W; ]" @" W& S$ _ t9 e/ u" H- q" d #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;} 其中(GPIOB->CRL) &= 0X0FFFFFFF等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF0 M. q" X' _( y2 a, b9 g 其含义为:把PB端口的CRL寄存器的第28、29、30和31位清空,把GPIOB端口中的CNF7[1:0]和MODE7[1:0]置零, h& s( o; t, ^& z+ [ 其中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 00008 [% T9 V: u) | R 分析左移之后的数据为,将31、30、29、28位设置为00 11 PB的CRL寄存器中29和28位设置为11,其中29位MODE7[1]的值=1;28位MODE7[0]的值为1。即为输出模式,最大速度为50Mhz。! i- }$ u4 T" O7 n$ [& F @ b# n8 O) P. p& j& Z
7 B$ d' _7 W9 }6 J PB的CRL寄存器中31和30位设置为00,其中31位CNF7[1]的值=0;30位CNF7[0]的值为0;由图五可知:通用推挽式输出模式。1 T" L. L4 c0 v6 ]4 Q% L7 T; { ————————————————4 M7 {& }& q& z, w6 x0 c4 _! } 版权声明:枫华雪悦v 如有侵权请联系删除4 P* ?3 _ F3 P! ? 8 K; j& | s( H7 ] ! T! b# M$ }& Y+ ~3 s; z, T |
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之继电器模块
微信公众号
手机版