外設驅動庫開發筆記22:ADXL345三軸數字加速度計驅動
移動裝置的廣泛應用增加對移動過程中各種引數的檢測需求。ADXL345三軸數字加速度計可以用來檢測加速度、進而測量傾斜角度等。在這一篇中,我們將討論ADXL345三軸數字加速度計驅動程式的設計與實現。
1、功能概述
ADXL345是一款小而薄的超低功耗3軸加速度計,解析度高(13位),測量範圍達±16 g。數字輸出資料為16位二進位制補碼格式,可通過SPI(3線或4線)或I2C數字介面訪問。採用SPI通訊介面時,最大SPI時鐘速度為5 MHz,時序方案按照時鐘極性(CPOL)= 1、時鐘相位(CPHA)= 1執行。採用I2C通訊介面時,ALT ADDRESS引腳處於高電平,器件的7位I2C地址是0x1D,隨後為R / W位。這轉化為0x3A寫入,0x3B讀取。通過ALT ADDRESS引腳(引腳12)接地,可以選擇備用I2C地址0x53(隨後為R / W位)。這轉化為0xA6寫入,0xA7讀取。引腳定義及封裝如下:
ADXL345非常適合移動裝置應用。它可以在傾斜檢測應用中測量靜態重力加速度,還可以測量運動或衝擊導致的動態加速度。其高解析度(3.9 mg/LSB),能夠測量不到1.0°的傾斜角度變化。
該器件提供多種特殊檢測功能。活動和非活動檢測功能通過比較任意軸上的加速度與使用者設定的閾值來檢測有無運動發生。敲擊檢測功能可以檢測任意方向的單振和雙振動作。自由落體檢測功能可以檢測器件是否正在掉落。這些功能可以獨立對映到兩個中斷輸出引腳中的一個。正在申請專利的整合式儲存器管理系統採用一個32級先進先出(FIFO)緩衝器,可用於儲存資料,從而將主機處理器負荷降至最低,並降低整體系統功耗。
低功耗模式支援基於運動的智慧電源管理,從而以極低的功耗進行閾值感測和運動加速度測量。
ADXL345是一款完整的3軸加速度測量系統,可選擇的測量範圍有±2 g,±4 g,±8 g或±16 g。既能測量運動或衝擊導致的動態加速度,也能測量靜止加速度,例如重力加速度,使得器件可作為傾斜感測器使用。
2、驅動設計與實現
我們對ADXL345驅動設計與其它裝置一樣。我們先抽象物件型別並考慮對物件的初始化和操作。
2.1、物件定義
基於物件的操作至少要包括3方面內容:物件的定義,物件的初值以及物件的操作。接下來我們就從這裡個方面入手設計並實現ADXL345的驅動。
2.1.1、抽象物件型別
對於ADXL345也同時支援SPI介面通訊和I2C介面通訊。所以我們在抽象ADXL345物件型別時將介面型別作為屬性以區別不同的特性。在使用I2C時,裝置有地址以區別不同的裝置,所以我們將I2C裝置地址也定義為屬性。而使用SPI時,沒有裝置地址但有片選訊號,我們將對片選的操作定義為物件的操作函式。
1 /*定義ADXL345三軸資料結構*/ 2 typedef struct Adxl345Object { 3 uint8_t devAddress; 4 uint8_t devID; 5 Adxl345PortType port; 6 int16_t incidence_X; 7 int16_t incidence_Y; 8 int16_t incidence_Z; 9 10 float incidence_Xf; 11 float incidence_Yf; 12 float incidence_Zf; 13 14 void (*ReadBytes)(struct Adxl345Object *adxl,uint8_t regAdd,uint8_t *rData,uint16_t rSize); //讀ADXL345暫存器操作 15 void (*WriteBytes)(struct Adxl345Object *adxl,uint8_t regAdd,uint8_t *wData,uint16_t wSize);//寫ADXL345暫存器操作 16 void (*ChipSelect)(Adxl345CSType en); //使用SPI介面時,片選操作 17 void (*Delayus)(volatile uint32_t nTime); /*實現us延時操作*/ 18 }Adxl345ObjectType;
2.1.2、物件初始化函式
一個物件必須賦初值方可使用,所以我們還需要一個初始化函式來對物件初始化。初始化函式除了為物件屬性賦初始值和給操作指定函式指標外,還需要檢測引數的合法性以及對硬體裝置做必要的配置。基於此我們設計ADXL345的初始化函式如下:
1 /*對ADXL345進行初始化配置*/ 2 void Adxl345Initialization(Adxl345ObjectType *adxl,uint8_t devAdd, 3 Adxl345PortType port, 4 Adxl345ReadBytesType read, 5 Adxl345WriteBytesType write, 6 Adxl345ChipSelectType cs, 7 Adxl345DelayType delay) 8 { 9 uint8_t devID=0; 10 uint8_t setValue=0; 11 12 if((adxl==NULL)||(read==NULL)||(write=NULL)||(delay==NULL)) 13 { 14 return; 15 } 16 17 if(port==SPI) 18 { 19 if(cs==NULL) 20 { 21 return; 22 } 23 24 adxl->ChipSelect=cs; 25 adxl->devAddress=0x00; 26 } 27 else 28 { 29 if((devAdd==0xA6)||(devAdd==0x3A)) 30 { 31 adxl->devAddress=devAdd; 32 } 33 else if((devAdd==0x53)||(devAdd==0x1D)) 34 { 35 adxl->devAddress=(devAdd<<1); 36 } 37 else 38 { 39 adxl->devAddress=0x00; 40 } 41 adxl->ChipSelect=NULL; 42 } 43 44 adxl->port=port; 45 adxl->devID=0xE5; 46 adxl->incidence_X=0; 47 adxl->incidence_Xf=0.0; 48 adxl->incidence_Y=0; 49 adxl->incidence_Yf=0.0; 50 adxl->incidence_Z=0; 51 adxl->incidence_Zf=0.0; 52 53 adxl->ReadBytes=read; 54 adxl->WriteBytes=write; 55 adxl->Delayus=delay; 56 57 /*讀取裝置ID,在每次操作前讀一次*/ 58 devID=Adxl345ReadRegister(adxl,REG_DEVID); 59 if(adxl->devID!=devID) 60 { 61 return; 62 } 63 adxl->Delayus(300); 64 65 /*配置資料格式*/ 66 setValue = 0x2B; 67 Adxl345WriteRegister(adxl,REG_DATA_FORMAT,setValue); 68 adxl->Delayus(50); 69 70 /*配置資料速率及功率模式*/ 71 setValue = 0x0A; 72 Adxl345WriteRegister(adxl,REG_BW_RATE,setValue); 73 adxl->Delayus(50); 74 75 /*配置電源特性*/ 76 setValue = 0x28; 77 Adxl345WriteRegister(adxl,REG_POWER_CTL,setValue); 78 adxl->Delayus(50); 79 80 /*配置中斷使能*/ 81 setValue = 0; 82 Adxl345WriteRegister(adxl,REG_INT_ENABLE,setValue); 83 adxl->Delayus(50); 84 85 /*配置X軸偏移*/ 86 Adxl345WriteRegister(adxl,REG_OFSX,setValue); 87 adxl->Delayus(50); 88 89 /*配置Y軸偏移*/ 90 Adxl345WriteRegister(adxl,REG_OFSY,setValue); 91 adxl->Delayus(50); 92 93 /*配置Z軸偏移*/ 94 Adxl345WriteRegister(adxl,REG_OFSZ,setValue); 95 adxl->Delayus(500); 96 }
2.2、物件操作
我們定義一個物件的目的是操作這個物件,這也是驅動程式的主要內容。接下來我們就來實現對ADXL345物件的操作函式。
2.2.1、寫資料操作
對ADXL345物件的寫操作因為使用的介面不同其資料幀格式也會有不同。使用SPI介面時,其資料幀格式如下:
而使用I2C介面時,可以同時寫多個暫存器,其資料幀格式如下:
根據上述的資料幀格式和時序圖我們可以編寫寫ADXL345的暫存器函式:
1 /* 寫ADXL345的暫存器 */ 2 static void Adxl345WriteRegister(Adxl345ObjectType *adxl,uint8_t regAdd,uint8_t wData) 3 { 4 5 if(adxl->port==SPI) 6 { 7 adxl->ChipSelect(ADXL345CS_Enable); 8 adxl->Delayus(50); 9 adxl->WriteBytes(adxl,regAdd,&wData,1); 10 adxl->Delayus(50); 11 adxl->ChipSelect(ADXL345CS_Disable); 12 } 13 else 14 { 15 adxl->WriteBytes(adxl,regAdd,&wData,1); 16 } 17 }
2.2.2、讀資料操作
對ADXL345物件的讀操作也同樣在使用不同的介面時擁有不同的資料幀結構。使用SPI介面時,其資料幀格式如下:
而在使用I2C介面時,可以實現一個或多個暫存器的讀操作,其資料幀格式如下:
根據以上的資料幀格式和時序圖我們可以開發讀取ADXL345的暫存器操作函式:
1 /* 讀取ADXL345的暫存器 */ 2 static uint8_t Adxl345ReadRegister(Adxl345ObjectType *adxl,uint8_t regAdd) 3 { 4 uint8_t regValue=0; 5 6 if(adxl->port==SPI) 7 { 8 adxl->ChipSelect(ADXL345CS_Enable); 9 adxl->Delayus(50); 10 adxl->ReadBytes(adxl,regAdd,®Value,1); 11 adxl->Delayus(50); 12 adxl->ChipSelect(ADXL345CS_Disable); 13 } 14 else 15 { 16 adxl->ReadBytes(adxl,regAdd,®Value,1); 17 } 18 19 return regValue; 20 }
2.2.3、測量資料輸出
我們操作ADXL345物件的目的當然是獲取我們想要的資料。最基本的,我們開發從ADXL345獲取3個座標資料。
1 /*讀取資料值,解析度(3.9 mg/LSB)*/ 2 void GetValueFromAdxl345(Adxl345ObjectType *adxl) 3 { 4 uint8_t devID = 0; 5 uint8_t dataTemp[6]; 6 7 /*讀取裝置ID,在每次操作前讀一次*/ 8 devID=Adxl345ReadRegister(adxl,REG_DEVID); 9 if(adxl->devID!=devID) 10 { 11 return; 12 } 13 adxl->Delayus(200); 14 15 /*讀取三軸資料值*/ 16 Adxl345ReadMultiReg(adxl,REG_DATAX0,dataTemp,6); 17 18 /*資料解析*/ 19 adxl->incidence_X = (int16_t)(dataTemp[0] + ((uint16_t)dataTemp[1] << 8)); 20 adxl->incidence_Y = (int16_t)(dataTemp[2] + ((uint16_t)dataTemp[3] << 8)); 21 adxl->incidence_Z = (int16_t)(dataTemp[4] + ((uint16_t)dataTemp[5] << 8)); 22 23 adxl->incidence_Xf = (float)(adxl->incidence_X * 0.0039); 24 adxl->incidence_Yf = (float)(adxl->incidence_Y * 0.0039); 25 adxl->incidence_Zf = (float)(adxl->incidence_Z * 0.0039); 26 }
3、驅動的使用
完成了驅動的設計開發,我們還要使用驅動實現ADXL345的應用。與其它外設一樣,我們也按照實際專案的使用流程來驗證之。
3.1、宣告並初始化物件
首先我們使用前面定義的Adxl345ObjectType型別宣告一個ADXL345物件變數。如:Adxl345ObjectType adxl345;
宣告物件變數後還需要呼叫Adxl345Initialization初始化函式對ADXL345物件變數進行初始化。當然在呼叫初始化函式前需要考慮傳入的引數。特別是幾個函式指標需要實現響應的函式。需要實現的函式型別如下:
typedef void (*Adxl345ReadBytesType)(struct Adxl345Object *adxl,uint8_t regAdd,uint8_t *rData,uint16_t rSize); //讀ADXL345暫存器操作
typedef void (*Adxl345WriteBytesType)(struct Adxl345Object *adxl,uint8_t regAdd,uint8_t *wData,uint16_t wSize);//寫ADXL345暫存器操作
typedef void (*Adxl345ChipSelectType)(Adxl345CSType en); //使用SPI介面時,片選操作
typedef void (*Adxl345DelayType)(volatile uint32_t nTime); /*實現us延時操作*/
定義這幾個函式後,就可以將器函式指標作為實參傳遞給初始化函式。呼叫如下:
Adxl345Initialization(&adxl345,devAdd,port,read,write,cs,delay);
其中adxl345為需要初始化的ADXL345物件。devAdd為使用I2C通訊時的裝置地址,使用SPI時無用。port為通訊埠的型別,SPI或者I2C。read讀操作函式指標,是對硬體層的封裝。write為寫操作函式指標,是對硬體層的封裝。cs為使用SPI介面時,片選操作函式指標。delay為延時函式的指標。
3.2、基於物件進行操作
物件初始化完成後就可進行相應的操作。ADXL345的操作比較簡單就是呼叫GetValueFromAdxl345函式獲取我們需要的資料。具體的呼叫樣式如下:
GetValueFromAdxl345(&adxl345);
這個使用比較簡單,因為我們在初始化時將資料格式、資料速率及功率模式、電源特性、中斷使能、各軸的資料偏移量等都按我們的需要在初始化時作了配置。如果需要不同配置則需要做相應的修改。
4、應用總結
在我們的應用中,我們將其設定為全解析度,±16g的測量範圍,讀取資料與預期一致。
使用I2C介面時,裝置地址使用7位輸入或8位輸入都沒問題,地址一共有4種可能。其他的都為非法地址,在地址輸入不符合要求時,會被預設初始化為廣播地址。
在使用SPI介面時,如果是通過軟體操作片選訊號則需要實現操作函式並將函式指標傳遞給初始化函式。如果硬體上採取永久選中的形式則可將NULL作為引數傳入。
歡迎關注: