软件I2C读写MPU6050(文章最后附上源码)
编码
概况
-
首先建立通信层的.c和.h模块
-
在通信层里写好I2C底层的GPIO初始化
-
以及6个时序基本单元
- 起始、终值、发送一个字节、接收一个字节、发送应答、接收应答
-
写好I2C通信层之后,再建立MPU6050的.c和.h模块
-
基于I2C通信的模块,来实现指定地址读、指定地址写
-
再实现写寄存器对芯片进行配置
-
都寄存器得到传感器数据
-
-
最终在main.c里调用MPU6050的模块
-
初始化
-
拿到数据
-
显示数据
-
-
这就是程序的基本架构
步骤
-
初始化GPIO
-
引脚都要配置成开漏输出的模式
-
开漏输出模式仍然可以输入
-
输入时先输出1,再直接读取数据寄存器就行了
-
-
调用Setbits,把pin10和pin11都置高电平
-
这也初始化就完成了
-
调用MyI2C_Init函数
-
pb10和pb11两个端口就被初始化为开漏输出模式
-
然后释放总线
-
SCL和SDA处于高电平
-
此时I2C总线处于空闲状态
-
-
接下来就根据ppt时序波形来完成6个时序单元
-
初始化函数之前,定义函数,对操作端口的函数进行封装
-
void MyI2C_w_SCL(uint8_t BitValue)
-
函数里面调用WriteBit函数
-
后面再调用MyI2C_w_SCL函数,参数给1或0
-
就可以释放或拉低SCL
-
-
复制函数,定义SDA函数
-
再写一个读SDA函数uint8_t MyI2C_R_SDA(void)
-
写六个时序单元
-
开始的函数
-
在前面最好先释放SDA,这样保险一些
-
如果起始条件之前,SCL和SDA已经是高电平了,先释放哪个都无所谓
-
但是在图示
还要兼容这里的重复起始条件Sr -
Sr开始,SCl是低电平,SDA电平不敢确定
-
所以保险起见,我们趁SCL是低电平,先确保释放SDA,再释放SCL,这是SDA和SCL都是高电平
-
然后再拉低SDA拉低SCl
-
这样这个Start可以兼容起始条件和重复起始条件
-
-
结束的函数
- 为了确保再SCL高电平期间,SDA产生上升沿,先把SDA拉低
-
发送一个字节数据
-
接收一个字节数据
-
防止主机干扰从机写入数据
-
主机需要先释放SDA,释放SDA也相当于切换为输入模式
-
再释放SCL
-
在SCL低电平时,从机会把数据已经放到SDA上
-
如果从机想发1,就释放SDA,如果从机想发0,就拉低SDA
-
主机释放SCL,在SCL高电平期间,读取SDA
-
再拉低SCL,从机把下一位数据放在SDA上
-
-
发送应答
-
接收应答
-
编写MPU6050模块
-
调用MyI2C.h函数
-
初始化MPU6050,调用I2C_Init
-
之后在上面 先封装指定地址写和指定地址读 的时序
-
MPU6050_WriteReg指定地址写寄存器 参数是8位的指定地址(指定读写哪个寄存器,就是要读写寄存器的地址),另一个参数是要写入的数据
-
为了方便修改MyI2C_SendByte()的参数,并且突出它是从机地址,可以用宏定义替换一下这个数据
-
-
MyI2C_ReceiveAck应答位是可以不处理的
-
在接收一个字节函数里uint8_t MPU6050_ReadReg(uint8_t RegAdress)
-
如果只接受一个字节,应答位给1(非应答)
-
如果想继续接收数据,就要给0(应答)
-
如果想进阶为指定地址读多个字节,可以用for循环套起来,重读读取多次,最后一个应答给非应答1
-
写寄存器注意事项
-
首先解除芯片的睡眠模式
-
睡眠模式是电源管理寄存器1的SLEEP位
-
直接写入0x00 这样就可以解除睡眠模式了
-
在MPU初始化函数里配置电源管理寄存器
-
先用宏定义把寄存器的地址用一个字符串来表示
-
寄存器比较少的话可以直接在上面进行宏定义
-
如果比较多的话,可以再新建一个单独的头文件进行存放
-
再添加一个.h文件 MPU6050_Reg 存放宏定义
-
-
配置电源管理寄存器1 0x01
-
配置电源管理寄存器2 0x00
-
配置头文件里上面四个寄存器
-
配置完之后陀螺仪内部就在连续不断的进行数据转换了
-
输出的数据就存放在数据寄存器里
-
接下来想获取数据的话
-
只需要再写一个获取数据寄存器的函数
-
-
在初始化下面编写一个获取数据寄存器数据的函数
-
根据任务要求,函数需要返回6个int16_t数据
-
分别表示xyz的加速度值和陀螺仪值
-
但是c语言中,函数的返回值只能有一个
-
使用指针,进行变量的地址传递来实现多返回值
-
高8位左移8位或上低8位
-
-
MyI2C.c程序
#include "stm32f10x.h" // Device header
#include "Delay.h"
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_2, (BitAction)BitValue);
Delay_us(10);
}
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_3, (BitAction)BitValue);
Delay_us(10);
}
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3);
Delay_us(10);
return BitValue;
}
void MyI2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// GPIO_SetBits(GPIOA, GPIO_Pin_2 | GPIO_Pin_3);
}
void MyI2C_Start(void)
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i ++)
{
MyI2C_W_SDA(Byte & (0x80 >> i));
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t i, Byte = 0x00;
MyI2C_W_SDA(1);
for (i = 0; i < 8; i ++)
{
MyI2C_W_SCL(1);
if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
MyI2C_W_SCL(0);
}
return Byte;
}
void MyI2C_SendAck(uint8_t AckBit)
{
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t AckBit;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
AckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return AckBit;
}
MyI2C.h程序
#ifndef __MYI2C_H
#define __MYI2C_H
void MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);
#endif
MPU6050.c程序
#include "stm32f10x.h" // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"
#define MPU6050_ADDRESS 0xD0
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveAck();
MyI2C_SendByte(Data);
MyI2C_ReceiveAck();
MyI2C_Stop();
}
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
uint8_t Data;
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveAck();
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS | 0x01);
MyI2C_ReceiveAck();
Data = MyI2C_ReceiveByte();
MyI2C_SendAck(1);
MyI2C_Stop();
return Data;
}
void MPU6050_Init(void)
{
MyI2C_Init();
MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);
MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);
MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);
MPU6050_WriteReg(MPU6050_CONFIG, 0x06);
MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);
MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);
}
uint8_t MPU6050_GetID(void)
{
return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
uint8_t DataH, DataL;
DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
*AccX = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
*AccY = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
*AccZ = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
*GyroX = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
*GyroY = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
*GyroZ = (DataH << 8) | DataL;
}
MPU6050.h程序
#ifndef __MPU6050_H
#define __MPU6050_H
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);
void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);
#endif
main.c程序
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID;
int16_t AX, AY, AZ, GX, GY, GZ;
uint32_t light;
int main(void)
{
OLED_Init();
MPU6050_Init();
OLED_ShowString(1, 1, "ID:");
ID = MPU6050_GetID();
OLED_ShowHexNum(1, 4, ID, 2);
while (1)
{
MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
OLED_ShowSignedNum(2, 1, AX, 5);
OLED_ShowSignedNum(3, 1, AY, 5);
OLED_ShowSignedNum(4, 1, AZ, 5);
OLED_ShowSignedNum(2, 8, GX, 5);
OLED_ShowSignedNum(3, 8, GY, 5);
OLED_ShowSignedNum(4, 8, GZ, 5);
}
}
如果发现错误或者需要改进的地方请私信或者评论