我們的經常需要采集一些精度要求較高的模擬信號,使用MCU集成的ADC難以達到要求、所以我們需要獨立的ADC芯片。這一節我們就來設計並實現AD7705芯片的驅動、並探討驅動的使用方法。
1、功能概述
AD7705/AD7706是用於低頻測量的完整模擬前端。可以直接從傳感器接收低電平輸入信號,並產生串行數字輸出。
1.1、硬件結構
AD7705和AD7706均為完整16位、低成本、Σ-Δ型ADC,適合直流和低頻交流測量應用。其具有低功耗(3 V時最大值為1 mW)特性,因而可用於環路供電、電池供電或本地供電的應用中。片內可編程增益放大器提供從1至128的增益設置,無需使用外部信號調理硬件便可接受低電平和高電平模擬輸入。
AD7705擁有兩個差分通道,而AD7706則擁有一個差分通道和兩個偽差分通道。在定制比率應用器件時,差分基准電壓輸入還能提供極大的靈活性。采用16引腳封裝,具體的定義及結構如下:
AD7705/AD7706設備的工作電壓從2.7 V到3.3 V或4.75 V到5.25 V不等。在VDD為5v和參考電壓為2.5 V的情況下,輸入信號范圍從0 mV到20 mV,從0 V到2.5 V,都可以在這兩種設備上使用。在VDD為3v和參考電壓為1.25 V的情況下,可以處理0 mV到10 mV到0 V到1.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模數轉換器的對象類型如下:
1 /* 定義AD7705對象類型 */ 2 typedef struct AD7705Object { 3 uint8_t registers[3]; //用於存儲通訊、配置和時鍾寄存器 4 uint8_t (*ReadWriteByte)(uint8_t data); //讀寫操作 5 uint8_t (*CheckDataIsReady)(void); //就緒信號檢測 6 void (*ChipSelect)(AD7705CSType cs); //實現片選 7 void (*Delayms)(volatile uint32_t nTime); //實現ms延時操作 8 void (*Delayus)(volatile uint32_t nTime); //實現us延時操作 9 }AD7705ObjectType;
2.1.2、對象初始化
我們雖然得到了是對象,但對象不能直接使用,我們需要對其進行初始化方能使用。所以接下來我們考慮AD7705模數轉換器對象的初始化函數。
初始化函數至少包含有2方面內容:一是為對象變量賦必要的初值;二是檢查這些初值是否是有效的。特別是一些操作指針錯誤的話可能產生嚴重的后果。基於這一原則,我們設計AD7705模數轉換器的對象初始化函數如下:
1 /* AD7705對象初始化函數 */ 2 3 void AD7705Initialization(AD7705ObjectType *ad, 4 AD7705GainType gain, 5 AD7705MclkType mclk, 6 AD7705OutRateType rate, 7 AD7705ReadWriteByteType spiReadWrite, 8 AD7705CheckDataIsReadyType checkReady, 9 AD7705ChipSelect cs, 10 AD7705Delay msDelay, 11 AD7705Delay usDelay) 12 { 13 if((ad==NULL)||(spiReadWrite==NULL)||(checkReady==NULL)||(msDelay==NULL)||(usDelay==NULL)) 14 { 15 return; 16 } 17 18 ad->CheckDataIsReady=checkReady; 19 ad->ReadWriteByte=spiReadWrite; 20 ad->Delayms=msDelay; 21 ad->Delayus=usDelay; 22 23 if(cs==NULL) //硬件電路實現片選 24 { 25 ad->ChipSelect=DefaultChipSelect; 26 } 27 else 28 { 29 ad->ChipSelect=cs; 30 } 31 32 //設置成單極性、無緩沖、增益為1、濾波器工作、自校准 33 ad->registers[REG_SETUP]=SelfCalibration|Unipolar|BufferDisable|FSYNCEnable|gains[gain]; 34 35 ad->registers[REG_CLOCK]=CLKEnable; //默認主時鍾輸出 36 37 if((mclk==Mclk4915200)||(mclk==Mclk2000000)) 38 { 39 ad->registers[REG_CLOCK]|=CLKDIVEnable; 40 } 41 else 42 { 43 ad->registers[REG_CLOCK]|=CLKDIVDisable; 44 } 45 46 if(((mclk<=Mclk4915200)&&(rate<=Rate200Hz))||((mclk>=Mclk1000000)&&(rate>=Rate50Hz))) 47 { 48 ad->registers[REG_CLOCK]|=updateRate[rate]; 49 } 50 else 51 { 52 ad->registers[REG_CLOCK]=0x00; 53 return; 54 } 55 }
2.2、對象操作
我們獲取對象的目的就是希望通過對象來得到我們想要的數據。對於AD7705模數轉換器來說,我們想要得到的就是各個通道的輸入信號。所以我們對AD7705模數轉換器對象的操作就是得到通道的模數轉換值。據此我們設計讀取AD7705模數轉換器單個通道的值的函數如下:
1 //讀取AD7705單個通道的值 2 uint16_t GetAD7705ChannelValue(AD7705ObjectType *ad,AD7705ChannelType channel) 3 { 4 ad->ChipSelect(AD7705CS_Enable); 5 6 //初始化通道 7 AD7705ChannelConfig(ad,channel); 8 9 ad->Delayms(20); 10 11 ad->registers[REG_COMM]=DataRegister|ReadOperation|OperatingMode|channels[channel]; 12 13 ad->ReadWriteByte(ad->registers[REG_COMM]); 14 15 //等待數據准備好 16 while(ad->CheckDataIsReady()==1) 17 { 18 } 19 20 uint16_t dataLowByte; 21 uint16_t dataHighByte; 22 dataHighByte = ad->ReadWriteByte(0xFF); //讀數據寄存器 23 ad->Delayus(200); 24 dataLowByte = ad->ReadWriteByte(0xFF); //讀數據寄存器 25 ad->Delayus(200); 26 27 dataHighByte = dataHighByte << 8; 28 uint16_t value; 29 value = dataHighByte | dataLowByte; 30 31 ad->ChipSelect(AD7705CS_Disable); 32 33 return value; 34 }
其中設置寄存器和時鍾寄存器的值,在初始化函數中已經記錄下來,在配置時,我們只需要下發數據就好了。
3、驅動的使用
我們已經設計並實現了AD7705模數轉換器的驅動,接下來我們考慮如何使用這一驅動程序實現AD7705模數轉換器的應用。
3.1、聲明並初始化對象
應用的設計一如既往,我們需要使用AD7705模數轉換器對象類型聲明一個對象變量。形式如下:
AD7705ObjectType ad7705;
聲明了這個對象變量並不能用於操作AD7705模數轉換器,我們還需要使用初始化函數對對象變量進行初始化。初始換函數所需參數如下:
AD7705ObjectType *ad,要初始化的AD7705對象
AD7705GainType gain,增益系數
AD7705MclkType mclk,主時鍾頻率
AD7705OutRateType rate,輸出更新速率
AD7705ReadWriteByteType spiReadWrite,SPI口讀寫操作函數
AD7705CheckDataIsReadyType checkReady,就緒檢測函數
AD7705ChipSelect cs,片選操作函數
AD7705Delay msDelay,毫秒延時函數
AD7705Delay usDelay,微秒延時函數
對於這些參數,對象變量我們已經定義了。采用的增益倍數根據實際情況選擇,為枚舉。AD7705采用的數字時鍾則根據我們的實際使用情況輸入,為枚舉。而輸出更新速率根據需要選擇,為枚舉。主要的是我們需要定義幾個函數,並將函數指針作為參數。這幾個函數的類型如下:
1 /*定義讀寫AD7705函數指針類型*/ 2 typedef uint8_t (*AD7705ReadWriteByteType)(uint8_t data); 3 4 /*定義就緒檢測函數指針類型*/ 5 typedef uint8_t (*AD7705CheckDataIsReadyType)(void); 6 7 /*定義片選信號函數指針類型*/ 8 typedef void (*AD7705ChipSelect)(AD7705CSType cs); 9 10 /*定義延時操作函數指針類型*/ 11 typedef void (*AD7705Delay)(volatile uint32_t nTime);
對於這幾個函數我們根據樣式定義就可以了,具體的操作可能與使用的硬件平台有關系。片選操作函數用於多設備需要軟件操作時,如采用硬件片選可以傳入NULL即可。具體函數定義如下:
1 /*定義片選信號函數*/ 2 void AD7705CS(AD7705CSType en) 3 { 4 if(AD7705CS_Enable==en) 5 { 6 HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_RESET); 7 } 8 else 9 { 10 HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_SET); 11 } 12 } 13 14 /* 定義就緒信號讀取函數 */ 15 uint8_t AD7705CheckReady(void) 16 { 17 return HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_0); 18 } 19 20 /*定義發送數據函數*/ 21 uint8_t AD7705WriteReadData(uint8_t wData) 22 { 23 uint8_t rxData=0; 24 25 HAL_SPI_TransmitReceive(&ad7705hspi,&wData,&rxData,1,1000); 26 27 return rxData; 28 }
對於延時函數我們可以采用各種方法實現。我們采用的STM32平台和HAL庫則可以直接使用HAL_Delay()函數。於是我們可以調用初始化函數如下:
AD7705Initialization(&ad7705,Gain_1,Mclk2457600,Rate200Hz,AD7705WriteReadData,AD7705CheckReady,AD7705CS,HAL_Delay,Delayus);
3.2、基於對象進行操作
我們定義了對象變量並使用初始化函數給其作了初始化。接着我們就來考慮操作這一對象獲取我們想要的數據。在驅動中我們已經封裝了獲取某一通道模數轉換數據的函數,這里我們將調用這一函數獲取AD7705兩個通道的模數轉換值。
1 /*獲取通道數據*/ 2 void GetChannelValue(void) 3 { 4 uint16_t dataCode[2]; 5 6 dataCode[0]=GetAD7705ChannelValue(&ad7705,Channel1); 7 8 dataCode[1]=GetAD7705ChannelValue(&ad7705,Channel2); 9 }
獲取了ADC的數據后就可以根據每個通道所對應的物理量量程范圍計算得到物理量值。
4、應用總結
我們實現了AD7705模數轉換器的驅動並使用驅動實現了簡單的應用,得到了AD7705兩個通道的模數轉換數據,結果與預期一致。
在使用驅動時需注意,采用SPI接口的器件需要考慮片選操作的問題。如果片選信號是通過硬件電路來實現的,我們在初始化時給其傳遞NULL值。如果是軟件操作片選則傳遞我們編寫的片選操作函數。
完整的源代碼可在GitHub下載:https://github.com/foxclever/ExPeriphDriver
歡迎關注: