一.硬件I2C基本信息
stm32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能
控制寄存器CR 数据寄存器DR 状态寄存器SR
硬件I2C流程与软件基本一致
二.硬件I2C基本流程
1.指定地址写
I2C_GenerateSTART();生成开始事件
I2C_CheckEvent();检查EV5事件是否发生
I2C_Send7bitAddress();发送7位从机地址以及1位读写位,(Transmitter)就是发送也就是最低位置0, (Receive)就是接收最低位置1
(注:发送应答和接收应答不需要处理,因为接收数据和发送数据都自带应答功能,如果应答错误,硬件就会通过标志位和中断提示我们)
I2C_CheckEvent();检查EV6事件
进入EV8_1事件,不需要等待,数据通过DR寄存器,直接发送数据,.I2C_SendData();
I2C_CheckEcent();检查EV8事件(字节正在发送)
然后进入下一个数据发送I2C_SendData();
之后I2C_Check();检查EV8_2事件(字节发送完毕)
之后终止时序I2C_GenerateStop();
2.指定地址读
I2C_GenerateSTART();生成开始事件
I2C_CheckEvent();检查EV5事件是否发生
I2C_Send7bitAddress();发送7位从机地址以及1位读写位,(Transmitter)就是发送也就是最低位置0, (Receive)就是接收最低位置1
I2C_CheckEvent();检查EV6事件
进入EV8_1事件,不需要等待,数据通过DR寄存器,直接发送数据,.I2C_SendData();
I2C_CheckEcent();检查EV8事件(字节发送完成)
I2C_GenerateSTART();再次生成开始事件
I2C_CheckEvent();再次检查EV5事件是否发生
I2C_Send7bitAddress();这次接收数据(Recrive)
I2C_CheckEvent();再次检查EV6事件
进入EV6_1事件提前给应答位和停止位,非应答和停止.(注:这里应答与软件相反,不应答(0)应答(1))
进入EV7事件等待数据发送I2C_CheckEvent();
I2C_ReceiveData();读取数据
恢复Ack应答给应答默认Ack=1
三.代码实现
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
int8_t AX,AY,AZ,GX,GY,GZ;
uint8_t ID;
int main(void)
{
OLED_Init ();
MPU6050_Init();
//显示MPU6050_GetID里面从机寄存器数据
OLED_ShowString(4,1,"ID:");
ID = MPU6050_GetID();
OLED_ShowHexNum (4,4,ID,2);
while(1)
{
//显示加速度XYZ轴,陀螺仪XYZ轴寄存器数据
MPU6050_GetData(&AX,&AY,&AZ,&GX,&GY,&GZ);
OLED_ShowSignedNum (1,1,AX,5);
OLED_ShowSignedNum (2,1,AY,5);
OLED_ShowSignedNum (3,1,AZ,5);
OLED_ShowSignedNum (1,8,GX,5);
OLED_ShowSignedNum (2,8,GY,5);
OLED_ShowSignedNum (3,8,GZ,5);
}
}
MPU6050.c
#include "stm32f10x.h" // Device header
#include "MPU6050_Reg.h"
//宏定义从机地址
#define MPU6050_ADDRESS 0xD0 //宏定义从机MPU6050地址
//封装等待事件
void MPU6050_WriteEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t Timerout;
Timerout = 10000;//定义一个时间结束的变量
while(I2C_CheckEvent(I2Cx,I2C_EVENT) != SUCCESS )
{
Timerout--;//时间自减,等待事件
if(Timerout == 0)
{
break;//当时间走完直接结束事件,不等了
}
}
}
//指定地址写
void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)
{
I2C_GenerateSTART(I2C2,ENABLE);//开启I2C
MPU6050_WriteEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//等待EV5事件完成
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter);//选择从机发送模式
MPU6050_WriteEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待EV6事件完成(发送)
I2C_SendData (I2C2,RegAddress);//发送寄存器地址
MPU6050_WriteEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING);//等待EV8事件完成(寄存地址输送)
I2C_SendData (I2C2,Data);//发送硬件数据
MPU6050_WriteEvent (I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待EV8_2事件完成(数据传输完成)
I2C_GenerateSTOP(I2C2,ENABLE);//停止I2C
}
//指定地址读
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
uint8_t Data;//接收数据
I2C_GenerateSTART(I2C2,ENABLE);//开启I2C
MPU6050_WriteEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//等待EV5事件完成
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter);//选择从机发送模式
MPU6050_WriteEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//等待EV6事件完成(发送)
I2C_SendData (I2C2,RegAddress);//发送寄存器地址
MPU6050_WriteEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED);//等待EV8_2事件完成(寄存地址输送)
I2C_GenerateSTART(I2C2,ENABLE);//开启I2C
MPU6050_WriteEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT);//等待EV5事件完成
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver);//选择从机接收模式
MPU6050_WriteEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//等待EV6事件完成(接收)
I2C_AcknowledgeConfig(I2C2,DISABLE);//在数据传输之前提前应答(选择不应答)
I2C_GenerateSTOP(I2C2,ENABLE );//因为数据发送过快提前停止
MPU6050_WriteEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED);//等待EV7事件完成(数据接收)
Data = I2C_ReceiveData (I2C2);//将接收的数据传给Data
I2C_AcknowledgeConfig(I2C2,ENABLE);//再次开启应答
return Data;
}
//定义MPU6050函数
void MPU6050_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd (RCC_APB1Periph_I2C2,ENABLE);//开启I2C时钟
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE);//开启GPIOB时钟
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD ;//复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;//引脚PB10PB11
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;//速度50
GPIO_Init(GPIOB,&GPIO_InitStructure);
//初始化I2C
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//应答默认应答
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//选择7位从机地址
I2C_InitStructure.I2C_ClockSpeed = 50000;//时钟速度这里我给的50000KHz
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//占空比我给的2:1
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//I2C模式
I2C_InitStructure.I2C_OwnAddress1 = 0x00;//自身地址从机地址才有效
I2C_Init(I2C2,&I2C_InitStructure);
//打开或关闭开关
I2C_Cmd (I2C2,ENABLE);
//初始化从机写入寄存器地址,需要的时候直接主函数调用读取寄存器地址就可以获取寄存器地址下的数据
MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);//电源管理寄存器1:关闭0x0关闭睡眠模式,1陀螺仪时钟
MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);//电源管理寄存器2:不待机
MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);//采样分频寄存器:0x00-0x09,控制传输速度,越大约快
MPU6050_WriteReg(MPU6050_CONFIG,0x06);//配置寄存器:给0x06是最平滑的滤波
MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);//陀螺仪配置寄存器:0x18是满量程
MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);//加速度配置寄存器:0x18满量程
}
//获取MPU6050_WHO_AM_I寄存器数据
uint8_t MPU6050_GetID(void)
{
return MPU6050_ReadReg (MPU6050_WHO_AM_I);//MPU6050_WHO_AM_I:从机寄存器编号
}
//获取加速度、陀螺仪角速度寄存器中数据
void MPU6050_GetData(int8_t *AccrX,int8_t *AccrY,int8_t *AccrZ,
int8_t *GyroX,int8_t *GyroY,int8_t *GyroZ)
{
uint8_t DataH,DataL;//定义一个接收高8位和低8位数据的变量
//加速度X轴
DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
*AccrX = (DataH << 8) | DataL;//将高低电平传给取地址*AccrX
//加速度Y轴
DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
*AccrY = (DataH << 8) | DataL;//将高低电平传给取地址*AccrY
//加速度Z轴
DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
*AccrZ = (DataH << 8) | DataL;//将高低电平传给取地址*AccrY
//陀螺仪X轴
DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
*GyroX = (DataH << 8) | DataL;//将高低电平传给取地址*GyroX
//陀螺仪Y轴
DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
*GyroY = (DataH << 8) | DataL;//将高低电平传给取地址*GyroY
//陀螺仪Z轴
DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
*GyroZ = (DataH << 8) | DataL;//将高低电平传给取地址*GyroY
}
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(int8_t *AccrX,int8_t *AccrY,int8_t *AccrZ,
int8_t *GyroX,int8_t *GyroY,int8_t *GyroZ);
#endif
MPU6050_Reg.h
#ifndef _MPU6050_REG_H
#define _MPU6050_REG_H
#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 //加速度X轴高8位
#define MPU6050_ACCEL_XOUT_L 0x3C //加速度X轴低8位
#define MPU6050_ACCEL_YOUT_H 0x3D //加速度Y轴高8位
#define MPU6050_ACCEL_YOUT_L 0x3E //加速度Y轴低8位
#define MPU6050_ACCEL_ZOUT_H 0x3F //加速度Z轴高8位
#define MPU6050_ACCEL_ZOUT_L 0x40 //加速度Z轴低8位
#define MPU6050_TEMP_OUT_H 0x41
#define MPU6050_TEMP_OUT_L 0x42
#define MPU6050_GYRO_XOUT_H 0x43 //陀螺仪X轴高8位
#define MPU6050_GYRO_XOUT_L 0x44 //陀螺仪X轴低8位
#define MPU6050_GYRO_YOUT_H 0x45 //陀螺仪Y轴高8位
#define MPU6050_GYRO_YOUT_L 0x46 //陀螺仪Y轴低8位
#define MPU6050_GYRO_ZOUT_H 0x47 //陀螺仪Z轴高8位
#define MPU6050_GYRO_ZOUT_L 0x48 //陀螺仪Z轴低8位
#define MPU6050_PWR_MGMT_1 0x6B //电源配置寄存器2
#define MPU6050_PWR_MGMT_2 0x6C //电源配置寄存器1
#define MPU6050_WHO_AM_I 0x75 //从机地址编号
#endif