外設驅動庫開發筆記6:AD719x系列ADC驅動

前面我們討論了AD7705這種ADC器件的驅動開發,在實際中我們使用更多的是AD719x系列的ADC芯片、包括有AD7191AD7192AD7193等。接下來我們就來設計並開發AD719x的驅動程序。

1、功能概述

AD7192是一款適合高精密測量應用的低噪聲完整模擬前端,內置一個低噪聲、 24 Σ-Δ型模數轉換器 (ADC)。片內低噪聲增益級意味着可直接輸入小信號。

1.1、硬件結構

AD7192可配置爲兩路差分輸入或四路僞差分輸入。片內通道序列器可以使能多個通道,AD7192 按順序在各使能通道上執行轉換,這可以簡化與器件的通信。 片內 4.92 MHz時鐘可以用作 ADC 的時鐘源; 或者也可以使用外部時鐘或晶振。 該器件的輸出數據速率可在 4.7 Hz 4.8 kHz 的範圍內變化。

AD7192提供兩種數字濾波器選項。 濾波器的選擇會影響以編程輸出數據速率工作時的均方根噪聲和無噪聲分辨率、建立時間以及 50 Hz/60 Hz 抑制。 針對要求所有轉換均需建立的應用, AD7192 具有零延遲特性。

其功能結構圖如下:

1.2、內部寄存器

AD7192內部具有多個寄存器,對AD7192的操作就是通過這些片內寄存器進行控制和數據寄存器/數據寄存器加狀態信息配置。這些寄存器包括:通信寄存器、狀態寄存器、模式寄存器、配置寄存器、ID寄存器、GPOCON寄存器、失調寄存器以及滿量程寄存器。其中通信寄存器和狀態寄存器共享地址,讀操作時針對的是狀態寄存器,寫操作時針對的是通訊寄存器。對任何寄存器的操作都是從寫通訊寄存器開始。

1.2.1、通信寄存器

通信寄存器是一個 8 位只寫寄存器。與該器件的所有通信均必須以對通信寄存器的寫操作開始。寫入通信寄存器的數據決定下一個操作是讀操作還是寫操作,以及此操作發生在哪一個寄存器。通訊寄存器的格式如下:

其中RS2RS1RS0這些位用於指示下一次操作的寄存器是哪一個寄存器,具體如下:

我們使用比較多的就是狀態寄存器、模式寄存器、配置寄存器以及數據寄存器。後續會進一步瞭解這些寄存器。

1.2.2、狀態寄存器

狀態寄存器是一個8位只讀寄存器。要訪問ADC狀態寄存器,用戶必須寫入通信寄存器,選擇下一個操作爲讀操作,並將 0 載入位RS2、位RS1 和位RS0。狀態寄存器的格式如下:

CHD0CHD1CHD2這些位指示哪一通道對應數據寄存器的內容。這些位不是指示目前正在轉換哪一通道,而是指示產生數據寄存器所含轉換結果時選定了哪一通道。

1.2.3、模式寄存器

模式寄存器是一個24位寄存器,可以從中讀取數據,也可以將數據寫入其中。此寄存器用來選擇工作模式、輸出數據速率和時鐘源。模式寄存器的格式如下:

AD7192的工作模式有模式寄存器的MD2MD1MD0這幾位來決定。具體的配置如下:

我們配置什麼樣的工作模式決定了後續的操作。默認情況下爲連續模式,應用比較多的有單次模式、校準模式等。

1.2.4、配置寄存器

配置寄存器是一個 24 位寄存器,可以從中讀取數據,也可以將數據寫入其中。此寄存器用來配置 ADC 的單極性或雙極性模式,使能或禁用緩衝器,使能或禁用激勵電流,選擇增益,以及選擇模擬輸入通道。

其中G2G1G0等位用於配置通道的增益。可以實現1倍、8倍、16倍、32倍、64倍、128倍等增益,輸入的範圍也對應調整,具體如下:

CH7CH6CH5CH4CH3CH2CH1CH0用於設置使用的通道。每一位對應一個通道,具體配置方式如下:

其中CH0CH4CH5不可同時使用,CH1CH6CH7不能同時使用。CH0CH1是差分輸入,CH4CH5CH6CH7是單端輸入。

餘下的ID寄存器、GPOCON寄存器、失調寄存器以及滿量程寄存器比較容易理解不再說明。

2、驅動設計與實現

我們已經瞭解AD7192的基本情況,接下來我們將據此來開發AD7192的驅動程序。

2.1、對象設計

我們的驅動程序依然設計爲基於對象的操作。所以首先我們需要先設計對象。而關於對象的設計過程包括兩方面內容:對象類型的定義;對象的初始化。

2.1.1、抽象對象類型

對於一個AD7192對象,我們首先考慮到它有8個寄存器,這些寄存器決定了它的操作特性,所以我們記錄這些寄存器的值用以標識它的狀態。此外增益與極性雖然存在於配置寄存器中,但我們希望在初始化的時候明確的指定它,所以我們也將其作爲屬性以標識AD7192對象的配置狀態。

而對於片選信號的控制、轉換就緒信號的讀取,總線數據的輸入輸出這些依賴於硬件電路的操作,我們都將其作爲對象的操作來實現。此外,延時操作在不同的硬件平臺、有無操作系統的情況下,實現方式大相徑庭,因此我們也將其作爲對象的操作以便靈活處理。

據此我們可以抽象得AD7192對象類型爲:

/*定義用於操作的結構體*/
typedef struct Ad7192Object {
  uint32_t polar;                            //通道的極性
  uint32_t gain;                     //通道增益
  uint32_t Registers[8];         //存放寄存器值的數組
  void (*ReadWrite)(uint8_t *wData,uint8_t *rData,uint16_t size);            //實現讀寫操作
  void (*ChipSelect)(AD7192CSType cs);                     //實現片選
  uint16_t (*GetReadyInput)(void);                                      //實現Ready狀態監視
  void (*Delay)(volatile uint32_t nTime);               //實現ms延時操作
}Ad7192ObjectType;

2.1.2、初始化函數

在使用AD7192前先對其實行初始化,所以我們需要一個對象初始化函數。初始化函數主要包括三個方面的內容:檢查對象即各種初始輸入是否有效;爲對象的屬性賦初值以及爲操作指定函數指針;對AD7192做前期配置。

關於AD7192的前期配置,首先是軟件復位,連續寫入401就可對AD7192實現復位。復位完成後,對零點和量程進行校準。而後獲取各寄存器的當前狀態。初始化函數的具體實現代碼如下:

/*AD7192初始化配置*/
AD7192ErrorType AD7192Initialization(Ad7192ObjectType *adObj,
                                      uint32_t Channels,
                                      AD7192PolarType polar,
                                      AD7192GainType gain,
                                      AD7192ReadWriteType readWrite,
                                      AD7192ChipSelectType cs,
                                      AD7192GetReadyInputType ready,
                                      AD7192DelaymsType delayms)
{
       uint32_t polarity[]={UB_UNI,UB_BI};
       uint32_t gains[]={GAIN_1,GAIN_8,GAIN_16,GAIN_32,GAIN_64,GAIN_128};
      
       if((adObj==NULL)||(readWrite==NULL)||(ready==NULL)||(delayms==NULL))
       {
              return AD7192_InitError;
       }
      
       if(cs==NULL)
       {
              adObj->ChipSelect=AD719xChipSelect;
       }
       else
       {
              adObj->ChipSelect=cs;
       }
      
       adObj->polar=polarity[polar];
       adObj->gain=gains[gain];
      
       adObj->Registers[REG_COM_STA]=0x00;
       adObj->Registers[REG_MODE]=0x00;
       adObj->Registers[REG_CONF]=0x00;
       adObj->Registers[REG_DATA]=0x00;
       adObj->Registers[REG_ID]=0x00;
       adObj->Registers[REG_GPOCON]=0x00;
       adObj->Registers[REG_OFFSET]=0x00;
       adObj->Registers[REG_FS]=0x00;
      
       adObj->ReadWrite=readWrite;
       adObj->GetReadyInput=ready;
       adObj->Delay=delayms;
      
       AD7192SoftwareReset(adObj);
       adObj->Delay(1);
       AD7192InternalZeroScaleCalibration(adObj,Channels);
       adObj->Delay(1);
       AD7192InternalFullScaleCalibration(adObj,Channels);

       /*讀取並存儲全部寄存器的值*/
       ReadAD7192Register(adObj,REG_COM_STA, 8, REG_COM_STA);

       return AD7192_OK;
}

2.2、對象操作

對象初始化後就可對其進行操作。我們無論對AD7192進行何種操作,其目的都是爲了得到我們想要的數據。而從AD7192讀取轉換的結果有2種方式:單次獲取和連續獲取。接下來我們就按此來說明AD7192的驅動設計。

2.2.1、實現單次數據轉換

單次轉換模式下,AD7192 在完成轉換後處於關斷模式。 將模式寄存器中的MD2MD1MD0分別設置爲001,便可啓動單次轉換,此時AD7192將上電,執行單次轉換,然後返回關斷模式。時序圖如下所示:

單次轉換數據獲取具體實現代碼如下:

/*單次轉換數據獲取*/
uint32_t GetSingleConvertionValue(Ad7192ObjectType *adObj,uint32_t Channels)
{
  uint32_t dataCode=0;
  AD7192StartSingleConvertion(adObj,Channels);
 
  adObj->Delay(1);
 
  dataCode = AD7192ReadConvertingData(adObj);
  dataCode =dataCode & 0x00FFFFFF;

  ReadAD7192Register(adObj,REG_DATA, 1,REG_DATA);
 
  return dataCode;
}

2.2.2、實現連續數據轉換

連續轉換模式是上電後的默認轉換模式。AD7192連續轉換,每次完成轉換後,狀態寄存器中的RDY位變爲低電平。如果CS爲低電平,則完成一次轉換時,DOUT/RDY 線路也會變爲低電平。若要讀取轉換結果,用戶需要寫入通信寄存器,指示下一操作爲讀取數據寄存器。從數據寄存器中讀取數據字後,DOUT/RDY變爲高電平。時序圖如下所示:

連續轉換數據獲取具體實現代碼如下:

/*連續轉換數據獲取,dataCodes爲8個元素的數組對應8個通道*/
void GettContinuousConvertionValue(Ad7192ObjectType *adObj,uint32_t Channels,uint32_t *dataCodes,int number)
{
  uint32_t dataCode=0;
  uint8_t status=255;
  AD7192StartContinuousConvertion(adObj,Channels);
 
  for(int i=0;i<number;i++)
  {
    dataCode = AD7192ReadConvertingData(adObj);
    status=((uint8_t)dataCode)&0x07;
    dataCode =(dataCode>>8) & 0x00FFFFFF;
    dataCodes[status]=dataCode;
  }
}

2.2.3、零點和量程校準

零點和量程校準包括內部校準和外部校準,我們這裏使用內部校準。

/*內部零點校準*/
static void AD7192InternalZeroScaleCalibration(Ad7192ObjectType *adObj,uint32_t Channels)
{
  //配置寄存器:斬波禁用,基準電壓1,AI1-AI4單通道4路,禁用激勵電流,禁用基準電壓檢測,禁用緩衝器
  adObj->Registers[REG_CONF] = 0;
  adObj->Registers[REG_CONF] = CHOP_DIS|REF_IN1|Channels|BURN_DIS|REFDET_DIS|BUF_DIS|adObj->polar|adObj->gain;
  WriteAD7192Register(adObj,REG_CONF,1);

  //模式寄存器:內部零點校準,禁用狀態同傳,內部時鐘輸出,斬波4,使能奇偶校驗,不分頻,僅用單週期轉換使能,禁用60Hz陷波,FS=128
  adObj->Registers[REG_MODE] = 0;
  adObj->Registers[REG_MODE] = MODE_INZCL|DAT_STA_DIS|INCLK_MCLK2EN|SINC_4|ENPAR_EN|CLK_DIV_DIS|SINGLECYCLE_DIS|REJ60_DIS|0x080;
  WriteAD7192Register(adObj,REG_MODE, 1);

  adObj->ChipSelect(AD7192CS_Enable);
  while(adObj->GetReadyInput()== 1){;}               //等待RDY爲0;
  adObj->ChipSelect(AD7192CS_Disable);
}

/*內部量程校準*/
static void AD7192InternalFullScaleCalibration(Ad7192ObjectType *adObj,uint32_t Channels)
{
  //配置寄存器:斬波禁用,基準電壓1,AI1-AI4單通道4路,禁用激勵電流,禁用基準電壓檢測,禁用緩衝器
  adObj->Registers[REG_CONF] = 0;
  adObj->Registers[REG_CONF] = CHOP_DIS|REF_IN1|Channels|BURN_DIS|REFDET_DIS|BUF_DIS|adObj->polar|adObj->gain;
  WriteAD7192Register(adObj,REG_CONF, 1);
 
  //模式寄存器:內部量程校準,禁用狀態同傳,內部時鐘輸出,斬波4,使能奇偶校驗,不分頻,禁用單週期轉換使能,禁用60Hz陷波,FS=128
  adObj->Registers[REG_MODE] = 0;
  adObj->Registers[REG_MODE] = MODE_INFCL|DAT_STA_DIS|INCLK_MCLK2EN|SINC_4|ENPAR_EN|CLK_DIV_2|SINGLECYCLE_DIS|REJ60_DIS|0x080;             
  WriteAD7192Register(adObj,REG_MODE, 1);

  adObj->ChipSelect(AD7192CS_Enable);
  while(adObj->GetReadyInput()== 1){;}               //等待RDY爲0;
  adObj->ChipSelect(AD7192CS_Disable);
}

2.2.4、實現內部溫度轉換

AD7192內置一個溫度傳感器。利用配置寄存器中的CH2位可以選擇溫度傳感器。如果CH2位設置爲1,就會使能溫度傳感器。使用溫度傳感器並選擇雙極性模式時,如果溫度爲0K,器件應返回0x800000碼。爲使傳感器發揮最佳性能,需要執行單點校準。因此,應記錄25°C 時的轉換結果並計算靈敏度。 靈敏度約爲2815 /°C。溫度傳感器的計算公式爲

溫度 (K) = ( 轉換結果 – 0x800000)/2815 K

溫度 (°C) = 溫度 (K) – 273

單點校準之後,內部溫度傳感器的精度典型值爲 ±2°C。具體的實現代碼如下:

/*讀取內部溫度數據,返回攝氏度溫度*/
float GetTemperatureValue(Ad7192ObjectType *adObj)
{
  uint32_t temperatureCode=0;
  float temp = 0.0;
  //模式寄存器:單次轉換模式,禁用狀態同傳,內部時鐘輸出,斬波4,使能奇偶校驗,不分頻,禁用單週期轉換使能,禁用60Hz陷波,FS=128
  adObj->Registers[REG_MODE] = 0;
  adObj->Registers[REG_MODE] = MODE_SING|DAT_STA_DIS|INCLK_MCLK2EN|SINC_4|ENPAR_EN|CLK_DIV_DIS|SINGLECYCLE_DIS|REJ60_DIS|0x080;
  WriteAD7192Register(adObj,REG_MODE, 1);
  //配置寄存器:斬波禁用,基準電壓1,內部溫度,禁用激勵電流,禁用基準電壓檢測,禁用緩衝器
  adObj->Registers[REG_CONF] = 0; 
  adObj->Registers[REG_CONF] = CHOP_DIS|REF_IN1|TEMP|BURN_DIS|REFDET_DIS|BUF_DIS|UB_BI|GAIN_1;
  WriteAD7192Register(adObj,REG_CONF, 1);

  temperatureCode = AD7192ReadConvertingData(adObj);
  temp = (temperatureCode-0x800000)/2815.0-273;
  return temp;
}

3、驅動的使用

我們已經設計並實現了AD7192模數轉換器的驅動。現在我們將考慮如何使用這一驅動實現基於AD7192模數轉換器的簡單應用。

3.1、聲明並初始化對象

使用基於對象的操作我們需要先得到這個對象,所以我們先要使用前面定義的AD7192模數轉換器對象類型聲明一個AD7192模數轉換器對象變量,具體操作格式如下:

Ad7192ObjectType ad7192;

聲明瞭這個對象變量並不能立即使用,我們還需要使用驅動中定義的初始化函數對這個變量進行初始化。這個初始化函數所需要的輸入參數如下:

Ad7192ObjectType *adObj,所要初始化的對象變量

uint32_t Channels,需要初始化的通道

AD7192PolarType polar,通道初始化的極性

AD7192GainType gain,通道的增益

AD7192ReadWriteType readWrite,讀寫操作函數

AD7192ChipSelectType cs,片選信號操作函數

AD7192GetReadyInputType ready,就緒信號檢測函數

AD7192DelaymsType delayms,毫秒延時函數

對於這些參數,對象變量我們已經定義了。所需要初始化的通道、通道的信號極性、採用的增益倍數根據實際情況選擇,通道爲宏定義、極性和增益爲枚舉。主要的是我們需要定義幾個函數,並將函數指針作爲參數。這幾個函數的類型如下:

/*定義讀寫操作函數指針類型*/
typedef void (*AD7192ReadWriteType)(uint8_t *wData,uint8_t *rData,uint16_t size);

/*實現片選*/
typedef void (*AD7192ChipSelectType)(AD7192CSType cs);

/*實現Ready狀態監視*/
typedef uint16_t (*AD7192GetReadyInputType)(void);

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

對於這幾個函數我們根據樣式定義就可以了,具體的操作可能與使用的硬件平臺有關係。片選操作函數用於多設備需要軟件操作時,如採用硬件片選可以傳入NULL即可。具體函數定義如下:

/*定義讀寫操作函數指針類型*/
void AD7192ReadWrite(uint8_t *wData,uint8_t *rData,uint16_t size)
{
       HAL_SPI_TransmitReceive(&ad77192hspi,wData,rData,size,1000);
}

/*實現片選*/
void AD7192ChipSelect(AD7192CSType cs)
{
       if(AD7192CS_Enable==cs)
       {
              HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_RESET);
       }
       else
       {
              HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_SET);
       }
}

/*實現Ready狀態監視*/
uint16_t AD7192GetReadyInput(void)
{
       return HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_0);
}

對於延時函數我們可以採用各種方法實現。我們採用的STM32平臺和HAL庫則可以直接使用HAL_Delay()函數。於是我們可以調用初始化函數如下:

AD7192Initialization(&ad7192AIN1_AIN2|AIN3_AIN4AD7192_UnipolarAD7192Gain1AD7192ReadWriteAD7192ChipSelectAD7192GetReadyInputHAL_Delay);

3.2、基於對象進行操作

我們定義了對象變量並使用初始化函數給其作了初始化。接着我們就來考慮操作這一對象獲取我們想要的數據。在驅動中我們已經封裝了單次或是連續獲取某一通道模數轉換數據的函數。接下來我們就採用我們封裝的AD7192數模轉換其的驅動獲取通道的數據。

/* 單次獲取兩個差分通道的值 */
void GetDifferentialData(void)
{
       uint16_t dataCode[2];
      
       dataCode[0]=GetSingleConvertionValue(&ad7192,AIN1_AIN2);

       dataCode[1]=GetSingleConvertionValue(&ad7192,AIN3_AIN4);
}

因爲初始化函數初始化爲兩個差分通道,所以我們的函數就是獲取了兩個差分通道的轉換值。獲取了ADC的數據後就可以根據每個通道所對應的物理量量程範圍計算得到物理量值。

4、應用總結

在這一篇中我們設計並實現了AD7192數模轉換器的驅動程序。並使用這一驅動程序實現了單次獲取兩個差分通道轉換數據的簡單應用。得到了我們想要的結果,這說明我們的驅動設計是正確的。

在使用驅動時需要注意。選擇通道時,由於可以是2個差分通道和4個單端通道,所以差分通道一不可以與AIN1AIN2同時使用,同樣的差分通道二不能與AIN3AIN4同時使用。

在使用驅動時需注意,採用SPI接口的器件需要考慮片選操作的問題。如果片選信號是通過硬件電路來實現的,我們在初始化時給其傳遞NULL值。如果是軟件操作片選則傳遞我們編寫的片選操作函數。

完整的源代碼可在GitHub下載:https://github.com/foxclever/ExPeriphDriver

歡迎關注:

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