在使用MPU6050时遇到的令人头疼的问题
1.整个程序在一开始运行就卡死在启动文件
首先看一下,Target设置选项下的Use MicroLIB选项是不是选上勾了。
如果这个设置也解决不了问题,那么你需要检查一下程序里,是否写了printf函数的接口函数。因为MPU6050里面有
用到了这个函数,如果你没写这个函数的接口函数,就会导致系统卡死在系统文件里。
printf函数的接口函数
///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USARTx, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return (ch);
}
2.while循环里的条件明明不满足了,程序却一致卡死在此循环里
检查一下,判断条件里的变量的类型是否正确。另外,如果这个变量在中断服务函数里面需要改变值,那么在定义这个变量的语句前应该加上关键字
volatile
volatile的具体描述参考
3.MPU6050在执行mpu_dmp_init()函数时,一直出现错误,且错误在run_self_test()
1)首先,看一下是不是因为你在程序初始化的时候,没有把MPU6050水平放置,至于为什么要水平放置呢?
因为在run_self_test()函数里面有一个mpu_run_self_test(gyro, accel)函数,这个函数上面有一个注释特别重要:
This function must be called with the device either face-up or face-down
* (z-axis is parallel to gravity).此函数必须在设备面朝上或面朝下时调用(z轴与重力平行)
2)如果照着1)的方法还是解决不了这个问题,那么请你检查一下run_self_test函数里面的具体内容是否和下面的一模一样:
//MPU6050自测试
//返回值:0,正常
// 其他,失败
u8 run_self_test(void)
{
int result;
//char test_packet[4] = {0};
long gyro[3], accel[3];
result = mpu_run_self_test(gyro, accel);
if (result == 0x3) //这个地方要的别注意,有的例程会把0X3错误的写为0X7
{
/* Test passed. We can trust the gyro data here, so let's push it down
* to the DMP.
*/
float sens;
unsigned short accel_sens;
mpu_get_gyro_sens(&sens);
gyro[0] = (long)(gyro[0] * sens);
gyro[1] = (long)(gyro[1] * sens);
gyro[2] = (long)(gyro[2] * sens);
dmp_set_gyro_bias(gyro);
mpu_get_accel_sens(&accel_sens);
accel[0] *= accel_sens;
accel[1] *= accel_sens;
accel[2] *= accel_sens;
dmp_set_accel_bias(accel);
return 0;
}else return 1;
}
4.mpu_dmp_get_data(&pitch,&roll,&yaw)函数的返回值不正确(一直不是0)
MPU6050读不出来数据
如果参考以上这篇文章还是无法帮助解决问题,那么不妨试一下,把模拟iic的这部分代码换一下,也许可能就是因为iic这部分程序不正确。
模拟IIC的代码:
#include "mpuiic.h"
#include "./systick/bsp_SysTick.h"
#define Soft_I2C_SDA GPIO_Pin_3
#define Soft_I2C_SCL GPIO_Pin_4
#define Soft_I2C_PORT GPIOB
#define MPU6050_GPIO_CLK RCC_APB2Periph_GPIOB
//IO方向设置 ---PB11
#define MPU_SDA_IN() {Soft_I2C_PORT ->CRL &= 0XFFFF0FFF;Soft_I2C_PORT ->CRL |= 8<<12;} //上拉/下拉 输入模式
#define MPU_SDA_OUT() {Soft_I2C_PORT ->CRL &= 0XFFFF0FFF;Soft_I2C_PORT ->CRL |= 7<<12;} //开漏输出 输出模式
/* 定义读写SCL和SDA的宏,已增加代码的可移植性和可阅读性 */
#if 1 /* 条件编译: 1 选择GPIO的库函数实现IO读写 */
#define MPU6050_I2C_SCL_1() GPIO_SetBits(Soft_I2C_PORT, Soft_I2C_SCL) /* SCL = 1 */
#define MPU6050_I2C_SCL_0() GPIO_ResetBits(Soft_I2C_PORT, Soft_I2C_SCL) /* SCL = 0 */
#define MPU6050_I2C_SDA_1() GPIO_SetBits(Soft_I2C_PORT, Soft_I2C_SDA) /* SDA = 1 */
#define MPU6050_I2C_SDA_0() GPIO_ResetBits(Soft_I2C_PORT, Soft_I2C_SDA) /* SDA = 0 */
#define MPU6050_I2C_SDA_READ() GPIO_ReadInputDataBit(Soft_I2C_PORT, Soft_I2C_SDA) /* 读SDA口线状态 */
#else /* 这个分支选择直接寄存器操作实现IO读写 */
/* 注意:如下写法,在IAR最高级别优化时,会被编译器错误优化 */
#define MPU6050_I2C_SCL_1() Soft_I2C_PORT->BSRR = Soft_I2C_SCL /* SCL = 1 */
#define MPU6050_I2C_SCL_0() Soft_I2C_PORT->BRR = Soft_I2C_SCL /* SCL = 0 */
#define MPU6050_I2C_SDA_1() Soft_I2C_PORT->BSRR = Soft_I2C_SDA /* SDA = 1 */
#define MPU6050_I2C_SDA_0() Soft_I2C_PORT->BRR = Soft_I2C_SDA /* SDA = 0 */
#define MPU6050_I2C_SDA_READ() ((Soft_I2C_PORT->IDR & Soft_I2C_SDA) != 0) /* 读SDA口线状态 */
#endif
/**********************************************
函数名称:MPU_IIC_Delay
函数功能:MPU IIC延时函数,延时2ms
函数参数:无
函数返回值:无
**********************************************/
void MPU_IIC_Delay(void)
{
uint8_t i;
/*
下面的时间是通过逻辑分析仪测试得到的。
工作条件:CPU主频72MHz ,MDK编译环境,1级优化
循环次数为10时,SCL频率 = 205KHz
循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
*/
for (i = 0; i < 100; i++);
}
/**********************************************
函数名称:MPU_IIC_Init
函数功能:MPU IIC初始化
函数参数:无
函数返回值:无
**********************************************/
void MPU_IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(MPU6050_GPIO_CLK|RCC_APB2Periph_AFIO,ENABLE); //先使能外设IO PORTB时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
GPIO_InitStructure.GPIO_Pin = Soft_I2C_SCL|Soft_I2C_SDA; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(Soft_I2C_PORT , &GPIO_InitStructure); //根据设定参数初始化GPIO
GPIO_SetBits(Soft_I2C_PORT,Soft_I2C_SCL|Soft_I2C_SDA); //PB10,PB11 输出高
}
//产生IIC起始信号
void MPU_IIC_Start(void)
{
MPU_SDA_OUT(); //sda线输出
MPU6050_I2C_SDA_1();
MPU6050_I2C_SCL_1();
MPU_IIC_Delay();
MPU6050_I2C_SDA_0();//START:when CLK is high,DATA change form high to low
MPU_IIC_Delay();
MPU6050_I2C_SCL_0();//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void MPU_IIC_Stop(void)
{
MPU_SDA_OUT();//sda线输出
MPU6050_I2C_SCL_0();
MPU6050_I2C_SDA_0();//STOP:when CLK is high DATA change form low to high
MPU_IIC_Delay();
MPU6050_I2C_SCL_1();
MPU6050_I2C_SDA_1();//发送I2C总线结束信号
MPU_IIC_Delay();
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 MPU_IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
MPU_SDA_IN(); //SDA设置为输入
MPU6050_I2C_SDA_1();MPU_IIC_Delay();
MPU6050_I2C_SCL_1();MPU_IIC_Delay();
while(MPU6050_I2C_SDA_READ())
{
ucErrTime++;
if(ucErrTime>250)
{
MPU_IIC_Stop();
return 1;
}
}
MPU6050_I2C_SCL_0();//时钟输出0
return 0;
}
//产生ACK应答
void MPU_IIC_Ack(void)
{
MPU6050_I2C_SCL_0();
MPU_SDA_OUT();
MPU6050_I2C_SDA_0();
MPU_IIC_Delay();
MPU6050_I2C_SCL_1();
MPU_IIC_Delay();
MPU6050_I2C_SCL_0();
}
//不产生ACK应答
void MPU_IIC_NAck(void)
{
MPU6050_I2C_SCL_0();
MPU_SDA_OUT();
MPU6050_I2C_SDA_1();
MPU_IIC_Delay();
MPU6050_I2C_SCL_1();
MPU_IIC_Delay();
MPU6050_I2C_SCL_0();
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void MPU_IIC_Send_Byte(u8 _ucByte)
{
uint8_t i;
MPU_SDA_OUT();
MPU6050_I2C_SCL_0();//拉低时钟开始数据传输
/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
MPU6050_I2C_SDA_1();
}
else
{
MPU6050_I2C_SDA_0();
}
_ucByte <<= 1; /* 左移一个bit */
MPU6050_I2C_SCL_1();
MPU_IIC_Delay();
MPU6050_I2C_SCL_0();
MPU_IIC_Delay();
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 MPU_IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
MPU_SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
MPU6050_I2C_SCL_0();
MPU_IIC_Delay();
MPU6050_I2C_SCL_1();
receive<<=1;
if(MPU6050_I2C_SDA_READ())receive++;
MPU_IIC_Delay();
}
if (!ack)
MPU_IIC_NAck();//发送nACK
else
MPU_IIC_Ack(); //发送ACK
return receive;
}
5.读取的数据成功率低(也就是说读取10次,mpu_dmp_get_data有5次都返回1,而不是0)
1.可能是 MPU_IIC_Delay()这个函数取值不恰当,可以调整一下看看效果。
2.读取MPU6050数据卡死的总结
6.容易忽视的小问题,却恰恰最致命
例如:
STM32的PB3、PB4,分别是JTAG的JTDO和NJTRST引脚,在没关闭JTAG功能之前,在程序中是配置不了这些引脚的功能的。要配置这些引脚,首先要开启AFIO时钟,然后在AFIO的设置中,释放这些引脚。具体看STM32的参考手册中有关AFIO的部分。
RCC_APB2PeriphClockCmd(MPU6050_GPIO_CLK|RCC_APB2Periph_AFIO,ENABLE); //先使能外设IO PORTB时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
/******************************************************************
#define GPIO_Remap_SWJ_NoJTRST ((uint32_t)0x00300100) < Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST
#define GPIO_Remap_SWJ_JTAGDisable ((uint32_t)0x00300200) < JTAG-DP Disabled and SW-DP Enabled
#define GPIO_Remap_SWJ_Disable ((uint32_t)0x00300400) !< Full SWJ Disabled (JTAG-DP + SW-DP)
******************************************************************/