GPIO模拟IIC通信(读取MPU6050)
开发板:STM32F407VET 传感器:正点原子MPU6050,IIC模块
GPIO模拟IIC通信的时候,需要注意输出一定要设置为开漏输出,外部上拉。这个是GPIO的输入输出图。因为开漏输出可以进行线与操作。推挽输出可能造成IO口短路,烧毁IO口。

首先了解IIC通信的时序,这里以MPU6050举例子。

这个图就是IIC的通信时序(所有的IIC设备都符合这个规则):我之前也写了一个IIC笔记(可能也有点抽象)
起始信号:SCL高电平,SDA下降沿。停止信号:SCL高电平,SDA上升沿。
等待应答:主设备写一个数据之后,从设备需要应答,主设备将SDA拉高,读取SDA电平(由于有线与的操作),读到低电平则认为子设备应答了,可以继续的发送数据。
主动应答:主设备读取一个数据之后,主动的将SDA拉低,从设备就知道你回应了它,他就会继续发送下一个字节的数据。如果SDA拉高,则从设备就会认为你不需要下一个数据,这就是非应答了。
非应答:读取一个数据后,主设备将SDA拉高,不管子设备了,然后就直接发送停止信号结束通信。
来看一下MPU6050的官方文档给的读写时序图

单字节的写入用伪代码的方式写出来就长这样子。
多字节的写入,只是在写入数据之后,主动的应答(主动拉低SDA),就可以发送下一个数据。长度是没有像限制的。(发送的长度是没有限制的,所以每次等待应答都要实际的读取SDA输入端的数据,不要认为时序图对了就可以了。可能会发生一些意外)

从上图可以看到,读取数据比写入稍微复杂一点点,但是按照时序图,将时序依次完成就可以读取到数据了。
下面是实际的代码部分
首先是一些宏,这些宏可以使得代码更简洁直观。(使用的标准库)
首先是初始化GPIO,GPIO一定要是开漏输出,外部上拉!!!其他的没有什么值得注意的
初始化GPIO之后,就可以根据时序,来写程序了。
应答这一部分得注意一下,应答是和读写相关的。等待应答(写入操作)需要返回是否应答,这个地方一定要读取SDA的电平状态(注:读取SDA电平之前一定要把SDA置高!!!)!虽然之前把SDA置高了,但是由于线与操作,SDA会被应答的从设备拉低。所以读到的电平状态为低电平。这里我直接从输入寄存器(IDR)中查看的输入端口电平。(当然乐意的话,输出的电平也可以直接用输出寄存器(ODR)去修改。)
应答之后,把SCL拉低,然后把SDA拉高,释放SDA,我三个应答的末尾都是这样子的。这样子三个应答之后,状态都是一样的。
GPIO模拟发送一个字节的电平状态。这没啥好说的。只需要记住只有SCL低电平的时候才能够修改SDA的电平状态
GPIO模拟发送一个字节的电平状态,再次强调:读取SDA电平之前一定要将SDA置高!
按照时序,将上面的部分组合起来即可,这里需要注意的是,仔细的检查各个部分之间的电平是不是存在冲突。
到了这里,已经是一个可以调用的API了。(每一次写入,后头都跟了一个等待子设备应答,认为返回0(SDA低电平)子设备应答了,返回1(SDA高电平)子设备没有应答)
地址问题:地址都是用的7位地址(0x68)。在使用的时候,高七位是设备地址,bit0是表示读写操作的位,0是写入,1是读取。所以写入的时候就直接左移一位(0x68<<1)表示写入操作。读取就左移一位,然后让最后一位置为1,这可以通过或(|)运算做到:(0x68<<1)|0x01
读取函数相对角为复杂一点点,从时序图上就看出啦了。按照时序图写下来就可以正常使用了。
这个时候就可以反过来写MPU6050的程序了!!!
MPU6050的寄存器详解可以看一下这个博客:
https://blog.csdn.net/m0_46278925/article/details/110568112
初始化完了之后,就可以从数据的寄存器中读取数据了。每个数据占两个字节,所以将高位左移八位,然后低位或操作,就可得到原始数据。在main函数中调用这个函数,就可以得到数据了!!
但是这样子是不是觉得不舒服呢,对连续的寄存器单个单个字节的读取,实在是太蠢了。总共需要12个字节的数据,从0x3B读取到0x48也只是需要14个字节数据。(中间有两个字节是温度数据)。
我们可以继续编写一次读取多个字节的函数。依旧是按照时序图来编写。这个时候,读取到数据之后就需要主动的拉低SDA应答从设备,告诉从设备继续发送下一个字节的数据,等读取完14个字节数据后,再给一个非应答信号。
这就是所有函数了!!!将这些函数罗列出来。
到了如果想要尝试GPIO模拟串口,并且拥有MPU6050这个传感器,copy这些代码就可以尝试运行了。这些代码都写在main.c中。延时函数和串口函数需要自己去写。当然我使用的GPIOC组的Pin8和Pin9做SDA和SCL。修改了引脚,那么读取SDA的电平信息的宏也需要改变。可以百度查阅GPIOx_IDR寄存器,当然也可以直接用标准库的函数去读取哈。
在串口中展示以下成果!!!下面这个图是将传感器拔掉,重新插上的时候的结果。自设没没有应答一定要立刻停止本次通信。

如果有需要这个代码的话,可以Q我。需要STM32的延时函数也可以Q我。

制作不易,点赞投币