外設驅動庫開發筆記5:AD7705系列ADC驅動

我們的經常需要採集一些精度要求較高的模擬信號,使用MCU集成的ADC難以達到要求、所以我們需要獨立的ADC芯片。這一節我們就來設計並實現AD7705芯片的驅動、並探討驅動的使用方法。

1、功能概述

AD7705/AD7706是用於低頻測量的完整模擬前端。可以直接從傳感器接收低電平輸入信號,併產生串行數字輸出。

1.1、硬件結構

AD7705AD7706均爲完整16位、低成本、Σ-ΔADC,適合直流和低頻交流測量應用。其具有低功耗(3 V時最大值爲1 mW)特性,因而可用於環路供電、電池供電或本地供電的應用中。片內可編程增益放大器提供從1128的增益設置,無需使用外部信號調理硬件便可接受低電平和高電平模擬輸入。

AD7705擁有兩個差分通道,而AD7706則擁有一個差分通道和兩個僞差分通道。在定製比率應用器件時,差分基準電壓輸入還能提供極大的靈活性。採用16引腳封裝,具體的定義及結構如下:

AD7705/AD7706設備的工作電壓從2.7 V3.3 V4.75 V5.25 V不等。在VDD5v和參考電壓爲2.5 V的情況下,輸入信號範圍從0 mV20 mV,從0 V2.5 V,都可以在這兩種設備上使用。在VDD3v和參考電壓爲1.25 V的情況下,可以處理0 mV10 mV0 V1.225 V的單極輸入信號範圍。

1.2、片上寄存器

AD7705/AD7706每個包含8個片上寄存器,可以通過串口訪問。第一個是通信寄存器,第二個是配置寄存器,第三個是時鐘寄存器,第四個是數據寄存器,餘下的是校準寄存器。具體如下:

需要說明的是測試寄存器,不要改變此寄存器中的任何位的狀態,這個寄存器用於設備測試。

1.2.1、通信寄存器

通信寄存器用於控制通道選擇,並決定下一個操作是讀操作還是寫操作,並決定下一個讀操作或寫操作訪問哪個寄存器。通訊寄存器各位含義如下:

所有到AD7705/AD7706的通信都必須從通信寄存器的寫操作開始。寫入該寄存器的數據決定下一個操作是讀操作還是寫操作,以及該操作發生在哪個寄存器。其中RS2–RS0三位決定下一操作是針對哪一個寄存器進行的。

CH1 CH0兩位決定是針對哪一通道的操作。

1.2.2、配置寄存器

配置寄存器是一個8位寄存器,可以從中讀取或寫入數據,用於確定校準模式、增益設置、雙極/單極操作和緩衝模式。

1.2.3、時鐘寄存器

時鐘寄存器是一個8位寄存器,可以從中讀取或寫入數據,,幷包含篩選器選擇位和時鐘控制位。

1.2.4、數據寄存器

數據寄存器是一個16位的只讀寄存器,它包含來自AD7705/AD7706的最新轉換結果。如果通信寄存器爲該寄存器的寫操作設置了部件,則必須執行寫操作才能將部件返回到其默認狀態。然而,寫入該部分的16位數據將被AD7705/AD7706忽略。

1.2.5、校準寄存器

校準寄存器是一系列的寄存器對,用於存儲通道校準數據。每對包括一個零標度校準寄存器和一個滿標度校準寄存器,並對應一個通道。當開啓系統零點或量程校準時,將根據對應通道上的數據來校準。當然如果有必要,也可以通過數字接口來讀寫這些寄存器。

2、驅動設計與實現

我們已經瞭解了AD7705模數轉換器的結構及內部寄存器配置,接下來我們將根據我們的瞭解設計並實現AD7705模數轉換器的驅動。

2.1、對象定義

我們對AD7705模數轉換器的操作依然是基於對象的,所以我們要先得到對象。首先的工作當然是抽象得到對象的特性進而得到我們需要的對象。

2.1.1、抽象對象類型

一個對象最起碼包含屬性和操作兩方面內容,我們先來分析一下AD7705模數轉換器對象需要包含哪些屬性和操作。

對於AD7705模數轉換器來說,總共有8個寄存器,這些寄存器是實現操作的基礎,我們要配置並瞭解這些寄存器的狀態,所以我們將這些寄存器抽象爲對象的屬性,以便隨時掌握操作的目標。

進而我們考慮AD7705模數轉換器對象的操作。首先我們要操作AD7705模數轉換器就是向其發送命令和讀取數據,所以我們將向AD7705模數轉換器發送命令和讀取數據作爲對象的一個操作。AD7705模數轉換器採用SPI通訊接口,有時需要在軟件中對片選信號進行操作,所以我們將片選型號的操作作爲對象的另一個操作。在一些情況下,有些針對對象的活動需要延時進行,而在不同的平臺中採取的延時方式不盡相同,爲了操作方便我們將延時操作作爲對象的一個操作。此外AD7705模數轉換器有一個轉換數據就緒檢測的功能,我們將讀取就緒信號作爲它的另一個操作。

據以上的分析我們可以抽象AD7705模數轉換器的對象類型如下:

/* 定義AD7705對象類型 */
typedef struct AD7705Object {
       uint8_t registers[3];                      //用於存儲通訊、配置和時鐘寄存器
       uint8_t (*ReadWriteByte)(uint8_t data);    //讀寫操作
       uint8_t (*CheckDataIsReady)(void);         //就緒信號檢測
       void (*ChipSelect)(AD7705CSType cs);       //實現片選
       void (*Delayms)(volatile uint32_t nTime);  //實現ms延時操作
       void (*Delayus)(volatile uint32_t nTime);  //實現us延時操作
}AD7705ObjectType;

2.1.2、對象初始化

我們雖然得到了是對象,但對象不能直接使用,我們需要對其進行初始化方能使用。所以接下來我們考慮AD7705模數轉換器對象的初始化函數。

初始化函數至少包含有2方面內容:一是爲對象變量賦必要的初值;二是檢查這些初值是否是有效的。特別是一些操作指針錯誤的話可能產生嚴重的後果。基於這一原則,我們設計AD7705模數轉換器的對象初始化函數如下:

/* AD7705對象初始化函數 */
void AD7705Initialization(AD7705ObjectType *ad,
       AD7705GainType gain,
       AD7705MclkType mclk,
       AD7705OutRateType rate,
       AD7705ReadWriteByteType spiReadWrite,
       AD7705CheckDataIsReadyType checkReady,
       AD7705ChipSelect cs,
       AD7705Delay msDelay,
       AD7705Delay usDelay)
{
       if((ad==NULL)||(spiReadWrite==NULL)||(checkReady==NULL)||(msDelay==NULL)||(usDelay==NULL))
       {
              return;
       }
      
       ad->CheckDataIsReady=checkReady;
       ad->ReadWriteByte=spiReadWrite;
       ad->Delayms=msDelay;
       ad->Delayus=usDelay;
      
       if(cs==NULL)     //硬件電路實現片選
       {
              ad->ChipSelect=DefaultChipSelect;
       }
       else
       {
              ad->ChipSelect=cs;
       }
      
       //設置成單極性、無緩衝、增益爲1、濾波器工作、自校準
       ad->registers[REG_SETUP]=SelfCalibration|Unipolar|BufferDisable|FSYNCEnable|gains[gain];
      
       ad->registers[REG_CLOCK]=CLKEnable; //默認主時鐘輸出
      
       if((mclk==Mclk4915200)||(mclk==Mclk2000000))
       {
              ad->registers[REG_CLOCK]|=CLKDIVEnable;
       }
       else
       {
              ad->registers[REG_CLOCK]|=CLKDIVDisable;
       }
      
       if(((mclk<=Mclk4915200)&&(rate<=Rate200Hz))||((mclk>=Mclk1000000)&&(rate>=Rate50Hz)))
       {
              ad->registers[REG_CLOCK]|=updateRate[rate];
       }
       else
       {
              ad->registers[REG_CLOCK]=0x00;
              return;
       }    
}

2.2、對象操作

我們獲取對象的目的就是希望通過對象來得到我們想要的數據。對於AD7705模數轉換器來說,我們想要得到的就是各個通道的輸入信號。所以我們對AD7705模數轉換器對象的操作就是得到通道的模數轉換值。據此我們設計讀取AD7705模數轉換器單個通道的值的函數如下:

//讀取AD7705單個通道的值
uint16_t GetAD7705ChannelValue(AD7705ObjectType *ad,AD7705ChannelType channel)
{
       ad->ChipSelect(AD7705CS_Enable);
      
       //初始化通道
       AD7705ChannelConfig(ad,channel);
       ad->Delayms(20);
 
       ad->registers[REG_COMM]=DataRegister|ReadOperation|OperatingMode|channels[channel];
       ad->ReadWriteByte(ad->registers[REG_COMM]);

      
       //等待數據準備好
       while(ad->CheckDataIsReady()==1)   
       {
       }
  
       uint16_t dataLowByte;
       uint16_t dataHighByte;
       dataHighByte = ad->ReadWriteByte(0xFF);        //讀數據寄存器
       ad->Delayus(200);
       dataLowByte = ad->ReadWriteByte(0xFF);        //讀數據寄存器
       ad->Delayus(200);
 
       dataHighByte = dataHighByte << 8;
       uint16_t value;
       value  =  dataHighByte | dataLowByte; 
 
       ad->ChipSelect(AD7705CS_Disable);
      
       return value;
}

其中設置寄存器和時鐘寄存器的值,在初始化函數中已經記錄下來,在配置時,我們只需要下發數據就好了。

3、驅動的使用

我們已經設計並實現了AD7705模數轉換器的驅動,接下來我們考慮如何使用這一驅動程序實現AD7705模數轉換器的應用。

3.1、聲明並初始化對象

應用的設計一如既往,我們需要使用AD7705模數轉換器對象類型聲明一個對象變量。形式如下:

AD7705ObjectType ad7705;

聲明瞭這個對象變量並不能用於操作AD7705模數轉換器,我們還需要使用初始化函數對對象變量進行初始化。初始換函數所需參數如下:

AD7705ObjectType *ad,要初始化的AD7705對象

AD7705GainType gain,增益係數

AD7705MclkType mclk,主時鐘頻率

AD7705OutRateType rate,輸出更新速率

AD7705ReadWriteByteType spiReadWriteSPI口讀寫操作函數

AD7705CheckDataIsReadyType checkReady,就緒檢測函數

AD7705ChipSelect cs,片選操作函數

AD7705Delay msDelay,毫秒延時函數

AD7705Delay usDelay,微秒延時函數

對於這些參數,對象變量我們已經定義了。採用的增益倍數根據實際情況選擇,爲枚舉。AD7705採用的數字時鐘則根據我們的實際使用情況輸入,爲枚舉。而輸出更新速率根據需要選擇,爲枚舉。主要的是我們需要定義幾個函數,並將函數指針作爲參數。這幾個函數的類型如下:

/*定義讀寫AD7705函數指針類型*/
typedef uint8_t (*AD7705ReadWriteByteType)(uint8_t data);

/*定義就緒檢測函數指針類型*/
typedef uint8_t (*AD7705CheckDataIsReadyType)(void);

/*定義片選信號函數指針類型*/
typedef void (*AD7705ChipSelect)(AD7705CSType cs);

/*定義延時操作函數指針類型*/
typedef void (*AD7705Delay)(volatile uint32_t nTime);

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

/*定義片選信號函數*/
void AD7705CS(AD7705CSType en)
{
       if(AD7705CS_Enable==en)
       {
              HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_RESET);
       }
       else
       {
              HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_SET);
       }
}

/* 定義就緒信號讀取函數 */
uint8_t AD7705CheckReady(void)
{
       return HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_0);
}

/*定義發送數據函數*/
uint8_t AD7705WriteReadData(uint8_t wData)
{
       uint8_t rxData=0;
 
       HAL_SPI_TransmitReceive(&ad7705hspi,&wData,&rxData,1,1000);
 
       return rxData;
}

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

AD7705Initialization(&ad7705Gain_1Mclk2457600Rate200HzAD7705WriteReadDataAD7705CheckReadyAD7705CSHAL_DelayDelayus);

3.2、基於對象進行操作

我們定義了對象變量並使用初始化函數給其作了初始化。接着我們就來考慮操作這一對象獲取我們想要的數據。在驅動中我們已經封裝了獲取某一通道模數轉換數據的函數,這裏我們將調用這一函數獲取AD7705兩個通道的模數轉換值。

/*獲取通道數據*/
void GetChannelValue(void)
{
       uint16_t dataCode[2];
      
       dataCode[0]=GetAD7705ChannelValue(&ad7705,Channel1);
      
       dataCode[1]=GetAD7705ChannelValue(&ad7705,Channel2);
}

獲取了ADC的數據後就可以根據每個通道所對應的物理量量程範圍計算得到物理量值。

4、應用總結

我們實現了AD7705模數轉換器的驅動並使用驅動實現了簡單的應用,得到了AD7705兩個通道的模數轉換數據,結果與預期一致。

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

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

歡迎關注:

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