5 m* Y q* {2 x3 m* }6 H 对于我们学的STM32都是习惯性的调用库函数,操作GPIO直接对结构体成员直接赋值即可。学到I2C通信时,看到以上代码中突如其来的寄存器操作,对于我这个新手小白来说是一脸懵逼状态,经过一个下午的学习,也算是搞懂了GPIO寄存器操作。分享一下个人学结,有错误的地方欢迎各路大佬指正。 + v$ j% \ v/ [; Z4 m ; N! @- ?4 M4 E3 v4 p 1、所需基础知识回顾 由参考手册可知,GPIO端口每个位可以由软件分别配置为: 输入浮空 输入上拉 输入下拉 模拟输入 推挽式输出 推挽式复用功能 ]8 S! \4 ?6 T 开漏复用功能: R5 ?, a, d( Q 每个GPIO端口由七个寄存器来控制,分别为: 端口配置低寄存器(GPIOx_CRL) 端口配置高寄存器(GPIOx_CRH) 端口输入数据寄存器(GPIOx_IDR)/ p% a; c# S7 x! F4 U 端口输出数据寄存器(GPIOx_ODR); I1 Q* I9 c7 d# I' N 端口位设置/清除寄存器(GPIOx_BSRR)6 H' ?. ]% i( [3 I8 P& ], F 端口位清除数据寄存器(GPIOx_BRR)6 z$ N& I- v6 F$ g$ H' ^# h 端口配置锁定寄存器(GPIOx_LCKR) 注:其中GPIOx_CRL配置GPIOx_Pin0——GPIOx_Pin7引脚,GPIOx_CRH配置GPIOx_Pin8——GPIOx_Pin15引脚(这个必须要明白) $ Z2 Q, A8 n# n! n/ h6 s 逻辑运算/ V- D9 B0 N) {; F0 o I 左移运算 Value << num9 A% z( H. b* c# F, Z; m num:指定要移位值Value移动的位数( r, X1 n4 u9 H 丢弃最高位,0补在最低位 左移一位相当于乘以2的1次方,左移n位相当于乘以2的n次方. ]% K6 h& Q) P/ W6 P7 y/ T 例如:8<<28,即:8向左移动28位( n5 B6 @( p' O7 m% a/ s 与运算:1 & A = A ;可得出1跟任意数进行”与“运算不会改变其值 STM32寄存器描述+ N/ H8 U+ }$ F( [& q" W GPIOx_CRL寄存器,也就是低位寄存器。配置:Pin0—Pin7。图一如下: " N" Q8 f+ ^% f6 m
GPIOx_CRH寄存器,也就是高位寄存器.配置:Pin8—Pin15。图二如下:
也就是一个GPIOx端口分为GPIOx_CRL寄存器和GPIOx_CRH寄存器,其中MODE0[1:0]和CNF7[1:0]一起表示Pin0的配置值,以下依此类推,MODE7[1:0]和CNF7[1:0]一起表示Pin7的配置值。% ]( O" B- H& |! x4 {! Q 2、代码详解 对于文章开头提到的两个宏定义分开两条解释,SDA_IN()如下: #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}2 a0 u$ `) i4 @# a( N ! X* G6 V; K9 X% ^7 x( Q 其中GPIOB->CRL&=0X0FFFFFFF 等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF 含义为:由于上述提到的CRL为低位寄存器,意思是把GPIOB端口的CRL寄存器的28、29、30、31位清0,即将PB7中CNF7[1:0]和MODE[1:0]四位都置零。(这里应该还是蛮好理解的)8 k- T- w; E6 j 5 ~; ` k: y; l- N 其中GPIOB->CRL|=(u32)8<<28等价于GPIOB->CRL = GPIOB->CRL | (u32)(8<<28) 由于u32强制类型转换为32位,则8的二进制为:0000 0000 0000 0000 0000 0000 0000 1000 将8左移28位得:1000 0000 0000 0000 0000 0000 0000 0000( J1 d( v- x9 }/ r' k' U! u1 ]" F v 由左移之后的数据可知:将31、30、29、28位设置为1000。下面对上图中的CNF和MODE解析 即将PB的CRL寄存器中29和28位设置为00,由图一可知,其中29位MODE7[1]的值=0;28位MODE7[0]的值为0;图三如下: 4 ?9 I( {7 _! f9 Y4 X8 b9 ^# A ( K* G& M6 M0 n+ W: V
由上图得知:29:28位配置为00,由下表得知,MODE1和MODE0为00时为输入模式。图四如下:6 S7 b5 A, u; V8 {. j, w/ `5 { e) g) X5 l- V/ j$ J 6 _0 k# A K/ E/ [+ ]5 \$ Z8 S
5 A! _' G y! J2 G 接下来分析31:30位的值:10,由图一可知,其中31位CNF7[1]的值=1;30位CNF7[0]的值为0。图五如下:" Z0 M/ s9 j, m$ b* r6 B 8 I5 C' t7 R( B/ N! i0 F
由29:28位得知:为输入模式,且31:30位的值为10,对照图四亦可得出,即为上下拉输入模式# q: T t `- S, Z SDA_OUT()如下: #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;} 5 U/ q1 i) n- R+ b+ o 其中(GPIOB->CRL) &= 0X0FFFFFFF等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF: j5 h0 e- f8 t( P9 ?6 p* { 其含义为:把PB端口的CRL寄存器的第28、29、30和31位清空,把GPIOB端口中的CNF7[1:0]和MODE7[1:0]置零: F4 @* ^3 }7 c9 N$ ]" ~+ L' V 其中GPIOB->CRL |= (u32)3<<28 等价于 GPIOB->CRL = GPIOB->CRL | (u32)(3<<12)( |; J8 [! f0 l& N) T 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% s' ^# i' x& N. o( \! z PB的CRL寄存器中29和28位设置为11,其中29位MODE7[1]的值=1;28位MODE7[0]的值为1。即为输出模式,最大速度为50Mhz。 & E# O( p0 u# i% n, h9 O
PB的CRL寄存器中31和30位设置为00,其中31位CNF7[1]的值=0;30位CNF7[0]的值为0;由图五可知:通用推挽式输出模式。 ————————————————, A! G* h; `: m5 l! [0 y 版权声明:枫华雪悦v 如有侵权请联系删除0 D' A. R+ ^8 Z [4 c. k) m * z2 q: q0 [3 h* [+ O 8 S+ z y* {1 _6 A1 @ |
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之继电器模块
微信公众号
手机版