在產(chǎn)品設(shè)計(jì)過程中,很多時(shí)候都會(huì)用到ADC器件,而在一些特殊場合還需要一些特別的ADC器件。我們?cè)谶@篇中將討論常用于醫(yī)療器件方面的,DDC114這款電流輸入ADC,并為其設(shè)計(jì)一個(gè)驅(qū)動(dòng)程序。
1、功能概述
??模數(shù)轉(zhuǎn)換器DDC114是一款電流輸入型ADC,通過對(duì)微小電流信號(hào)采用電荷積分的方式進(jìn)行模數(shù)轉(zhuǎn)換。包括4路輸入,每路輸入有2路積分電容;有2路AD轉(zhuǎn)換器,每2路輸入共用1個(gè)AD轉(zhuǎn)換器。其內(nèi)部結(jié)構(gòu)框圖如下:
??DDC114采用48腳QFN封裝,其量程范圍、數(shù)據(jù)格式、轉(zhuǎn)換周期、操作模式等都是通過配置硬件引腳來實(shí)現(xiàn)配置的。其封裝樣式及引腳定義如下:
??DDC114通過SPI接口輸出數(shù)字化的結(jié)果,該SPI接口由一組數(shù)據(jù)時(shí)鐘(DCLK),一個(gè)有效數(shù)據(jù)引腳(DVALID),一組串行數(shù)據(jù)輸出引腳(DOUT),和一組串行數(shù)據(jù)輸入引腳(DIN)組成。DDC114電流積分型AD芯片各路交叉積分與轉(zhuǎn)換,積分與轉(zhuǎn)換過程以及數(shù)據(jù)輸出基本是獨(dú)立進(jìn)行的。DIN只在多個(gè)轉(zhuǎn)換器級(jí)聯(lián)時(shí)使用,否則應(yīng)該綁定到DGND。當(dāng)移位寄存器包含有效數(shù)據(jù)時(shí),DVALID輸出變低。相應(yīng)的邏輯時(shí)序如下:
??DDC114存在連續(xù)模式和非連續(xù)模式兩種。當(dāng)積分時(shí)間大于數(shù)據(jù)轉(zhuǎn)換時(shí)間時(shí)就切換到了連續(xù)模式,當(dāng)積分時(shí)間小于數(shù)據(jù)轉(zhuǎn)換時(shí)間時(shí)就轉(zhuǎn)換到了非連續(xù)模式。有關(guān)連續(xù)與非連續(xù)模式的所需時(shí)鐘周期如下:
??為了獲得最好的噪聲抑制特性,一般來說,我們需要將CONV轉(zhuǎn)換信號(hào)與CLK時(shí)鐘的上升沿同步。
2、驅(qū)動(dòng)設(shè)計(jì)與實(shí)現(xiàn)
??我們已經(jīng)了解了DDC114電流輸入型ADC的基本特性,接下來我們就依據(jù)這些特性和要求實(shí)現(xiàn)DDC114電流輸入型ADC的驅(qū)動(dòng)程序。
2.1、對(duì)象定義
??與前面一樣,我們依然是基于對(duì)象的思想來實(shí)現(xiàn)DDC114電流輸入型ADC的驅(qū)動(dòng)。所以我們首先來考慮DDC114電流輸入型ADC作為對(duì)象的基本屬性和操作。
??首先我們來考慮DDC114對(duì)象的屬性,我們使用DDC114的目的就是獲取各通道的數(shù)據(jù),所以我們將當(dāng)前時(shí)刻的數(shù)據(jù)作為屬性記錄下來。數(shù)據(jù)格式雖然是通過硬件管腳來設(shè)置的,但在不同的數(shù)據(jù)格式下解析數(shù)據(jù)的方式也是不一樣的,所以我們將當(dāng)前配置的數(shù)據(jù)格式當(dāng)作屬性記錄下來。同樣的,不同數(shù)據(jù)格式下量程和零點(diǎn)的設(shè)置也是不一樣的,所以我你們將其作為屬性記錄下來,方便完成數(shù)據(jù)解析。我們知道DDC114連續(xù)工作時(shí)需要控制轉(zhuǎn)換信號(hào)CONV,我們需要記錄他每一時(shí)刻的狀態(tài),以便控制CONV信號(hào)的切換,所以我們將器狀態(tài)也定義為對(duì)象的屬性。
??而DDC114對(duì)象所需要進(jìn)行的基本操作,主要有控制CONV信號(hào)、控制RESET信號(hào)、控制TEST信號(hào)、獲取DVALID狀態(tài)并從SPI獲取,這些操作都依賴于具體的平臺(tái),所以我們將其定義為對(duì)象的操作,這樣可以通過函數(shù)指針的方式方便操作。還有基于時(shí)序控制的需要,我們需要微秒延時(shí)函數(shù),而延時(shí)函數(shù)也依賴于具體的軟硬件平臺(tái),所以我們將其定義為對(duì)象的操作。綜上所述,我們可以定義DDC114的對(duì)象類型如下:
/*定義DDC114對(duì)象類型*/typedef struct Ddc114Object {
uint32_t dCode[4];
Ddc114PinSetType convStatus;
Ddc114FormatType format; //數(shù)據(jù)輸出格式
uint32_t codeRange; //輸出量程編碼
uint32_t codeZero; //輸出零點(diǎn)編碼
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); //實(shí)現(xiàn)us延時(shí)操作}Ddc114ObjectType;
??我們定義了DDC114的對(duì)象類型,這樣我們就可以很容易得到我們想要的DDC114對(duì)象變量。但是每個(gè)對(duì)象都是獨(dú)一無二的,所以我們需要一個(gè)初始化對(duì)象變量的過程,所以我們考慮實(shí)現(xiàn)一個(gè)DDC114對(duì)象變量的初始化函數(shù)。而需要初始化的就是對(duì)象的屬性和操作,據(jù)此我們定義DDC114對(duì)象變量的初始化函數(shù)如下:
/*DDC114對(duì)象初始化*/void Ddc114Initialization(Ddc114ObjectType *ddc, //DDC114對(duì)象變量
Ddc114FormatType format, //數(shù)據(jù)輸出格式
DDC114GetDatas getDatas, //獲取測量數(shù)據(jù)函數(shù)指針
DDC114GetValid getValid, //數(shù)據(jù)有效性狀態(tài)獲取函數(shù)指針
DDC114SetConv conv, //轉(zhuǎn)換設(shè)置函數(shù)指針
DDC114SetReset reset, //復(fù)位操作函數(shù)指針
DDC114SetTest test, //測試模式操作函數(shù)指針
DDC114Delayus delayus //微秒延時(shí)函數(shù)指針
){ 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、對(duì)象操作
??我們對(duì)DDC114要進(jìn)行哪些操作呢?復(fù)位、測試、獲取狀態(tài)等都是我們可以進(jìn)行的操作,但最重要的還是從DDC114獲取轉(zhuǎn)換數(shù)據(jù)。當(dāng)DDC114的數(shù)據(jù)準(zhǔn)備好之后,就會(huì)將DVALID信號(hào)拉低,檢測到DVALID信號(hào)就可以通過SPI端口獲取數(shù)據(jù)。DDC114通過SPI端口一次性傳送4個(gè)通道的數(shù)據(jù),根據(jù)數(shù)據(jù)格式的不同分別傳送80位或者64位。其操作時(shí)序如下圖所示:
??根據(jù)上述的描述及時(shí)序圖我們可以編寫獲取DDC114轉(zhuǎn)換數(shù)據(jù)的函數(shù)如下:
/*DDC114獲取各通道的轉(zhuǎn)換數(shù)據(jù)*/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的輸出數(shù)據(jù)后,是一個(gè)80位或者64位的數(shù)據(jù)流。我們感興趣的是各個(gè)通道的轉(zhuǎn)換數(shù)據(jù),所以我們還需要對(duì)數(shù)據(jù)進(jìn)行解析。而各通道轉(zhuǎn)換數(shù)據(jù)的格式如下:
??我們根據(jù)設(shè)定的數(shù)據(jù)格式以及讀取的數(shù)據(jù)位,可以解析得到各個(gè)通道的轉(zhuǎn)換值。
/*解析從DDC114讀取的數(shù)據(jù)*/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、驅(qū)動(dòng)的使用
??我們已經(jīng)設(shè)計(jì)并實(shí)現(xiàn)了DDC114電流輸入型ADC的驅(qū)動(dòng)程序。接下來我們將簡單的說明如何使用這一驅(qū)動(dòng),并設(shè)計(jì)一個(gè)簡單的示例驗(yàn)證這一驅(qū)動(dòng)程序的正確性。
3.1、聲明并初始化對(duì)象
??我們是基于對(duì)象設(shè)計(jì)的DDC114電流輸入型ADC的驅(qū)動(dòng)程序,所以在使用驅(qū)動(dòng)時(shí),我們需要先聲明一個(gè)對(duì)象變量,然后基于該對(duì)象變量來實(shí)現(xiàn)具體的對(duì)象操作。我們先聲明對(duì)象如下:
Ddc114ObjectType ddc;
??聲明了這個(gè)對(duì)象變量之后,我們還需要使用初始化函數(shù)對(duì)其進(jìn)行初始化方可使用。這一初始化函數(shù)擁有8個(gè)參數(shù):
Ddc114ObjectType *ddc, //DDC114對(duì)象變量Ddc114FormatType format, //數(shù)據(jù)輸出格式DDC114GetDatas getDatas, //獲取測量數(shù)據(jù)函數(shù)指針DDC114GetValid getValid, //數(shù)據(jù)有效性狀態(tài)獲取函數(shù)指針DDC114SetConv conv, //轉(zhuǎn)換設(shè)置函數(shù)指針DDC114SetReset reset, //復(fù)位操作函數(shù)指針DDC114SetTest test, //測試模式操作函數(shù)指針DDC114Delayus delayus //微秒延時(shí)函數(shù)指針
??第一個(gè)參數(shù)為所要初始化的對(duì)象變量。第二個(gè)參數(shù)是數(shù)據(jù)格式,根據(jù)具體的應(yīng)用設(shè)置,我們這里是將它設(shè)置為20數(shù)據(jù)的格式。主要的實(shí)現(xiàn)的是后面6個(gè)函數(shù)指針,它們都是用于實(shí)現(xiàn)DDC114在具體的應(yīng)用平臺(tái)的操作函數(shù)。原型定義如下:
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); //實(shí)現(xiàn)us延時(shí)操作
??在使用前我們先來實(shí)現(xiàn)這6個(gè)面向平臺(tái)的操作接口函數(shù),具體實(shí)現(xiàn)如下:
/* 讀取數(shù)據(jù) */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);
}/*設(shè)置轉(zhuǎn)換引腳*/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;
}/*設(shè)置復(fù)位引腳*/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;
}/*設(shè)置測試引腳*/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;
}
??而延時(shí)函數(shù),則用我們?cè)?a href="http://www.wenjunhu.com/v/tag/751/" target="_blank">STM32平臺(tái)統(tǒng)一使用的微秒延時(shí)函數(shù)。定義了這些函數(shù)后,我們就可以初始化對(duì)象變量如下:
/*DDC114對(duì)象初始化*/
Ddc114Initialization(&ddc, //DDC114對(duì)象變量
DDC114_OUT20, //數(shù)據(jù)輸出格式
GetDatasFromDDC, //獲取測量數(shù)據(jù)函數(shù)指針
ReadValidPin, //數(shù)據(jù)有效性狀態(tài)獲取函數(shù)指針
SetConvPin, //轉(zhuǎn)換設(shè)置函數(shù)指針
SetResetPin, //復(fù)位操作函數(shù)指針
SetTestPin, //測試模式操作函數(shù)指針
Delayus //微秒延時(shí)函數(shù)指針
);
3.2、基于對(duì)象進(jìn)行操作
??完成了對(duì)象的初始化后,我們就可以基于對(duì)象來實(shí)現(xiàn)相應(yīng)的操作了。在我們這個(gè)應(yīng)用實(shí)例中,我們使用它來采集光度數(shù)據(jù)值。
/*光度檢測處理*/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獲取各通道的轉(zhuǎn)換數(shù)據(jù)*/
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、應(yīng)用總結(jié)
??我們?cè)O(shè)計(jì)并實(shí)現(xiàn)了DDC114電流輸入型ADC的驅(qū)動(dòng)程序。在我們的一個(gè)應(yīng)用實(shí)例中,我們使用這一驅(qū)動(dòng)程序采集了光度數(shù)據(jù)。測試結(jié)果如下圖:
??其中的紅色數(shù)據(jù)就是我們通過調(diào)試工具查看到的DDC114的數(shù)據(jù),對(duì)應(yīng)4個(gè)通道。上面的為原始數(shù)據(jù),通過上位軟件查看的結(jié)果如下:
??經(jīng)過上述測試,說明我們?cè)O(shè)計(jì)的DDC114的驅(qū)動(dòng)程序是正確的。在使用時(shí)需要注意幾點(diǎn),一是,最好將MCU的SPI接口配置為主機(jī)模式而不僅僅是只讀模式;二是,SPI的速度最好不要太快,雖然DDC114可以達(dá)到10M的頻率,但高速時(shí)波形會(huì)變差;三是,積分時(shí)間一定要規(guī)劃好,否則很容易出現(xiàn)超量程的現(xiàn)象。
評(píng)論
查看更多