ARTICLE DETAIL

资讯详情

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

S32K144 SAR型ADC开发实战:采样时间、参考电压与滤波调校全指南

S32K144 SAR型ADC开发实战:采样时间、参考电压与滤波调校全指南 1. 先搞懂S32K144的ADC模块到底是什么样的硬件1.1 模块架构与通道分配S32K144内部集成两个独立的12位SAR型ADC模块也就是ADC0和ADC1。SAR型ADC的核心是电容阵列和逐次逼近寄存器原理上像一台老式天平先用最大的砝码试重了就拿下来换小的逐位逼近最终平衡值。比起Sigma-Delta架构SAR的优势是单次转换速度快、功耗适中而且在汽车环境常见的温度范围里表现稳定所以车规MCU基本都选SAR ADC。从资源上看ADC0拥有更多的外部模拟输入通道一般传感器密集的项目优先用ADC0ADC1通道数少一些适合留给那些固定用途的监测信号比如电源电压监控、温度采样。两个ADC可以独立工作也可以配合触发源做同步采样。比如电机控制里需要同时采集两相电流就可以让ADC0和ADC1在同一触发沿采样保证两路数据的时刻对齐这是软件轮询很难做到的事情。引脚复用是很多人第一次配置ADC时踩坑的地方。S32K144的ADC输入引脚散落在PTA、PTB等端口上需要通过SIM模块的引脚复用寄存器把引脚切换到ADC模拟功能。漏掉这一步的典型现象是代码明明已经读了ADC结果寄存器但数据永远是0量一下引脚电压明明正常最后发现是引脚复用根本没配出来。建议拿到板子后先把所有要用到的ADC引脚复用配置写清楚再去做模块配置。1.2 采样保持与转换过程ADC转换不是瞬间完成的。SAR型ADC的转换过程分成采样阶段和转换阶段。采样阶段内部会把输入信号接到采样电容上给电容充电到和输入电压一致转换阶段断开输入电容上的电压作为基准通过逐次逼近的方式输出数字码。这里有个非常关键的物理约束采样电容充电需要时间。外部信号源的输出阻抗越高充电时间越长。如果采样时间不够电容上没充满电转换结果就会偏低而且这种误差不是固定偏移它随信号源阻抗变化而变化。S32K144提供了可编程采样时间配置在ADC_CFG2寄存器里通过SMPLTS字段设置单位是ADC时钟周期。实际配置时采样时间要综合考虑信号源阻抗和ADC时钟频率。转换阶段所需的时间则主要取决于分辨率。SAR ADC在12位分辨率下需要逐位判断12次加上内部复位、比较等环节一般需要13到15个ADC时钟周期。再加上采样阶段就构成了单次转换的总时间。换算采样率时记得把整个链路都算进去别只算转换周期。2. ADC转换精度取决于这几个关键参数2.1 分辨率与参考电压设置S32K144的ADC支持12位、10位、8位三种分辨率配置位在ADC_CFG1寄存器的MODE字段。默认12位分辨率下满量程码值范围是0到4095。以3.3V参考电压为例一个LSB对应的电压约0.8mV。如果把分辨率降到10位一个LSB就变成3.2mV理论上能够分辨的最小电压差了4倍。所以除非有极端的转换速度需求一般建议保持12位。参考电压是另一个决定精度的因素。S32K144的ADC参考电压由VREFH和VREFL引脚提供。开发板上常见做法是把VREFH直接连到电源VDD这样做能用是能用但有个问题MCU的数字电源上往往叠加了开关纹波和负载突变引起的噪声这些噪声会直接耦合进ADC转换结果里导致读数周期性跳动。对精度要求高的项目VREFH应该单独接一个低噪声基准源比如专用的电压基准芯片VREFL直接接地即可。参考电压不稳后面做再好的滤波都救不回来。2.2 采样时间与转换时间的计算采样时间和转换时间的计算是配置ADC躲不开的数学题。采样时间等于采样周期乘以ADC时钟周期其中采样周期等于SMPLTS字段值加1。转换时间则由分辨率决定12位分辨率下固定需要一定数量的ADC时钟周期。两者相加才是单次转换总时间。举个例子。假设ADC时钟配到10MHz周期100ns。SMPLTS设成19采样周期就是20个ADC时钟采样时间2μs。12位转换按13个ADC时钟计算约1.3μs。单次转换总时间3.3μs左右换算下来最大采样率约300KSPS。如果信号源内阻很小采样时间可以缩短到1μs以内采样率就能上来。反过来如果前端接了RC滤波器或者信号源内阻很大采样时间就要适当加长宁可牺牲一点采样率也要保证读数准确。2.3 硬件平均功能到底好不好用S32K144的ADC内置了硬件平均功能在ADC_SC3寄存器里通过AVGS字段配置可以设置1、4、8、16次平均。这个功能的工作方式是硬件层面连续转换多次把累加结果经过移位后输出整个过程不占CPU资源。我的实际感受是硬件平均非常适合低频缓变信号比如温度、液位、压力这类量。把这些信号设置成16次平均读数稳定性肉眼可见地提升而且代码几乎不用改。但硬件平均不是万能的它本质上相当于降低了等效采样率。用在电机电流环这种需要快速响应的场景里会明显增加信号延迟。对于快速信号我建议关闭硬件平均在软件里做轻量滤波或者靠提高采样率来换取噪声裕量。想清楚信号的时间尺度再决定要不要开平均比盲目追求稳定读数更靠谱。2.4 校准与偏移修正的必要性有些新手不知道S32K144的ADC还有校准这一说其实参考手册里写得很清楚每次上电之后要执行一次校准流程。原因是芯片出厂后ADC内部存在一定的偏移误差和增益误差这些误差在片内通过校准寄存器来修正。不校准直接使用读数可能会偏差几十个LSB这个级别对于12位ADC来说已经相当可观了。校准操作本身不复杂在SDK中调用对应接口即可但有个细节校准前要保证ADC时钟已经配置完成校准过程中不能打断。校准完成后会生成一系列校准值自动存入寄存器后续转换会使用这些值做修正。我习惯把校准放在初始化函数里每次上电自动跑一遍相当于给ADC做了一个出厂标定这样后面读到的数据一致性要好很多。硬件平均和校准叠加使用时数据稳定性提升非常明显。3. 基于S32K SDK的ADC开发实操3.1 用Processor Expert配置还是直接写代码S32K144的开发路径大致有两条一条是基于S32 Design Studio里集成的Processor Expert工具图形化配置外设另一条是直接在SDK基础上写代码调用驱动接口。Processor Expert的优势是生成初始化代码快适合项目早期验证缺点也不少比如生成的代码层级多出了问题很难一眼定位到寄存器而且一旦手动修改生成代码再次生成时容易被覆盖。我个人的习惯是评估阶段用Processor Expert快速验证板子能不能跑正式项目里直接基于SDK写驱动或者干脆操作寄存器。ADC这种外设逻辑相对规整写清楚一套初始化函数之后复用性很强也方便后续做代码审查和维护。3.2 ADC驱动代码结构分析不管用SDK还是寄存器开发S32K144的ADC驱动都逃不开几个关键步骤时钟使能、引脚复用、模块配置、通道配置、触发转换、读取结果。这里用一段基于寄存器的示例代码来说明具体字段以参考手册为准。void ADC_Init(void) { /* 使能ADC0模块时钟 */ PCC-PCCn[PCC_ADC0_INDEX] | PCC_PCCn_CGC_MASK; /* 复位ADC0到默认状态 */ ADC0-CFG1 0x0u; ADC0-CFG2 0x0u; ADC0-SC2 0x0u; ADC0-SC3 0x0u; /* 配置ADC转换时钟和分辨率 */ /* ADIV3 表示8分频, MODE3 表示12位分辨率 */ ADC0-CFG1 ADC_CFG1_ADIV(3) | ADC_CFG1_MODE(3); /* 配置采样时间, SMPLTS19 表示20个ADC时钟周期 */ ADC0-CFG2 ADC_CFG2_SMPLTS(19); /* 选择软件触发方式 */ ADC0-SC2 ~ADC_SC2_TRGGSEL_MASK; /* 执行校准 */ ADC0-SC3 | ADC_SC3_CAL_MASK; while (ADC0-SC3 ADC_SC3_CAL_MASK) { /* 等待校准完成 */ } if (ADC0-SC3 ADC_SC3_CALF_MASK) { /* 校准失败, 需要重新初始化 */ } }这段代码的核心逻辑是先给模块供时钟然后复位到默认状态避免上次运行的配置残留影响本次初始化。分辨率、时钟分频、采样时间分别由两个配置寄存器完成SC1寄存器的通道切换则在每次读取不同通道时做放在轮询循环里更灵活。校准在这段代码里的位置是模块配置完成后、正式转换之前。校准标志清除后再配置具体通道并且开启转换。3.3 多通道扫描与触发方式的选择S32K144的ADC触发方式分软件触发和硬件触发两种。软件触发就是写SC1寄存器启动转换适合低速、低实时性的场景。硬件触发则靠PDB模块产生触发沿让ADC在精确的时刻开始转换这是电机控制和功率变换里最常见的用法。PDB模块在做电机控制时特别有用。它可以跟PWM同步在PWM周期的特定相位点触发ADC采样从而在电流纹波最小的时刻采集电流信号这样做出来的电流环带宽和稳定性都更好。PDB的配置分两步先是设置PDB时钟和计数器周期再配置ADC预触发通道。延迟时间决定了触发点相对PWM边沿的位置这个参数要根据开关频率和采样时刻需求反复调。多通道顺序采样的实现我比较推荐PDB预触发和ADC通道切换配合。每次PDB触发后进入ADC中断中断里切换到下一个通道号同时读取上一次的转换结果。这样做的好处是采样间隔均匀CPU负担小。如果通道数多、数据量又大还可以把DMA加进来转换结果自动搬运到内存缓冲区CPU只负责在双缓冲切换时处理数据。3.4 从0到1一个完整的电压采集示例用一个最简单的例子把整个流程走一遍读取ADC0的通道0模拟输入通过轮询方式读取并把原始码值换算成电压。示例基于寄存器操作方便理解数据流。uint16_t ADC_ReadChannel(uint8_t channel) { /* 切换通道并启动软件转换 */ ADC0-SC1[0] ADC_SC1_ADCH(channel) ~ADC_SC1_AIEN_MASK; /* 等待转换完成, COCO位置1表示转换结束 */ while (!(ADC0-SC1[0] ADC_SC1_COCO_MASK)) { /* 空循环等待 */ } /* 读取结果 */ return (uint16_t)ADC0-R[0]; } float ADC_ConvertToVoltage(uint16_t raw, float vref) { /* 12位分辨率, 满量程码值4096 */ return (float)raw * vref / 4096.0f; }使用的时候先调用ADC_Init完成模块初始化再配置引脚复用之后就可以循环调用ADC_ReadChannel读取数据了。换算电压的公式本质上是线性映射把0到4095的码值对应到0到VREFH的电压区间。这里的vref如果直接用3.3V实际读出来的电压值可能需要用万用表校准一下因为参考电压本身可能不是精确的3.3V。把电压换算之后再进入后级滤波处理环节。4. 数据漂移与滤波处理4.1 数据漂移的几个常见来源ADC数据漂移可能是每个嵌入式工程师都遇到过的问题表现为读数缓慢变化或者随机跳动。常见的漂移来源首先就是电源噪声。参考电压的纹波、模拟供电的开关噪声都会直接叠加进ADC的采样结果里特别是在电源没有做充分去耦的情况下。我遇到过一个案子ADC读数每隔固定周期就会出现一个明显的尖峰排查到最后发现是板子上的LED扫描信号引起的电源跌落给ADC单独供电后才解决。第二个常见原因是信号源阻抗过高。分压电阻网络、温度传感器这类电路输出阻抗可能高达几十kΩ如果ADC采样时间配置偏短采样电容充不满读数就会低于真实值而且信号源阻抗越高误差越明显。第三个原因是温度漂移这是物理特性决定的参考电压芯片以及ADC内部的基准源都会随温度变化导致同样一个输入电压在不同温度下读到的码值不同。最后还有一个比较隐蔽的原因就是软件逻辑问题。比如中断里读数据时没有关闭ADC中断导致读取到错误状态的数据。4.2 常用的C语言滤波函数滤波是ADC数据处理里最立竿见影的一步但选滤波算法要结合信号特性。这里分享三个实现简单、工程上常用的滤波函数。中值滤波适合信号变化不快但偶发脉冲噪声的场景。实现思路是连续采集N次排序后取中间值。N一般取3、5、7N越大对脉冲噪声的抑制能力越强但延迟也越大。滑动平均滤波则是维护一个窗口每来一个新数据就丢一个最旧的数据对窗口内的数据求平均。它的优点是实现简单、对周期性噪声抑制效果好缺点是信号快速变化时会有明显滞后。一阶低通滤波用递推公式实现系数alpha控制滤波强度alpha越小越平滑但延迟越大适合需要在线调节平滑度的应用。这三个函数我都写出来放在项目里当工具函数用实际选择时看需求有尖峰用中值噪声均匀用滑动平均需要快速响应就用一阶低通。4.3 采样校正与内部基准除了软件滤波采样校正能让数据从源头上更准。S32K144没有专用的内部参考通道但可以利用已知电压做软件校正。比如把通道接到VREFH附近的一个精密分压点上读到的值和理论值之间的比例关系就能推算出一个校正系数后续所有ADC读数都乘以这个系数。更精确一点的做法是用外部基准电压源配合精密电阻分压产生一个标定点。系统上电时先读这个标定点计算出实际增益和理想增益的偏差再动态修正整个采集链路。这个办法虽然要占用一个ADC通道但对精度有要求的项目非常值。校准和滤波是两个层面的问题校准解决“准不准”滤波解决“稳不稳”两者互不替代配合使用效果最好。5. ADC前端电路与PCB布局注意点5.1 输入阻抗、RC滤波与运放缓冲ADC输入电路的设计直接决定采样质量。很多时候代码没问题、配置没问题但读数还是不对问题就出在引脚前面的电路上。ADC的采样电容在采样瞬间会有一个充电电流冲击这个电流如果从高阻抗的信号源来充电速度就会受限。解决思路有两个一是把RC滤波的电阻值尽量选小二是信号源阻抗实在大的话加一级运放跟随器。RC低通滤波器几乎是ADC前端标配既能滤除高频干扰还能抑制采样瞬间的电荷冲击带来的振铃。RC截止频率要高于有用信号的最高频率但又要低于噪声频率一般取信号带宽的5到10倍。电阻取几百欧姆以下电容容量根据截止频率倒推。运算放大器采用电压跟随器接法时输入阻抗极高、输出阻抗极低把传感器信号和ADC隔离开来这就是“缓冲”的含义实际电路中非常管用。5.2 端口保护电路ADC引脚在恶劣环境下很容易被静电或者误接电压打坏。保守的设计会给引脚前加钳位保护和限流电阻。钳位二极管可以把引脚电压限制在供电轨和地之间超过安全范围的电压会被引导到电源或地从而保护MCU内部结构。常用的做法是接一个双二极管BAT54S这类低位钳到地高位钳到VDD或参考电压。串联限流电阻的作用是限制过压时流过引脚内部保护二极管的电流。电阻值选几百欧到几kΩ根据信号源能力和引脚承受能力权衡。加了这个电阻后即使出现异常高电压电流也被限制在安全范围内。不过限流电阻会和输入电容构成低通滤波选值太大会影响信号带宽需要一起评估。5.3 PCB布局的三个关键要点PCB布局对ADC性能的影响往往比代码还大。这块我总结出三个关键点。第一参考电压引脚的去耦电容要靠近引脚放置推荐1μF和0.1μF电容组合0.1μF靠近引脚1μF次之回流路径尽量短。参考电压引脚上的噪声直接决定ADC转换的底噪去耦做不好后面白搭。第二模拟地和数字地要有清晰划分。ADC输入信号的走线下方不要有数字信号穿越特别是时钟和高速PWM线。模拟信号区域要保持干净数字信号回流不要从模拟区域内穿过。第三ADC输入走线远离高频开关节点。电机驱动板上PWM输出线电流变化剧烈会产生很强的电磁干扰如果和ADC输入线平行走线噪声会通过耦合串进模拟通道。实在避免不了的时候中间加一条接地隔离走线把干扰通道切断。6. 开发中常见的坑与排查方法6.1 芯片被锁怎么办S32K144芯片被锁基本是每个开发者都会遇到的事尤其在你尝试全擦除烧录或者操作Flash配置不当的时候。芯片锁定本质上是Flash中的配置字段被改成了把调试口禁用并且安全级别提高的值。复位之后芯片进入安全状态调试器连不上表现为连接报错或者无法擦写Flash。我的建议是先在烧录和擦除配置界面里确认有没有包含Flash配置字段开发调试阶段不要轻易全擦除。真被锁了也别慌用J-Link的JLink Commander命令行工具可以尝试恢复。连接芯片后用unlock命令解锁成功之后立即对Flash配置字段重新编程把安全配置恢复成开发模式。这个操作的方向是对的关键是别在解锁之后又随手全擦除一遍。6.2 CSEC与真随机数安全应用S32K144的CSEC模块提供了硬件级安全功能其中包含真随机数发生器。热搜词里也有人问CSEC如何获取真随机数这里顺便说一句。如果需要随机数用于密钥生成、安全通信之类的场景从应用层面来看要优先查安全手册和SDK文档调用官方提供的安全驱动接口不要自己去读寄存器和内存拼随机数那样熵源不足随机数质量堪忧。CSEC模块的使用会涉及安全状态机、密钥管理等一系列概念项目里真正用到安全功能时最好先梳理清楚需求和使用边界再决定是否启用。如果只是随机数应用已经内置了足够多的低层次接口应用层按需调用即可。CSEC还有一个好处是模块独立运行不占用ADC或者其他外设资源。6.3 ADC读数为0或满量程的排查思路ADC读数固定在0或者固定在4095是新手最容易碰到的异常。先把排查思路理顺读数为0优先检查引脚复用有没有配置正确通道号有没有对应到实际连接的引脚信号是否真的送到了引脚上。读数为4095优先考虑信号有没有被驱动以及输入电压是不是超过了参考电压范围。还有个比较隐蔽的情况是SC1寄存器在读取后没有正确清除旧标志位导致再次读取时读到的还是上一次的数据。解决方法是每次启动转换前重新写SC1不在转换过程中同时读取标志和数据。多通道模式下来回切换通道时通道切换后的第一次转换结果也建议丢弃因为采样电容上的残留电荷还没有释放干净读出来的数据可能不准。6.4 ADC实际项目中容易忽略的细节开发S32K144 ADC外设过程中还有一些零散细节需要留意。ADC中断服务函数里不建议做耗时的数据处理应该把数据读出来放到缓冲里待主循环或者后续中断再处理否则会影响下一次采样周期的到来。用了DMA搬运数据时要注意缓存的完整性避免在DMA写入一半时主循环去读数据造成半新半旧的情况。外部信号源发生了变化比如更换了传感器型号采样时间参数也要跟着评估。直接从老代码里复制参数信号源内阻变大后原来够用的采样时间就不一定够了。ADC的性能是芯片自身和外部电路、软件协同作用的结果系统地看待整个链路比单独盯某一个寄存器有效得多。最后说一点个人体会。ADC调好之后系统里其他很多问题解决起来都会轻松很多。同样的一块板子把参考电压去耦做好、采样时间算清楚、滤波算法选对采集出来的数据质量能差出一大截。这也是为什么我在这篇笔记里花了大量篇幅讲硬件和配置细节。做S32K144的ADC代码反而只是入门真正花时间的是对每个环节的充分理解。希望你调试过程中不要急着动代码先想想信号链路里还有什么环节没有被照顾到。
返回列表