由於數據采集站基本都安裝在野外或者樓頂,安裝位置以及震動對檢測數據的准確性有一定影響。所以想要有一個位置狀態數據,正好發現麒麟作上有ADXL345,這樣一個數字輸出的加速度傳感器。如圖中紅框所示:
1、ADXL345概述
ADXL345是ADI公司推出的基於iMEMS技術的3軸、數字輸出加速度傳感器。該加速度傳感器的特點如下:
- 分辨率高。最高13位分辨率。
- 量程可變。具有+/-2g,+/-4g,+/-8g,+/-16g可變的測量范圍。
- 靈敏度高。最高達3.9mg/LSB,能測量不到1.0°的傾斜角度變化。
- 功耗低。 40~145uA 的超低功耗,待機模式只有 0.1uA。
- 尺寸小。整個 IC 尺寸只有 3mm*5mm*1mm, LGA 封裝。
ADXL支持標准的I2C或SPI數字接口,自帶32級FIFO存儲,並且內部有多種運動狀態檢測和靈活的中斷方式等特性。ADXL345傳感器的檢測軸如下圖所示:
當 ADXL345 沿檢測軸正向加速時,它對正加速度進行檢測。在檢測重力時用戶需要注意,當檢測軸的方向與重力的方向相反時檢測到的是正加速度。下圖所示為輸出響應與相對於重力的方向的關系:
2、硬件設計
ADXL345支持SPI 和I2C兩種通信方式,麒麟座采用的I2C通訊方式,具體連接如下圖所示:
對於任何不使用的引腳,沒有內部上拉或下拉電阻,因此,CS引腳或ALT ADDRESS引腳懸空或不連接時,任何已知狀態或默認狀態不存在。使用I2C時, CS引腳必須連接至VDD I/O, ALT ADDRESS引腳必須連接至任一VDD I/O或接地。
ALT ADDRESS引腳處於高電平,器件的7位I2C地址是0x1D,隨后為R / W位。這轉化為0x3A寫入,0x3B讀取。通過ALT ADDRESS引腳(引腳12)接地,可以選擇備用I2C地址0x53(隨后為R / W位)。這轉化為0xA6寫入, 0xA7讀取。
由於在麒麟座中,ALT ADDRESS引腳(引腳12)接地,所以地址為0x53,也就是0xA6寫入, 0xA7讀取。此外2個終端信號也引入了MCU。
3、軟件設計
接下來我們開始軟件方面的設計。因為麒麟座中ADXL345使用I2C接口與MCU通訊,並連接到了I2C2(PB10、PB11)端口,接下來我們將逐步實現這部分軟件。
(1)、I2C讀寫操作
因為ADXL345加速度傳感器使用了標准的I2C通訊接口,所以我們來看一看I2C讀寫單個或多個子解釋的報文格式(如下圖)。
根據以上報文圖示,並結合STM32F1的庫函數我們封裝讀寫字節操作的函數。封裝發送命令和獲取數據函數:
/*向ADXL345下發指令,指令格式均為1個字節*/ void WriteByteToADXL345(I2C_TypeDef* I2Cx,uint8_t deviceAddress,uint8_t command) { uint16_t i2cTimeout=TimeoutPeriod; /*產生I2C起始信號*/ I2C_GenerateSTART(I2C2, ENABLE); /*檢測 EV5 事件並清除標志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)) { if ((i2cTimeout--) == 0) return ; } /*發送ADXL345的地址*/ I2C_Send7bitAddress(I2Cx,deviceAddress,I2C_Direction_Transmitter); i2cTimeout=TimeoutPeriod; /*檢測 EV6 事件並清除標志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if ((i2cTimeout--) == 0) return ; } /*下發操作命令*/ I2C_SendData(I2Cx, command); i2cTimeout=TimeoutPeriod; /*檢測 EV8 事件並清除標志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if ((i2cTimeout--) == 0) return ; } /* 產生I2C停止信號 */ I2C_GenerateSTOP(I2Cx, ENABLE); } /*從ADXL345讀取多個字節數據的值*/ void ReadBytesFromADXL345(I2C_TypeDef* I2Cx,uint8_t deviceAddress,uint8_t *pData,uint16_t bytesNum) { uint16_t i2cTimeout=TimeoutPeriod; /*如果I2C總線忙,則等待一段時間*/ while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)) { if ((i2cTimeout--) == 0) return ; } /* 產生 I2C 起始信號 */ I2C_GenerateSTART(I2Cx, ENABLE); /*檢測 EV5 事件並清除標志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)) { if ((i2cTimeout--) == 0) return ; } /*發送ADXL345的地址*/ I2C_Send7bitAddress(I2Cx,deviceAddress,I2C_Direction_Receiver); i2cTimeout=TimeoutPeriod; /*檢測 EV6 事件並清除標志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) { if ((i2cTimeout--) == 0) return ; } while(bytesNum) { if(bytesNum==1) { I2C_AcknowledgeConfig(I2Cx, DISABLE);//關閉應答 I2C_GenerateSTOP(I2Cx, ENABLE);//停止信號 } i2cTimeout=TimeoutPeriod; /*檢測 EV7 事件並清除標志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)) { if ((i2cTimeout--) == 0) return ; } /*通過 I2C,從設備中讀取一個字節的數據 */ *pData=I2C_ReceiveData(I2Cx); pData++; bytesNum--; } /*使能應答,方便下一次 I2C 傳輸*/ I2C_AcknowledgeConfig(I2Cx, ENABLE); }
(2)ADXL寄存器及操作
ADXL345傳感器擁有29個用戶可見的寄存器,如下圖所示。但是我們不對其進行具體的介紹,在數據手冊上有非常詳細的說明。這些寄存器我們並不會全部使用,事實上通常我們只是用配置寄存器和部分數據寄存器。
在使用ADXL345讀取我們想要的數居前,我們先使用相關寄存器對傳感器進行必要的配置,以期獲取我們想要得到的值。如下初始化配置過程:
void ADXL345_Init_Configuration(void) { uint8_t devid = 0, val = 0; Delayus (300); ReadByteFromADXL345 (ADXL345_ADDRESS, 0x00, &devid); //獲取設備ID Delayus (300); val = 0x2B; WriteByteToADXL345 (ADXL345_ADDRESS, DATA_FORMAT_REG, &val); //數據格式控制 Delayus(50); val = 0x0A; WriteByteToADXL345 (ADXL345_ADDRESS, BW_RATE, &val); //數據速率及功率模式控制 Delayus(50); val = 0x28; WriteByteToADXL345 (ADXL345_ADDRESS, POWER_CTL, &val); //上電特性控制 Delayus(50); val = 0; WriteByteToADXL345 (ADXL345_ADDRESS, INT_ENABLE, &val); //中斷使能控制 Delayus(50); WriteByteToADXL345 (ADXL345_ADDRESS, OFSX, &val); //X軸偏移 Delayus(50); WriteByteToADXL345 (ADXL345_ADDRESS, OFSY, &val); //Y軸偏移 Delayus(50); WriteByteToADXL345 (ADXL345_ADDRESS, OFSZ, &val); //Z軸偏移 Delayus(500); }
(3)ADXL345數據獲取
接下來我們就可以讀取數據了,獲取和解析數據前我們先來了解一下數據的格式。分別在寄存器0x2C中進行速率控制,在寄存器0x31中進行數據格式控制。ADXL345的輸出數據可以采用右對齊和左對齊兩種方式。
數據右對齊方式:
數據左對齊方式:
此外,ADXL345通過VS = 2.5 V電源電壓的測試且以其為額定電壓;然而, VS可高至3.6 V或低至2.0 V。在麒麟座中解釋使用的3.3VDC供源,偏移、靈敏度、噪聲、自測和電源電流等參數隨着電源電壓變化而變化。隨着電源電壓的改變,靜電力也發生細微變化,因此,偏移和靈敏度也有細微變化。在電源電壓VS = 3.3 V下運行時, x軸和y軸偏移通常比VS = 2.5 V運行時高25 mg。在電源電壓3.3 V下運行與在VS = 2.5V時比較, z軸偏移一般低20 mg。VS = 2.5V時, x軸和y軸的靈敏度為標稱256 LSB/g(全分辨率或±2 g, 10位運行),而電源電壓為3.3 V時,其靈敏度轉換為265 LSB/g。 z軸靈敏度不受電源電壓影響, 2.5 V時或3.3 V時都相同。可以用簡單的線性插值來確定其他電源電壓下的偏移和靈敏度的典型轉換。
搞清楚了以上問題就可以讀取正確的數據了:
void GetValue FromADXL345 (void) { unsigned char devid = 0; unsigned char dataTemp[6]; Delayus(200); ReadByteFromADXL345(ADXL345_ADDRESS, 0x00, &devid); //讀取設備ID Delayus(200); ReadBytes FromADXL345 (ADXL345_ADDRESS, 0x32, dataTemp, 6); //獲取原始數據(4mg/LSB) adxlInfo.incidence_X = (short)(dataTemp[0] + ((unsigned short)dataTemp[1] << 8)); adxlInfo.incidence_Y = (short)(dataTemp[2] + ((unsigned short)dataTemp[3] << 8)); adxlInfo.incidence_Z = (short)(dataTemp[4] + ((unsigned short)dataTemp[5] << 8)); adxlInfo.incidence_Xf = (float)adxlInfo.incidence_X * 0.0039; //換算為物理量值 adxlInfo.incidence_Yf = (float)adxlInfo.incidence_Y * 0.0039; //換算為物理量值 adxlInfo.incidence_Zf = (float)adxlInfo.incidence_Z * 0.0039; //換算為物理量值 }
至此軟件編寫完成。
4、結果驗證
完成編寫后,編譯無誤。我們查看最終的運行結果如何。首先我們登錄到OneNET,並組態好設備和應用,然后下載程序重新上電,就可以看到Xg、Yg、Zg的數據以及上次我們讀取的溫濕度數據:
我們再看一下顯示屏中顯示的數據是什么樣的。因顯示精度不同有些差異,但基本是一致的。
至此,我們完成了ADXL345數據的獲取以及上傳到OneNET,在后續我們將繼續各種OneNET以及麒麟座開發板的研究。OneNET真的是越用越有意思。