1. MPU6050传感器简介
智能手环需要实时检测人体姿态,如加速度和角速度等。本项目使用MPU6050传感器来检测人体姿态。MPU6050传感器模块是一种六轴传感器模块,采用InvenSense 公司的MPU6050 作为主芯片,能同时检测三轴加速度、三轴陀螺仪(三轴角速度)的运动数据以及温度数据。
利用MPU6050 芯片内部的DMP 模块(Digital Motion Processor 数字运动处理器),可对传感器数据进行滤波、融合处理,它直接通过I2C 接口向主控器输出姿态解算后的姿态数据,降低主控器的运算量。其姿态解算频率最高可达200Hz,非常适合智能手环的姿态检测。
图1 DMP模块
1.1 引脚功能说明
DMP模块引出的8 个引脚功能说明,如下表所示;
序号 |
引脚名称 |
说明 |
1 |
VCC |
3.3/5V 电源输入 |
2 |
GND |
地线 |
3 |
SCL |
I2C 从时钟信号线SCL (模块上已接上拉电阻) |
4 |
SDA |
I2C 从数据信号线SDA (模块上已接上拉电阻) |
5 |
XDA |
I2C 主串行数据信号线,用于外接传感器(模块上已接上拉电阻) |
6 |
XCL |
I2C 主串行时钟信号线,用于外接传感器(模块上已接上拉电阻) |
7 |
AD0 |
从机地址设置引脚 接地或悬空时, 地址为: 0x68 接VCC 时,地址为:0x69 |
8 |
INT |
中断输出引脚 |
其中的SDA/SCL、XDA/XCL 通讯引脚分别为两组I2C 信号线。当模块与外部主机通讯时,使用SDA/SCL,如与STM32 芯片通讯;而XDA/XCL 则用于MPU6050 芯片与其它I2C传感器通讯时使用,例如使用它与磁场传感器连接,MPU6050 模块可以把从主机SDA/SCL 接收的数据或命令通过XDA/XCL 引脚转发到磁场传感器中。
但实际上这种功能控制麻烦且效率低,一般会直接把磁场传感器之类的I2C传感器直接与MPU6050 挂载在同一条总线上(即都连接到SDA/SCL),使用主机直接控制。
图2 主机控制
1.2 MPU6050 模块的特性参数
图3 MPU6050 模块参数
从表中还可了解到传感器的加速度及陀螺仪的采样频率分别为1000Hz 及8000Hz,它们是指加速度及角速度数据的采样频率,我们可以使用STM32 控制器把这些数据读取出来然后进行姿态融合解算,以求出传感器当前的姿态(即求出偏航角、横滚角、俯仰角)。而如果使用传感器内部的DMP 单元进行解算,它可以直接对采样得到的加速度及角速度进行姿态解算,解算得到的结果再输出给STM32 控制器,即STM32 无需自己计算,可直接获取偏航角、横滚角及俯仰角,该DMP 每秒可输出200 次姿态数据。
1.3 硬件设计
用杜邦线把STM32 开发板与该MPU6050 模块连接起来,如下图所示;
图4 模块连接
详细的管脚连接,如下表所示;
序号 |
引脚名称 |
与开发板连接 |
1 |
VCC |
接3.3V或5V |
2 |
GND |
GND |
3 |
SCL |
PB6 |
4 |
SDA |
PB7 |
5 |
AD0 |
悬空或接地 |
6 |
INT |
悬空 |
2. 软件实现
1)驱动移植。将提供好的硬件驱动程序”mpu6050.c”、”mpu6050.h”、”bsp_i 2c.c”、”bsp_i2c.h”文件移植到工程中。
图5 mpu6050
2)硬件初始化。初始化硬件主要包括三部分,LCD初始化、I2C总线初始化和MPU6050传感器初始化。
//LCD 初始化
ILI9341_Init ();
//I2C初始化
i2c_GPIO_Config();
//MPU6050初始化
MPU6050_Init();
LCD初始化调用函数ILI9341_Init (),该函数前面已经讲解,这里不在叙述。I2C总线初始化调用函数i2c_GPIO_Config(),该函数位于I2C驱动文件bsp_i2c.c中,需要注意的是使用该函数不要忘记添加头文件,如下所示;
#include "bsp_i2c.h"
i2c_GPIO_Config()函数源码如下,该函数主要功能是配置I2C总线管脚。
void i2c_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_I2C_PORT, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出 */
GPIO_Init(GPIO_PORT_I2C, &GPIO_InitStructure);
/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
i2c_Stop();
}
MPU6050_Init()函数位于MPU6050.c文件中,使用时需要添加头文件“MPU6050.h”,其源码如下所示;
void MPU6050_Init(void)
{
int i=0,j=0;
//在初始化之前要延时一段时间,若没有延时,则断电后再上电数据可能会出错
for(i=0;i<1000;i++)
{
for(j=0;j<1000;j++)
{
;
}
}
MPU6050_WriteReg(MPU6050_RA_PWR_MGMT_1, 0x00);
MPU6050_WriteReg(MPU6050_RA_SMPLRT_DIV , 0x07);
MPU6050_WriteReg(MPU6050_RA_CONFIG , 0x06);
MPU6050_WriteReg(MPU6050_RA_ACCEL_CONFIG , 0x00);
MPU6050_WriteReg(MPU6050_RA_GYRO_CONFIG, 0x18);
}
用于初始化MPU6050,如设置陀螺仪采样率、设置低通滤波器、配置加速度传感器、设置陀螺仪自检及测量范围等。
3)硬件初始化完成以后,读取MPU6050的ID。使用函数MPU6050ReadID(),函数源码如下所示;
uint8_t MPU6050ReadID(void)
{
unsigned char Re = 0;
MPU6050_ReadData(MPU6050_RA_WHO_AM_I,&Re,1); //读器件地址
if(Re != 0x68)
{
// printf("MPU6050 dectected error!\r\n检测不到MPU6050模块。");
return 0;
}
else
{
// printf("MPU6050 ID = %d\r\n",Re);
return 1;
}
}
若返回1,则表示读取成功。读取成功以后即可进行下一步操作-读取加速度、角速度及温度的原始值。
4)获取加速度、角速度和温度的原始值
① 获取加速度值调用MPU6050ReadAcc()函数,函数原型如下所示;
void MPU6050ReadAcc(short *accData)
{
u8 buf[6];
MPU6050_ReadData(MPU6050_ACC_OUT, buf, 6);
accData[0] = (buf[0] << 8) | buf[1];
accData[1] = (buf[2] << 8) | buf[3];
accData[2] = (buf[4] << 8) | buf[5];
}
此处,需要定义一个short类型的数组变量,用来接收加速度值。
② 获取角速度值调用MPU6050ReadGyro()函数,函数原型如下所示;
void MPU6050ReadGyro(short *gyroData)
{
u8 buf[6];
MPU6050_ReadData(MPU6050_GYRO_OUT,buf,6);
gyroData[0] = (buf[0] << 8) | buf[1];
gyroData[1] = (buf[2] << 8) | buf[3];
gyroData[2] = (buf[4] << 8) | buf[5];
}
此处,同样需要定义一个short类型的数组变量,用来接收角速度值。
③ 温度获取使用MPU6050_ReturnTemp()函数,函数原型如下所示;
void MPU6050_ReturnTemp(short*Temperature)
{
short temp3;
u8 buf[2];
MPU6050_ReadData(MPU6050_RA_TEMP_OUT_H,buf,2); //读取温度值
temp3= (buf[0] << 8) | buf[1];
*Temperature=(((double) (temp3 + 13200)) / 280)-13;
}
4)主函数代码,如下所示;
int main(void)
{
short Accel[3];
short Gyro[3];
short Temp;
char temp_num[4];
//LCD 初始化
ILI9341_Init ();
//I2C初始化
i2c_GPIO_Config();
//MPU6050初始化
MPU6050_Init();
//检测MPU6050
if (MPU6050ReadID() == 1)
{
while(1)
{
MPU6050ReadAcc(Accel);
int2str(Accel[0],temp_num);
ILI9341_DispString_EN ( 20, 20, "Accel->x=", macBLUE2, macRED );
ILI9341_DispString_EN ( 90, 20, temp_num, macBLUE2, macRED );
int2str(Accel[1],temp_num);
ILI9341_DispString_EN ( 20, 40, "Accel->y=", macBLUE2, macRED );
ILI9341_DispString_EN ( 90, 40, temp_num, macBLUE2, macRED );
int2str(Accel[2],temp_num);
ILI9341_DispString_EN ( 20, 60, "Accel->z=", macBLUE2, macRED );
ILI9341_DispString_EN ( 90, 60, temp_num, macBLUE2, macRED );
MPU6050ReadGyro(Gyro);
int2str(Gyro[0],temp_num);
ILI9341_DispString_EN ( 20, 100, "Gyro->x=", macBLUE2, macRED );
ILI9341_DispString_EN ( 80, 100, temp_num, macBLUE2, macRED );
int2str(Gyro[1],temp_num);
ILI9341_DispString_EN ( 20, 120, "Gyro->y=", macBLUE2, macRED );
ILI9341_DispString_EN ( 80, 120, temp_num, macBLUE2, macRED );
int2str(Gyro[2],temp_num);
ILI9341_DispString_EN ( 20, 140, "Gyro->z=", macBLUE2, macRED );
ILI9341_DispString_EN ( 80, 140, temp_num, macBLUE2, macRED );
MPU6050_ReturnTemp(&Temp);
int2str(Temp,temp_num);
ILI9341_DispString_EN ( 20, 180, "Temp=", macBLUE2, macRED );
ILI9341_DispString_EN ( 60, 180, temp_num, macBLUE2, macRED );
Delay(0x15FFFF);
}
}
else
while(1);
}
主函数是对获取到数据进行在加工,将数值转换成字符,并打印输出到LCD中。LCD显示屏显示的数据是寄存器的原始数据。这些原始数据需要进一步进行换算才能得到我们角速度和加速度,关于换算方法,会在后面进行讲解。