想到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設置。