关于mpu6050六轴传感器例程中一些位操作

起因

最近在学习stm32开发板,最近学习到了需要IIC通信MPU6050六轴传感器,看了正点原子的例程,发现其中有很多位操作不是很理解。经过补习了一番C语言,总结了一些位操作的知识。

接下来直接贴代码+讲解操作

1.

void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    

首先让我们看一下这个函数,里面多次用到了IIC_SDA=(txd&0x80)>>7; 这个方法,0x80实际上就是二进制的10000000,txd&0x80就是取txd低八位的最高位(如果不能理解就先向下看), 此时的“>>7”是将结果右移七位,若txd = 10010110, 那么结果就是 00000001;这里的操作是取变量txd的最高位,然后通过循环将txd的值赋给IIC_SDA。因为IIC通信是从最高位开始读取,所以要进行这样一部操作。

2.

u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        
		IIC_Ack(); //发送ACK   
    return receive;
}

这个函数应该比较好理解
receive<<=1;
if(READ_SDA)receive++;
READ_SDA是在IO上直接读取到的数据0/1
如果SDA = 1 的话,receive则会+1,反之则不操作,相当于对最低位进行赋值。
之后再将receive左移一位,达成逐位赋值的效果。

3.

u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{ 
 	IIC_Start(); 
	IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令	
	if(IIC_Wait_Ack())	//等待应答
	{
		IIC_Stop();		 
		return 1;		
	}
    IIC_Send_Byte(reg);	//写寄存器地址
    IIC_Wait_Ack();		//等待应答
    IIC_Start();
	IIC_Send_Byte((addr<<1)|1);//发送器件地址+读命令	
    IIC_Wait_Ack();		//等待应答 
	while(len)
	{
		if(len==1)*buf=IIC_Read_Byte(0);//读数据,发送nACK 
		else *buf=IIC_Read_Byte(1);		//读数据,发送ACK  
		len--;
		buf++; 
	}    
    IIC_Stop();	//产生一个停止条件 
	return 0;	
}

首先看IIC_Send_Byte((addr<<1)|0); 这句,因为寄存器内最低位为设置读/写,所以要将addr左移一位并对最低位赋值 x|0x|1

在这里我要说一下这个指针 *buf

u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz) 
{
    u8 buf[6],res;  
	res=MPU_Read_Len(MPU_ADDR,MPU_GYRO_XOUTH_REG,6,buf);
	if(res==0)
	{
		*gx=((u16)buf[0]<<8)|buf[1];  
		*gy=((u16)buf[2]<<8)|buf[3];  
		*gz=((u16)buf[4]<<8)|buf[5];
	} 	
    return res;;
}

例程中调用的时候是这么写的

res=MPU_Read_Len(MPU_ADDR,MPU_GYRO_XOUTH_REG,6,buf);

首先为什么len的入口参数为6,因为我们读取了三个数据,gx,gy,gy,
由于每一个都是16位,所以要分高低位分别读取,所以len为6。
其次
*gx=((u16)buf[0]<<8)|buf[1];
*gy=((u16)buf[2]<<8)|buf[3];
*gz=((u16)buf[4]<<8)|buf[5];
为什么这么写
在read_len函数中我们将所有的值都赋给了 *buf这个指针,正确来讲是赋给了 *buf指针所指的内存区域,
*buf每+1就会指向距离现在位置8byte距离的下一个内存区域,(u8 *buf)
所以进行上述操作,就可以将read到的每一个8byte大小的数据,通过指针 *buf 从内存空间依次获取,重新组装,从而得到我们需要的u16大小的gx,gy,gz的值了。

4.

void usart1_report_imu(short aacx,short aacy,short aacz,short gyrox,short gyroy,short gyroz,short roll,short pitch,short yaw)
{
	u8 tbuf[28]; 
	u8 i;
	for(i=0;i<28;i++)tbuf[i]=0;//清0
	tbuf[0]=(aacx>>8)&0XFF;
	tbuf[1]=aacx&0XFF;
	tbuf[2]=(aacy>>8)&0XFF;
	tbuf[3]=aacy&0XFF;
	tbuf[4]=(aacz>>8)&0XFF;
	tbuf[5]=aacz&0XFF; 
	tbuf[6]=(gyrox>>8)&0XFF;
	tbuf[7]=gyrox&0XFF;
	tbuf[8]=(gyroy>>8)&0XFF;
	tbuf[9]=gyroy&0XFF;
	tbuf[10]=(gyroz>>8)&0XFF;
	tbuf[11]=gyroz&0XFF;	
	tbuf[18]=(roll>>8)&0XFF;
	tbuf[19]=roll&0XFF;
	tbuf[20]=(pitch>>8)&0XFF;
	tbuf[21]=pitch&0XFF;
	tbuf[22]=(yaw>>8)&0XFF;
	tbuf[23]=yaw&0XFF;
	usart1_niming_report(0XAF,tbuf,28);//飞控显示帧,0XAF
} 

最后是这个函数,里面有这样一个操作:

tbuf[0]=(aacx>>8)&0XFF;
tbuf[1]=aacx&0XFF;

这里tbuf[0]为高八位,tbuf[1]为第八位, 以此类推。
这里我要说一下为什么要 &0XFF,(&11111111);
accy等变量都是16位数据,而对一个变量进行&0xFF操作就是取它的低八位
让一个16位的变量最终返回的值为一个八位数据
之前的IIC_SDA=(txd&0x80)>>7; 也有着异曲同工之处,

至此就是我对例程中的一些奇葩操作的理解了。

你可能感兴趣的:(c语言,stm32,硬件)