在產品設計過程中,很多時候都會用到ADC器件,而在一些特殊場合還需要一些特別的ADC器件。我們在這篇中將討論常用于醫療器件方面的,DDC114這款電流輸入ADC,并為其設計一個驅動程序。
1、功能概述
??模數轉換器DDC114是一款電流輸入型ADC,通過對微小電流信號采用電荷積分的方式進行模數轉換。包括4路輸入,每路輸入有2路積分電容;有2路AD轉換器,每2路輸入共用1個AD轉換器。其內部結構框圖如下:
??DDC114采用48腳QFN封裝,其量程范圍、數據格式、轉換周期、操作模式等都是通過配置硬件引腳來實現配置的。其封裝樣式及引腳定義如下:
??DDC114通過SPI接口輸出數字化的結果,該SPI接口由一組數據時鐘(DCLK),一個有效數據引腳(DVALID),一組串行數據輸出引腳(DOUT),和一組串行數據輸入引腳(DIN)組成。DDC114電流積分型AD芯片各路交叉積分與轉換,積分與轉換過程以及數據輸出基本是獨立進行的。DIN只在多個轉換器級聯時使用,否則應該綁定到DGND。當移位寄存器包含有效數據時,DVALID輸出變低。相應的邏輯時序如下:
??DDC114存在連續模式和非連續模式兩種。當積分時間大于數據轉換時間時就切換到了連續模式,當積分時間小于數據轉換時間時就轉換到了非連續模式。有關連續與非連續模式的所需時鐘周期如下:
??為了獲得最好的噪聲抑制特性,一般來說,我們需要將CONV轉換信號與CLK時鐘的上升沿同步。
2、驅動設計與實現
??我們已經了解了DDC114電流輸入型ADC的基本特性,接下來我們就依據這些特性和要求實現DDC114電流輸入型ADC的驅動程序。
2.1、對象定義
??與前面一樣,我們依然是基于對象的思想來實現DDC114電流輸入型ADC的驅動。所以我們首先來考慮DDC114電流輸入型ADC作為對象的基本屬性和操作。
??首先我們來考慮DDC114對象的屬性,我們使用DDC114的目的就是獲取各通道的數據,所以我們將當前時刻的數據作為屬性記錄下來。數據格式雖然是通過硬件管腳來設置的,但在不同的數據格式下解析數據的方式也是不一樣的,所以我們將當前配置的數據格式當作屬性記錄下來。同樣的,不同數據格式下量程和零點的設置也是不一樣的,所以我你們將其作為屬性記錄下來,方便完成數據解析。我們知道DDC114連續工作時需要控制轉換信號CONV,我們需要記錄他每一時刻的狀態,以便控制CONV信號的切換,所以我們將器狀態也定義為對象的屬性。
??而DDC114對象所需要進行的基本操作,主要有控制CONV信號、控制RESET信號、控制TEST信號、獲取DVALID狀態并從SPI獲取,這些操作都依賴于具體的平臺,所以我們將其定義為對象的操作,這樣可以通過函數指針的方式方便操作。還有基于時序控制的需要,我們需要微秒延時函數,而延時函數也依賴于具體的軟硬件平臺,所以我們將其定義為對象的操作。綜上所述,我們可以定義DDC114的對象類型如下:
/*定義DDC114對象類型*/typedef struct Ddc114Object {
uint32_t dCode[4];
Ddc114PinSetType convStatus;
Ddc114FormatType format; //數據輸出格式
uint32_t codeRange; //輸出量程編碼
uint32_t codeZero; //輸出零點編碼
void (*GetDatas)(uint8_t *rData,uint16_t rSize); uint8_t (*GetValid)(void); void (*SetConv)(Ddc114PinSetType conv); void (*SetReset)(Ddc114PinSetType reset); void (*SetTest)(Ddc114PinSetType test); void (*Delayus)(volatile uint32_t nTime); //實現us延時操作}Ddc114ObjectType;
??我們定義了DDC114的對象類型,這樣我們就可以很容易得到我們想要的DDC114對象變量。但是每個對象都是獨一無二的,所以我們需要一個初始化對象變量的過程,所以我們考慮實現一個DDC114對象變量的初始化函數。而需要初始化的就是對象的屬性和操作,據此我們定義DDC114對象變量的初始化函數如下:
/*DDC114對象初始化*/void Ddc114Initialization(Ddc114ObjectType *ddc, //DDC114對象變量
Ddc114FormatType format, //數據輸出格式
DDC114GetDatas getDatas, //獲取測量數據函數指針
DDC114GetValid getValid, //數據有效性狀態獲取函數指針
DDC114SetConv conv, //轉換設置函數指針
DDC114SetReset reset, //復位操作函數指針
DDC114SetTest test, //測試模式操作函數指針
DDC114Delayus delayus //微秒延時函數指針
){ if((ddc==NULL)||(getDatas==NULL)||(getValid==NULL)||(conv==NULL)||(reset==NULL)||(test==NULL)||(delayus==NULL))
{ return ;
}
ddc->GetDatas=getDatas;
ddc->GetValid=getValid;
ddc->SetConv=conv;
ddc->SetReset=reset;
ddc->SetTest=test;
ddc->Delayus=delayus;
ddc->format=format; if(format==DDC114_OUT20)
{
ddc->codeRange=1048575;
ddc->codeZero=4096;
} else
{
ddc->codeRange=65535;
ddc->codeZero=256;
}
for(int i=0;i<4;i++)
{
ddc->dCode[i]=ddc->codeZero;
}
ddc->SetTest(DDC114_Pin_Reset);
ddc->SetReset(DDC114_Pin_Reset);
ddc->Delayus(100);
ddc->SetReset(DDC114_Pin_Set);
ddc->convStatus=DDC114_Pin_Reset;
ddc->SetConv(ddc->convStatus);
}
2.2、對象操作
??我們對DDC114要進行哪些操作呢?復位、測試、獲取狀態等都是我們可以進行的操作,但最重要的還是從DDC114獲取轉換數據。當DDC114的數據準備好之后,就會將DVALID信號拉低,檢測到DVALID信號就可以通過SPI端口獲取數據。DDC114通過SPI端口一次性傳送4個通道的數據,根據數據格式的不同分別傳送80位或者64位。其操作時序如下圖所示:
??根據上述的描述及時序圖我們可以編寫獲取DDC114轉換數據的函數如下:
/*DDC114獲取各通道的轉換數據*/void Ddc114GetDataCode(Ddc114ObjectType *ddc){ uint8_t rData[10]; uint16_t timeOut=0;
if(ddc->convStatus==DDC114_Pin_Reset)
{
ddc->convStatus=DDC114_Pin_Set;
} else
{
ddc->convStatus=DDC114_Pin_Reset;
}
ddc->SetConv(ddc->convStatus);
while((ddc->GetValid())&&(timeOut<500))
{
timeOut++;
}
if(ddc->format == DDC114_OUT20)
{
ddc->GetDatas(rData,10);
} else
{
ddc->GetDatas(rData,8);
}
Ddc114ParseDatas(ddc,rData);
}
??我們讀到了DDC114的輸出數據后,是一個80位或者64位的數據流。我們感興趣的是各個通道的轉換數據,所以我們還需要對數據進行解析。而各通道轉換數據的格式如下:
??我們根據設定的數據格式以及讀取的數據位,可以解析得到各個通道的轉換值。
/*解析從DDC114讀取的數據*/static void Ddc114ParseDatas(Ddc114ObjectType *ddc,uint8_t *rData){ uint32_t tCode[10]={0};
if(ddc->format==DDC114_OUT20)
{ for(int i=0;i<10;i++)
{
tCode[i]=(uint32_t)(rData[i]);
}
ddc->dCode[0]=((tCode[7]&0x0F)<<16)+(tCode[8]<<8)+tCode[9];
ddc->dCode[1]=(tCode[5]<<12)+(tCode[6]<<4)+((tCode[7]&0xF0)>>4);
ddc->dCode[2]=((tCode[2]&0x0F)<<16)+(tCode[3]<<8)+tCode[4];
ddc->dCode[3]=(tCode[0]<<12)+(tCode[1]<<4)+((tCode[2]&0xF0)>>4);
} else
{ for(int i=0;i<8;i++)
{
tCode[i]=(uint32_t)rData[i];
}
ddc->dCode[0]=(tCode[6]<<8)+tCode[7];
ddc->dCode[1]=(tCode[4]<<8)+tCode[5];
ddc->dCode[2]=(tCode[2]<<8)+tCode[3];
ddc->dCode[3]=(tCode[0]<<8)+tCode[1];
}
}
3、驅動的使用
??我們已經設計并實現了DDC114電流輸入型ADC的驅動程序。接下來我們將簡單的說明如何使用這一驅動,并設計一個簡單的示例驗證這一驅動程序的正確性。
3.1、聲明并初始化對象
??我們是基于對象設計的DDC114電流輸入型ADC的驅動程序,所以在使用驅動時,我們需要先聲明一個對象變量,然后基于該對象變量來實現具體的對象操作。我們先聲明對象如下:
Ddc114ObjectType ddc;
??聲明了這個對象變量之后,我們還需要使用初始化函數對其進行初始化方可使用。這一初始化函數擁有8個參數:
Ddc114ObjectType *ddc, //DDC114對象變量Ddc114FormatType format, //數據輸出格式DDC114GetDatas getDatas, //獲取測量數據函數指針DDC114GetValid getValid, //數據有效性狀態獲取函數指針DDC114SetConv conv, //轉換設置函數指針DDC114SetReset reset, //復位操作函數指針DDC114SetTest test, //測試模式操作函數指針DDC114Delayus delayus //微秒延時函數指針
??第一個參數為所要初始化的對象變量。第二個參數是數據格式,根據具體的應用設置,我們這里是將它設置為20數據的格式。主要的實現的是后面6個函數指針,它們都是用于實現DDC114在具體的應用平臺的操作函數。原型定義如下:
typedef void (*DDC114GetDatas)(uint8_t *rData,uint16_t rSize);typedef uint8_t (*DDC114GetValid)(void);typedef void (*DDC114SetConv)(Ddc114PinSetType conv);typedef void (*DDC114SetReset)(Ddc114PinSetType reset);typedef void (*DDC114SetTest)(Ddc114PinSetType test);typedef void (*DDC114Delayus)(volatile uint32_t nTime); //實現us延時操作
??在使用前我們先來實現這6個面向平臺的操作接口函數,具體實現如下:
/* 讀取數據 */static void GetDatasFromDDC(uint8_t *rData,uint16_t rSize){
HAL_SPI_Receive(&hspi1, rData, rSize, 1);
}/*讀取有效引腳*/static uint8_t ReadValidPin(void){ return HAL_GPIO_ReadPin(DDC_DVALID_GPIO_Port,DDC_DVALID_Pin);
}/*設置轉換引腳*/static void SetConvPin(Ddc114PinSetType conv){ if(conv==DDC114_Pin_Reset)
{
HAL_GPIO_WritePin(DDC_CONV_CTL_GPIO_Port, DDC_CONV_CTL_Pin, GPIO_PIN_RESET); return;
}
HAL_GPIO_WritePin(DDC_CONV_CTL_GPIO_Port, DDC_CONV_CTL_Pin, GPIO_PIN_SET); return;
}/*設置復位引腳*/static void SetResetPin(Ddc114PinSetType reset){ if(reset==DDC114_Pin_Reset)
{
HAL_GPIO_WritePin(DDC_RESET_GPIO_Port, DDC_RESET_Pin, GPIO_PIN_RESET); return;
}
HAL_GPIO_WritePin(DDC_RESET_GPIO_Port, DDC_RESET_Pin, GPIO_PIN_SET); return;
}/*設置測試引腳*/static void SetTestPin(Ddc114PinSetType test){ if(test==DDC114_Pin_Reset)
{
HAL_GPIO_WritePin(DDC_TEST_GPIO_Port, DDC_TEST_Pin, GPIO_PIN_RESET); return;
}
HAL_GPIO_WritePin(DDC_TEST_GPIO_Port, DDC_TEST_Pin, GPIO_PIN_SET); return;
}
??而延時函數,則用我們在STM32平臺統一使用的微秒延時函數。定義了這些函數后,我們就可以初始化對象變量如下:
/*DDC114對象初始化*/
Ddc114Initialization(&ddc, //DDC114對象變量
DDC114_OUT20, //數據輸出格式
GetDatasFromDDC, //獲取測量數據函數指針
ReadValidPin, //數據有效性狀態獲取函數指針
SetConvPin, //轉換設置函數指針
SetResetPin, //復位操作函數指針
SetTestPin, //測試模式操作函數指針
Delayus //微秒延時函數指針
);
3.2、基于對象進行操作
??完成了對象的初始化后,我們就可以基于對象來實現相應的操作了。在我們這個應用實例中,我們使用它來采集光度數據值。
/*光度檢測處理*/void DePhotometricMeasurement(void){ if(aPara.phyPara.waveband>9)
{
luxCode[0].dCode=0;
luxCode[1].dCode=0;
luxCode[2].dCode=0;
luxCode[3].dCode=0;
ddcPeriod=0;
} else
{
ddcPeriod++;
if(ddcPeriod==1)
{ /*DDC114獲取各通道的轉換數據*/
Ddc114GetDataCode(&ddc);
if(dataOrder[aPara.phyPara.waveband]>15)
{
dataOrder[aPara.phyPara.waveband]=0;
}
for(int i=0;i<4;i++)
{
luxCode[i].fData.waveBand=aPara.phyPara.waveband+1;
luxCode[i].fData.serialnumber=dataOrder[aPara.phyPara.waveband];
luxCode[i].fData.dataCode=ddc.dCode[i]&0xFFFFF;
}
dataOrder[aPara.phyPara.waveband]++;
ddcPeriod=0;
}
}
aPara.phyPara.dataCode1=luxCode[0].dCode;
aPara.phyPara.dataCode2=luxCode[1].dCode;
aPara.phyPara.dataCode3=luxCode[2].dCode;
aPara.phyPara.dataCode4=luxCode[3].dCode;
}
4、應用總結
??我們設計并實現了DDC114電流輸入型ADC的驅動程序。在我們的一個應用實例中,我們使用這一驅動程序采集了光度數據。測試結果如下圖:
??其中的紅色數據就是我們通過調試工具查看到的DDC114的數據,對應4個通道。上面的為原始數據,通過上位軟件查看的結果如下:
??經過上述測試,說明我們設計的DDC114的驅動程序是正確的。在使用時需要注意幾點,一是,最好將MCU的SPI接口配置為主機模式而不僅僅是只讀模式;二是,SPI的速度最好不要太快,雖然DDC114可以達到10M的頻率,但高速時波形會變差;三是,積分時間一定要規劃好,否則很容易出現超量程的現象。
評論
查看更多