一.I2C与MPU基本信息
1.I2C信息
定义
2C总线是飞利浦公司开发的一种通用总线
有两根通信线:SCL时钟线;SDA数据线
I2C通信是同步,半双工的通信线,带数据应答
支持挂载多设备(一主多从\多主多从).一主多从:单片机主导I2C总线,挂载在总线上的都是从机,
从机只有被点名之后才能控制总线.多主多从:就是任何一个模块都可以作为主机控制总线
2C从机地址: 1101000 (AD0 = 0)
1101001(AD0 = 1)
16进制从机地址就是:(0x68<<1) | 1/0,意思就是将从机地址左移一位(0xD0).
0xD(0/1):这是加上读写位的从机地址,0xD0就是写地址,0xD1就是读地址
2.MPU6050信息
定义
MPU6050是一个六轴姿态传感器,常用于平衡车、飞行器等检测自身姿态的场景。
3轴加速度计:测量X、Y、Z轴的加速度
3轴陀螺仪传感器:测量X、Y、Z轴的角速度
MPU6050参数
16位ADC采集传感器模拟信号,范围:-32768 ~ 32767(AD与加速度以及陀螺仪成比例)
加速度计满量程:+-2,+-4,+-8,+-16(g) 也就是重力加速度 1g=9.8m/s*s
陀螺仪满量程:+-250,+-500,+-1000,+-2000(°/s)
可配置数字低通滤波器、时钟源、采样分频
二.I2C通信基本流程
1.I2C时序基本单元
开始条件:SCL处于高电平期间,SDA从高电平下降到低电平
终止条件:SCL处于高电平期间,SDA从低电平到高电平
发送字节(高位先行):SCL处于低电平期间,主机将数据放到SDA上,然后SCL置高电平,从机读取数据,这时SDA不许变化,当SCL数据被读完之后下降至低电平.依次进行8次(一个字节).如果发生中断SDA,SCL都暂停变化等待主机完成.
接收字节:主机放手,SCL处于低电平期间,从机发数据,然后SCL之高电平,主机读数据,这是SDA不许变化,,当SCL数据被读完后置低电平,依次进行8次
发送应答:主机在接受字节之后,从机下一个数据之前,回应从机,0表示应答,1表示非应答
接收应答:主机发送完字节之后,在下一次发送之前,判断从机是否应答,0表示应答,1表示非应答
2.MPU6050时序基本单元
指定地址写:就是在从机内部的指定寄存器中写入指定数据(从机地址-从机地址下寄存器-指定数据)。
时序开始之后,有8位,从机地址7位,读写位1位,加起来是一个字节
发送从机地址就是确定从机对象,发送读写位就是接下来是写入(低电平0)还是读出(高电平1)
当前地址读:对于指定从机设备,在当前地址指针的从机寄存器地址下,读取寄存器地址数据.(从机地址-当前地址-当前地址数据)
//注:当前地址就是上一次指定地址写地址+1的地址(上一个是0x1A,当前就是0x1B)
指定地址读:对于指定设备,在指定地址下,读取从机地址的指定数据(指定地址写+当前地址读)
就是指定的从机设备下写入从机内的寄存器中,之后再次进入从机设备,就能直接读当前寄存器的值了.
因为地址指针读后会自增所以可以在连续的地址下写入或读取多个数据如果你只想读一个地址,那么应答位要没有应答
3.流程结构
I2C.c
GPIO初始化
6个时序基本单元(开始,终止,发送字节,接收字节,发送应答,接收应答)
MPU.c
指定地址读,指定地址写,写寄存器对芯片控制,读寄存器得到传感器数据
main.c
调用MPU6050模块(初始化、拿到数据、显示数据)
三.代码实现
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);
}
}
MyI2C.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
//I2C写SCL引脚电平
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue); //根据BitValue,设置SCL引脚的电平
Delay_us(10); //延时10us,防止时序频率超过要求
}
//I2C写SDA引脚电平
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue); //根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性
Delay_us(10); //延时10us,防止时序频率超过要求
}
//I2C读SDA引脚电平
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11); //读取SDA电平
Delay_us(10); //延时10us,防止时序频率超过要求
return BitValue; //返回SDA电平
}
//I2C初始化
void MyI2C_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
/*GPIO初始化*/
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); //将PB10和PB11引脚初始化为开漏输出
/*设置默认电平*/
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11); //设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}
//I2C起始
void MyI2C_Start(void)
{
MyI2C_W_SDA(1); //释放SDA,确保SDA为高电平
MyI2C_W_SCL(1); //释放SCL,确保SCL为高电平
MyI2C_W_SDA(0); //在SCL高电平期间,拉低SDA,产生起始信号
MyI2C_W_SCL(0); //起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
}
//I2C终止
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0); //拉低SDA,确保SDA为低电平
MyI2C_W_SCL(1); //释放SCL,使SCL呈现高电平
MyI2C_W_SDA(1); //在SCL高电平期间,释放SDA,产生终止信号
}
//I2C发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i ++) //循环8次,主机依次发送数据的每一位
{
MyI2C_W_SDA(Byte & (0x80 >> i)); //使用掩码的方式取出Byte的指定一位数据并写入到SDA线
MyI2C_W_SCL(1); //释放SCL,从机在SCL高电平期间读取SDA
MyI2C_W_SCL(0); //拉低SCL,主机开始发送下一位数据
}
}
//I2C接收一个字节
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t i, Byte = 0x00; //定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
MyI2C_W_SDA(1); //接收前,主机先确保释放SDA,避免干扰从机的数据发送
for (i = 0; i < 8; i ++) //循环8次,主机依次接收数据的每一位
{
MyI2C_W_SCL(1); //释放SCL,主机机在SCL高电平期间读取SDA
if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);} //读取SDA数据,并存储到Byte变量
//当SDA为1时,置变量指定位为1,当SDA为0时,不做处理,指定位为默认的初值0
MyI2C_W_SCL(0); //拉低SCL,从机在SCL低电平期间写入SDA
}
return Byte; //返回接收到的一个字节数据
}
//I2C发送应答位
void MyI2C_SendAck(uint8_t AckBit)
{
MyI2C_W_SDA(AckBit); //主机把应答位数据放到SDA线
MyI2C_W_SCL(1); //释放SCL,从机在SCL高电平期间,读取应答位
MyI2C_W_SCL(0); //拉低SCL,开始下一个时序模块
}
//I2C接收应答位
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t AckBit; //定义应答位变量
MyI2C_W_SDA(1); //接收前,主机先确保释放SDA,避免干扰从机的数据发送
MyI2C_W_SCL(1); //释放SCL,主机机在SCL高电平期间读取SDA
AckBit = MyI2C_R_SDA(); //将应答位存储到变量里
MyI2C_W_SCL(0); //拉低SCL,开始下一个时序模块
return AckBit; //返回定义应答位变量
}
MyI2C.h
#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
#include "stm32f10x.h" // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"
//宏定义从机地址
#define MPU6050_ADDRESS 0xD0 //宏定义从机MPU6050地址
//指定地址写
void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)
{
MyI2C_Start ();//开始
MyI2C_SendByte (MPU6050_ADDRESS);//指定从机MPU6050地址
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);//指定从机MPU6050地址
MyI2C_ReceiveAck();//接收应答位
MyI2C_SendByte (RegAddress);//指定从机寄存器地址
MyI2C_ReceiveAck();//接收应答位
MyI2C_Start ();//重新开始
MyI2C_SendByte (MPU6050_ADDRESS | 0x01);//指定从机MPU6050地址,0x01表示我要读数据
MyI2C_ReceiveAck();//接收应答位
Data = MyI2C_ReceiveByte();//Data获取从机寄存器数据
MyI2C_SendAck(1);//发送应答位,不应答
MyI2C_Stop ();
return Data;
}
//定义MPU6050函数
void MPU6050_Init(void)
{
MyI2C_Init ();
//初始化从机写入寄存器地址,需要的时候直接主函数调用读取寄存器地址就可以获取寄存器地址下的数据
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