外設驅動庫開發筆記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引腳處於高電平,器件的7I2C地址是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時,沒有設備地址但有片選信號,我們將對片選的操作定義爲對象的操作函數。

/*定義ADXL345三軸數據結構*/
typedef struct Adxl345Object {
       uint8_t devAddress;
       uint8_t devID;
       Adxl345PortType port;
       int16_t incidence_X;
       int16_t incidence_Y;
       int16_t incidence_Z;
      
       float incidence_Xf;
       float incidence_Yf;
       float incidence_Zf;
      
       void (*ReadBytes)(struct Adxl345Object *adxl,uint8_t regAdd,uint8_t *rData,uint16_t rSize);  //讀ADXL345寄存器操作
       void (*WriteBytes)(struct Adxl345Object *adxl,uint8_t regAdd,uint8_t *wData,uint16_t wSize);//寫ADXL345寄存器操作
       void (*ChipSelect)(Adxl345CSType en);    //使用SPI接口時,片選操作
       void (*Delayus)(volatile uint32_t nTime);      /*實現us延時操作*/
}Adxl345ObjectType;

2.1.2、對象初始化函數

一個對象必須賦初值方可使用,所以我們還需要一個初始化函數來對對象初始化。初始化函數除了爲對象屬性賦初始值和給操作指定函數指針外,還需要檢測參數的合法性以及對硬件設備做必要的配置。基於此我們設計ADXL345的初始化函數如下:

/*對ADXL345進行初始化配置*/
void Adxl345Initialization(Adxl345ObjectType *adxl,uint8_t devAdd,
                           Adxl345PortType port,
                           Adxl345ReadBytesType read,
                           Adxl345WriteBytesType write,
                           Adxl345ChipSelectType cs,
                           Adxl345DelayType delay)
{
       uint8_t devID=0;
       uint8_t setValue=0;
      
       if((adxl==NULL)||(read==NULL)||(write=NULL)||(delay==NULL))
       {
              return;
       }
      
       if(port==SPI)
       {
              if(cs==NULL)
              {
                     return;
              }
             
              adxl->ChipSelect=cs;
              adxl->devAddress=0x00;
       }
       else
       {
              if((devAdd==0xA6)||(devAdd==0x3A))
              {
                     adxl->devAddress=devAdd;
              }
              else if((devAdd==0x53)||(devAdd==0x1D))
              {
                     adxl->devAddress=(devAdd<<1);
              }
              else
              {
                     adxl->devAddress=0x00;
              }
              adxl->ChipSelect=NULL;
       }
      
       adxl->port=port;
       adxl->devID=0xE5;
       adxl->incidence_X=0;
       adxl->incidence_Xf=0.0;
       adxl->incidence_Y=0;
       adxl->incidence_Yf=0.0;
       adxl->incidence_Z=0;
       adxl->incidence_Zf=0.0;
      
       adxl->ReadBytes=read;
       adxl->WriteBytes=write;
       adxl->Delayus=delay;
      
       /*讀取設備ID,在每次操作前讀一次*/
       devID=Adxl345ReadRegister(adxl,REG_DEVID);
       if(adxl->devID!=devID)
       {
              return;
       }
       adxl->Delayus(300);
 
       /*配置數據格式*/
       setValue = 0x2B;
       Adxl345WriteRegister(adxl,REG_DATA_FORMAT,setValue);
       adxl->Delayus(50);
   
       /*配置數據速率及功率模式*/
       setValue = 0x0A;
       Adxl345WriteRegister(adxl,REG_BW_RATE,setValue);
       adxl->Delayus(50);
   
       /*配置電源特性*/
       setValue = 0x28;
       Adxl345WriteRegister(adxl,REG_POWER_CTL,setValue);
       adxl->Delayus(50);
   
       /*配置中斷使能*/
       setValue = 0;
       Adxl345WriteRegister(adxl,REG_INT_ENABLE,setValue);
       adxl->Delayus(50);

       /*配置X軸偏移*/
       Adxl345WriteRegister(adxl,REG_OFSX,setValue);
       adxl->Delayus(50);

       /*配置Y軸偏移*/
       Adxl345WriteRegister(adxl,REG_OFSY,setValue);
       adxl->Delayus(50);

       /*配置Z軸偏移*/
       Adxl345WriteRegister(adxl,REG_OFSZ,setValue);
       adxl->Delayus(500);
}

2.2、對象操作

我們定義一個對象的目的是操作這個對象,這也是驅動程序的主要內容。接下來我們就來實現對ADXL345對象的操作函數。

2.2.1、寫數據操作

ADXL345對象的寫操作因爲使用的接口不同其數據幀格式也會有不同。使用SPI接口時,其數據幀格式如下:

而使用I2C接口時,可以同時寫多個寄存器,其數據幀格式如下:

根據上述的數據幀格式和時序圖我們可以編寫寫ADXL345的寄存器函數:

/* 寫ADXL345的寄存器 */
static void Adxl345WriteRegister(Adxl345ObjectType *adxl,uint8_t regAdd,uint8_t wData)
{
       if(adxl->port==SPI)
       {
              adxl->ChipSelect(ADXL345CS_Enable);
              adxl->Delayus(50);
              adxl->WriteBytes(adxl,regAdd,&wData,1);
              adxl->Delayus(50);
              adxl->ChipSelect(ADXL345CS_Disable);
       }
       else
       {
              adxl->WriteBytes(adxl,regAdd,&wData,1);
       }
}

2.2.2、讀數據操作

ADXL345對象的讀操作也同樣在使用不同的接口時擁有不同的數據幀結構。使用SPI接口時,其數據幀格式如下:

而在使用I2C接口時,可以實現一個或多個寄存器的讀操作,其數據幀格式如下:

根據以上的數據幀格式和時序圖我們可以開發讀取ADXL345的寄存器操作函數:

/* 讀取ADXL345的寄存器 */
static uint8_t Adxl345ReadRegister(Adxl345ObjectType *adxl,uint8_t regAdd)
{
       uint8_t regValue=0;
      
       if(adxl->port==SPI)
       {
              adxl->ChipSelect(ADXL345CS_Enable);
              adxl->Delayus(50);
              adxl->ReadBytes(adxl,regAdd,&regValue,1);
              adxl->Delayus(50);
              adxl->ChipSelect(ADXL345CS_Disable);
       }
       else
       {
              adxl->ReadBytes(adxl,regAdd,&regValue,1);
       }
      
       return regValue;
}

2.2.3、測量數據輸出

我們操作ADXL345對象的目的當然是獲取我們想要的數據。最基本的,我們開發從ADXL345獲取3個座標數據。

/*讀取數據值,分辨率(3.9 mg/LSB)*/
void GetValueFromAdxl345(Adxl345ObjectType *adxl)
{
  uint8_t devID = 0;
  uint8_t dataTemp[6];

  /*讀取設備ID,在每次操作前讀一次*/
  devID=Adxl345ReadRegister(adxl,REG_DEVID);
  if(adxl->devID!=devID)
  {
     return;
  }
  adxl->Delayus(200);
 
  /*讀取三軸數據值*/
  Adxl345ReadMultiReg(adxl,REG_DATAX0,dataTemp,6);
 
  /*數據解析*/
  adxl->incidence_X = (int16_t)(dataTemp[0] + ((uint16_t)dataTemp[1] << 8));
  adxl->incidence_Y = (int16_t)(dataTemp[2] + ((uint16_t)dataTemp[3] << 8));
  adxl->incidence_Z = (int16_t)(dataTemp[4] + ((uint16_t)dataTemp[5] << 8));
      
  adxl->incidence_Xf = (float)(adxl->incidence_X * 0.0039);
  adxl->incidence_Yf = (float)(adxl->incidence_Y * 0.0039);
  adxl->incidence_Zf = (float)(adxl->incidence_Z * 0.0039);
}

3、驅動的使用

完成了驅動的設計開發,我們還要使用驅動實現ADXL345的應用。與其它外設一樣,我們也按照實際項目的使用流程來驗證之。

3.1、聲明並初始化對象

首先我們使用前面定義的Adxl345ObjectType類型聲明一個ADXL345對象變量。如:Adxl345ObjectType adxl345

聲明對象變量後還需要調用Adxl345Initialization初始化函數對ADXL345對象變量進行初始化。當然在調用初始化函數前需要考慮傳入的參數。特別是幾個函數指針需要實現響應的函數。需要實現的函數類型如下:

typedef void (*Adxl345ReadBytesType)(struct Adxl345Object *adxluint8_t regAdduint8_t *rDatauint16_t rSize);    //ADXL345寄存器操作

typedef void (*Adxl345WriteBytesType)(struct Adxl345Object *adxluint8_t regAdduint8_t *wDatauint16_t wSize);//ADXL345寄存器操作

typedef void (*Adxl345ChipSelectType)(Adxl345CSType en);       //使用SPI接口時,片選操作

typedef void (*Adxl345DelayType)(volatile uint32_t nTime);      /*實現us延時操作*/

定義這幾個函數後,就可以將器函數指針作爲實參傳遞給初始化函數。調用如下:

Adxl345Initialization(&adxl345devAddportreadwritecsdelay);

其中adxl345爲需要初始化的ADXL345對象。devAdd爲使用I2C通訊時的設備地址,使用SPI時無用。port爲通訊端口的類型,SPI或者I2Cread讀操作函數指針,是對硬件層的封裝。write爲寫操作函數指針,是對硬件層的封裝。cs爲使用SPI接口時,片選操作函數指針。delay爲延時函數的指針。

3.2、基於對象進行操作

對象初始化完成後就可進行相應的操作。ADXL345的操作比較簡單就是調用GetValueFromAdxl345函數獲取我們需要的數據。具體的調用樣式如下:

GetValueFromAdxl345(&adxl345);

這個使用比較簡單,因爲我們在初始化時將數據格式、數據速率及功率模式、電源特性、中斷使能、各軸的數據偏移量等都按我們的需要在初始化時作了配置。如果需要不同配置則需要做相應的修改。

4、應用總結

在我們的應用中,我們將其設置爲全分辨率,±16g的測量範圍,讀取數據與預期一致。

使用I2C接口時,設備地址使用7位輸入或8位輸入都沒問題,地址一共有4種可能。其他的都爲非法地址,在地址輸入不符合要求時,會被默認初始化爲廣播地址。

在使用SPI接口時,如果是通過軟件操作片選信號則需要實現操作函數並將函數指針傳遞給初始化函數。如果硬件上採取永久選中的形式則可將NULL作爲參數傳入。

歡迎關注:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章