
因为我们的读写操作都是依靠SCL和SDA的电平变化来达成的,所以直接封装好,然后再进行操作

不多bb,直接看代码吧
main.c
cpp
#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;
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);
}
}
myl2C.c
cpp
#include "stm32f10x.h" // Device header
#include "Delay.h"
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
Delay_us(10);
}
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
Delay_us(10);
}
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
Delay_us(10);
return BitValue;
}
void MyI2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}
//还记得吗怎么开始的,即SCL和SDA都是高电平,然后SDA先变为低电平然后SCL再变为低电平
void MyI2C_Start(void)
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
//还记得怎么停止的吗,此时SCL已经变为低电平了(由于是数据的跳变),由于SDA为数据位,不管其为高低电平
//先将其置为低电平,然后SCL变为高电平,然后SDA开始跳变为高电平
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
//对于SDA来说由于有电平的跳变,所以在开始和结束的时候,都要将其给规整到合适的位置
//发送数据,主机先将数据放到SDA上去,然后在由低电平跳跃到高电平,然后从机会接收数据,然后SCL关闭
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);
}
}
//由于是从高位开始发送数据,所以先从0x80开始去调用位置
//这个是怎么理解的(Byte & (0x80 >> i),就是取一个二进制位,然后在主机控制SCL高低电平,从机在高电平处取位
//比如你有个二进制位数10101010 这里有个10000000即(0x80)
//将两者按位置与即10101010
// 即 10000000
//很明显就只能得到了你这个二进制数据的高位即1
//对于接收的流程,说白了就是主机读取从机数据的流程
//就是从机将数据放在SDA线上,然后主机从低电平变为高电平
//然后此时从机将数据发送给主机,然后主机变为低电平,此时从机继续准备数据
//MyI2C_W_SDA(1);图中有一行这个代码是什么意思呢
//就是从机没有主动权将数据放在SDA上,只有当主机将SDA给释放了,才能有这个机会
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()){Byte |= (0x80 >> i);}
//这段代码很简单,如果此时读取的数据是1,则对byte进行或操作
//如果此时读取的数据是0,则此时输入0
MyI2C_W_SCL(0);
}
return Byte;
}
//当主机读取到从机的数据后,就会给从机发送信号,自己读取到了
//AckBit=1,表示从机继续发,AckBit=0,表示从机别发了
//然后继续主机控制SCL高电平,然后从机获取信息,然后主机控制高电平
void MyI2C_SendAck(uint8_t AckBit)
{
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
//当从机收到主机的数据后的反应,还是和之前所说的一样,从机是没有主动权的
//当从机想要在SDA上发送数据,只有一条路,就是主机先释放SDA
//所以可以看到主机先释放了SDA,然后主机高电平,从机告诉主机是否继续,然后主机控制SCL为低电平
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
cpp
#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
cpp
#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;
//通过mpu6050自动读取陀螺仪等等的位置信息
//然后传输到我们定义的函数里面
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
cpp
#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