关于ADC我们已经讨论过不少了,但在不同的应用需求下,我们会选择不同的原件。在这里我们将讨论ADS111x系列ADC驱动的设计与实现。

1、功能概述

  ADS1113、 ADS1114 和 ADS1115 器件 (ADS111x)是兼容 I2C 的 16 位高精度低功耗模数转换器。ADS111x 器件采用了低漂移电压基准和振荡器。ADS1114 和 ADS1115 还采用可编程增益放大器(PGA) 和数字比较器。凭借这些特性加之较宽的工作电源电压范围,使得ADS111x非常适合功率受限和空间受限的传感器测量应用。其引脚定义及封装如下:

  ADS111x 可在数据速率高达每秒 860 个样本 (SPS)的情况下执行转换。 PGA 可提供从 ±256mV 到±6.144V 的输入范围,从而实现精准的大小信号测量。ADS1115具有一个输入多路复用器 (MUX),可实现两路差分输入测量或四路单端输入测量。

1.1、通讯接口

  ADS111x通过I2C接口进行通信。ADS111x有一个地址引脚ADDR,用来配置设备的I2C地址。这个引脚可以连接到GND、VDD、SDA或SCL,允许用一个引脚选择四个不同的地址,具体如下:

  器件会对地址引脚的状态进行连续采样。优先使用GND,VDD和SCL来设置期间地址。如果使用SDA设置设备地址,需要在SCL线路低电平后,至少保持SDA线路低电平100 ns,以确保I2C通信时设备正确解码该地址。

1.2、寄存器

  ADS111x有四个寄存器,可以通过使用地址指针寄存器的I2C接口访问。转换寄存器包含最后一次转换的结果。配置寄存器用于改变ADS111x的工作模式和查询设备状态。另外两个寄存器Lo_thresh和Hi_thresh设置了用于比较器函数的阈值,在ADS1113中不可用。

1.2.1、地址指针寄存器

  对ADS111x寄存器的操作都是通过寄存器地址指针寄存器来实现的,其数据结构如下:

  对于做后两位的定义为:00,转换寄存器;01,配置寄存器;10,低门限值寄存器;11,高门限值寄存器。

1.2.2、转换寄存器

  16位转换寄存器以二进制补码格式存放最后一次转换的结果。其格式如下:

  在上电之后,转换寄存器被清除为0,并保持0直到第一次转换完成。

1.2.3、配置寄存器

  16位配置寄存器用于控制操作模式,输入选择,数据速率,满量程和比较器模式。其格式如下:

  多路选择器用于配置当前需要采集的通道,只有ADS1115具有该功能。PGA用于配置采集的增益大小,DR用于配置苏剧的输出速率。

1.2.4、门限值寄存器

  比较器使用的上下限阈值以二进制补码格式存储在两个16位寄存器中。比较器实现为数字比较器;因此,当PGA设置发生更改时,这些寄存器中的值必须更新。具体的数据格式如下:

  通过设置高阈值寄存器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读取数据时,先前写入到地址指针寄存器的值决定了被读取的是哪一个寄存器。要想改变所要度的寄存器需要先修改地址指针寄存器的值。也就是说都一个寄存器的值分为两步完成,首先写地址指针寄存器,然后再读所指向的寄存器的值。具体的操作时序图如下:

  根据前面的分析以及上述的时序图我们可以实现读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系列模数转换器来说,写一个寄存器也是从写地址指针寄存器开始的。但是与读寄存器不同的是并不是两步完成的,而是一次全部完成,即先写地址指针寄存器接着就写所要写的寄存器。具体的操作时序如下:

  根据我们前述的分析以及上面的时序图,我们可以实现写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 器件。

欢迎关注:

外设驱动库开发笔记47:ADS111x系列ADC驱动相关推荐

  1. 外设驱动库开发笔记44:DDC114 ADC驱动

      在产品设计过程中,很多时候都会用到ADC器件,而在一些特殊场合还需要一些特别的ADC器件.我们在这篇中将讨论常用于医疗器件方面的,DDC114这款电流输入ADC,并为其设计一个驱动程序. 1.功能 ...

  2. 外设驱动库开发笔记23:AT24Cxx外部存储器驱动

    在我们的应用开发过程中,经常会使用到外部的EEPROM外部存储器来保存一些参数和配置数据等.而比较常用的就是AT24Cxx系列产品,这一节我们来开发用于操作AT24Cxx系列产品的驱动. 1.功能概述 ...

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

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

  4. 外设驱动库开发笔记16:MS5536C压力变送器驱动

    压力检测也是经常会遇到的需求,比如环境压力或者低压气体等都会用到压力检测.这类检测压力都比较低,一般不会超过大气压,有时甚至是负压.这一篇我们要讨论的MS5536C就属于这类器件.接下来我们将设计并实 ...

  5. 外设驱动库开发笔记40:AT25xxx外部存储器驱动

      我们在前面开发过AT24CXX系列EEPROM存储器,它使用的是I2C接口.不过有时候我们也会使用SPI接口的EEPROM存储器.在这一篇我们将来讨论AT25XXX系列EEPROM存储器的驱动设计 ...

  6. 外设驱动库开发笔记17:MS5803压力变送器驱动

    很多时候我们需要检测被控对象的绝对压力,而且在我们的多款产品中也有这样的需求.当然检测绝对压力的传感器有很多,我们经常使用MS5803来实现压力检测.本篇中我们将设计并实现MS5803系列压力传感器的 ...

  7. 外设驱动库开发笔记34:OLED显示屏驱动

      现在OLED显示屏在嵌入式系统中应用的越来越多.对于一些显示信息不太复杂,以显示信息为主的需求,我们一般会选择OLED显示屏.在这一篇中,我们将讨论OLED显示屏驱动的设计与实现. 1.功能概述 ...

  8. 外设驱动库开发笔记18:MS5837压力变送器驱动

    绝对压力的检测是常见的需求.在我们的系统中也常常会遇到.而MS5837压力传感器也是我们进场会采用的方案.在这篇里我们将讨论并实现MS5837压力传感器的驱动. 1.功能概述 MS5837压力传感器是 ...

  9. 外设驱动库开发笔记14:DS18B20温度变送器驱动

    在一时候我们需要相对简单的检测温度信号,而DS18B20就是一款功能和应用都相对简单的温度传感器,通过单线就可以实现检测温度信号的需求.这一篇我们就来实现操作DS18B20获取温度数据的驱动. 1.功 ...

最新文章

  1. squid命中率分析参数注释
  2. linux:配置jdk环境变量
  3. poj2318 TOYS
  4. HTTP请求消息数据格式分析以及request和response
  5. assign,copy,strong,weak,nonatomic的理解
  6. private、protected、public、published 访问限制(或者叫类成员的可见性)
  7. BugkuCTF-MISC题虎符
  8. 银杏谷资本合伙人郑雨林:我为什么围绕阿里云生态做投资?
  9. java mail 收发邮件
  10. 再见了,mover。当打之年,感恩相遇,感恩联汇,感恩一切。
  11. OEA ORM 框架中的冗余属性设计
  12. 大屏样式(全屏禁止滚动)
  13. 网络盒子安装 linux,天猫魔盒安装原生linux的一些初步成果
  14. 数学建模计算机部分知识,数学建模相关论文范文资料,与数学建模与计算机关系相关毕业论文题目范文...
  15. 【已解决ie浏览器不能打印预览的问题,页面跳转失败,无法打开】
  16. 用asp.net写的一个购物网站
  17. 免费DNS服务器地址,谁家速度快
  18. linux 网速限制
  19. 概率分布 ---- 泊松分布
  20. EDA项目 出租车计价器 赏析

热门文章

  1. Scrapy爬取P2P新闻入门教程
  2. python实现支付宝
  3. CSS 3之菜单的横竖转换
  4. 变量定义、赋值、初始化
  5. 操作系统期末复习-第一章:操作系统概论
  6. ppt如何删除所有特效?
  7. jquery通过name,id名称获取当前value值
  8. Oh-my-Posh V3更新说明
  9. 居家办公以外一些有意思的事
  10. 《关于炒股有意思的几个“故事” 》