6 Z3 M+ d. V3 Z+ ]0 S) F 对于我们学的STM32都是习惯性的调用库函数,操作GPIO直接对结构体成员直接赋值即可。学到I2C通信时,看到以上代码中突如其来的寄存器操作,对于我这个新手小白来说是一脸懵逼状态,经过一个下午的学习,也算是搞懂了GPIO寄存器操作。分享一下个人学结,有错误的地方欢迎各路大佬指正。 ; V3 G3 c' Z( v1 F# S0 ~9 o+ \$ v 1、所需基础知识回顾 由参考手册可知,GPIO端口每个位可以由软件分别配置为: 输入浮空$ g* @7 w/ z/ d5 z 输入上拉; {+ e, h" f* l t1 }5 s* X 输入下拉& l- G$ Q+ ?7 G3 L g 模拟输入 推挽式输出/ d- G9 n" C, K4 U4 A" U 推挽式复用功能: K# ^7 S% l) G7 A 开漏复用功能 每个GPIO端口由七个寄存器来控制,分别为:' v z( y% w, z5 r* q# A5 X9 X 端口配置低寄存器(GPIOx_CRL): S6 {, x1 _+ A 端口配置高寄存器(GPIOx_CRH) K: {. } M; j 端口输入数据寄存器(GPIOx_IDR) U1 L0 | N$ j. \/ Y 端口输出数据寄存器(GPIOx_ODR), b1 T! F5 ^/ {; E0 X# r S8 R. Y 端口位设置/清除寄存器(GPIOx_BSRR) 端口位清除数据寄存器(GPIOx_BRR) 端口配置锁定寄存器(GPIOx_LCKR): t* Z& |6 z/ V 注:其中GPIOx_CRL配置GPIOx_Pin0——GPIOx_Pin7引脚,GPIOx_CRH配置GPIOx_Pin8——GPIOx_Pin15引脚(这个必须要明白) : Q' Z; l2 G2 `4 \% a/ i, W$ J4 S 5 c7 F0 [3 A- x. a% U; Z- o 逻辑运算( U! W8 G0 Y9 P8 P) J 左移运算 Value << num num:指定要移位值Value移动的位数 丢弃最高位,0补在最低位0 g6 N- ^2 k0 ~/ ^% { 左移一位相当于乘以2的1次方,左移n位相当于乘以2的n次方* c. {5 _: k' S) s1 z, [ 例如:8<<28,即:8向左移动28位 与运算:1 & A = A ;可得出1跟任意数进行”与“运算不会改变其值; Q8 y8 D' L* h STM32寄存器描述9 C$ C1 z. C; e1 h GPIOx_CRL寄存器,也就是低位寄存器。配置:Pin0—Pin7。图一如下:
GPIOx_CRH寄存器,也就是高位寄存器.配置:Pin8—Pin15。图二如下: * E' I* e8 @6 }; `& r
也就是一个GPIOx端口分为GPIOx_CRL寄存器和GPIOx_CRH寄存器,其中MODE0[1:0]和CNF7[1:0]一起表示Pin0的配置值,以下依此类推,MODE7[1:0]和CNF7[1:0]一起表示Pin7的配置值。; @) Q6 o. k+ `$ O% G! M/ B( I/ V! x 2、代码详解9 u/ \' {5 d9 I/ x% [ 对于文章开头提到的两个宏定义分开两条解释,SDA_IN()如下:+ \- S+ \% Q1 n( N) a' l8 { & b+ E- h. S1 c2 S #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;} 其中GPIOB->CRL&=0X0FFFFFFF 等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF0 T* S1 h7 ~( O G 含义为:由于上述提到的CRL为低位寄存器,意思是把GPIOB端口的CRL寄存器的28、29、30、31位清0,即将PB7中CNF7[1:0]和MODE[1:0]四位都置零。(这里应该还是蛮好理解的) # R: a5 U/ _2 Y1 H4 y9 B 其中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 由左移之后的数据可知:将31、30、29、28位设置为1000。下面对上图中的CNF和MODE解析 : ~1 V- c& F/ [- \2 x2 B1 e 即将PB的CRL寄存器中29和28位设置为00,由图一可知,其中29位MODE7[1]的值=0;28位MODE7[0]的值为0;图三如下: 7 Q) d1 x( j* p3 G) T
由上图得知:29:28位配置为00,由下表得知,MODE1和MODE0为00时为输入模式。图四如下:/ \2 e) Z E/ o5 I) ~/ c ( ?: T3 a+ T. S3 |. y4 f ! u5 \9 `$ f; c- \
1 f! A: l+ R. a1 q 接下来分析31:30位的值:10,由图一可知,其中31位CNF7[1]的值=1;30位CNF7[0]的值为0。图五如下:$ k: u7 E1 r5 m O- |+ i V$ y 5 ^6 c4 i5 r3 V% @8 q
$ Q/ Z; Q( h4 p6 f5 R2 d 由29:28位得知:为输入模式,且31:30位的值为10,对照图四亦可得出,即为上下拉输入模式 SDA_OUT()如下: #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}" Y" z6 [, f) l % w' Y! Q' W5 B/ q/ f 其中(GPIOB->CRL) &= 0X0FFFFFFF等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF, |2 o- A& w: g& P% M* e6 w# U/ t& A* ~ \ 其含义为:把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)! b6 y! W1 d5 m- v2 i+ v! i1 P, x 3转换为2进制为:0000 0000 0000 0000 0000 0000 0000 0011. [& w. Z; @# `) \! S# n& d 将3左移28位得:0011 0000 0000 0000 0000 0000 0000 0000 分析左移之后的数据为,将31、30、29、28位设置为00 11" z9 ]# I7 |* Y& e/ H5 D PB的CRL寄存器中29和28位设置为11,其中29位MODE7[1]的值=1;28位MODE7[0]的值为1。即为输出模式,最大速度为50Mhz。, a3 F k0 T# _3 q 0 E- ?* f8 _5 t- M
PB的CRL寄存器中31和30位设置为00,其中31位CNF7[1]的值=0;30位CNF7[0]的值为0;由图五可知:通用推挽式输出模式。 ———————————————— 版权声明:枫华雪悦v 如有侵权请联系删除 ! K4 ]3 H! _( h6 S. z" w/ \: ^. y |
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之继电器模块
微信公众号
手机版