10. I2C

1. 原理

1.1 I2C 框图

1.2 I2C基本结构

2. 软件 I2C读写MPU6050

2.1 接线图

2.2 实现代码

2.2.1 I2C.c

#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);
	
}

void MyI2C_Start(void)
{
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(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;
}

2.2.2 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();					//可设定一个值来接受Ack判断接下该进行什么操作
	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);					//最后一位置1,表示读
	MyI2C_ReceiveAck();
	Data = MyI2C_ReceiveByte();
	MyI2C_ReceiveAck();
	MyI2C_Stop();
	
	return Data;
}

void MPU6050_Init(void)
{
	MyI2C_Init();
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);				//解除睡眠,选择陀螺仪时钟
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);				//6个轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);				//采样分频为10
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);					//滤波参数给最大
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);			//陀螺仪选择最大量程
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);			//加速度计选择最大量程
}

uint8_t MPU6050_Get_ID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}


/*
	bief	分别获取加速度和螺旋仪的x轴,y轴,z轴
	para	AccX 加速度计的x轴数据 ...
	para	GyroX螺旋仪的x轴数据 ...
	retval	加速度和螺旋仪的x轴,y轴,z轴
	note	此处还有一个更加高效的方法,使用I2C读取多个字节的时序,从一个基地址开始连续读取一片的寄存器,此寄存器地址
			是连续的,从0x3B(MPU6050_Reg.h中可见详情)开始连续读取14个字节,在效率上可大大提升
*/
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t Data_H, Data_L;
	//获取加速度计x轴数据
	Data_H = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (Data_H << 8) |  Data_L;						//8位数据进行左移后,数据类型会自动转换
	
	Data_H = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (Data_H << 8) |  Data_L;
	
	Data_H = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (Data_H << 8) |  Data_L;
	
	//获取陀螺仪x轴数据
	Data_H = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (Data_H << 8) |  Data_L;
	
	Data_H = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (Data_H << 8) |  Data_L;
	
	Data_H = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (Data_H << 8) |  Data_L;
}

2.2.3 MPU6050_Reg.h

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

/*根据MPU6050数据手册定义*/
#define	MPU6050_SMPLRT_DIV		0x19
#define	MPU6050_CONFIG			0x1A
#define	MPU6050_GYRO_CONFIG		0x1B
#define	MPU6050_ACCEL_CONFIG	0x1C

#define	MPU6050_ACCEL_XOUT_H	0x3B
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40
#define	MPU6050_TEMP_OUT_H		0x41
#define	MPU6050_TEMP_OUT_L		0x42
#define	MPU6050_GYRO_XOUT_H		0x43
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48

#define	MPU6050_PWR_MGMT_1		0x6B
#define	MPU6050_PWR_MGMT_2		0x6C
#define	MPU6050_WHO_AM_I		0x75


#endif

2.2.4 main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
//#include "MyI2C.h"

int8_t ID;
int16_t AX, AY, AZ, GX, GY, GZ;

int main(void)
{
	OLED_Init();
	MPU6050_Init();
	
	ID = MPU6050_Get_ID();
	OLED_ShowString(1, 1, "ID:");
	//OLED_ShowString(1, 1, "ACCEL: GYRO:");
	OLED_ShowHexNum(1, 4, ID, 2);
	
	
	/*
	MyI2C_Start();
	MyI2C_SendByte(0XD0);					//1101 000 0	前七位代表从机地址,最后一位0代表写入操作
	uint8_t Ack = MyI2C_ReceiveAck();		//Ack = 0 表明有应答默认为高电平
	MyI2C_Stop();
	
	OLED_ShowNum(1, 1, Ack, 3);
	*/
	
	/*
	uint8_t ID = MPU6050_ReadReg(0X75);		//在地址为0x75的寄存器读出其内容:芯片ID号
	OLED_ShowHexNum(1, 1, ID, 2);			
	*/
	
	/*
	MPU6050_WriteReg(0X6B, 0X00);			//在电源管理寄存器1写入0x00解除睡眠模式
	MPU6050_WriteReg(0X19, 0XAA);	

	uint8_t VALUE = MPU6050_ReadReg(0X19);		
	OLED_ShowHexNum(1, 1, VALUE, 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);
	}
}

3. 硬件读写 MPU6050

3.1 接线图

3.2 序列图

3.3 实现代码

3.3.1 MPU6050.c

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"

#define MPU6050_ADDRESS			0XD0

//解决等待超时
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t Timeout;
	Timeout = 10000;
	while(I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)
	{
		Timeout --;
		if(Timeout == 0)
		{
			break;
		}
	}
	
}


void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	/*	此部分为软件方式实现写寄存器
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);	//发送目标MPU6050的地址
	MyI2C_ReceiveAck();					//可设定一个值来接受Ack判断接下该进行什么操作
	MyI2C_SendByte(RegAddress);			//发送目标MPU6050指定寄存器的地址
	MyI2C_ReceiveAck();
	MyI2C_SendByte(Data);				//若是想要进行写入多组数据,可循环下面两行代码
	MyI2C_ReceiveAck();
	MyI2C_Stop();
*/

	
	//以下为硬件方式实现写寄存器
	I2C_GenerateSTART(I2C2, ENABLE);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);		//发送数据自带应答过程,接受数据也自带发送应答过程
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	
	I2C_SendData(I2C2, RegAddress);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
	
	I2C_SendData(I2C2, Data);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	
	I2C_GenerateSTOP(I2C2, ENABLE);
	
}

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);					//最后一位置1,表示读
	MyI2C_ReceiveAck();
	Data = MyI2C_ReceiveByte();
	MyI2C_ReceiveAck();
	MyI2C_Stop();
	
	return Data;
	*/
	
	
	//以下为硬件方式实现写寄存器
	I2C_GenerateSTART(I2C2, ENABLE);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);		//发送数据自带应答过程,接受数据也自带发送应答过程
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	
	I2C_SendData(I2C2, RegAddress);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);			//此处选择TRANSMITTING或者TRANSMITTED都可以
	
	I2C_GenerateSTART(I2C2, ENABLE);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
	
	I2C_AcknowledgeConfig(I2C2, DISABLE);				//谁在ACK等于0,不应答,接下来的数据不读取
	I2C_GenerateSTOP(I2C2, ENABLE);
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);
	
	Data = I2C_ReceiveData(I2C2);
	I2C_AcknowledgeConfig(I2C2, ENABLE);				//方便后面实现指定地址收多个字节
	
	return Data;
}

void MPU6050_Init(void)
{
//	MyI2C_Init();
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	I2C_InitTypeDef I2C_InitStructure;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_ClockSpeed = 50000;			//50khz的时钟频率,小于400khz即可
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;					//时钟频率大于100khz时才有用,其他时间高电平比上低电平为1:1
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;			//STM32暂时不需要作为从机被使唤,所以此处地址随意设置
	I2C_Init(I2C2, &I2C_InitStructure);
	
	I2C_Cmd(I2C2, ENABLE);
	
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);				//解除睡眠,选择陀螺仪时钟
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);				//6个轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);				//采样分频为10
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);					//滤波参数给最大
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);			//陀螺仪选择最大量程
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);			//加速度计选择最大量程
}

uint8_t MPU6050_Get_ID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}


/*
	bief	分别获取加速度和螺旋仪的x轴,y轴,z轴
	para	AccX 加速度计的x轴数据 ...
	para	GyroX螺旋仪的x轴数据 ...
	retval	加速度和螺旋仪的x轴,y轴,z轴
	note	此处还有一个更加高效的方法,使用I2C读取多个字节的时序,从一个基地址开始连续读取一片的寄存器,此寄存器地址
			是连续的,从0x3B(MPU6050_Reg.h中可见详情)开始连续读取14个字节,在效率上可大大提升
*/
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t Data_H, Data_L;
	//获取加速度计x轴数据
	Data_H = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
	*AccX = (Data_H << 8) |  Data_L;						//8位数据进行左移后,数据类型会自动转换
	
	Data_H = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
	*AccY = (Data_H << 8) |  Data_L;
	
	Data_H = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*AccZ = (Data_H << 8) |  Data_L;
	
	//获取陀螺仪x轴数据
	Data_H = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*GyroX = (Data_H << 8) |  Data_L;
	
	Data_H = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
	*GyroY = (Data_H << 8) |  Data_L;
	
	Data_H = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	Data_L = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
	*GyroZ = (Data_H << 8) |  Data_L;
}

3.3.2 MPU6050_Reg.h

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

/*根据MPU6050数据手册定义*/
#define	MPU6050_SMPLRT_DIV		0x19
#define	MPU6050_CONFIG			0x1A
#define	MPU6050_GYRO_CONFIG		0x1B
#define	MPU6050_ACCEL_CONFIG	0x1C

#define	MPU6050_ACCEL_XOUT_H	0x3B
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40
#define	MPU6050_TEMP_OUT_H		0x41
#define	MPU6050_TEMP_OUT_L		0x42
#define	MPU6050_GYRO_XOUT_H		0x43
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48

#define	MPU6050_PWR_MGMT_1		0x6B
#define	MPU6050_PWR_MGMT_2		0x6C
#define	MPU6050_WHO_AM_I		0x75


#endif

3.3.3 main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
//#include "MyI2C.h"

int8_t ID;
int16_t AX, AY, AZ, GX, GY, GZ;

int main(void)
{
	OLED_Init();
	MPU6050_Init();
	
	ID = MPU6050_Get_ID();
	OLED_ShowString(1, 1, "ID:");
	//OLED_ShowString(1, 1, "ACCEL: GYRO:");
	OLED_ShowHexNum(1, 4, ID, 2);
	
	
	/*
	MyI2C_Start();
	MyI2C_SendByte(0XD0);					//1101 000 0	前七位代表从机地址,最后一位0代表写入操作
	uint8_t Ack = MyI2C_ReceiveAck();		//Ack = 0 表明有应答默认为高电平
	MyI2C_Stop();
	
	OLED_ShowNum(1, 1, Ack, 3);
	*/
	
	/*
	uint8_t ID = MPU6050_ReadReg(0X75);		//在地址为0x75的寄存器读出其内容:芯片ID号
	OLED_ShowHexNum(1, 1, ID, 2);			
	*/
	
	/*
	MPU6050_WriteReg(0X6B, 0X00);			//在电源管理寄存器1写入0x00解除睡眠模式
	MPU6050_WriteReg(0X19, 0XAA);	

	uint8_t VALUE = MPU6050_ReadReg(0X19);		
	OLED_ShowHexNum(1, 1, VALUE, 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);
	}
}

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值