目录
软件I2C读写MPU6050
MyI2C
写6个基本时序单元的函数,然后将这些函数拼接起来写 发送数据接收数据,发送应答接收应答函数。
#include "stm32f10x.h" // Device header
#include "DELAY.H"
void MyI2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;//开漏输出模式仍然可以输入,输入时,先输出1 再直接读取输入寄存器
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_W_SCL(uint8_t BitValue) //W 释放,输出
{
GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue); //BitAction:Bit_RESET;Bit_SET
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_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)); //为了获得某一位。传入参数ox....到函数里会变成1/0
MyI2C_W_SCL(1); //释放SCL,从机会读取SDA。
MyI2C_W_SCL(0); //除了终止的函数,其余的在最后把SCL拉低,为了方便拼接
}
}
uint8_t MyI2C_RecieveByte(void)
{
uint8_t Byte=0x00;
MyI2C_W_SDA(1); //主机释放SDA也相当于切换为输入模式,从机把数据放到SDA
uint8_t i;
for(i=0;i<8;i++)
{
MyI2C_W_SCL(1); //主机可以读取SDA
if(MyI2C_R_SDA()==1)
{
Byte|=(0x80>>i);
}
MyI2C_W_SCL(0);
}
return Byte;
}
void MyI2C_SendAck(uint8_t Ack)
{
MyI2C_W_SDA(Ack); //为了获得某一位。传入参数ox....到函数里会变成1/0
MyI2C_W_SCL(1); //释放SCL,从机会读取SDA。
MyI2C_W_SCL(0); //除了终止的函数,其余的在最后把SCL拉低,为了方便拼接
}
uint8_t MyI2C_RecieveAck(void)
{
uint8_t Ack;
MyI2C_W_SDA(1); //主机释放SDA也相当于切换为输入模式,从机把数据放到SDA
MyI2C_W_SCL(1); //主机可以读取SDA
Ack=MyI2C_R_SDA();
MyI2C_W_SCL(0);
return Ack;
}
MPU6050
初始化MPU6050将MyI2C封装,将发送数据接收数据,发送应答接收应答函数完成完整的I2C时序,进行对指定地址读,指定地址写。
#include "stm32f10x.h" // Device header
#include "MYI2C.H"
#define MPU6050_Address 0xD0
void MPU6050_WriteReg(uint8_t RegAddress,uint8_t data)
{
MyI2C_Start();
MyI2C_SendByte(MPU6050_Address);
MyI2C_RecieveAck();
MyI2C_SendByte(RegAddress);
MyI2C_RecieveAck();
MyI2C_SendByte(data);
MyI2C_RecieveAck();
MyI2C_Stop();
}
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
uint8_t data;
MyI2C_Start();
MyI2C_SendByte(MPU6050_Address);
MyI2C_RecieveAck();
MyI2C_SendByte(RegAddress);
MyI2C_RecieveAck();
MyI2C_Start(); //重新开始
MyI2C_SendByte(MPU6050_Address|0x01); //换成读
data=MyI2C_RecieveByte();
MyI2C_SendAck(1); //不传输数据了,将总线控制权交给主机
MyI2C_Stop();
return data;
}
void MPU6050_Init(void)
{
MyI2C_Init();
MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01); //解除睡眠 内部晶振000,陀螺仪001
MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0X00); //均不待机
MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09); //十分频
MPU6050_WriteReg(MPU6050_CONFIG,0x06); //110最平滑的滤波
MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18); //最大量程.16g
MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);
}
//一定要注意这里的符号
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; //16位无符号补码
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;
}
uint8_t MPU6050_Get_ID(void)
{
return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}
主函数
int16_t AX,AY,AZ,GX,GY,GZ;
MPU6050_Init();
ID=MPU6050_Get_ID();
OLED_ShowHexNum(4,1,ID,2);
while(1)
{
MPU6050_GetData(&AX,&AY,&AZ,&GX,&GY,&GZ); //要放在while循环里,要不然数据不会变化
OLED_ShowSignedNum(1,1,AX,5);
OLED_ShowSignedNum(2,1,AY,5);
OLED_ShowSignedNum(3,1,AZ,5); //2070/32768=x/16 x=1.010g
OLED_ShowSignedNum(1,8,GX,5);
OLED_ShowSignedNum(2,8,GY,5);
OLED_ShowSignedNum(3,8,GZ,5);
}
硬件I2C读写MPU6050
硬件读写不需要手动翻转电平。不需要上面的MyI2C.c和MyI2C.h文件,修改MPU6050读寄存器和写寄存器代码(根据上一篇的主机发送和主机接收来配置每一步)
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t Timeout=1000;
while(I2C_CheckEvent(I2Cx,I2C_EVENT) != SUCCESS)
{
Timeout--;
if(Timeout == 0)
{
break;
}
}
}
void MPU6050_WriteReg(uint8_t RegAddress,uint8_t data)
{
//软件函数内部有DELAY,运行完后对应的波形发送完毕可紧跟下一个函数
//这些函数非阻塞型,调用后需要等待相应标志位确保函数操作执行到位
I2C_GenerateSTART(I2C2,ENABLE);
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //STM32默认为从机,发送起始条件后变为主机
I2C_Send7bitAddress(I2C2,MPU6050_Address,I2C_Direction_Transmitter);
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
I2C_SendData(I2C2,RegAddress); //第一个数据写入DR会立即进入移位寄存器,第二个数据不用等
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING);
I2C_SendData(I2C2,data); //发多个字节时,中间的字节写入DR需要等待event8,最后一个要等待event8_2
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);
I2C_GenerateSTOP(I2C2,ENABLE);
}
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
uint8_t 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_TRANSMITTED); //可以改成tansmitted
//这里按照图246从头开始配置
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);
//读取多字节时,等待eve7读取DR,最后一个字节前eve7_1。
//读取一个字节时,发生event6_1 :恰好在eve6之后,要ack=0,stop=1。所以要现在配置
I2C_AcknowledgeConfig(I2C2,DISABLE);
I2C_GenerateSTOP(I2C2,ENABLE);
MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED); //RXNE=1,可以读取数据
data=I2C_ReceiveData(I2C2);
I2C_AcknowledgeConfig(I2C2,ENABLE); //为了方便多字节
return data;
}