天天看點

外設驅動庫開發筆記47:ADS111x系列ADC驅動

  關于ADC我們已經讨論過不少了,但在不同的應用需求下,我們會選擇不同的原件。在這裡我們将讨論ADS111x系列ADC驅動的設計與實作。

1、功能概述

  ADS1113、 ADS1114 和 ADS1115 器件 (ADS111x)是相容 I2C 的 16 位高精度低功耗模數轉換器。ADS111x 器件采用了低漂移電壓基準和振蕩器。ADS1114 和 ADS1115 還采用可程式設計增益放大器(PGA) 和數字比較器。憑借這些特性加之較寬的工作電源電壓範圍,使得ADS111x非常适合功率受限和空間受限的傳感器測量應用。其引腳定義及封裝如下:

外設驅動庫開發筆記47:ADS111x系列ADC驅動

  ADS111x 可在資料速率高達每秒 860 個樣本 (SPS)的情況下執行轉換。 PGA 可提供從 ±256mV 到±6.144V 的輸入範圍,進而實作精準的大小信号測量。ADS1115具有一個輸入多路複用器 (MUX),可實作兩路差分輸入測量或四路單端輸入測量。

1.1、通訊接口

  ADS111x通過I2C接口進行通信。ADS111x有一個位址引腳ADDR,用來配置裝置的I2C位址。這個引腳可以連接配接到GND、VDD、SDA或SCL,允許用一個引腳選擇四個不同的位址,具體如下:

外設驅動庫開發筆記47:ADS111x系列ADC驅動

  器件會對位址引腳的狀态進行連續采樣。優先使用GND,VDD和SCL來設定期間位址。如果使用SDA設定裝置位址,需要在SCL線路低電平後,至少保持SDA線路低電平100 ns,以確定I2C通信時裝置正确解碼該位址。

1.2、寄存器

  ADS111x有四個寄存器,可以通過使用位址指針寄存器的I2C接口通路。轉換寄存器包含最後一次轉換的結果。配置寄存器用于改變ADS111x的工作模式和查詢裝置狀态。另外兩個寄存器Lo_thresh和Hi_thresh設定了用于比較器函數的門檻值,在ADS1113中不可用。

1.2.1、位址指針寄存器

  對ADS111x寄存器的操作都是通過寄存器位址指針寄存器來實作的,其資料結構如下:

外設驅動庫開發筆記47:ADS111x系列ADC驅動

  對于做後兩位的定義為:00,轉換寄存器;01,配置寄存器;10,低門限值寄存器;11,高門限值寄存器。

1.2.2、轉換寄存器

  16位轉換寄存器以二進制補碼格式存放最後一次轉換的結果。其格式如下:

外設驅動庫開發筆記47:ADS111x系列ADC驅動

  在上電之後,轉換寄存器被清除為0,并保持0直到第一次轉換完成。

1.2.3、配置寄存器

  16位配置寄存器用于控制操作模式,輸入選擇,資料速率,滿量程和比較器模式。其格式如下:

外設驅動庫開發筆記47:ADS111x系列ADC驅動

  多路選擇器用于配置目前需要采集的通道,隻有ADS1115具有該功能。PGA用于配置采集的增益大小,DR用于配置蘇劇的輸出速率。

1.2.4、門限值寄存器

  比較器使用的上下限門檻值以二進制補碼格式存儲在兩個16位寄存器中。比較器實作為數字比較器;是以,當PGA設定發生更改時,這些寄存器中的值必須更新。具體的資料格式如下:

外設驅動庫開發筆記47:ADS111x系列ADC驅動
外設驅動庫開發筆記47:ADS111x系列ADC驅動

  通過設定高門檻值寄存器MSB為1和低門檻值寄存器MSB為0,可以啟用ALERT/RDY引腳的轉換準備函數。要使用ALERT/RDY引腳的比較器函數,高門檻值寄存器的值必須總是大于低門檻值寄存器的值。當設定為RDY模式時,ALERT/RDY引腳在單發模式下輸出OS位,在連續轉換模式下提供連續轉換就緒脈沖。

2、驅動設計與實作

在前述中我們已經梳理了ADS111x系列模數轉換器的相關技術特性。接下來我們需要依據我們了解的這些技術資料設計并設計ADS111x系列模數轉換器的驅動程式。

2.1、對象定義

  與以前的驅動設計一樣,我們依然是基于對象來設計ADS111x系列模數轉換器的驅動程式。是以我們要先抽象并定義ADS111x系列模數轉換器對象類型。一般來講對象包括屬性與操作兩方面,我們将據此逐一分析ADS111x系列模數轉換器對象的屬性與操作。

  先考慮ADS111x系列模數轉換器對象的屬性。ADS111x系列模數轉換器采用I2C接口總線,而每台I2C的從站都有一個裝置位址,該位址表示了每台裝置的身份,是以我們将器作為ADS111x系列模數轉換器對象的一個屬性。此外為了檢視目前ADS111x系列模數轉換器對象的配置情況,我們希望記錄配置寄存器的目前值,是以我們設定一個屬性來記錄它。每次讀取回來的個通道的資料實際表示了各通道的目前狀态是以我們也将其作為ADS111x系列模數轉換器對象的屬性來記錄之。

  再來考慮ADS111x系列模數轉換器對象的操作。我們對ADS111x系列模數轉換器所要進行的操作主要有下發指令、讀取資料等,這些動作都需要依據具體的軟硬體平台來實作,我們以對象操作的方式來處理它。為了控制時序,我們需要延時操作函數,而延時操作也需要基于具體的平台來實作,是以我們将延時函數也作為對象的一個操作。

  根據上述對ADS111x系列模數轉換器對象屬性和操作的分析,我們可以抽象出ADS111x系列模數轉換器的對象類型如下:

/*定義ADS111x對象類型*/
typedef struct Ads111xObject {
    uint8_t devAddress;     //裝置位址
    uint16_t dataCode[8];      //讀取的資料值
    uint16_t config;      //配置寄存器值
    void (*Transmit)(struct Ads111xObject *ads,uint8_t *tData,uint16_t tSize);
    void (*Receive)(struct Ads111xObject *ads,uint8_t *rData,uint16_t rSize);
    void (*Delayus)(volatile uint32_t nTime);       //實作us延時操作
}Ads111xObjectType;      

  抽象了對象類型後就可聲明對象變量,可是這個對象變量必須作必要的初始化才能使用。是以我們需要一個初始化函數來對其進行初始化。在此函數中,我們将檢測變量的有效性和初始狀态指派,并對裝置進行必要的配置。根據這些要求我們設計ADS111x系列模數轉換器的對象初始化函數如下:

/*ADS111x初始化配置*/
void Ads111xInitialization(Ads111xObjectType *ads,  //ADS111x對象變量
                           uint8_t devAddress,      //裝置位址
                           Ads111xGainType gain,    //增益
                           Ads111xDataRateType dr,  //輸出速率
                           Ads111xTransmit transmit,//發送函數指針
                           Ads111xReceive receive,  //接收函數指針
                           Ads111xDelayus delayus   //us延時函數指針
                           )
{
    uint16_t channels[]={0x0000,0x1000,0x2000,0x3000,0x4000,0x5000,0x6000,0x7000};
    uint16_t gains[]={0x0000,0x0200,0x0400,0x0600,0x0800,0x0A00};
    uint16_t dataRates[]={0x0000,0x0020,0x0040,0x0060,0x0080,0x00A0,0x00C0,0x00E0};
    uint16_t config=0x8103;

    if((ads==NULL)||(transmit==NULL)||(receive==NULL)||(delayus==NULL))
    {
        return ;
    }
    
    ads->Transmit=transmit;
    ads->Receive=receive;
    ads->Delayus=delayus;
    
    Ads111xReset(ads);
    
    if((devAddress==0x48)||(devAddress==0x49)||(devAddress==0x4A)||(devAddress==0x4B))
    {
        ads->devAddress=(devAddress<<1);
    }
    else if((devAddress==0x90)||(devAddress==0x92)||(devAddress==0x94)||(devAddress==0x96))
    {
        ads->devAddress=devAddress;
    }
    else
    {
        ads->devAddress=0x00;
    }

    config=config|channels[ADS111X_AIN0_AIN1]|gains[gain]|dataRates[dr];
    
    Ads111xWriteRegister(ads,ConfigRegister,config);
    ads->Delayus(200);
    ads->config=Ads111xReadRegister(ads,ConfigRegister);
}      

2.2、對象操作

  為了從ADS111x通路特定的寄存器,必須先向位址指針寄存器中寫一個适當的值來訓示要通路的寄存器位址。也就是說不論讀寫那個寄存器都需要首先寫位址指針寄存器才能實作。

2.2.1、讀寄存器

  當從ADS111x讀取資料時,先前寫入到位址指針寄存器的值決定了被讀取的是哪一個寄存器。要想改變所要度的寄存器需要先修改位址指針寄存器的值。也就是說都一個寄存器的值分為兩步完成,首先寫位址指針寄存器,然後再讀所指向的寄存器的值。具體的操作時序圖如下:

外設驅動庫開發筆記47:ADS111x系列ADC驅動

  根據前面的分析以及上述的時序圖我們可以實作讀ADS111x系列模數轉換器寄存器的操作函數如下:

/*讀ADS111x寄存器*/
static uint16_t Ads111xReadRegister(Ads111xObjectType *ads,Ads111xRegisterType reg)
{
     uint8_t wData;
     uint8_t rData[2];
     uint16_t result=0;
     
     wData=(uint8_t)reg;
     
     ads->Transmit(ads,&wData,1);
     ads->Delayus(200);
     ads->Receive(ads,rData,2);
     
     result=rData[0];
     result=(result<<8)+rData[1];
     
     return result;
}      

  需要說明一下的是,如果是連續多次讀取同一個寄存器則不需要先修改位址指針寄存器的值。

2.2.2、寫寄存器

  對于ADS111x系列模數轉換器來說,寫一個寄存器也是從寫位址指針寄存器開始的。但是與讀寄存器不同的是并不是兩步完成的,而是一次全部完成,即先寫位址指針寄存器接着就寫所要寫的寄存器。具體的操作時序如下:

外設驅動庫開發筆記47:ADS111x系列ADC驅動

  根據我們前述的分析以及上面的時序圖,我們可以實作寫ADS111x系列模數轉換器的函數如下:

/*寫ADS111x寄存器*/
static void Ads111xWriteRegister(Ads111xObjectType *ads,Ads111xRegisterType reg,uint16_t regValue)
{
    uint8_t wData[3];
    
    wData[0]=(uint8_t)reg;
    wData[1]=(uint8_t)(regValue>>8);
    wData[2]=(uint8_t)regValue;
    
    ads->Transmit(ads,wData,3);
}      

3、驅動的使用

  我們已經設計并實作了ADS111x系列模數轉換器的驅動程式,為了驗證驅動程式的正确性,這一節我們将來讨論基于驅動程式設計一個簡單的應用驗證。

3.1、聲明并初始化對象

  我們是基于對象實作的ADS111x系列模數轉換器驅動程式,是以我們先來聲明一個ADS111x系列模數轉換器的對象變量。

Ads111xObjectType ads1115;      

  聲明了這個對象變量後,我們還需要使用前面實作的初始化函數Ads111xInitialization對這個對象變量進行初始化。這個初始化函數所需要的輸入參數如下:

Ads111xObjectType *ads,  //ADS111x對象變量
uint8_t devAddress,      //裝置位址
Ads111xGainType gain,    //增益
Ads111xDataRateType dr,  //輸出速率
Ads111xTransmit transmit,//發送函數指針
Ads111xReceive receive,  //接收函數指針
Ads111xDelayus delayus   //us延時函數指針      

  在這些參數中,第一個為我們想要初始化的對象變量。devAddress就是ADS111x系列模數轉換器的裝置位址,根據實際輸入即可。增益和資料輸出均為枚舉量,根據實際需要選擇輸入即可。還有三個函數指針是我們需要實作的參數,這幾個函數的原型定義如下:

typedef void (*Ads111xTransmit)(struct Ads111xObject *ads,uint8_t *tData,uint16_t tSize);
typedef void (*Ads111xReceive)(struct Ads111xObject *ads,uint8_t *rData,uint16_t rSize);
typedef void (*Ads111xDelayus)(volatile uint32_t nTime);       //實作us延時操作      

  這些函數的實作依賴于具體的軟硬體平台,這裡我們基于STM32F103硬體平台和HAL庫來實作的相關操作函數。

/*通過I2C2發送資料到ADS115*/
static void BmcbAds111xTransmit(struct Ads111xObject *ads,uint8_t *tData,uint16_t tSize)
{
    HAL_I2C_Master_Transmit(&hi2c2,ads->devAddress,tData,tSize,1000);
}

/*通過I2C2端口從ADS1115接收資料*/
static void BmcbAds111xReceive(struct Ads111xObject *ads,uint8_t *rData,uint16_t rSize)
{
    HAL_I2C_Master_Receive(&hi2c2,ads->devAddress,rData,rSize,1000);
}      

  而延時函數采用我們在STM32系統下實作的通用函數即可。于是我們可以使用初始化函數實作對象變量初始化如下:

Ads111xInitialization(&ads1115,             //ADS111x對象變量
                  0x90,                 //裝置位址
                  ADS111X_GAIN_8,      //增益
                  ADS111X_DR_128,       //輸出速率
                  BmcbAds111xTransmit,  //發送函數指針
                  BmcbAds111xReceive,   //接收函數指針
                  Delayus               //us延時函數指針
                  );      

3.2、基于對象進行操作

  我們初始化了對象變量就可一使用它來實作相應的操作,是以我們含需要實作相關的應用函數。事實上,這款驅動程式我們已經應用到實際的工程應用中,是以我們節選其中關于讀取差分通道的操作。

Ads111xChannelType Channel[sChCount]={ADS111X_AIN0_AIN1,ADS111X_AIN2_AIN3};
    
    if(sCh>=sChCount)
    {
        sCh=0;
    }
    
    Ads111xGetDataCode(&ads1115,Channel[sCh]);
    sCh++;

    aPara.phyPara.pressure=Ads111xCalcPhysicalValue(&ads1115,ADS111X_AIN0_AIN1,PRES_RANGE,PRES_ZERO);
    aPara.phyPara.vacuum=Ads111xCalcPhysicalValue(&ads1115,ADS111X_AIN2_AIN3,VACU_RANGE,VACU_ZERO);      

4、應用總結

  我們就已經完成了ADS111x系列模數轉換器的驅動設計與驗證。在我們的實際使用過程中運作穩定,效果也很好。

  在使用驅動程式時需要注意,此驅動隻适用于ADS1113、 ADS1114 和 ADS1115 器件。

歡迎關注: