ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SGM5860x高精度ADC驱动开发实战:从裸机到Linux内核

SGM5860x高精度ADC驱动开发实战:从裸机到Linux内核 SGM5860x这块芯片我前后折腾了小两个月从裸机驱动到Linux内核层适配再到PCB改版排查数据漂移踩了不少坑也积累了一些实打实的经验。最近刚好看到不少人在问高精度ADC的驱动开发问题索性把这套东西整理出来从芯片选型思路到SPI时序细节从裸机代码到设备树配置再到电路板布局对采样数据的影响一次说清楚。先交代一下背景。我手头这个项目是做工业级的多通道数据采集要求24位分辨率采样率不需要太高几百SPS就够但对长期稳定性和温漂有要求而且必须支持多片级联同步采集。当时对比了好几款芯片最后选了SGM5860x系列。这颗料是圣邦微电子的24位高精度ADC内部集成可编程增益放大器PGA和多种数字滤波选项通过SPI接口通信非常适合称重、压力传感、温度测量这类低速高精度的采集场景。后面所有内容都基于我实际调试的这颗芯片展开踩坑记录和代码实现都是验证过的可以直接抄作业。1. 认识SGM5860x这颗高精度ADC到底适合什么场景1.1 芯片定位与核心参数解读先聊芯片本身。SGM5860x系列是圣邦微推出的一款delta-sigma架构ADC主打高精度、低噪声、低功耗。和市面上常见的逐次逼近型SARADC不同delta-sigma架构通过过采样和噪声整形技术把量化噪声推到高频段再用数字滤波器滤掉从而换取极高的有效分辨率。这也是为什么很多高精度称重、高精度测量方案都采用delta-sigma架构的根本原因。这款芯片的核心参数包括24位无失码分辨率、内置PGA可编程增益典型支持1到128倍以及可配置的数字滤波器。供电方面支持模拟电源和数字电源分开供电内部有参考电压缓冲器也可以使用外部精密参考源。SPI接口支持标准四线模式SCLK、DIN、DOUT、CS也支持菊花链级联这些特性在做多通道同步采集系统时非常关键。我选这颗料而不是选进口品牌同级别芯片最直接的原因是交期和成本。在目前的大环境下一颗24位ADC如果走进口渠道交期动不动就是十几周而且价格波动大。SGM5860x的供货稳定得多性能指标在同价位段也相当能打。对于做产品的工程师来说供应链安全本身就是一个很重要的决策因素。1.2 为什么选择SPI作为通信接口高精度ADC的通信接口一般有几种选择I2C、SPI以及部分芯片支持并行接口。SGM5860x选择了SPI这个选择在实际开发中有很明显的优势。SPI的时钟频率远高于I2C虽然对ADC这种低速采样设备来说通信速率并不是瓶颈但SPI的简单性和灵活性才是关键。SPI是全双工通信支持极高频的寄存器读写操作这对于配置PGA增益、切换滤波模式、读取状态位这类频繁操作来说非常友好。更重要的是SPI没有I2C那种地址应答机制时序更简单在嵌入式Linux环境下可以很容易地挂接到内核的SPI子系统上甚至可以直接利用通用的spidev接口在用户空间完成操作这对前期验证和后期调试都提供了很大便利。另外要提的是SPI的菊花链功能。SGM5860x支持多片级联意味着可以用一个片选信号串联多颗ADC通过数据在链路上的移位来访问每一颗芯片。这在多通道同步采样系统里非常有用至少不用为每一颗ADC单独分配一个片选引脚对MCU的GPIO资源占用也小得多。1.3 传感器采集链路中的典型应用布局SGM5860x适合的场景基本都是一些对精度要求极高但对速度要求不高的前端采集系统。我在项目中用它来采集称重传感器的毫伏级差分信号传感器桥臂输出直接被PGA放大后再进入ADC核心整个过程没有额外的模拟调理电路大大简化了前端设计。典型的信号链是这样的传感器模拟信号→RC滤波网络→SGM5860x的差分输入引脚→内部PGA→delta-sigma调制器→数字滤波器→SPI输出→MCU或SoC读取。如果你做的是热电偶测温、压力变送器、流量计这类4-20mA或者mV级信号采集方案这个芯片的架构几乎就是为你准备的。还有一点值得注意SGM5860x内部集成了系统校准和自校准功能可以在上电初始化后做一次偏移校准和增益校准把前端放大器和调制器的固有失调消除掉这一点在量产标定时能省不少事。2. SPI接口时序拆解与驱动设计基础2.1 读懂数据手册里的SPI时序图数据手册里的时序图乍看很唬人其实拆开来看就那么几个关键参数SCLK最高频率、CS建立时间、数据建立保持时间、以及DOUT在SCLK下降沿之后多久数据才稳定。SGM5860x的SPI接口支持最高20MHz的时钟速率这个速率对于低速ADC来说其实是远远过剩的但高时钟速率能让寄存器配置和状态读取变得非常快尤其是菊花链模式下高位速率能显著减少多片芯片的访问延迟。在实际写驱动之前我强烈建议你先用逻辑分析仪抓一下参考代码的时序而不是直接对着数据手册闷头写。因为数据手册里的时序图是理想化的而实际的MCU SPI外设在时钟极性和相位配置上存在各种组合。SGM5860x要求SPI工作在CPOL0、CPHA1也就是SPI Mode 1或者CPOL1、CPHA1SPI Mode 3具体看你是读数据还是写寄存器。这个细节我在第一次调试时就踩了坑后面会详细展开。还有个很重要的细节是命令字节的结构。SGM5860x的每一次通信都由一个命令字节开始这个字节包含了读/写标志位和寄存器地址之后的字节才是真正的数据。这意味着驱动里封装SPI收发函数时必须把命令字节和数据字节放在同一个SPI事务里不能在中间拉高CS。如果中间释放了CS芯片会认为这次通信结束命令就被丢弃了。2.2 关键寄存器配置与采样周期计算SGM5860x的寄存器映射相对清晰核心寄存器包括设备ID寄存器、控制寄存器、数据速率配置寄存器、PGA增益配置寄存器、以及校准相关寄存器。初始化驱动时第一步是读取设备ID确认SPI通信正常这一步非常关键能帮你快速区分硬件连接问题还是软件配置问题。采样周期的计算逻辑看起来简单但在实际项目中很容易被忽略。ADC的输出数据速率ODR由内部主时钟和数字滤波器的抽取率共同决定。比如你配置ODR为10SPS和配置为1000SPS数字滤波器对带外噪声的抑制能力是完全不同的。采样周期直接关系到你能不能从数据里看出真实的信号变化如果采样率过低快速变化的信号会被严重平滑如果过高噪声又会增大。以SGM5860x来说它的数字滤波器是sinc3结构典型配置下10SPS时50Hz/60Hz工频抑制能力非常强这正好适合称重这类工业现场应用。我建议新手起步时先用最低ODR测试确认数据稳定后再逐步提高速率这样能减少排查问题的范围。2.3 时钟抖动对采样精度的影响到底有多大高精度ADC最容易被低估的干扰源就是时钟抖动。很多人觉得SPI时钟只是用来通信的抖动大点最多通信出错跟采样精度有什么关系其实关系非常大。delta-sigma调制器内部对时钟的边沿非常敏感如果主时钟或者SPI时钟存在明显的抖动会直接反映为采样数据的噪声恶化。具体的表现就是数据低几位不稳定实测的信噪比指标达不到数据手册标称值。我在调试过程中就遇到过这种情况用逻辑分析仪看通信时序完全正常但采集到的数据总是比理论值差几个LSB最后排查发现是MCU的外部晶振布局离数字信号线太近时钟信号被串扰干扰了。这里给一个实际的经验值对于24位ADC应用建议采样时钟的抖动控制在皮秒级。普通的MCU内部RC振荡器通常不够用最好使用外部有源晶振或者高精度的无源晶振加匹配电容。如果使用FPGA或者MCU内部的PLL输出时钟也要注意PLL的抖动指标是否满足要求。3. 驱动开发实战从裸机到Linux内核3.1 裸机驱动实现基于标准SPI先看最简单的裸机驱动实现这里以STM32为例。裸机驱动的好处是逻辑直观方便验证硬件时序适合在项目初期快速把板子跑起来。// SGM5860x SPI命令定义 #define SGM5860_CMD_READ 0x80 #define SGM5860_CMD_WRITE 0x00 // 读取设备ID uint8_t sgm5860_read_device_id(void) { uint8_t cmd SGM5860_CMD_READ | 0x00; // 假设设备ID寄存器地址为0x00 uint8_t rx_data 0; CS_LOW(); spi_transfer(cmd, 1); spi_transfer(rx_data, 1); CS_HIGH(); return rx_data; }这段代码看着简单但有几个细节要注意。首先CS拉低和第一个字节发出之间要有足够的延时这个延时是CS到SCLK建立时间通常在数据手册里会有标注。实际测试下来即使MCU主频很高也建议至少预留几百纳秒的建立时间避免在高速模式下的通信不稳定。其次读取寄存器和读取转换数据要用不同的命令。SGM5860x的转换结果寄存器是24位的读取时要连读三个字节。这里必须保证SPI的传输是连续的中间不能有CS拉高的操作。如果SPI驱动封装层面把每次transfer都自动控制CS那就要特别注意了。读取一次转换结果的裸机代码如下// 读取24位转换结果 int32_t sgm5860_read_conversion(void) { uint8_t buf[4] {0}; int32_t raw 0; uint8_t cmd SGM5860_CMD_READ | 0x04; // 转换结果寄存器地址为0x04 CS_LOW(); spi_transfer(cmd, 1); spi_transfer(buf[1], 3); CS_HIGH(); raw ((int32_t)buf[1] 16) | ((int32_t)buf[2] 8) | buf[3]; // 处理24位符号扩展 if (raw 0x800000) { raw | 0xFF000000; } return raw; }3.2 Linux下SPI设备驱动与设备树配置在嵌入式Linux环境下驱动开发的思路要换一下。Linux内核的SPI子系统已经封装了底层的硬件操作我们只需要写一个客户端驱动通过设备树描述硬件连接关系内核就会帮我们完成SPI控制器的初始化、片选管理和数据传输调度。设备树节点配置是关键。我在项目中的配置如下spi1 { status okay; pinctrl-names default; pinctrl-0 spi1_pins; cs-gpios gpio1 10 GPIO_ACTIVE_LOW; sgm5860: sgm58600 { compatible sgm,sgm5860; reg 0; spi-max-frequency 5000000; spi-cpha; // 根据芯片要求配置时钟相位 vref-supply vref_3v3; }; };这里有几个地方值得注意。spi-max-frequency我设的是5MHz实际上SGM5860x支持更高的速率但Linux SPI子系统默认会有一些软件开销如果速率设得太高反而可能导致传输不稳定。spi-cpha这个属性对应的是CPHA1如果你的MCU控制器在硬件上已经配置了SPI模式这里可以不重复设置但建议统一在设备树里声明方便后续维护。另外cs-gpios指定了片选GPIO。Linux下有两种片选方式一种是硬件自动片选一种是GPIO片选。对于高精度ADC应用建议使用GPIO片选因为硬件自动片选在某些控制器上存在毛刺风险可能会在CS拉高后又被短时间拉低导致SPI通信误触发。这个问题在低速高精度设备上尤其致命因为ADC的配置寄存器一旦被误写整个采样链路就可能彻底失控。3.3 SPI数据读取与校验逻辑实现Linux驱动里读取ADC数据的核心逻辑其实和裸机很像但要用内核的SPI API来做数据传输。这里推荐使用spi_write_then_read接口它的好处是会把写命令和读数据合并成一个SPI事务中间不会释放CS正好符合SGM5860x的命令加数据读取要求。static int sgm5860_read_raw(struct sgm5860_dev *dev, int *raw) { uint8_t tx_buf[1] {SGM5860_CMD_READ | SGM5860_REG_DATA}; uint8_t rx_buf[3] {0}; int ret; struct spi_transfer tr { .tx_buf tx_buf, .rx_buf rx_buf, .len 3, }; struct spi_message msg; spi_message_init(msg); spi_message_add_tail(tr, msg); // 注意这里CS由GPIO控制 gpiod_set_value(dev-cs_gpio, 0); ret spi_sync(dev-spi, msg); gpiod_set_value(dev-cs_gpio, 1); if (ret) { dev_err(dev-spi-dev, SPI read failed: %d\n, ret); return ret; } *raw (rx_buf[0] 16) | (rx_buf[1] 8) | rx_buf[2]; return 0; }这段代码里用gpiod控制CS而不是依赖SPI控制器的自动片选就是为了避免自动片选在多字节传输时出现不必要的毛刺。实际测试下来GPIO片选方式在稳定性和灵活性上都比硬件片选好代价是每次传输多了一两次GPIO操作但影响微乎其微。驱动里还应该加一个简单的数据校验逻辑。我一般会在读取转换结果后先读取设备ID寄存器确认通信链路正常再读取转换结果。如果设备ID不对直接返回-EIO错误避免把错误数据往上层传。这个习惯在调试初期帮我省了很多时间因为SPI飞线连接时经常出现虚接、错位的问题靠数据校验能很快定位是硬件还是软件问题。4. ADC电路设计影响精度的几个PCB布局细节4.1 电源噪声与参考电压的布线要点高精度ADC对电源的敏感程度远超很多工程师的想象。你写再好的软件如果电路板布局有问题数据照样飘得没法看。SGM5860x内部虽然有LDO但模拟电源和数字电源还是要做合理的隔离和滤波。电源走线设计上我总结了三个门槛级的经验。第一模拟电源AVDD和数字电源DVDD必须从电源源头就分开走线最好是星形连接在中点汇合而不是走一路然后分支。第二每个电源引脚旁边的去耦电容必须贴近引脚放置过孔到电容再到引脚的路径越短越好我在改版时把去耦电容从原来的3mm距离缩到1mm以内电源纹波实测降了一个数量级。第三参考电压引脚的去耦要特别注意参考电压的稳定性直接影响转换结果的绝对精度建议使用低ESR的陶瓷电容并联一个大容量的钽电容构成去耦网络。电源噪声对ADC的影响主要体现在输出码值的抖动上。如果你发现采集数据在小范围内反复跳动而且跳动的幅度超过了数据手册标称的噪声指标优先检查电源纹波而不是怀疑芯片本身有问题。4.2 前端RC滤波与ADC驱动电路设计高精度ADC的前端设计直接关系到你能不能用满这颗芯片的性能。SGM5860x这类delta-sigma ADC的内部采样电容会在每次采样时从信号源抽取电荷如果信号源阻抗过高就会导致采样不完整引入增益误差和失真。解决办法是在ADC输入端加RC滤波网络电容作为电荷储备电阻限制来自传感器的高频噪声。RC的取值需要谨慎。电阻太大和ADC内部采样电容构成的分压会造成增益误差电阻太小又起不到滤波效果。我在项目中采用电阻100欧姆加电容1uF的组合实测信号源阻抗在10k欧姆以下时增益误差都在可接受范围内。还有一个容易被忽视的点是PGA的输入端偏置。SGM5860x的PGA输入是真正的差分结构但如果你用的是单端传感器必须把负输入端接到一个干净的参考电位上不能直接接地。这个参考电位的纹波会直接叠加到信号上。我在项目里用了一个低噪声的基准源芯片产生2.5V的共模电压再通过运算放大器缓冲后接到ADC的负输入端效果比直接接地好太多。4.3 模拟地与数字地的划分策略地平面的处理是高精度ADC电路设计中最经典也最容易被误导的话题。很多教材会说模拟地和数字地要分开然后用磁珠或0欧电阻连接但在实际高速或者高频数字电路旁边这种做法的效果其实很有限。我的经验是SGM5860x所在的低速高精度电路区域建议采用统一的完整地平面不要做物理分割。数字信号的回流电流会自动选择阻抗最低的路径如果地平面是完整的回流路径就是紧贴着信号走线的不会穿过模拟信号区域。如果你在地平面上切了一道缝回流电流反而会绕远路形成一个大环路这个环路就成了天线更容易引入噪声。模拟地和数字地分开的做法并不是错误的但它适用于混合信号芯片本身内部有独立的地引脚、并且封装内部就已经做好隔离的场景。SGM5860x的AGND和DGND引脚需要分别连接到统一地平面但在引脚附近做好单点汇聚。我在实际项目中就是一颗完整的底层地平面没有做分割实测各项指标都优于之前做分割时的版本。5. 常见问题与排查技巧实录5.1 数据漂移问题的完整排查思路数据漂移是我在高精度ADC项目里遇到最多的现象也是客户反馈最多的问题之一。现象描述通常是“数据零点随时间缓慢变化”或者“校准后过一段时间又不准了”。这个问题的原因是多种多样的需要系统排查。第一步要区分是芯片本身的问题还是外部环境的问题。做一个简单的测试把ADC输入端短路然后连续记录数据看输出码值是否稳定。如果短路时数据还漂移那问题在芯片、电源或者基准源如果短路时数据稳定那问题在传感器或者前端电路。第二步检查温漂。SGM5860x的失调温漂系数是ppm级别的但你外围电路上的电阻、电容、基准源等器件的温漂可能远大于这个值。尤其是分压电阻网络、传感器激励电源等温度变化几个摄氏度就可能引起几十ppm的漂移。解决思路是选用低温漂系数的电阻或者做软件温度补偿。第三步看机械应力。PCB板在受热或者受力时会发生微小形变这种形变会影响焊接点的应力分布引起芯片内部基准电压的微小变化。解决办法是在芯片周围留足空隙不要在ADC芯片正下方走其他信号线必要时可以在PCB上开槽隔离应力。5.2 SPI通信异常的定位方法SPI通信异常的表现形式很多可能是寄存器读回来全是0xFF也可能是不定期的通信超时。定位这类问题我有一套固定的排查顺序。第一优先检查CS信号。CS在高精度ADC的通信中扮演着“门卫”的角色任何CS上的毛刺都可能导致通信状态机错乱。用示波器抓CS上升沿观察是否有回勾或者多次触发。如果CS是MCU GPIO控制的检查初始化代码里是否有把这个GPIO拉高的延迟操作。第二检查时钟空闲电平。SGM5860x对CPOL有明确要求如果配置错了通信会完全失败。这里建议在调试时先用逻辑分析仪抓一次完整的通信波形对照数据手册的时序图检查。不要只看通信的字节内容对不对还要看每个位的时间位置是否准确。第三检查数据线方向。SPI的MISO和MOSI接反是新手最容易犯的错误。特别是使用飞线连接时经常会把主机的MISO接到从机的MISO上结果就是读什么都读到0xFF。连接前多看几遍引脚定义连接后用万用表逐根确认连通性。5.3 采样值跳变与软件滤波处理技巧即便硬件设计做得再好ADC的采样数据也难免有跳变。尤其是工业现场环境电磁干扰、大电流开关设备的启停都会在采样值上造成瞬时尖峰。处理这些跳变软件滤波是必要的手段。很多人最先想到的是滑动平均滤波把最近N次采样取平均。这个方法确实能平滑数据但对尖峰干扰的抵抗能力比较弱一个大的尖峰就能把平均值拉偏。更推荐的做法是先用中值滤波把明显的离群点剔除再做滑动平均。中值滤波的原理很简单把最近N个数据排序取中间值作为本次输出它能非常有效地去掉脉冲干扰。我在项目中实现的复合滤波逻辑如下先取最近5次采样做中值滤波再对中值滤波输出做16次滑动平均。实际效果是数据在静态时噪声峰峰值从原来的十几个码值降到了三个码值以内动态响应速度也还在可接受范围。如果你对实时性要求更高对应地减少滑动平均的窗口长度即可。软件滤波还有一个容易忽略的细节滤波输出的数据类型必须用32位或64位整型不能用16位保存累加结果否则超过32767的采样值累加后会发生溢出导致数据莫名其妙地跳变。这个问题我在早期版本里踩过排查了很久才发现是数据溢出导致的。5.4 设备树配置常见错误与避坑指南Linux设备树配置看起来简单但出错的时候定位起来却非常痛苦。我总结了几类高频错误。第一类是compatible字符串和驱动里的of_match_table不匹配。内核匹配设备树节点和驱动时是严格字符串匹配的多一个空格、少一个字母都会导致驱动不被加载。而且有些情况下驱动没有被编译进内核设备树节点写了也白写系统启动日志里不会报错只是对应的设备节点不会出现在/sys/bus/spi/devices/下。第二类是spi-max-frequency设置过高导致的偶发通信失败。这个问题在硬件环境稳定时不容易复现但在环境温度变化或者线上干扰增大时就会频繁出现。稳妥起见建议在量产固件里把SPI频率降到1MHz以下代价是每次读取时间长了那么几十微秒对低速ADC来说完全不是问题。第三类是对spi-cpha和spi-cpol的设置理解不透彻。这两个属性描述的是从设备的工作模式并不等同于控制器硬件上的模式设置。如果设备树里已经声明了spi-cpha控制器会在通信前把CPHA位配置为1但你自己的GPIO初始化代码可能会覆盖这个配置。调试时如果发现通信时好时坏去查一下GPIO复用配置是否覆盖了SPI引脚的复用功能。6. 实测数据表现与调优心得驱动写完、硬件改版完成之后我对整套系统的性能做了一轮完整的摸底测试。测试条件是在恒温环境下使用外部精密基准源作为参考电压PGA增益设置为1倍ODR配置为10SPS输入端接一个稳定的2.5V电压。连续采样24小时统计输出码值的分布情况。实测结果为峰峰值噪声在3个LSB以内RMS噪声约为0.6个LSB对应到电压就是大约0.9uV的RMS噪声。这个数据和数据手册标称的典型值是吻合的说明硬件设计和驱动配置都没有大的问题。在PGA增益为128倍时测试微弱信号RMS噪声大约为6nV性能依然相当可观。有一件事值得展开说就是差分输入的好处。我最初也想过用单端输入简化电路但数据手册上明明写着差分输入支持更高的动态范围和更好的共模抑制比。实际对比测试后发现在相同的硬件条件下差分输入的RMS噪声比单端输入低了将近30%。如果你对性能和成本都有要求前端多加一个运放构成差分驱动电路是非常值得的投入。在调优过程中我还尝试过调整数字滤波器的配置来观察噪声变化。SGM5860x支持配置滤波器的抽取率来改变输出的有效分辨率。算下来ODR每降低为原来的四分之一有效分辨率大约能提升1位。比如从1000SPS降到250SPS有效分辨率能从19位提升到20位。对于称重这类实时性要求不高的场景完全可以用低ODR换取更高的分辨率。另外提一下驱动里我最终没有采用Linux内核的IIO子系统虽然有现成的ADC框架可以用但SGM5860x的不少自定义功能比如特殊校准序列、多片同步触发用标准IIO抽象表达反而麻烦。我选用了标准的miscdevice接口实现了read、ioctl等操作用户空间用起来非常直接。如果你的项目后续有扩展需求再迁到IIO框架也不迟核心的读写逻辑并不需要大改。SPI通信在SGM5860x上还有一个比较好用的场景是同步采集。当系统的多个通道分别由多片ADC采集时可以利用SPI的菊花链模式和外部触发引脚实现严格同步。实际实现中我把多片ADC的SYNC引脚连在一起由MCU输出一个同步脉冲所有ADC同时开始采样然后通过SPI依次读回数据。这个方案比用独立ADC各自采样的方案在多通道相位一致性上好了很多省掉了软件对齐的麻烦。最后说一个生产阶段的小技巧。量产烧录固件之后每块板子的ADC偏移和增益都会有细微差异如果完全不校准就直接使用产品的一致性会受影响。SGM5860x内部有自校准命令上电后依次触发偏移校准和增益校准然后校验校准前后的数据偏移量是否在合理范围内就能筛掉一部分焊接不良或者器件异常的不良品。这个流程放到产线测试固件里比后期逐台人工标定省人力得多。
返回列表