我們經(jīng)常會(huì)碰到多通道AD采集的需求,有時(shí)候甚至需要高精度的ADC器件。本篇我們將來設(shè)計(jì)并實(shí)現(xiàn)ADS1256模數(shù)轉(zhuǎn)換器的驅(qū)動(dòng)。并簡(jiǎn)單討論該驅(qū)動(dòng)使用方式。
1、功能概述
??ADS1256是TI公司推出的一款低噪聲高分辨率的24位Sigma-Delta(E-v)模數(shù)轉(zhuǎn)換器(ADC)。E-vADC與傳統(tǒng)的逐次逼近型和積分型ADC相比有轉(zhuǎn)換誤差小而價(jià)格低廉的優(yōu)點(diǎn),但由于受帶寬和有效采樣率的限制,E-vADC不適用于高頻數(shù)據(jù)采集的場(chǎng)合。該款A(yù)DS1256可適合于采集最高頻率只有幾千赫茲的模擬數(shù)據(jù)的系統(tǒng)中,數(shù)據(jù)輸出速率最高可為30K采樣點(diǎn)/秒,4路差分或8路偽差分輸入,有完善的自校正和系統(tǒng)校正系統(tǒng),SPI串行數(shù)據(jù)傳輸接口。其結(jié)構(gòu)圖如下所示:
??從結(jié)構(gòu)圖可以看出來,ADS1256是模擬區(qū)域與數(shù)字區(qū)域完全獨(dú)立的ADC,即AVDD給模擬區(qū)域供電,DVDD給數(shù)字區(qū)域供電,在原理圖設(shè)計(jì)方面按照官方指導(dǎo)文檔,需要對(duì)兩個(gè)區(qū)域做獨(dú)立的布線與隔離處理,才能讓信噪比最佳。
??ADS1256采用SSOP的封裝形式,具有8個(gè)模擬輸入通道,共28個(gè)引腳,與其類似的2通道產(chǎn)品ADS1255共有20引腳,其實(shí)兩者操作相同,所以我們?cè)O(shè)計(jì)驅(qū)動(dòng)也會(huì)考慮兼容性。其中ADS1256引腳排布和定義如下圖所示:
??ADS1255和ADS1256的操作是通過一組寄存器來控制的。這些寄存器包含了配置部件所需的所有信息,如數(shù)據(jù)速率、多路復(fù)用器設(shè)置、PGA設(shè)置、校準(zhǔn)等。這些寄存器的地址及結(jié)構(gòu)如下表所述:
??我們知道了這些寄存器的定義,那么就可以操作ADS1256了??墒俏覀?cè)趺磥韺?shí)現(xiàn)對(duì)這些寄存器的訪問呢?這就涉及到操作命令的問題了。ADS1256有多個(gè)操作命令,具體如下表所示:
??在以上這些命令中,除了讀寫寄存器操作需要有第二個(gè)字節(jié)命令和數(shù)據(jù)外,其它命令都是獨(dú)立使用的。
2、驅(qū)動(dòng)設(shè)計(jì)與實(shí)現(xiàn)
??我們已經(jīng)了解了ADS1256的相關(guān)結(jié)構(gòu)、寄存器及操作命令。接下來我們就來考慮如何設(shè)計(jì)ADS1256的驅(qū)動(dòng)程序。
2.1、對(duì)象定義
??與以往一樣,我們依然是基于對(duì)象來實(shí)現(xiàn)ADS1256的驅(qū)動(dòng)程序,所以我們需要抽象出ADS1256的對(duì)象類型。
2.1.1、抽象數(shù)據(jù)類型
??我們先來考慮一下ADS1256對(duì)象類型的定義問題。一個(gè)對(duì)象一般來說主要包括屬性和操作兩個(gè)方面的內(nèi)容,我們也從這兩個(gè)方面來分析ADS1256對(duì)象。
??首先我們來考慮ADS1256模數(shù)轉(zhuǎn)換器對(duì)象的屬性。這些屬性必須能夠標(biāo)識(shí)ADS1256模數(shù)轉(zhuǎn)換器對(duì)象的特征,或者是存儲(chǔ)ADS1256模數(shù)轉(zhuǎn)換器對(duì)象的某種狀態(tài)。對(duì)于ADS1256模數(shù)轉(zhuǎn)換器對(duì)象我們希望可以記錄寄存器的狀態(tài),所以我們將各個(gè)寄存器定義為該對(duì)象的屬性。
??接下來我們?cè)賮砜紤]一下ADS1256模數(shù)轉(zhuǎn)換器對(duì)象的操作。一個(gè)對(duì)象有各種各樣的操作,或者說他能實(shí)現(xiàn)很多的操作,但不是所有的操作都是我們要提取的。我們需要考慮的是那些對(duì)象所獨(dú)有并且同類對(duì)象都必不可少的操作,以及那些不能由對(duì)象獨(dú)自完成,依賴于具體平臺(tái)但又決定對(duì)象的行為的必要操作。對(duì)于ADS1256莫數(shù)轉(zhuǎn)換器,我們需要讀寫數(shù)據(jù),操作片選信號(hào),讀取就緒信號(hào)等。但這些操作都依賴于具體的軟硬件平臺(tái),我們將這些操作定義對(duì)象的操作,通過函數(shù)指針的方式將具體的操作函數(shù)傳遞給對(duì)象變量,以便于適用于不同的軟硬件平臺(tái)。此外,由于時(shí)序控制的需要我們需要在驅(qū)動(dòng)中使用延時(shí)操作,而延時(shí)操作的實(shí)現(xiàn)依賴于具體的軟硬件平臺(tái),所以我們也將其抽象為對(duì)象的操作。根據(jù)上述我們的分析,可以定義ADS1256模數(shù)轉(zhuǎn)換器對(duì)象的類型如下:
/*定義ADS1256對(duì)象類型*/
typedef struct ADS1256Object {
uint8_t Register[11];
void (*ReadWrite)(uint8_t *wData,uint8_t *rData,uint16_t size); //實(shí)現(xiàn)讀寫操作
void (*ChipSelect)(ADS1256CSType cs); //實(shí)現(xiàn)片選
uint16_t (*GetReadyInput)(void); //實(shí)現(xiàn)Ready狀態(tài)監(jiān)視
void (*Delay)(volatile uint32_t nTime); //實(shí)現(xiàn)ms延時(shí)操作
}ADS1256ObjectType;
2.1.2、對(duì)象初始化函數(shù)
??我們抽象了ADS1256模數(shù)轉(zhuǎn)換器的對(duì)象類型,使用這一對(duì)象類型我們可以獲得具體的對(duì)象變量,但這一對(duì)象變量必須要進(jìn)行必要的屬性和操作設(shè)定才能進(jìn)行正確的操作。為了完成對(duì)象變量屬性和操作的配置,我們需要一個(gè)對(duì)象初始化函數(shù)。
/*ADS1256初始化配置函數(shù)*/
void ADS1256Initialization(ADS1256ObjectType *ads, //待初始化的ADS1256對(duì)象
ADS1256OrderType order, //數(shù)據(jù)順序
ADS1256ACALType acal, //自動(dòng)校準(zhǔn)使能
ADS1256BufenType bufEn, //模擬量緩存使能
ADS1256ClkoutType clkOut, //時(shí)鐘輸出類型
ADS1256SDCSType sdcs, //傳感器檢測(cè)電流
ADS1256GainType gain, //增益
ADS1256DRateType dataRate, //數(shù)據(jù)輸出速率
ADS1256DIOType *dio, //輸入輸出配置
ADS1256ReadWriteType readWrite, //讀寫函數(shù)指針
ADS1256ChipSelectType cs, //片選函數(shù)指針
ADS1256GetReadyInputType ready, //就緒函數(shù)指針
ADS1256DelaymsType delayms //毫秒延時(shí)函數(shù)指針
)
{
uint8_t Order[]={STATUS_ORDER_MOST,STATUS_ORDER_LEAST};
uint8_t ACAL[]={STATUS_ACAL_DISABLE,STATUS_ACAL_ENABLE};
uint8_t BUFEN[]={STATUS_BUFEN_DISABLE,STATUS_BUFEN_ENABLE};
uint8_t clkck[]={ADCON_CLOCK_OFF,ADCON_CLOCK_FCLKIN,ADCON_CLOCK_HALF,ADCON_CLOCK_QUARTER};
uint8_t sDCS[]={ADCON_SDCS_OFF,ADCON_SDCS_05uS,ADCON_SDCS_2uS,ADCON_SDCS_10uS};
uint8_t gains[]={ADCON_PGA_GAIN1,ADCON_PGA_GAIN2,ADCON_PGA_GAIN4,ADCON_PGA_GAIN8,
ADCON_PGA_GAIN16,ADCON_PGA_GAIN32,ADCON_PGA_GAIN64};
uint8_t dRate[]={DRATE_30000SPS,DRATE_15000SPS,DRATE_7500SPS,DRATE_3750SPS,
DRATE_2000SPS,DRATE_1000SPS,DRATE_500SPS,DRATE_100SPS,
DRATE_60SPS,DRATE_50SPS,DRATE_30SPS,DRATE_25SPS,
DRATE_15SPS,DRATE_10SPS,DRATE_5SPS,DRATE_2_5SPS};
uint8_t dir[4][2]={{GPIO_DIR0_OUTPUT,GPIO_DIR0_INPUT},
{GPIO_DIR1_OUTPUT,GPIO_DIR1_INPUT},
{GPIO_DIR2_OUTPUT,GPIO_DIR2_INPUT},
{GPIO_DIR3_OUTPUT,GPIO_DIR3_INPUT}};
if((ads==NULL)||(readWrite==NULL)||(ready==NULL)||(delayms==NULL))
{
return;
}
ads->ReadWrite=readWrite;
ads->GetReadyInput=ready;
ads->Delay=delayms;
if(cs==NULL)
{
ads->ChipSelect=ADS1256ChipSelect;
}
else
{
ads->ChipSelect=cs;
}
for(int i=0; i<11;i++)
{
ads->Register[i]=0x00;
}
ads->Register[REG_STATUS]=Order[order]||ACAL[acal]||BUFEN[bufEn];
ads->Register[REG_MUX]=0x00;
ads->Register[REG_ADCON]=clkck[clkOut]||sDCS[sdcs]||gains[gain];
ads->Register[REG_DRATE]=dRate[dataRate];
ads->Register[REG_IO]=dir[0][dio[0]]||dir[1][dio[1]]||dir[2][dio[2]]||dir[3][dio[3]];
WriteADS1256Register(ads,REG_STATUS,1);
WriteADS1256Register(ads,REG_MUX,1);
WriteADS1256Register(ads,REG_ADCON,1);
WriteADS1256Register(ads,REG_DRATE,1);
WriteADS1256Register(ads,REG_IO,1);
ADS1256Calibration(ads,SELFCAL);
ReadADS1256Register(ads,REG_STATUS,11);
}
??在這個(gè)初始化函數(shù)中,我們完成兩個(gè)方面的內(nèi)容:一是對(duì)屬性的賦值和對(duì)操作函數(shù)指針進(jìn)行初始化;二是對(duì)對(duì)象變量所代表的對(duì)象進(jìn)行初始化配置。
2.2、對(duì)象操作
??我們獲得了ADS1256模數(shù)轉(zhuǎn)換器的對(duì)象類型,也編寫了初始化對(duì)象變量的函數(shù),接下來我們考慮一下ADS1256模數(shù)轉(zhuǎn)換器的一些主要的操作過程。
2.2.1、讀數(shù)據(jù)
??作為模數(shù)轉(zhuǎn)換器,我們首要的目的就是從其獲得我們想要的數(shù)據(jù)。ADS1256讀數(shù)據(jù)分為連續(xù)讀取和非連續(xù)讀取,在這里我們考慮單次讀取數(shù)據(jù)的操作。當(dāng)模數(shù)轉(zhuǎn)換完成后,就緒信號(hào)下拉到“0”,這個(gè)時(shí)候可以讀取數(shù)據(jù),數(shù)據(jù)讀取后就緒信號(hào)將上拉到“1”。
??根據(jù)上述描述和時(shí)序圖我們可以編寫讀取數(shù)據(jù)的操作如下:
/*ADS1256讀取數(shù)據(jù)*/
static uint32_t ADS1256ReadData(ADS1256ObjectType *ads)
{
uint8_t cmd[1]={RDATA};
uint8_t rData[3];
uint32_t result=0;
while(ads->GetReadyInput()==1);
ads->ChipSelect(ADS1256CS_Enable);
ads->ReadWrite(cmd,rData,3);
ads->ChipSelect(ADS1256CS_Disable);
result=rData[0];
result=(result<<8)+rData[1];
result=(result<<8)+rData[2];
return result;
}
2.2.2、讀寄存器
??我們已經(jīng)了解ADS1256有11個(gè)寄存器,這些寄存器都可讀取。讀取寄存器的命令由2個(gè)字節(jié)組成。第一個(gè)字節(jié)是讀寄存器命令0x10與寄存器起始地址合并而成。第二個(gè)字節(jié)是所要讀取的寄存器數(shù)量減1。具體的操作時(shí)序如下圖所示:
??根據(jù)上述對(duì)讀寄存器的描述和時(shí)序圖我們可以編寫讀寄存器的操作如下:
/*讀ADS1256寄存器*/
static void ReadADS1256Register(ADS1256ObjectType *ads,uint8_t regAddr,uint8_t regNum)
{
uint8_t cmd[2];
uint8_t rData[11];
cmd[0]=RREG|regAddr;
cmd[1]=regNum-1;
ads->ChipSelect(ADS1256CS_Enable);
ads->ReadWrite(cmd,rData,2);
cmd[0]=0;
cmd[1]=0;
ads->ReadWrite(cmd,rData,regNum);
ads->ChipSelect(ADS1256CS_Disable);
for(int i=0;iRegister[regAddr+i]=rData[i];
}
}
2.2.3、寫寄存器
??在ADS1256的11個(gè)寄存器中有一些寄存器用于配置ADS1256的工作特性,可以寫這些寄存器。寫寄存器的命令也是由2個(gè)自己組成。第一個(gè)字節(jié)是讀寄存器命令0x50與寄存器起始地址合并而成。第二個(gè)字節(jié)是所要寫的寄存器數(shù)量減1。具體的操作時(shí)序如下圖所示:
??根據(jù)上述對(duì)寫寄存器的描述和時(shí)序圖我們可以編寫寫寄存器的操作如下:
/*寫ADS1256寄存器*/
static void WriteADS1256Register(ADS1256ObjectType *ads,uint8_t regAddr,uint8_t regNum)
{
uint8_t wData[7];
uint16_t index=0;
uint8_t rData[2];
wData[index++]=WREG|regAddr;
wData[index++]=regNum-1;
for(int i=0;iRegister[regAddr+i];
}
ads->ChipSelect(ADS1256CS_Enable);
ads->ReadWrite(wData,rData,index);
ads->ChipSelect(ADS1256CS_Disable);
}
3、驅(qū)動(dòng)的使用
??我們已經(jīng)設(shè)計(jì)并實(shí)現(xiàn)了ADS1256模數(shù)轉(zhuǎn)換器的驅(qū)動(dòng)程序。在這一節(jié)中我們將設(shè)計(jì)一個(gè)簡(jiǎn)單的例子,通過這個(gè)例子我們將簡(jiǎn)單的驗(yàn)證驅(qū)動(dòng)程序的正確性,并簡(jiǎn)要說明驅(qū)動(dòng)的使用方法。
3.1、聲明并初始化對(duì)象
??我們?yōu)锳DS1256模數(shù)轉(zhuǎn)換器設(shè)計(jì)的驅(qū)動(dòng)是基于對(duì)象開發(fā)的,所以我們?cè)谑褂抿?qū)動(dòng)之前需要聲明一個(gè)ADS1256模數(shù)轉(zhuǎn)換器對(duì)象變量。使用我們前面定義的ADS1256模數(shù)轉(zhuǎn)換器對(duì)象類型聲明這一變量如下:
??ADS1256ObjectType ads1256;
??聲明了這個(gè)對(duì)象變量后,我們還需要使用對(duì)象初始化函數(shù)來將這個(gè)對(duì)象變量變量進(jìn)行初始化。我們?cè)O(shè)計(jì)的初始換函數(shù)有多個(gè)參數(shù):
ADS1256ObjectType *ads, //待初始化的ADS1256對(duì)象
ADS1256OrderType order, //數(shù)據(jù)順序
ADS1256ACALType acal, //自動(dòng)校準(zhǔn)使能
ADS1256BufenType bufEn, //模擬量緩存使能
ADS1256ClkoutType clkOut, //時(shí)鐘輸出類型
ADS1256SDCSType sdcs, //傳感器檢測(cè)電流
ADS1256GainType gain, //增益
ADS1256DRateType dataRate, //數(shù)據(jù)輸出速率
ADS1256DIOType *dio, //輸入輸出配置
ADS1256ReadWriteType readWrite, //讀寫函數(shù)指針
ADS1256ChipSelectType cs, //片選函數(shù)指針
ADS1256GetReadyInputType ready, //就緒函數(shù)指針
ADS1256DelaymsType delayms //毫秒延時(shí)函數(shù)指針
??這些參數(shù)中,ADS1256對(duì)象我們已經(jīng)聲明。數(shù)據(jù)順序、自動(dòng)校準(zhǔn)使能、模擬量緩存使能、時(shí)鐘輸出類型、傳感器檢測(cè)電流、增益、數(shù)據(jù)輸出速率等幾個(gè)參數(shù)均為枚舉量,我們們根據(jù)需要選擇就可,在這里我們均按默認(rèn)值選擇。輸入輸出配置這個(gè)參數(shù),因有4個(gè)IO需獨(dú)立配置,所以我們定義一個(gè)數(shù)組,并將其傳入。剩下的幾個(gè)函數(shù)指針其原型定義如下:
/*定義讀寫操作函數(shù)指針類型*/
typedef void (*ADS1256ReadWriteType)(uint8_t *wData,uint8_t *rData,uint16_t size);
/*實(shí)現(xiàn)片選*/
typedef void (*ADS1256ChipSelectType)(ADS1256CSType cs);
/*實(shí)現(xiàn)Ready狀態(tài)監(jiān)視*/
typedef uint16_t (*ADS1256GetReadyInputType)(void);
/*實(shí)現(xiàn)ms延時(shí)操作*/
typedef void (*ADS1256DelaymsType)(volatile uint32_t nTime);
??我們需要根據(jù)函數(shù)的原型聲明來結(jié)合具體的軟硬件平臺(tái)設(shè)計(jì)這幾個(gè)函數(shù),并將函數(shù)指針以參數(shù)的形式傳遞給初始函數(shù)。我們是在STM32平臺(tái)來實(shí)現(xiàn)這個(gè)示例,所以延時(shí)函數(shù)我們直接采用HAL_Delay即可,其他幾個(gè)函數(shù)實(shí)現(xiàn)如下:
/*定義片選信號(hào)函數(shù)*/
void ADS1256CS(ADS1256CSType en)
{
if(ADS1256CS_Enable==en)
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_4, GPIO_PIN_SET);
}
}
/* 定義就緒信號(hào)讀取函數(shù) */
uint16_t ADS1256CheckReady(void)
{
return HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_0);
}
/*定義發(fā)送數(shù)據(jù)函數(shù)*/
void ADS1256WriteReadData(uint8_t *wData,uint8_t *rData,uint16_t size)
{
HAL_SPI_TransmitReceive(&ads1256hspi,wData,rxData,size,1000);
}
??根據(jù)上述這些定義后,我們可以調(diào)用初始化函數(shù)來實(shí)現(xiàn)對(duì)ADS1256對(duì)象變量進(jìn)行初始化。具體如下:
ADS1256DIOType dio[4]={ADS1256_DIO_INPUT,ADS1256_DIO_INPUT,ADS1256_DIO_INPUT,ADS1256_DIO_OUTPUT};
/*ADS1256初始化配置函數(shù)*/
ADS1256Initialization(&ads1256, //待初始化的ADS1256對(duì)象
ADS1256_ORDER_MOST, //數(shù)據(jù)順序
ADS1256_ACAL_DISABLE, //自動(dòng)校準(zhǔn)使能
ADS1256_BUFEN_DISABLE, //模擬量緩存使能
ADS1256_CLKOUT_FCLKIN, //時(shí)鐘輸出類型
ADS1256_SDCS_OFF, //傳感器檢測(cè)電流
ADS1256_GAIN1, //增益
ADS1256_DRATE_30000SPS, //數(shù)據(jù)輸出速率
dio, //輸入輸出配置
ADS1256WriteReadData, //讀寫函數(shù)指針
ADS1256CS, //片選函數(shù)指針
ADS1256CheckReady, //就緒函數(shù)指針
HAL_Delay //毫秒延時(shí)函數(shù)指針
);
3.2、基于對(duì)象進(jìn)行操作
??在完成對(duì)象變量的初始化后,我們就可以通過操作這個(gè)對(duì)象變量獲取采集的數(shù)據(jù)。這里我們采集8路單端輸入的數(shù)據(jù)。需要注意的是每次讀出來的數(shù)據(jù)并非我們當(dāng)前設(shè)定的通道的數(shù)據(jù),而是我們上次設(shè)定的通道的數(shù)據(jù)。據(jù)此我們?cè)O(shè)計(jì)簡(jiǎn)單的數(shù)據(jù)采集函數(shù)如下:
/*獲取通道數(shù)據(jù)*/
void GetADS1256ChannelValue(void)
{
int32_t dataCode[8];
for(ADS1256ChannelType ainP=ADS1256_AIN0;ainPif(ainP==ADS1256_AIN0)
{
dataCode[7]=ADS1256SingleReadData(&ads1256,ainP,ADS1256_AINCOM);
}
else
{
dataCode[ainP]=ADS1256SingleReadData(&ads1256,ainP,ADS1256_AINCOM);
}
}
}
4、應(yīng)用總結(jié)
??我們?cè)O(shè)計(jì)了ADS1256模數(shù)轉(zhuǎn)換器的驅(qū)動(dòng)程序,并利用一個(gè)簡(jiǎn)單的例子對(duì)齊進(jìn)行了驗(yàn)證,讀取數(shù)據(jù)沒有問題。
??在使用驅(qū)動(dòng)程序時(shí)需要注意,片選信號(hào)并非必須實(shí)現(xiàn)。因?yàn)橛行r(shí)候我們可能需要在硬件上直接將其選中,此時(shí)添加片選操作函數(shù)是沒有什么意義的,我們可以在初始化時(shí)傳入NULL來完成。
??在使用驅(qū)動(dòng)程序時(shí)需要注意,ADS1256模數(shù)轉(zhuǎn)換器在設(shè)置通道選擇然后就可以在轉(zhuǎn)換過程中讀取上一個(gè)轉(zhuǎn)換周期的數(shù)據(jù)。所以在驅(qū)動(dòng)程序中,設(shè)置通道選擇后,沒有等待轉(zhuǎn)化完成,而是直接讀取了數(shù)據(jù),這個(gè)數(shù)據(jù)實(shí)際上是上一次轉(zhuǎn)換的數(shù)據(jù),這樣可以充分利用轉(zhuǎn)換周期。所以在使用驅(qū)動(dòng)程序需要注意讀取的數(shù)據(jù)所對(duì)應(yīng)的通道。
評(píng)論