我们经常会碰到多通道AD采集的需求,有时候甚至需要高精度的ADC器件。本篇我们将来设计并实现ADS1256模数转换器的驱动。并简单讨论该驱动使用方式。

1、功能概述

  ADS1256是TI公司推出的一款低噪声高分辨率的24位Sigma-Delta(E-v)模数转换器(ADC)。E-vADC与传统的逐次逼近型和积分型ADC相比有转换误差小而价格低廉的优点,但由于受带宽和有效采样率的限制,E-vADC不适用于高频数据采集的场合。该款ADS1256可适合于采集最高频率只有几千赫兹的模拟数据的系统中,数据输出速率最高可为30K采样点/秒,4路差分或8路伪差分输入,有完善的自校正和系统校正系统,SPI串行数据传输接口。其结构图如下所示:

  从结构图可以看出来,ADS1256是模拟区域与数字区域完全独立的ADC,即AVDD给模拟区域供电,DVDD给数字区域供电,在原理图设计方面按照官方指导文档,需要对两个区域做独立的布线与隔离处理,才能让信噪比最佳。
  ADS1256采用SSOP的封装形式,具有8个模拟输入通道,共28个引脚,与其类似的2通道产品ADS1255共有20引脚,其实两者操作相同,所以我们设计驱动也会考虑兼容性。其中ADS1256引脚排布和定义如下图所示:

  ADS1255和ADS1256的操作是通过一组寄存器来控制的。这些寄存器包含了配置部件所需的所有信息,如数据速率、多路复用器设置、PGA设置、校准等。这些寄存器的地址及结构如下表所述:

  我们知道了这些寄存器的定义,那么就可以操作ADS1256了。可是我们怎么来实现对这些寄存器的访问呢?这就涉及到操作命令的问题了。ADS1256有多个操作命令,具体如下表所示:

  在以上这些命令中,除了读写寄存器操作需要有第二个字节命令和数据外,其它命令都是独立使用的。

2、驱动设计与实现

  我们已经了解了ADS1256的相关结构、寄存器及操作命令。接下来我们就来考虑如何设计ADS1256的驱动程序。

2.1、对象定义

  与以往一样,我们依然是基于对象来实现ADS1256的驱动程序,所以我们需要抽象出ADS1256的对象类型。

2.1.1、抽象数据类型

  我们先来考虑一下ADS1256对象类型的定义问题。一个对象一般来说主要包括属性和操作两个方面的内容,我们也从这两个方面来分析ADS1256对象。
  首先我们来考虑ADS1256模数转换器对象的属性。这些属性必须能够标识ADS1256模数转换器对象的特征,或者是存储ADS1256模数转换器对象的某种状态。对于ADS1256模数转换器对象我们希望可以记录寄存器的状态,所以我们将各个寄存器定义为该对象的属性。
  接下来我们再来考虑一下ADS1256模数转换器对象的操作。一个对象有各种各样的操作,或者说他能实现很多的操作,但不是所有的操作都是我们要提取的。我们需要考虑的是那些对象所独有并且同类对象都必不可少的操作,以及那些不能由对象独自完成,依赖于具体平台但又决定对象的行为的必要操作。对于ADS1256莫数转换器,我们需要读写数据,操作片选信号,读取就绪信号等。但这些操作都依赖于具体的软硬件平台,我们将这些操作定义对象的操作,通过函数指针的方式将具体的操作函数传递给对象变量,以便于适用于不同的软硬件平台。此外,由于时序控制的需要我们需要在驱动中使用延时操作,而延时操作的实现依赖于具体的软硬件平台,所以我们也将其抽象为对象的操作。根据上述我们的分析,可以定义ADS1256模数转换器对象的类型如下:

/*定义ADS1256对象类型*/
typedef struct ADS1256Object {uint8_t Register[11];void (*ReadWrite)(uint8_t *wData,uint8_t *rData,uint16_t size);       //实现读写操作void (*ChipSelect)(ADS1256CSType cs);  //实现片选uint16_t (*GetReadyInput)(void);      //实现Ready状态监视void (*Delay)(volatile uint32_t nTime);       //实现ms延时操作
}ADS1256ObjectType;

2.1.2、对象初始化函数

  我们抽象了ADS1256模数转换器的对象类型,使用这一对象类型我们可以获得具体的对象变量,但这一对象变量必须要进行必要的属性和操作设定才能进行正确的操作。为了完成对象变量属性和操作的配置,我们需要一个对象初始化函数。

/*ADS1256初始化配置函数*/
void ADS1256Initialization(ADS1256ObjectType *ads,              //待初始化的ADS1256对象ADS1256OrderType order,              //数据顺序ADS1256ACALType acal,                //自动校准使能ADS1256BufenType bufEn,              //模拟量缓存使能ADS1256ClkoutType clkOut,            //时钟输出类型ADS1256SDCSType sdcs,                //传感器检测电流ADS1256GainType gain,                //增益ADS1256DRateType dataRate,           //数据输出速率ADS1256DIOType *dio,                 //输入输出配置ADS1256ReadWriteType readWrite,      //读写函数指针ADS1256ChipSelectType cs,            //片选函数指针ADS1256GetReadyInputType ready,      //就绪函数指针ADS1256DelaymsType delayms           //毫秒延时函数指针)
{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);
}

  在这个初始化函数中,我们完成两个方面的内容:一是对属性的赋值和对操作函数指针进行初始化;二是对对象变量所代表的对象进行初始化配置。

2.2、对象操作

  我们获得了ADS1256模数转换器的对象类型,也编写了初始化对象变量的函数,接下来我们考虑一下ADS1256模数转换器的一些主要的操作过程。

2.2.1、读数据

  作为模数转换器,我们首要的目的就是从其获得我们想要的数据。ADS1256读数据分为连续读取和非连续读取,在这里我们考虑单次读取数据的操作。当模数转换完成后,就绪信号下拉到“0”,这个时候可以读取数据,数据读取后就绪信号将上拉到“1”。

  根据上述描述和时序图我们可以编写读取数据的操作如下:

/*ADS1256读取数据*/
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、读寄存器

  我们已经了解ADS1256有11个寄存器,这些寄存器都可读取。读取寄存器的命令由2个字节组成。第一个字节是读寄存器命令0x10与寄存器起始地址合并而成。第二个字节是所要读取的寄存器数量减1。具体的操作时序如下图所示:

  根据上述对读寄存器的描述和时序图我们可以编写读寄存器的操作如下:

/*读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;i<regNum;i++){ads->Register[regAddr+i]=rData[i];}
}

2.2.3、写寄存器

  在ADS1256的11个寄存器中有一些寄存器用于配置ADS1256的工作特性,可以写这些寄存器。写寄存器的命令也是由2个自己组成。第一个字节是读寄存器命令0x50与寄存器起始地址合并而成。第二个字节是所要写的寄存器数量减1。具体的操作时序如下图所示:

  根据上述对写寄存器的描述和时序图我们可以编写写寄存器的操作如下:

/*写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;i<regNum;i++){wData[index++]=ads->Register[regAddr+i];}ads->ChipSelect(ADS1256CS_Enable);ads->ReadWrite(wData,rData,index);ads->ChipSelect(ADS1256CS_Disable);
}

3、驱动的使用

  我们已经设计并实现了ADS1256模数转换器的驱动程序。在这一节中我们将设计一个简单的例子,通过这个例子我们将简单的验证驱动程序的正确性,并简要说明驱动的使用方法。

3.1、声明并初始化对象

  我们为ADS1256模数转换器设计的驱动是基于对象开发的,所以我们在使用驱动之前需要声明一个ADS1256模数转换器对象变量。使用我们前面定义的ADS1256模数转换器对象类型声明这一变量如下:
  ADS1256ObjectType ads1256;
  声明了这个对象变量后,我们还需要使用对象初始化函数来将这个对象变量变量进行初始化。我们设计的初始换函数有多个参数:

ADS1256ObjectType *ads,              //待初始化的ADS1256对象
ADS1256OrderType order,              //数据顺序
ADS1256ACALType acal,                //自动校准使能
ADS1256BufenType bufEn,              //模拟量缓存使能
ADS1256ClkoutType clkOut,            //时钟输出类型
ADS1256SDCSType sdcs,                //传感器检测电流
ADS1256GainType gain,                //增益
ADS1256DRateType dataRate,           //数据输出速率
ADS1256DIOType *dio,                 //输入输出配置
ADS1256ReadWriteType readWrite,      //读写函数指针
ADS1256ChipSelectType cs,            //片选函数指针
ADS1256GetReadyInputType ready,      //就绪函数指针
ADS1256DelaymsType delayms           //毫秒延时函数指针

  这些参数中,ADS1256对象我们已经声明。数据顺序、自动校准使能、模拟量缓存使能、时钟输出类型、传感器检测电流、增益、数据输出速率等几个参数均为枚举量,我们们根据需要选择就可,在这里我们均按默认值选择。输入输出配置这个参数,因有4个IO需独立配置,所以我们定义一个数组,并将其传入。剩下的几个函数指针其原型定义如下:

/*定义读写操作函数指针类型*/
typedef void (*ADS1256ReadWriteType)(uint8_t *wData,uint8_t *rData,uint16_t size);
/*实现片选*/
typedef void (*ADS1256ChipSelectType)(ADS1256CSType cs);
/*实现Ready状态监视*/
typedef uint16_t (*ADS1256GetReadyInputType)(void);
/*实现ms延时操作*/
typedef void (*ADS1256DelaymsType)(volatile uint32_t nTime);

  我们需要根据函数的原型声明来结合具体的软硬件平台设计这几个函数,并将函数指针以参数的形式传递给初始函数。我们是在STM32平台来实现这个示例,所以延时函数我们直接采用HAL_Delay即可,其他几个函数实现如下:

/*定义片选信号函数*/
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);}
}/* 定义就绪信号读取函数 */
uint16_t ADS1256CheckReady(void)
{return HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_0);
}/*定义发送数据函数*/
void ADS1256WriteReadData(uint8_t *wData,uint8_t *rData,uint16_t size)
{HAL_SPI_TransmitReceive(&ads1256hspi,wData,rxData,size,1000);
}

  根据上述这些定义后,我们可以调用初始化函数来实现对ADS1256对象变量进行初始化。具体如下:

ADS1256DIOType dio[4]={ADS1256_DIO_INPUT,ADS1256_DIO_INPUT,ADS1256_DIO_INPUT,ADS1256_DIO_OUTPUT};/*ADS1256初始化配置函数*/ADS1256Initialization(&ads1256,               //待初始化的ADS1256对象ADS1256_ORDER_MOST,     //数据顺序ADS1256_ACAL_DISABLE,   //自动校准使能ADS1256_BUFEN_DISABLE,  //模拟量缓存使能ADS1256_CLKOUT_FCLKIN,  //时钟输出类型ADS1256_SDCS_OFF,       //传感器检测电流ADS1256_GAIN1,          //增益ADS1256_DRATE_30000SPS, //数据输出速率dio,                    //输入输出配置ADS1256WriteReadData,   //读写函数指针ADS1256CS,              //片选函数指针ADS1256CheckReady,      //就绪函数指针HAL_Delay               //毫秒延时函数指针);

3.2、基于对象进行操作

  在完成对象变量的初始化后,我们就可以通过操作这个对象变量获取采集的数据。这里我们采集8路单端输入的数据。需要注意的是每次读出来的数据并非我们当前设定的通道的数据,而是我们上次设定的通道的数据。据此我们设计简单的数据采集函数如下:

/*获取通道数据*/
void GetADS1256ChannelValue(void)
{int32_t dataCode[8];for(ADS1256ChannelType ainP=ADS1256_AIN0;ainP<ADS1256_AINCOM;ainP++){if(ainP==ADS1256_AIN0){dataCode[7]=ADS1256SingleReadData(&ads1256,ainP,ADS1256_AINCOM);}else{dataCode[ainP]=ADS1256SingleReadData(&ads1256,ainP,ADS1256_AINCOM);}}}

4、应用总结

  我们设计了ADS1256模数转换器的驱动程序,并利用一个简单的例子对齐进行了验证,读取数据没有问题。
  在使用驱动程序时需要注意,片选信号并非必须实现。因为有些时候我们可能需要在硬件上直接将其选中,此时添加片选操作函数是没有什么意义的,我们可以在初始化时传入NULL来完成。
  在使用驱动程序时需要注意,ADS1256模数转换器在设置通道选择然后就可以在转换过程中读取上一个转换周期的数据。所以在驱动程序中,设置通道选择后,没有等待转化完成,而是直接读取了数据,这个数据实际上是上一次转换的数据,这样可以充分利用转换周期。所以在使用驱动程序需要注意读取的数据所对应的通道。

欢迎关注:

外设驱动库开发笔记41:ADS1256 ADC驱动相关推荐

  1. 外设驱动库开发笔记47:ADS111x系列ADC驱动

      关于ADC我们已经讨论过不少了,但在不同的应用需求下,我们会选择不同的原件.在这里我们将讨论ADS111x系列ADC驱动的设计与实现. 1.功能概述   ADS1113. ADS1114 和 AD ...

  2. 外设驱动库开发笔记13:MLX90614红外温度传感器驱动

    红外温度传感器一般用于非接触式的温度检测.在我们的系统中经常会有这样的需求.所以我们将其设计为通用的驱动库以备复用.这一篇我们将讲述MLX90614红外温度传感器驱动的设计与实现. 1.功能概述 ML ...

  3. 外设驱动库开发笔记0:EPD总体设计

    在产品开发过程中,不可避免需要使用很多外部的元件及传感器,这些元器件也许是板载的,也许是板外的,但不管怎样,为其开发驱动程序都是必须的.每次都需要为这些元器件编写驱动程序.但每次重复编写调试很麻烦,于 ...

  4. 外设驱动库开发笔记22:ADXL345三轴数字加速度计驱动

    移动设备的广泛应用增加对移动过程中各种参数的检测需求.ADXL345三轴数字加速度计可以用来检测加速度.进而测量倾斜角度等.在这一篇中,我们将讨论ADXL345三轴数字加速度计驱动程序的设计与实现. ...

  5. 外设驱动库开发笔记9:SHT1x系列温湿度传感器驱动

    在我们的产品中,经常需要检测温湿度数据.有很多检测温湿度的方法和模块,其中SHT1x系列温湿度传感器就是一种成本较低使用方便的温湿度检测模块.下面我们就来说一说如何实现SHT1x系列温湿度传感器的驱动 ...

  6. 外设驱动库开发笔记33:LCD1602液晶显示屏驱动

      LCD1602是一种工业字符型液晶,能够同时显示16x02即32个字符.LCD1602液晶显示的原理是利用液晶的物理特性,通过电压对其显示区域进行控制,即可以显示出图形.在这一章我们就来讨论LCD ...

  7. python实现sht驱动_【技术】外设驱动库开发笔记9:SHT1x系列温湿度传感器驱动

    在我们的产品中,经常需要检测温湿度数据.有很多检测温湿度的方法和模块,其中SHT1x系列温湿度传感器就是一种成本较低使用方便的温湿度检测模块.下面我们就来说一说如何实现SHT1x系列温湿度传感器的驱动 ...

  8. 外设驱动库开发笔记54:外设库驱动设计改进的思考

      不知不觉中我们已经发布了五十多篇外设驱动的文章.前段时间有一位网友提出了一些非常中肯的建议,这也让我们开始考虑怎么优化驱动程序设计的问题.在这一篇中我们将来讨论这一问题. 1.问题分析   首先我 ...

  9. 外设驱动库开发笔记29:DS17887实时时钟驱动

    一些时候,在我们的嵌入式产品中需要记录时间,所以我们就需要获取时钟,当然实现的方式多种多样,有的MCU本身就有这一功能,不过精度较低.当我们的应用要求较高时就需要使用专门的实时时钟芯片,如DS1788 ...

  10. 外设驱动库开发笔记35:迪文触摸屏驱动

      有些时候嵌入式系统也需要显示更为复杂的图形,需要更丰富的数据展示.为此,我们需要更大,色彩更丰富,带触屏的显示屏,当然性价比更高就最好了.在我们的项目中遇到此类需求,我们有时会选择DWIN触摸屏. ...

最新文章

  1. [二]Java虚拟机 jvm内存结构 运行时数据内存 class文件与jvm内存结构的映射 jvm数据类型 虚拟机栈 方法区 堆 含义...
  2. USRP E310启用SSH的X11 Forwarding功能
  3. 台式电脑没鼠标怎么移动光标_零基础学电脑从入门到精通 电脑零距离
  4. Xamarin的Kimono以及Google的Guetzli和Draco
  5. c语言iso校验算法,模式识别c语言ISODATA算法.doc
  6. Opencv——灰度变换、直方图均衡化
  7. 计算机网络学习笔记(22. Web应用概述)
  8. paip.oracle10g dmp文件导入总结
  9. Mac 上使用windows软件--wineskin
  10. Keras 中文文档地址
  11. 苹果Mac3D建模软件SketchUp Pro很专业
  12. PS磨皮—高低频磨皮
  13. java建行验签不成功_php无COM版建行支付返回验签记录
  14. spark学习之执行计划explain
  15. [CTF攻防世界] WEB区 关于备份的题目
  16. 用 javascript 解释 curry
  17. 数字信号处理——窗函数
  18. Hough变换直线检测
  19. Python中基本输入和输出
  20. 视频去水印-视频去水印哪个软件好用

热门文章

  1. 买腾讯云服务器怎么选择
  2. HDAO one error
  3. HCIA-Cloud Computing华为云计算IA认证笔记
  4. 利用python爬取租房信息_Python实战:爬取租房信息
  5. 竹子的故事:送给那些坚持了很久却准备放弃的人 --- 厚积方能薄发
  6. cad缩放_CAD中的AL是什么指令? 怎么操作哦
  7. BAT批处理文件,脚本时间值%time:~0,2%%time:~3,2%%time:~6,2%的用法
  8. 利用R语言进行主成分分析的步骤
  9. 宝塔面板php无法安装,宝塔面板php无法安装怎么办
  10. HTML教程: 网页字体的设置浅谈