MPU6050预期实现的功能
1.读取MPU6050的欧拉角
2.MPU6050串口传输数据,绘制成图像
3.MPU6050加速度功能进行计步
江科大MPU6050学习
1.基本工作原理
以MPU6050 6轴传感器为例子,包含3轴加速度计传感器可以测量x,y,z三个方向的加速度,3轴角速度传感器可以测量绕x,y,z三个轴的角加速度,加速度计具有静稳定性,角速度计具有动稳定性,两者通过互补滤波或者卡尔曼滤波就可以进行数据融合得到物体的姿态角也就是,Pitch(俯仰角),Roll(横滚角),Yaw(偏航角)
2.传感器参数
- MPU6050的内部有一个16位的ADC,可以进行-32768-+32767范围的量化
- 可以对加速度计量程从±2g-±16g进行配置,对角速度计从±250-±2000(°/sec)进行配置
- 可以通过AD0引脚配置传感器的地址:
AD0=0 传感器地址为1101000也就是0x68,考虑到I2C传输协议高七位为地址,最后一位为读写位,实际为0xD0
3.硬件电路
- 输入电压,由于内置一个LDO,所以输入电压范围在3.3v-5v之间
- AD0引脚配置地址,直接接地
- INT引脚配置中断事件
- SCL和SDA引脚通过上拉电阻可以直接和主机进行通信
- XDA和XCL引脚用于芯片外接磁力计和气压计扩展
4.内部框图
内部寄存器的介绍如下:
5.产品手册和芯片手册
建议选择内部晶振作为时钟源
The reset value is 0x00 for all registers other than the registers below.
- Register 107: 0x40.(107号是电源管理寄存器,默认0x40表示上电为休眠模式,需要手动解除)
- Register 117: 0x68.(117号是ID寄存器,默认0x68表明ID号)
6.代码部分
6.1 I2C通信部分
主要包括GPIO的初始化和6个时序单元(起始,终止,发送一个字节,接收一个字节,发送应答和接收应答)
#Hardware.c
//宏定义的技巧
#define SCL_PROT GPIOB
#define SCL_PIN GPIO_PIN_10
#define SDA_PROT GPIOB
#define SDA_PIN GPIO_PIN_11
//or
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)(x))
...
//or
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
Delay_us(10);//延时10us,防止时序频率超过要求
}
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
Delay_us(10); //延时10us,防止时序频率超过要求
}
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) //时钟初始化,GPIO初始化,设置开漏输出,设置完成置为高电平
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启时钟
...//GPIO初始化
GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);//释放总线
}
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也拉低,即为了占用总线,也为了方便总线时序的拼接
}
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0); //拉低SDA,确保SDA为低电平
MyI2C_W_SCL(1); //释放SCL,使SCL呈现高电平
MyI2C_W_SDA(1); //在SCL高电平期间,释放SDA,产生终止信号
}
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,主机开始发送下一位数据
}
}
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; //返回接收到的一个字节数据
}
void MyI2C_SendAck(uint8_t AckBit)
{
MyI2C_W_SDA(AckBit); //主机把应答位数据放到SDA线
MyI2C_W_SCL(1); //释放SCL,从机在SCL高电平期间,读取应答位
MyI2C_W_SCL(0); //拉低SCL,开始下一个时序模块
}
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; //返回定义应答位变量
}
int TEST(void)//点名应答
{
MyI2C_Init();
MyI2C_Start();
MyI2C_SendByte(0xD0);//0x110 1000 0
uint8_t ACK=MyI2C_ReceiveAck();//如果应答ACK为0
MyI2C_Stop();
OLED_ShowNum(1,1,ACK,3);
}
6.2 MPU6050部分
指定地址读,指定地址写,写寄存器对芯片进行配置,读寄存器对传感器进行配置
#include "MyI2C.h"
#define MPU6050_ADDRESS 0XD0
void MPU6050_Init(void)
{
MyI2C_Init();
}
void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)
{
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveACK(); // 注意从机接收到一个数据会发送ACK应答,主机不发送ACK应答
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveACK();
MyI2C_SendByte(Data);
MyI2C_ReceiveACK();
MyI2C_Stop();
}
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
uint_8 Data;
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveACK();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveACK();
//读写转换时,需要重新指定读写位
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS |0x01); //读时序
MyI2C_ReceiveACK();
//主机交出控制权
Data=MyI2C_ReceiveByte(RegAddress);
MyI2C_SendACK(1);//只需要读取一个字节,所以不给应答
MyI2C_Stop();
}
void MPU6050_Init(void)
{
MPU6050_WriteReg(PWR_MGMT_1,0x01);//解除睡眠模式 设置内部时钟
MPU6050_WriteReg(PWR_MGMT_2,0x00);//不需要待机
MPU6050_WriteReg(SMPLRT_DIV,0X09);//(9+1)=10分频率
MPU6050_WriteReg(CONFIG,0X06);//平滑滤波
MPU6050_WriteReg(GYRO_CONFIG,0X18);//满量程 ?讲的不对
MPU6050_WriteReg(ACCEL_CONFIG,0X18);//满量程 ?
}
//多返回值函数设计
//1.定义6个全局变量
//2.定义1个结构体指针
//3,指针传递
void MPU6050_GetData(int16_t *AccX,int16_t *Accy,
int16_t *AccZ,int16_t *GyrX,int16_t *GyrY,
int16_t *GyrZ)
{
uint16_t DataH,DataL;
DataH=MPU6050_ReadReg(ACCEL_XOUT_H);
DataL=MPU6050_ReadReg(ACCEL_XOUT_L);
*AccX=(DataH<<8) | DataL;//DataH,DataL补码表示的有符号数,所以直接赋值没有问题
...
}
//这里由于ACC和GYR地址是连着的可以连续读取接收数据,不需要使用循环
int TEST(void)
{
int16_t AX,AY,AZ,GX,GY,GZ;
OLED_Init();
MPU6050_Init();
while(1)
{
MPU6050_GetData(&AX,&AY,&AZ,&GX,&GY,&GZ);
OLED_ShowSignedNum(2,1,AX,5);
}
}