草庐IT

ADS1256使用心得及其STM32驱动编写 [原创www.cnblogs.com/helesheng]

一个电子爱好者的工作记录 2023-03-28 原文

最近培训学生做大学生电子设计竞赛,有学生用到TI的24位Sigma-delta模数转换器ADS1256。虽然这款芯片在能找到的介绍性文章很多,但大多属于简单翻译手册,很多实际问题没有提及。现在电子设计竞赛结束了,将学生在用STM32调试中遇到各种问题以及我的解决办法总结如下,以方便在未来的工程项目中使用,同时与各位网友共享。

以下原创内容欢迎网友转载,但请注明出处: https://www.cnblogs.com/helesheng

一、差分驱动电路

ADS1256具有差分输入能力,可以产生二进制补码输出,这让很多学生误以为ADS1256支持双极性输入,想从模拟输入端输入±5V范围的模拟电压。其实ADS1256与AD7606这样真正支持双极性(Bipolar Input)输入的模数转换器是不一样的:当你细看数据手册时就会发现,其每个模拟输入管脚的绝对输入电压(Absolute input voltage)只能在0到电源电压之间,能够达到负电压输入的是输入的差分电压(AINP减去AINN的值)——即输入为负值是指AINN大于AINP的电压,而此时AINN和AINP的绝对值都是正电压

 

ADS1256的数据手册并未给出驱动电路,原因是ADS1256这类的高分辨率ADC经常用来对电桥等差分输入的传感器进行测量,其内部含有的可编程差分放大器(PGA)还可以对这类传感器的输入进行程控放大。但更常见的情况是:你需要的是对真正的双极性单端信号(只有一根信号线,其值相对于地有可能是正电压,也有可能是负电压)进行测量,那么你就需要通过运算放大器将单端的双极性信号转换为双端的差分信号。但常见的差分ADC的驱动办法无非有二:

1、使用专用的差分驱动芯片,如TI的INA132、INA216,ADI的ADA4941、AD8138等等。关于这些芯片的使用,读者可以参考其数据手册,这里不再赘述。专用差分驱动芯片的优点在于增益(GAIN)和共模抑制比(CMRR),非常准确;缺点是小众芯片供货不稳定且价格较高。

2、我自己设计了以下差分驱动电路:

图1

 

上图中所有的电阻,除实现滤波的R11和R12之外,全部为100KΩ,精度为千分之一。运算放大器由单极性低压轨到轨输入输出(RIRO)运放构成,我是用了国产的低成本运放RS622,你也可以使用MCP6002、AD8632或OPA2335等进口型号,由于ADS1256的输入阻抗极高,你甚至可以使用更低成本的LMV358替代。双极性单端信号从接插件CNT输入。

图1所示的电路对双极性单端信号(图中蓝色虚线)完成的变换如图2所示。图1下半部分的反向放大电路对蓝色信号首先缩小为原来的一半;其次增加2.5V的偏置,使其变为图2中绿色的信号(图1中网络标号为IN_N_B)。图1上半部分的电路再次对绿色的信号反相得到图2中的红色信号。这样ADS1256的差分输入端对红色和绿色信号求差时,就可以恢复蓝色虚线所示的单端双极性信号,且红色和绿色信号的绝对电压都在0~5V之间。

图2 

不熟悉模拟电路的读者会问:为什么要让图1的运放N4B的同相端连接到5V的2/3电位?请读者回忆模拟电路课程学习过的叠加原理:将电路的两个输入端(信号端Ain和+5V输入端)分别接到0电位,在将两种情况下电路的输出叠加在一起,就可以得到电路在Ain和+5V同时输入时的输出。其中为达到将信号缩小为原来的一半的目的,R5、R6、R7构成的负反馈通路的增益为1/2;而同相输入端(管脚5)的电势将决定图2绿色信号叠加的直流偏置,具体来说根据叠加原理,绿色信号叠加的直流偏置等于同相端电压的3/2倍(反馈通路的增益,对于同相输入的电压为3/2)。这样只有当R8、R9、R10构成的分压电路对5V的分压比为1/3时,在输出端得到的绿色信号直流偏置才为 (1/3)*(3/2)*5V = 2.5V。

图1上部,由运放N4A为主构成的反向放大电路的增益为-1,其工作点为R3、R4产生的2.5V。其产生如图2所示的红色信号。

图1所示电路的偏置(OFFSET)、增益(GAIN)和共模抑制比(CMRR)全部由电阻R1~R10决定,为保证ADS1256的精度,这些电阻全部选用了100K@1‰的电阻。这种精度显然无法达到24bits绝对精度的要求,不过索性的是对于连续采样最看重的信噪比(SNR、THD)和信号动态范围(ENOB)却没有损失。这个电路适合高分辨率音响、模式识别等应用,以及具有出厂整机校准环节的采集系统。

另外,这里之所以串联使用两只一样的100KΩ@1‰电阻串联,是为了获得精确的获得一只100KΩ@1‰阻值的两倍。且使用一样的电阻,有利于高精度电阻供应链的管理。

使用上述差分驱动电路后,我的初始化代码如下所示:

 1 AD_RST = 0; //复位ADS1256
 2     AD_RST = 1;
 3     while(ADS1256_DRDY);//当前转换完成才能进一步配置
 4     ADS1256WREG(ADS1256_STATUS,0x06);               // 高位在前、使用缓冲
 5     ADS1256WREG(ADS1256_ADCON,ADS1256_GAIN_1);                // 增益为1
 6     ADS1256WREG(ADS1256_DRATE,ADS1256_DRATE_1000SPS);  // 数据1ksps
 7     ADS1256WREG(ADS1256_IO,0x00);               
 8     while(ADS1256_DRDY);//当前转换完成才能进一步配置
 9     CS_0();
10     SPI_WriteByte(ADS1256_CMD_SELFCAL); //自校准
11     while(ADS1256_DRDY);
12     CS_1();
13     temp = (chp << 4)|(chn + 1);//chp为同相输入,chn为反相输入
14     ADS1256WREG(ADS1256_MUX,temp);        //设置通道
15     CS_0();
16     SPI_WriteByte(ADS1256_CMD_SYNC);
17     SPI_WriteByte(ADS1256_CMD_WAKEUP);    
18     CS_1();
初始化代码

 

二、Sigma-Delta模数转换器的特点及其对驱动程序的影响

本质上说Sigma-Delta模数转换器是一个数字器件,其执行的是一个有限长冲击响应滤波器(FIR),包含一系列复杂的乘加运算,需要较长的计算时间。这一本质造成的影响包括:

1、Sigma-Delta转换器处理时间较慢,采样率较低;且存在一次转换的输出的“潜伏期”,数据手册提供的时序图如下。

图3

 

请注意我用红色圈住的部分,当你的代码在本次转换开始时DRDY变低后,将输入通道配置为AINP=AIN2,AINN=AIN3时,当前这次输出的结果将仍然是上个通道配置的结果(MUX = 01h)。这需要在进行多通道轮流采样的应用中得到足够的重视。实际上由于FIR滤波器是一个与过去的输出高度相关的因果系统,Sigma-Delta转换器并不特别适合实现多通道轮换采样的任务(这比较适合使用逐次逼近SAR式的模数转换器)。而TI公司的ADS12xx系列Sigma-Delta也是市面上能够看到的进行通道切换后,能够最快速度达到所需精度的模数转换器。下表是ADS1256数据手册中给出的在不同输出速度下切换通道后的建立时间(以转换周期为单位)。

 

2、进行通道轮换时的采样率

很多小伙伴想当然的认为在数据率寄存器(DRATE,03h)里配置的数据率就是所有情况下的采样率。其实当你知道Sigma-Delta器件内部的FIR滤波器本质时你就会知道,当你进行通道切换并重新对器件进行同步(synchronize)和唤醒(wakeup)后,转换速度会变慢。具体来讲,实际采样率和你配置的通过率之间的对应关系如数据手册中的下表所示。

 直接说人话——当你使用网上到处能找到的下面的代码启动每次转换时,你只能获得上表右列所示的转换速度。

1 ADS1256WREG(ADS1256_MUX,temp);        
2 SPI_WriteByte(ADS1256_CMD_SYNC);
3 SPI_WriteByte(ADS1256_CMD_WAKEUP);
4 SPI_WriteByte(ADS1256_CMD_RDATA);
5 UN_sum |= (SPI_WriteByte(0xff) << 16);
6 UN_sum |= (SPI_WriteByte(0xff) << 8);
7 UN_sum |= SPI_WriteByte(0xff);
错误的连续转换代码

因为上面的代码头两句进行同步和唤醒后,ADS1256会以为你切换了通道,需要多用一点时间来时滤波器稳定下来。要获得DRATE寄存器所需的采样率,你必须做到:1)只对一个通道进行采样,不切换输入通道配置;2)在每次读取采样结果时,只使用上面后边四句代码,将通道配置的那句代码放在采集开始之前一次性完成。

三、ADS1256在使用中广泛出现的问题及其解决办法

1、ADS1256 在继电器工作的时候自动复位问题的解决

TI官网问答论坛(https://e2echina.ti.com/support/data-converters/f/data-converters-forum/114019/ads1256)和阿莫电子论坛(https://www.amobbs.com/thread-5484227-1-1.html)等多个知名论坛中都出现讨论ADS1256会在电磁继电器等干扰条件下出现自动复位的现象(自动复位后采样率会自动切换到30KSPS),我在使用中也观察到了这个现象,但TI官方及其FAE都未对此问题作出正面回应。仔细阅读手册后发现ADS1256的复位脚RESET的复位时间非常短,仅为4个时钟周期(7.68M晶振下仅0.5us数量级),且没有连接施密特触发器输入滤波。

 我用STM32的GPIO控制RESET管脚亦无法避免自动复位现象,我在离ADS1256的RESET管脚很近的地方,对地之间连接了一只低ESR的1uF瓷片电容去耦。没想到“药到病除”,解决了ADS1256自动复位问题

图4

2、读写操作时序问题及其实现

1)读数据指令

ADS1256有两条度指令:读数据RDATA(01h)和连续读数据指令RDATAC(03h)。读数据指令是在每次数据转换完成后(DRDY信号变低电平),后发送一个RDATA(01h) 在读取本次输出的24bits结果;连续度数据指令是在发送一次RDATAC(03h)后,即可在每次DRDY信号变低后都直接读取24bits结果,但需要在退出连续读取指令时发送退出连续读取指令SDATAC(0Fh)。

个人觉得ADS1256转换速度较慢,如果使用STM32作为控制器,没必要省去每次发送一个指令字节的时间,因此我倾向于使用读数据指令RDATA,以降低代码的复杂程度。

2)指令后读取等待时间t6

不论使用读数据指令RDATA,还是连续读数据指令RDATAC指令都需要在发送指令后等待t6的延迟时间才能够读出转换时间,根据数据手册t6约为50个ADS1256时钟周期(在7.68M时钟下,略小于1us)。若使用51内核的MCU,基本可以不考虑刻意为t6进行延时处理,但对STM32而言,若不专门为t6进行延迟,则有可能造成后续读取的第一个位(MSB)为错误位。因此连续读取固定采样率的代码应如下所示:

1 SPI_WriteByte(ADS1256_CMD_RDATA);
2 delay_us(2);//指令后的等待时间
3 UN_sum |= (SPI_WriteByte(0xff) << 16);
4 UN_sum |= (SPI_WriteByte(0xff) << 8);
5 UN_sum |= SPI_WriteByte(0xff);
6 delay_us(2);//读取后等待DRDY管脚恢复时间

3)读取后的等待DRDY管脚恢复时间

每次ADS1256转换完成后,会使DRDY管脚输出低电平,以提示STM32来读取本次转换结果。STM32如果能够在下一次转换完成刷新结果寄存器之前将本次转换结果读走,DRDY管脚将恢复高电平提示STM32等待下一次转换结果。不幸的是,STM32的速度相对于ADS1256太快,以至于读走数据后DRDY管脚还来不及恢复高电平,STM32就有可能再次检测到DRDY管脚为低电平,而认为需要再次读取转换结果,从而造成同一个结果被连续读取两次。而这一现象在ADS1256的数据手册中并未正式提及,也没有用给出DRDY转换为高电平的延迟时间。我尝试性的在读取24bits转化结果后,加入了2us的延迟时间(如上面的代码最后一行所示),解决了同一个结果被STM32连续读取两次的现象。

4)SPI时钟速度

ADS1256手册要求的SPI最短时钟周期为4个ADS1256工作时钟,也就是最高为1~2MHz。对于51系列单片机而言,不可能超过这个极限,对STM32而言就需要尤其注意SPI时钟的配置,我是用了APB2总线的64分频作为SPI通信时钟频率。

 

helesheng编写spanstylecolor软件设计

有关ADS1256使用心得及其STM32驱动编写 [原创www.cnblogs.com/helesheng]的更多相关文章

  1. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  2. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  3. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  4. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  5. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  6. ruby-on-rails - 如何为空白字段编写 rspec? [Rails3.1] - 2

    我使用rails3.1+rspec和factorygirl。我对必填字段(validates_presence_of)的验证工作正常。我如何让测试将该事实用作“成功”而不是“失败”规范是:describe"Addanindustrywithnoname"docontext"Unabletocreatearecordwhenthenameisblank"dosubjectdoind=Factory.create(:industry_name_blank)endit{shouldbe_invalid}endend但是我失败了:Failures:1)Addanindustrywithnona

  7. ruby-on-rails - 尝试为 Rails 中的用户名验证编写 REGEX - 2

    我正在尝试用Ruby(Rails)编写一个正则表达式,以便用户名的字符仅包含数字和字母(也没有空格)。我有这个正则表达式,/^[a-zA-Z0-9]+$/,但它似乎没有用,我在Rails中收到一个错误,说“The如果正则表达式使用多行anchor(^或$),这可能会带来安全风险。您是要使用\A和\z,还是忘记添加:multiline=>true选项?"我的user.rb模型中此实现的完整代码是:classUser我做错了什么以及如何修复此正则表达式,使其仅对数字和字母有效而不对空格有效?谢谢。 最佳答案 简短回答:使用/\A[a-z

  8. ruby-on-rails - 如何编写跨模型、 Controller 和 View 的 Rails mixin - 2

    为了减少我的小Rails应用程序中的代码重复,我一直致力于将我的模型之间的通用代码放入它自己的单独模块中,到目前为止一切顺利。模型的东西相当简单,我只需要在开头包含模块,例如:classIso这工作正常,但是现在,我将有一些Controller和View代码,这些代码也将在这些模型之间通用,到目前为止,我有这个用于我的可发送内容:#Thisisamodulethatisusedforpages/formsthatarecanbe"sent"#eitherviafax,email,orprinted.moduleSendablemoduleModeldefself.included(kl

  9. ruby - 在不添加 "end"的情况下编写 Ruby 的任何方法? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭11年前。Ruby是一种美丽的语言,但有一个我讨厌写很多次的关键词“结束”。有什么方法可以写出简洁的代码而不用每次都写“end”吗?

  10. STM32的HAL和LL库区别和性能对比 - 2

    LL库和HAL库简介LL:Low-Layer,底层库HAL:HardwareAbstractionLayer,硬件抽象层库LL库和hal库对比,很精简,这实际上是一个精简的库。LL库的配置选择如下:在STM32CUBEMX中,点击菜单的“ProjectManager”–>“AdvancedSettings”,在下面的界面中选择“AdvancedSettings”,然后在每个模块后面选择使用的库总结:1、如果使用的MCU是小容量的,那么STM32CubeLL将是最佳选择;2、如果结合可移植性和优化,使用STM32CubeHAL并使用特定的优化实现替换一些调用,可保持最大的可移植性。另外HAL和L

随机推荐