5 ~& s0 P0 @) t0 j" i5 {8 c- c 对于我们学的STM32都是习惯性的调用库函数,操作GPIO直接对结构体成员直接赋值即可。学到I2C通信时,看到以上代码中突如其来的寄存器操作,对于我这个新手小白来说是一脸懵逼状态,经过一个下午的学习,也算是搞懂了GPIO寄存器操作。分享一下个人学结,有错误的地方欢迎各路大佬指正。4 I% |& z8 m/ J. s1 f2 m( T ) E) h7 `2 p/ P9 y6 ]! X 1、所需基础知识回顾( Y0 {2 f% k! j1 Z/ X4 @+ s" X 由参考手册可知,GPIO端口每个位可以由软件分别配置为: 输入浮空" Y) _; A% d/ v# n, x 输入上拉 输入下拉 模拟输入 推挽式输出 推挽式复用功能- `2 x& P* H8 O! {4 }+ `% E 开漏复用功能 每个GPIO端口由七个寄存器来控制,分别为: 端口配置低寄存器(GPIOx_CRL)& u+ ?. d9 D% S l# a; J+ a2 A 端口配置高寄存器(GPIOx_CRH) 端口输入数据寄存器(GPIOx_IDR) 端口输出数据寄存器(GPIOx_ODR)4 b- b: `' ]2 J, g3 ~ 端口位设置/清除寄存器(GPIOx_BSRR); e! G0 V r( D7 ` 端口位清除数据寄存器(GPIOx_BRR)9 b2 v- h2 \9 p7 o9 \8 N1 V 端口配置锁定寄存器(GPIOx_LCKR) 注:其中GPIOx_CRL配置GPIOx_Pin0——GPIOx_Pin7引脚,GPIOx_CRH配置GPIOx_Pin8——GPIOx_Pin15引脚(这个必须要明白) - G2 h1 e$ @: `& p, Z& _9 n& F 逻辑运算" u6 @' l2 E* ]$ }0 ?8 t 左移运算 Value << num num:指定要移位值Value移动的位数% x9 j. y) {% x P& r) i- b+ t$ v 丢弃最高位,0补在最低位 左移一位相当于乘以2的1次方,左移n位相当于乘以2的n次方 例如:8<<28,即:8向左移动28位 与运算:1 & A = A ;可得出1跟任意数进行”与“运算不会改变其值2 c1 m# E7 {+ X4 a* R: u STM32寄存器描述 GPIOx_CRL寄存器,也就是低位寄存器。配置:Pin0—Pin7。图一如下:+ ^& O P* i3 z) M2 ^ GPIOx_CRH寄存器,也就是高位寄存器.配置:Pin8—Pin15。图二如下:$ T7 }5 g0 a2 K6 r. Y4 |8 j0 ? ; E, q( f1 c X: {9 J$ x 也就是一个GPIOx端口分为GPIOx_CRL寄存器和GPIOx_CRH寄存器,其中MODE0[1:0]和CNF7[1:0]一起表示Pin0的配置值,以下依此类推,MODE7[1:0]和CNF7[1:0]一起表示Pin7的配置值。 I5 K' d2 z# W% p/ r! X! H0 u: h 2、代码详解' ^/ P2 e5 x- p! \4 T" u 对于文章开头提到的两个宏定义分开两条解释,SDA_IN()如下:1 ]* u# A9 W% N* \% C) G/ } #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}3 O; m- ]5 _) s8 c * ]2 s4 k3 H- y v- N 其中GPIOB->CRL&=0X0FFFFFFF 等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF: |9 G3 F, V- A( {' y 含义为:由于上述提到的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) 由于u32强制类型转换为32位,则8的二进制为:0000 0000 0000 0000 0000 0000 0000 1000; l$ D+ e# x& y" G% E! ^( p9 C 将8左移28位得:1000 0000 0000 0000 0000 0000 0000 0000! R0 J6 |: ?+ U. W g8 p* s4 u 由左移之后的数据可知:将31、30、29、28位设置为1000。下面对上图中的CNF和MODE解析- z5 c) H! [& P& _5 r( j. I l; v 即将PB的CRL寄存器中29和28位设置为00,由图一可知,其中29位MODE7[1]的值=0;28位MODE7[0]的值为0;图三如下: 0 s- Z7 z+ N, j+ g0 s 0 {- b( z' M; H" g) l! }' m9 T 由上图得知:29:28位配置为00,由下表得知,MODE1和MODE0为00时为输入模式。图四如下:% M$ {' K8 _" \& x; i, Q/ o% }/ v 5 c6 U1 t6 s f$ c% D4 ~: m' j - x# f0 u. Z( q ( L$ ?) P- D+ t2 \; ~9 X 接下来分析31:30位的值:10,由图一可知,其中31位CNF7[1]的值=1;30位CNF7[0]的值为0。图五如下: ; c7 l# h- y: e, x/ E 由29:28位得知:为输入模式,且31:30位的值为10,对照图四亦可得出,即为上下拉输入模式 SDA_OUT()如下:, A6 {7 s6 Y- j* ` #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;} 其中(GPIOB->CRL) &= 0X0FFFFFFF等价于 (GPIOB->CRL) = (GPIOB->CRL) & 0X0FFFFFFF 其含义为:把PB端口的CRL寄存器的第28、29、30和31位清空,把GPIOB端口中的CNF7[1:0]和MODE7[1:0]置零 1 p$ A. G( U+ p$ u0 p8 \- R! T* X) f! { 其中GPIOB->CRL |= (u32)3<<28 等价于 GPIOB->CRL = GPIOB->CRL | (u32)(3<<12). x8 a. o' \0 U. Y* j 3转换为2进制为:0000 0000 0000 0000 0000 0000 0000 00116 r0 A( g$ l/ Y" ]4 N 将3左移28位得:0011 0000 0000 0000 0000 0000 0000 0000 分析左移之后的数据为,将31、30、29、28位设置为00 11 PB的CRL寄存器中29和28位设置为11,其中29位MODE7[1]的值=1;28位MODE7[0]的值为1。即为输出模式,最大速度为50Mhz。 # x3 M! y. K3 a8 ^ 2 I. k$ m/ i/ r6 M3 M PB的CRL寄存器中31和30位设置为00,其中31位CNF7[1]的值=0;30位CNF7[0]的值为0;由图五可知:通用推挽式输出模式。 ————————————————1 P; k2 m9 H. a8 s% O( a 版权声明:枫华雪悦v/ O7 `6 {" D' p; _, M% r# I3 ~3 U% S. Q3 Y 如有侵权请联系删除 ' i" Z0 D- p5 r5 P3 b8 x2 Z |
【MCU实战经验】+STM32F107的USB使用
基于STM32F103两轮平衡小车设计(开源)
STM32固件库分享,超全系列整理
STM32F107VCT6官方原理图和PCB
【福利】用STM32库的朋友有福了:STM32F10x_StdPeriph_Lib_V3.5.0chm...
小马哥STM32F103开源小四轴RoboFly全部资料大放送
基于STM32F10xx存储器和系统架构经验分享
基于STM32F1的CAN通信之BH1750
基于STM32F1的CAN通信之OLED
基于STM32F1的CAN通信之之串口IAP