I2C
I2C有两条总线线路,分别是SCL(时钟线)和SDA(数据线)。
I2C的时序非常重要:读数据和写数据的时序有点不一样,在写程序的时候就体现得到。
I2C的SCL高电平时有效,SDA高电平为1,低电平为0。
I2C的驱动:
I2C.h
#ifndef __I2c__H
#define __I2c__H
#include "stm32f4xx.h"
#define MPU_ADDRESS 0xd0
#define I2C_SCL_CLK RCC_AHB1Periph_GPIOB
#define I2C_SDA_CLK RCC_AHB1Periph_GPIOB
#define MPU_I2C_CLK RCC_APB1Periph_I2C1
#define I2C_SCL_PORT GPIOB
#define I2C_SDA_PORT GPIOB
#define MPU_I2C_PORT I2C1
#define I2C_SCL_PIN GPIO_Pin_6
#define I2C_SDA_PIN GPIO_Pin_7
#define I2C_SCL_SOURCE_PIN GPIO_PinSource6
#define I2C_SDA_SOURCE_PIN GPIO_PinSource7
#define SCL_I2C_AF GPIO_AF_I2C1
#define SDA_I2C_AF GPIO_AF_I2C1
void I2C_GPIO_config(void);
void MPU_I2c_config(void);
void MPU6050_I2C_Init(void);
uint8_t MPU6050_WriteByte(uint8_t addr,uint8_t data);
uint8_t MPU6050_ReadByte(uint8_t addr);
uint16_t MPU6050_Readbuffer(uint8_t addr,short*buffer,uint8_t num);
#endif
1.初始化I2C的引脚,开启复用功能
I2C需要配置成开漏输出
void I2C_GPIO_config(void)
{
RCC_AHB1PeriphClockCmd(I2C_SCL_CLK|
I2C_SDA_CLK,
ENABLE);
GPIO_PinAFConfig(I2C_SCL_PORT,I2C_SCL_SOURCE_PIN,SCL_I2C_AF);
GPIO_PinAFConfig(I2C_SDA_PORT,I2C_SDA_SOURCE_PIN,SDA_I2C_AF);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType=GPIO_OType_OD;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_Pin=I2C_SCL_PIN;
GPIO_Init(I2C_SCL_PORT, & GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin=I2C_SDA_PIN;
GPIO_Init(I2C_SDA_PORT, & GPIO_InitStruct);
}
2.初始化I2C
I2C_OwnAddress1(MCU的设备地址,只要和I2C上挂载的设备地址不一样即可)
记得使能I2C
void MPU_I2c_config(void)
{
RCC_APB1PeriphClockCmd(MPU_I2C_CLK,ENABLE);
//I2C_DeInit(MPU_I2C_PORT);
I2C_InitTypeDef I2C_InitStruct;
I2C_InitStruct.I2C_Mode=I2C_Mode_I2C;
I2C_InitStruct.I2C_ClockSpeed=100000;
I2C_InitStruct.I2C_DutyCycle=I2C_DutyCycle_2 ;
I2C_InitStruct.I2C_OwnAddress1=0x0a;
I2C_InitStruct.I2C_Ack=I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
I2C_Init(MPU_I2C_PORT,&I2C_InitStruct);
I2C_Cmd(MPU_I2C_PORT,ENABLE);
}
3.编写I2C写一个字节的函数
体现I2C读数据的时序
注意应答事件,这里很容易写错,有两个EV6,分别是作为发送端和接收端
uint8_t MPU6050_WriteByte(uint8_t addr,uint8_t data)
{
while(I2C_GetFlagStatus(MPU_I2C_PORT,I2C_FLAG_BUSY));//检查总线是否在忙
I2C_GenerateSTART(MPU_I2C_PORT ,ENABLE);//发送起始信号
while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);//应答
I2C_Send7bitAddress(MPU_I2C_PORT,MPU_ADDRESS,I2C_Direction_Transmitter);//发送挂在在I2C上设备的地址
while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS);//应答
I2C_SendData(MPU_I2C_PORT,addr);//发送写入设备的内部地址
while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS);//应答
I2C_SendData(MPU_I2C_PORT,data);//发送写入的数据
while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS);//应答
I2C_GenerateSTOP(MPU_I2C_PORT ,ENABLE);//发送停止信号
return 0;
}
4.编写I2C读入一个字节的函数
在读数据过程中,需要发送两次的起始信号,第一次发送起始信号是要告诉I2C上挂载的设备要操作它的内部地址,第二次发送起始信号告诉设备现在要读它
uint8_t MPU6050_ReadByte(uint8_t addr)
{
uint8_t readdata=0;
I2C_GenerateSTART(MPU_I2C_PORT ,ENABLE);//发送起始信号
while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);//应答
I2C_Send7bitAddress(MPU_I2C_PORT,MPU_ADDRESS,I2C_Direction_Transmitter);//发送设备地址
while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS);//应答
I2C_Cmd(MPU_I2C_PORT,ENABLE);//清除事件EV6
I2C_SendData(MPU_I2C_PORT,addr);//发送要读取设备的内部地址
while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCCESS);//应答
I2C_GenerateSTART(MPU_I2C_PORT ,ENABLE);发送第二次起始信号
while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);//应答
I2C_Send7bitAddress(MPU_I2C_PORT,MPU_ADDRESS,I2C_Direction_Receiver);//发送设备地址
while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCCESS);//应答
I2C_AcknowledgeConfig(MPU_I2C_PORT ,DISABLE);//失能应答
readdata=I2C_ReceiveData(MPU_I2C_PORT);//读取数据
while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_BYTE_RECEIVED)!=SUCCESS);//应答
I2C_GenerateSTOP(MPU_I2C_PORT ,ENABLE);//发送停止信号
I2C_AcknowledgeConfig(MPU_I2C_PORT ,ENABLE);//使能应答
return readdata;返回读取数据
}
5.编写I2C连续读取函数
和读取一个字节函数区别不大
uint16_t MPU6050_Readbuffer(uint8_t addr,short *buffer,uint8_t num)
{
while(I2C_GetFlagStatus(MPU_I2C_PORT,I2C_FLAG_BUSY));
I2C_GenerateSTART(MPU_I2C_PORT ,ENABLE);
while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);
I2C_Send7bitAddress(MPU_I2C_PORT, MPU_ADDRESS,I2C_Direction_Transmitter);
while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS);
I2C_Cmd(MPU_I2C_PORT,ENABLE);
I2C_SendData(MPU_I2C_PORT,addr);
while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS);
I2C_GenerateSTART(MPU_I2C_PORT ,ENABLE);
while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);
I2C_Send7bitAddress(MPU_I2C_PORT, MPU_ADDRESS,I2C_Direction_Receiver);
while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCCESS);
while(num)
{
if(num==1)
{
I2C_AcknowledgeConfig(MPU_I2C_PORT, DISABLE);
I2C_GenerateSTOP(MPU_I2C_PORT, ENABLE);
}
while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_BYTE_RECEIVED)!=SUCCESS);
{
*buffer=I2C_ReceiveData(MPU_I2C_PORT);
buffer++;
num--;
}//这个中括号表示连续读取
}
I2C_AcknowledgeConfig(MPU_I2C_PORT,ENABLE);
return 1;
}
MPU6050的驱动
MPU6050.h
#ifndef __MPU6050__H
#define __MPU6050__H
#include "stm32f4xx.h"
#include "I2c.h"
#include "USART.h"
#include "stdio.h"
#include "Delay.h"
#define MPU6050_RA_WHO_AM_I 0x75
#define MPU6050_RA_PWR_MGMI 0x6B
#define MPU6050_RA_SAP_RT_DIV 0x19
#define MPU6050_RA_CONFIG 0x1A
#define MPU6050_RA_ACCEL_CONFIG 0x1C
#define MPU6050_RA_GYRO_CONFIG 0x1B
#define MPU6050_RA_FIFO_ENABLE 0X23
#define MPU6050_RA_ACCEL0_MESUME 0X3B
#define MPU6050_RA_GRYO0_MESUME 0X43
uint8_t MPU6050_ReadID(void);
void MPU6050_Init(void);
void MPU6050_Write(uint8_t addr,uint8_t data);
uint8_t MPU6050_Read(uint8_t addr);
void MPU6050_ReadACCEL(uint8_t addr,short *acc_data,uint8_t num);
void MPU6050_ReadGYRO(uint8_t addr,short *gyro_data,uint8_t num);
#endif
1.MPU6050读字节函数 写字节函数
void MPU6050_Write(uint8_t addr,uint8_t data)
{
MPU6050_WriteByte(addr,data);
}
uint8_t MPU6050_Read(uint8_t addr)
{
uint8_t readdata=0;
readdata=MPU6050_ReadByte(addr);
return readdata;
}
uint16_t MPU6050_Readdata(uint8_t addr,short *buffer,uint8_t num)
{
MPU6050_Readbuffer(addr,buffer,num);
return 0;
}
2.初始化MPU6050
初始化MPU6050思路:
1.MPU6050上电后处于休眠状态,所以要解除休眠状态,“POWER MANAGEMENT 1”寄存器中sleep位置0,就解除休眠状态
2.SAMPLE RATE DIVIDER 频率采样分频器
3.配置外部引脚采样,初始化不需要你用到什么,选择不做任何配置
4.陀螺仪配置(开启自检,选择量程)
5.加速度计(开启自检,选择量程)
void MPU6050_Init(void)
{//MPU6050上电要预热
delay (500);
MPU6050_Write(MPU6050_RA_PWR_MGMI,0x00);
MPU6050_Write(MPU6050_RA_SAP_RT_DIV,0x07);
MPU6050_Write(MPU6050_RA_CONFIG,0x06);
MPU6050_Write(MPU6050_RA_ACCEL_CONFIG,0xF8);
MPU6050_Write(MPU6050_RA_GYRO_CONFIG,0xF8);
}
3.读取MPU6050 ID的函数
根据手册 ID在设备地址的基础上右移一位
uint8_t MPU6050_ReadID(void)
{
uint8_t ID=0;
ID=MPU6050_Read(MPU6050_RA_WHO_AM_I);
ID = ID>>1;
if(ID!=0x68)
{
printf("检测不到MPU6050\n");
printf("error ID=0x%x",ID);
return 0;
}
else
{
printf("检测到MPU6050\n");
return 1;
}
}
4.编写读取加速度计和陀螺仪的函数
void MPU6050_ReadACCEL(uint8_t addr,short*acc_data,uint8_t num)
{
short buf[6];
MPU6050_Readdata(addr,buf,num);
acc_data[0]=((buf[0]<<8)|(buf[1]));
acc_data[1]=((buf[2]<<8)|(buf[3]));
acc_data[2]=((buf[4]<<8)|(buf[5]));
}
void MPU6050_ReadGYRO(uint8_t addr,short *gyro_data,uint8_t num)
{
short buf[6];
MPU6050_Readdata(addr,buf,num);
gyro_data[0]=((buf[0]<<8)|buf[1]);
gyro_data[1]=((buf[2]<<8)|buf[3]);
gyro_data[2]=((buf[4]<<8)|buf[5]);
}
mian函数
int main(void)
{
//uint16_t num=200;
short Accel[3];
short Gryo[3];
USART_config();
printf("检测MPU6050是否存在\n");
MPU6050_I2C_Init();
MPU6050_Init();
MPU6050_ReadID();
while(1)
{
MPU6050_ReadACCEL(MPU6050_RA_ACCEL0_MESUME,Accel,6);
printf("加速度计X=%d Y=%d Z=%d\n",Accel[0],Accel[1],Accel[2]);
MPU6050_ReadGYRO(MPU6050_RA_GRYO0_MESUME,Gryo,6);
printf("陀螺仪X=%d Y=%d Z=%d\n",Gryo[0],Gryo[1],Gryo[2]);
delay (100);
}