陀螺仪模块MPU6050使用的折腾过程—软件IIC的问题解决

陀螺仪模块MPU6050自检不过的折腾过程——软件IIC的问题解决

几天前从某多多入手了一块陀螺仪模块MPU6050, 用自己的STM32C8T6最小系统连接测试一下,结果无论如何都连不上,卡在自检初始化通不过。程序以及测试软件是从B站下载的。由于不知道是不是模块本身的问题,联系商家客服,开始说没有资料,我就说不好用,后来终于给了个链接,下载把里面的STM32的例程试用了一下,程序跑的的好好的,都能过但串口输出的x,y,z轴数据始终是0;把模块拔了竟然也能跑起来,不报错,直接忽略。
重新把原来的程序拿来用,至少数据传输过程有个校验,比商家给的靠谱多了。一步步分析到底问题卡在哪了。下面是main.c主程序:

//STM32F103C8T6+MPU6050姿态显示
//MPU6050接线: VCC->3V3
// 				GND->GND
// 				SCL->PB10
//				SDA->PB11
//USART1
//TX PA9
//RX PA10

int main(void)
{
	float pitch,roll,yaw; 	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	
	uart_init(115200);	 	
	delay_init();	
	LED_Init();		  			
	MPU_Init();				
	while(mpu_dmp_init())
	{
		delay_ms(20);
	}
	while(1)
	{
		delay_ms(2);
		if(mpu_dmp_get_data(&pitch,&roll,&yaw)==0)
		{
			LED=~LED;
		}
		SEND_OULA_ANGLE((short)(roll*100),(short)(-pitch*100),(short)(-yaw*100));
	} 	
}

由程序可以看出GPIOB的10、11分别接MUP6050的SCL,USART1接电脑串口,结构比较简单不上图了。
程序运行直接到delay_ms(20);这里形成循环,那就循着mpu_dmp_init一路查下去。进入inv_mpu.c下

//mpu6050,dmp初始化
//返回值:0,正常
//    其他,失败
u8 mpu_dmp_init(void)
{
	u8 res=0;
	MPU_IIC_Init(); 	//初始化IIC总线
	if(mpu_init()==0)	//初始化MPU6050
	{	 
		res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置所需要的传感器
		if(res)return 1; 
		res=mpu_configure_fifo(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置FIFO
		if(res)return 2; 
		res=mpu_set_sample_rate(DEFAULT_MPU_HZ);	//设置采样率
		if(res)return 3; 
		res=dmp_load_motion_driver_firmware();		//加载dmp固件
		if(res)return 4; 
		res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));//设置陀螺仪方向
		if(res)return 5; 
		res=dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT|DMP_FEATURE_TAP|	//设置dmp功能
		    DMP_FEATURE_ANDROID_ORIENT|DMP_FEATURE_SEND_RAW_ACCEL|DMP_FEATURE_SEND_CAL_GYRO|
		    DMP_FEATURE_GYRO_CAL);
		if(res)return 6; 
		res=dmp_set_fifo_rate(DEFAULT_MPU_HZ);	//设置DMP输出速率(最大不超过200Hz)
		if(res)return 7;   
		res=run_self_test();		//自检
		if(res)return 8;    
		res=mpu_set_dmp_state(1);	//使能DMP
		if(res)return 9;     
	}else return 10;
	return 0;
}

在这里程序运行结果正常就返回0,否则返回1-10。由于返回不了0,所以主程序卡在初始化部分。
在这爱仕达啊里插入图片描述
if(mpu_init()==0) 前插入断点,可以看到程序能运行到这里,但进不了循环说明mpu_init()函数返回了非0值,继续向下查mpu_init()。下面她的是具体内容:

/**
 *  @brief      Initialize hardware.
 *  Initial configuration:\n
 *  @return     0 if successful.
 */
int mpu_init(void)
{
    unsigned char data[6], rev;

    /* Reset device. */
    data[0] = BIT_RESET;
    if (i2c_write(st.hw->addr, st.reg->pwr_mgmt_1, 1, data))
        return -1;
    delay_ms(100);
    //......省略n行
}

在这里插入图片描述
很明显程序进入到返回值-1这行,而程序正常要返回 0;
循着i2c_write()查到#define i2c_write MPU_Write_Len
继续走MPU_Write_Len()这个程序在mpu6050.c下,

//IIC连续写
//addr:器件地址 
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
//    其他,错误代码
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
	u8 i; 
    MPU_IIC_Start(); 
	MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令	
	if(MPU_IIC_Wait_Ack())	//等待应答
	{
		MPU_IIC_Stop();		 
		return 1;		
	}
    MPU_IIC_Send_Byte(reg);	//写寄存器地址
    MPU_IIC_Wait_Ack();		//等待应答
	for(i=0;i<len;i++)
	{
		MPU_IIC_Send_Byte(buf[i]);	//发送数据
		if(MPU_IIC_Wait_Ack())		//等待ACK
		{
			MPU_IIC_Stop();	 
			return 1;		 
		}		
	}    
    MPU_IIC_Stop();	 
	return 0;	
} 

在这里插入图片描述
看到程序可以通过发送器件地址+写命令这过程 ,到达了第二个写寄存器这行;这说明我的模块应该是好的,有必要搞下去了。

    MPU_IIC_Send_Byte(reg);	//写寄存器地址
    MPU_IIC_Wait_Ack();		//等待应答

发现虽然这个过程,程序没设应答检测报错,但实际应答检测是失败的;
实际运行会卡在这里
![在这里插入图片描述](https://img-blog.csdnimg.cn/909ae727fa5e4769bc74763729b40409.png在这里插入图片描述
先进入 MPU_IIC_Wait_Ack(); 看一下。

//等待应答信号到来
//返回值:1,接收应答失败
//       0,接收应答成功
u8 MPU_IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	MPU_SDA_IN();      //SDA设置为输入  
	MPU_IIC_SDA=1;MPU_IIC_Delay();	   
	MPU_IIC_SCL=1;MPU_IIC_Delay();	 
	while(MPU_READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			MPU_IIC_Stop();
			return 1;
		}
	}
	MPU_IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 

程序出错如下图:
在这里插入图片描述
读MPU应答信号失败,没能在发送完8位数据后检测到应答信号。反复查找也没发现问题,线路连接都正常,每次实验结果都一样,就是发送器件地址能过,而后面发送寄存器地址和发送数据就会失败。正在走投无路的时候,突然想到好久不用的逻辑分析仪。马上拿出来接好测量了一下正常和错误时的波形。
在这里插入图片描述正常情况波形
发送器件地址 0xD0(0x68<<1|0),可以看到A1位置IIC的启动信号和A2位置的mpu的应答信号(低电平)。单片机在SCL为为1时读取到了应答信号。
在这里插入图片描述错误情况波形
发送寄存器器件地址 0x6B,可以看到A1位置发往IIC的第8位信号 1,在A2位置竟然有个低电平的应答信号,但在单片机在SCL为为1时读取时却变回了高电平。这个信号可以确定是mup6050的应答信号,因为在这个时候单片机的SDA是输出高电平的。那可不可以改一下程序把SCL读取信号的时间提前,是不是就能读到了呢?于是改了一下程序,如下图把MPU_IIC_Delay(); 移到循环前面
![在这里插入图片描述](https://img-blog.csdnimg.cn/4712c48f18294ede954b2e4db7d19895.png

再如下图把MPU_IIC_Delay(); 去掉。
在这里插入图片描述
运行结果波形见下图
在这里插入图片描述
让人不可思议的一幕出现了,mup6050的应答信号仍然在读取之前消失了,只持续了很短的时间。既然还是不行程序恢复回去。再分析一下正常读取的一次最后发送的是 0(D0),而错误的这次末尾发送的是 1(6B),那我在发1后补个0会怎么样呢,于是在改一下程序。
在这里插入图片描述
先简单在发送数据后直接加上试试,结果真的就通过了,竟然跟着下面发数据的一段也过了,那个原来也是末尾是0的数据(0x80)。既然可以,为了通用,这行移到MPU_IIC_Send_Byte();里面

在这里插入图片描述
再跑一下程序,久违的开发版指示灯亮起来了,连上电脑一看,一切正常,很晚了睡觉。
躺在床上总觉得哪里不对,突然想到我下载程序的帖子后面有个人说的一句话,在一个跟我一样问题的回复跟帖中说,他的问题一样,鼓弄鼓弄结果接口烧了。啊,这不是玩笑,这个IIC的程序初始化里PB10和PB11用的是推挽模式,能好使才怪。由于这个版本的程序同我在网上随便搜的一个完全一样,所以没太怀疑有太大问题,以为可能是器件的性能有差异。
第二天接着弄,下面是原来的IIC初始化程序,

//初始化IIC
void MPU_IIC_Init(void)
{					     
  GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//先使能外设IO PORTB时钟 
		
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;	 // 端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
  GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIO 
	
  GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11);						 //PB10,PB11 输出高	
 
}

读写状态状态转换程序

//IO方向设置
#define MPU_SDA_IN()  {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=8<<12;}
#define MPU_SDA_OUT() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=3<<12;}

这个IO方向设置里面也推挽模式,和上拉/下拉模式转换,转换到输入状态时都跟了一句MPU_IIC_SDA=1完成上拉输入设定,看了一下电路原理图,这两个引脚都接有上拉电阻,所以浮空输入更好一些。再说这种共用总线,输出模式必须改成开漏输出。既然是开漏输出,也没必要更改端口的输入输出模式了,直接在读总线时加一句MPU_IIC_SDA=1释放总线就可以了。
所以在初始化里把PB10和PB11设置成开漏输出模式,程序中不再改变。

//初始化IIC
void MPU_IIC_Init(void)
{					     
  GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//先使能外设IO PORTB时钟 
		
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;	 // 端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; 		 //开漏输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
  GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIO 
	
  GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11);						 //PB10,PB11 输出高	
 
}

去掉程序中的所有的MPU_SDA_OUT();MPU_SDA_IN();MPU_IIC_SDA=1;替换就可以了,最后发个视频嘚瑟一下。

MPU6050

  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值