DDC114电流输入型ADC的驱动设计与实现

模拟技术

2348人已加入

描述

在产品设计过程中,很多时候都会用到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的频率,但高速时波形会变差;三是,积分时间一定要规划好,否则很容易出现超量程的现象。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分