想到2017年的全国大学生电子竞技大赛,因为一款加速度传感器(MPU6050)而忙的焦头烂额;虽然那个时候真的很累,但是感觉还不错的样子,认识了很多很多的人,也包括那个只会挠头的傻子,一天都没个正行。哪一年,让我学到了很多,而我自己也没有想到会因此走上这条路,现在想想感觉就是如梦一般,可惜的是,这个梦好像醒不过来了。
到现在我依然记得哪年我做的那道题,检测不透明管道内小球的运动状态和个数(题号忘了),管道我们采用的是PVB管,小球则是铁球;
1、关于检测小球方向和个数我们用的是电涡流传感器(常闭)
1.1 原理:当铁球经过传感器的范围时,传感器会产生一个脉冲,通过对该脉冲进行计数,即可得到通过管道的小球个数
但是,当时我们没有考虑到时间的概念,以至于在比赛调试时,出现计数不准确,现象为:多个小球排列通过(小球之间没有间隔)
2、关于测量管道和水平面的角度问题
1.1 传感器开始准备使用的是MPU6050,不过到最后却使用的是MMA7455(数字输出三轴加速度传感器),到比赛前一天一切测试都ok,没有想到的是,到比赛现场之后却出现了诸多意外,顿时自己的心就慌了,完全平复不下来,那是我第二次真的感觉手足无措。
3、编写IIC时序:(只对STC89C51/C52有效)
- IIC起始时序
SCL高电平期间,SDA有高到低的跳变
1 void I2C_Start() 2 { 3 SDA = 1; //拉高数据线 4 SCL = 1; //拉高时钟线 5 Delay5us(); //延时 6 SDA = 0; //产生下降沿 7 Delay5us(); //延时 8 SCL = 0; //拉低时钟线 9 }
- IIC停止时序
SCL在高电平期间,SDA有低到高的跳变
1 void I2C_Stop() 2 { 3 SDA = 0; //拉低数据线 4 SCL = 1; //拉高时钟线 5 Delay5us(); //延时 6 SDA = 1; //产生上升沿 7 Delay5us(); //延时 8 }
- IIC发送应答
应答:SCL高电平期间,SDA为低电平
不应答:SCL高电平期间,SDA为高电平
1 void I2C_SendACK(bit ack) 2 { 3 SDA = ack; //写应答信号 4 SCL = 1; //拉高时钟线 5 Delay5us(); //延时 6 SCL = 0; //拉低时钟线 7 Delay5us(); //延时 8 }
- IIC接收应答
1 bit I2C_RecvACK() 2 { 3 SCL = 1; //拉高时钟线 4 Delay5us(); //延时 5 CY = SDA; //读应答信号 6 SCL = 0; //拉低时钟线 7 Delay5us(); //延时 8 return CY; 9 }
- IIC写一个字节(发送)
1 void I2C_SendByte(uchar dat) 2 { 3 uchar i; 4 for (i=0; i<8; i++) //8位计数器 5 { 6 dat <<= 1; //移出数据的最高位 7 SDA = CY; //送数据口 8 SCL = 1; //拉高时钟线 9 Delay5us(); //延时 10 SCL = 0; //拉低时钟线 11 Delay5us(); //延时 12 } 13 I2C_RecvACK(); 14 }
- IIC读一个字节(接收)
1 uchar I2C_RecvByte() 2 { 3 uchar i; 4 uchar dat = 0; 5 SDA = 1; //使能内部上拉,准备读取数据, 6 for (i=0; i<8; i++) //8位计数器 7 { 8 dat <<= 1; 9 SCL = 1; //拉高时钟线 10 Delay5us(); //延时 11 dat |= SDA; //读数据 12 SCL = 0; //拉低时钟线 13 Delay5us(); //延时 14 } 15 return dat; 16 }
4、 对传感器(MPU6050)读写时序
在读写之前先设置AD0的电平,此关系IIC的设备地址
AD0=0;b11010000 0xD0(IIC设备地址)
AD0=1;b11010001 0xD1(IIC设备地址)
时序中符号说明:
4.1 read:
4.1.1 单字节读
1 uchar Single_ReadI2C(uchar REG_Address) 2 { 3 uchar REG_data; 4 I2C_Start(); 5 I2C_SendByte(SlaveAddress); //设备地址+写 6 I2C_SendByte(REG_Address); 7 I2C_Start(); 8 I2C_SendByte(SlaveAddress+1); //设备地址+读 9 REG_data=I2C_RecvByte(); 10 I2C_SendACK(1); //接收应答 11 I2C_Stop(); 12 return REG_data; 13 }
4.1.2 多字节读:
1 //函数名: MMA8451_Multiple_Read 2 //函数功能: 从传感器设备读多个字节 3 //参数: REG_Address -- 寄存器地址 4 // num -- 要读的字节数 5 // *data_buf -- 存放读回数据的缓冲区 6 //返回值: 无 7 void MPU6050_Multiple_Read(unsigned char REG_Address, unsigned char num, unsigned char *data_buf) 8 { 9 unsigned char i; 10 num += 1; 11 I2C_Start(); 12 I2C_SendByte(SlaveAddress); //设备地址 13 I2C_SendByte(REG_Address); 14 I2C_Start(); //SR 15 I2C_SendByte(SlaveAddress+1); //设备地址 + read 方向 16 for(i=0;i<num-1;i++) 17 { 18 *(data_buf+i) = I2C_RecvByte(); 19 if(i == num-1) 20 I2C_SendACK(1); 21 else 22 I2C_SendACK(0); 23 } 24 I2C_Stop(); 25 }
4.2 write:
4.2.1 单字节写:
1 void Single_WriteI2C(uchar REG_Address,uchar REG_data) 2 { 3 I2C_Start(); 4 I2C_SendByte(SlaveAddress); //设备地址+写 5 I2C_SendByte(REG_Address); //寄存器地址, 6 I2C_SendByte(REG_data); 7 I2C_Stop(); 8 }
4、MPU6050初始化
寄存器 PWR_MGMT_1(read/write)
说明:该寄存器允许用户配置电源模式和时钟源。它还提供了用于重置整个设备的位,以及用于禁用温度传感器的位。
CYCLE: 休眠/唤醒
CYCLE=0; 唤醒
CYCLE=1; 休眠
寄存器 SMPRT_DIV(read/write)
采样速率由陀螺仪输出速率除以SMPLRT_DIV得到:
Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
当DLPF被禁用时陀螺仪输出速率为8kHz (DLPF_CFG = 0或7),当DLPF被启用时陀螺仪输出速率为1kHz(见寄存器26)。
注:加速度计输出速率为1kHz。这意味着对于大于1kHz的采样率,相同的加速度计采样可能不止一次地输出到FIFO、DMP和传感器寄存器。
SMPLRT_DIV:8位无符号值。采样速率是由陀螺仪输出速率除以这个值决定的。
寄存器CONFIG(read/write)
说明:
该寄存器为陀螺仪和加速度计配置外部帧同步(FSYNC) pin采样和数字低通滤波器(DLPF)设置。
可以通过配置EXT_SYNC_SET对连接到FSYNC pin的外部信号进行采样。
对FSYNC引脚的信号更改被锁定,以便可以捕获短频闪。锁定的FSYNC信号将按照寄存器25中定义的采样速率进行采样。采样后,锁存器复位到当前FSYNC信号状态。
在传感器数据寄存器中,根据下表EXT_SYNC_SET的值确定的最小有效位将被报告为采样值。
DLPF由DLPF_CFG配置。加速度计和陀螺仪根据DLPF_CFG的值进行滤波,如下表所示。
保留位:6、7
参数:
EXT_SYNC_SET: 3-bit无符号值。配置FSYNC采样。
DLPF:3-bit无符号值。配置DLPF设置。