软件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已经是高电平了,先释放哪个都无所谓
但是在图示
还要兼容这里的重复起始条件SrSr开始,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);
}
}
如果发现错误或者需要改进的地方请私信或者评论