ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

GD32H759模拟量采集与输出:基于RT-Thread的ADC/DAC驱动开发实战

2026/9/19 7:59:01 拓冰建站 浏览量
GD32H759模拟量采集与输出:基于RT-Thread的ADC/DAC驱动开发实战 1. 项目背景与需求盘点一块工控板上的模拟量难题做工业控制的朋友都知道现场设备最不缺少的就是模拟量。压力变送器输出4-20mA电流环温度传感器经过变送器变成0-10V电压信号伺服驱动器反馈位置信号比例阀需要0-5V的控制电压……这些信号最终都要汇入主控板经过ADC采样后参与闭环控制同时主控板计算出结果后还得通过DAC输出模拟量去驱动执行机构。所以ADC和DAC这两大模拟外设几乎是每一块工控主板都绕不开的枢纽。这块基于GD32H759的主控板项目功能定位很明确8路模拟量采集、2路模拟量输出、1路RS485通信、1路CAN通信、外扩16路数字量IO运行RT-Thread操作系统做任务调度。系统承担的是一个气体浓度在线监测装置的信号预处理和控制功能工业现场电磁环境比较复杂变频器、电机启停都会带来不小的干扰。这一篇我重点把ADC和DAC驱动的开发和调试过程完整梳理一遍。GD32H759是兆易创新GD32H7系列中的旗舰型号Cortex-M7内核主频最高能跑到600MHz片上资源相当丰富1MB SRAM、带FPU和DSP指令集、双bank Flash板载的ADC模块是三路12位逐次逼近型ADC可以配置为12/10/8/6位分辨率支持硬件过采样和均值滤波DAC模块是两路12位电压输出型支持三角波和噪声波生成还集成了波形发生器和DMA请求功能。从纸面参数来看这套模拟外设在同级别MCU里算是相当能打的。选择RT-Thread作为操作系统核心原因是它的设备驱动框架非常成熟ADC和DAC都有现成的抽象层上层应用不需要直接操作寄存器通过rt_adc_read()和rt_dac_write()这类标准API就能完成读写。这对后期维护和固件升级非常友好换一颗芯片或者换一路通道驱动层改改配置就行不用动应用代码。不过上手之后很快就发现芯片手册里写得清楚的东西和现场实际跑出来的数据中间隔着一大堆工程细节。这篇博文不会贴冗长的数据手册而是把从寄存器配置、DMA搬运、RT-Thread设备注册到PCB布局对采样精度的影响、数据漂移的处理、DAC输出波形失真这些实操环节用踩坑-排查-解决的方式完整还原出来。2. ADC部分从硬件通道配置到RT-Thread设备接入的完整链路2.1 参考电压选择和电源树设计直接影响采样精度很多人拿到芯片就开始配寄存器其实ADC的精度天花板首先由硬件决定参考电压VREF和供电电源占了八成权重。GD32H759的ADC参考电压可以从VREF引脚外部输入也可以内部连接到VDDA。在这块板子上我用了独立的外部基准电压源选的是REF50303.0V输出温漂系数3ppm/°C。为什么不用VDDA做参考因为板子上还有继电器、数码管这些数字负载VDDA上的纹波很难控制干净而ADC的量化误差是和参考电压的稳定度直接挂钩的。如果参考电压本身在波动采样结果出来了也说不清是信号变了还是参考变了。模拟电源部分VDDA通过磁珠和10Ω电阻从数字电源隔离出来并联了10μF钽电容和100nF陶瓷电容做低频和高频去耦。DCDC输出电压经过LDO线性稳压后给模拟电路供电避免开关电源的纹波直接进到ADC的电源引脚。这些处理在原理图阶段就要做好等板子打样回来再改就非常被动。关于参考电压的值这里有一个容易忽略的点。GD32H759的ADC是12位3.0V参考电压下LSB最低有效位约为0.732mV即3.0V/4096。如果现场传感器输出的满量程只有0-3V那么选3.0V参考会浪费分辨率。我在这块板子上把传感器信号前端加了一级运放做调理把0-2.5V的信号抬升到0.3-2.7V区间用满ADC输入范围的约80%实测下来有效分辨率提升非常明显。2.2 采样时间、分辨率与过采样先算清楚再配置寄存器GD32H759的ADC时钟来自APB2总线时钟经过ADC时钟分频器后得到ADC时钟ADCCLK。手册上标明ADC时钟最高不能超过40MHz超过这个频率采样精度会断崖式下跌因为采样电容没有足够的时间充满电。我当时第一版配置为了追求速度把ADC时钟设到了40MHz采样时间选了最短的2.5个ADC时钟周期。跑空载输入接地时数据看起来没问题但接上真实传感器后发现跳码幅度比预期大好几个LSB。后来翻了手册里的详细时序图才意识到采样时间本质上是采样电容的充电时间ADC内部采样电容约为几pF量级如果信号源内阻大RC充电时间常数就大充电时间不够采样值就会偏低。计算公式也一并放出来信号源内阻Rsource与采样电容Csample构成RC低通充电时间常数τRsource×Csample。要让采样电容上的电压在采样窗口内稳定到源电压的0.5个LSB以内需要满足采样时间ts ≥ (RsourceRon)×Csample×ln(2^N)其中N是分辨率位数Ron是ADC内部采样开关的导通电阻。GD32H759的内部采样开关阻抗典型值约为几kΩ信号源内阻如果在1kΩ以上采样时间至少要给几十个ADC时钟周期才稳妥。最终配置我采用如下参数ADC时钟分频30MHz采样时间28.5个ADC时钟周期对应约0.95μs分辨率12位转换模式规则组连续转换外部触发定时器具体见下文这样一改8路通道全部轮询一遍单次转换时间大约在30MHz下为15个时钟12位28.5个时钟≈1.45μs左右8个通道约11.6μs也就是采样率仍然能做到85kHz以上完全满足工控现场对低速模拟量的采样需求而且精度表现好了很多。很多时候不是芯片性能不够而是配置参数没有匹配实际的信号源阻抗。2.3 DMA搬运数据到内存避免CPU空转一次ADC转换出来的数据只有16位如果CPU每转换完一次就进入中断去搬数据不仅打断实时任务还得承担高频中断带来的上下文切换开销。我的做法是启用ADC的DMA传输让转换结果自动搬运到内存缓冲区中CPU只在DMA传输完成时收到一个中断。具体配置过程初始化DMA控制器选择ADC外设的DMA请求作为传输源方向为外设到内存。设置外设地址为ADC规则组数据寄存器地址内存地址指向一个uint16_t类型的数组。传输模式为循环模式这样DMA在搬运完一轮数据后自动重新开始不需要CPU干预。缓冲区大小设置为8和ADC通道数一致。DMA循环模式配合ADC连续转换是一个稳定的数据流水线ADC自动采集DMA自动搬运CPU负责消费数据。这个过程不占CPU也不阻塞RT-Thread的任务调度。有一个细节值得注意ADC的DMA请求需要在ADC使能之前配置好否则会出现数据丢失或者DMA传输不启动的问题。我调试时曾经把DMA配置放到了ADC使能之后现象是第一次转换后DMA不动作检查发现DMA的使能位是在ADC之前写入但这个前提下硬件并不会在后续ADC转换时才启动DMA。把初始化顺序调整过来之后问题消失。2.4 RT-Thread ADC设备注册上层代码不用碰寄存器RT-Thread的ADC设备驱动框架分两层底层是驱动库主要实现adc_read()、adc_enable()、adc_channel_enable()这些接口函数上层通过rt_device_find()查找设备节点名称然后调用rt_adc_enable()和rt_adc_read()获取转换值。以下是ADC驱动注册的核心函数实现我在驱动文件中写了这样的逻辑static rt_err_t drv_adc_enabled(struct rt_adc_device *device, rt_uint32_t channel, rt_bool_t enabled) { if (enabled) { adc_channel_enable(channel); } else { adc_channel_disable(channel); } return RT_EOK; } static rt_err_t drv_adc_read(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value) { rt_uint32_t result; adc_channel_enable(channel); adc_software_trigger_enable(ADC0); while (adc_flag_get(ADC0, ADC_FLAG_STRC) RESET); // 等待转换完成 result adc_regular_data_read(ADC0); *value result; return RT_EOK; } static const struct rt_adc_ops ops { .enabled drv_adc_enabled, .read drv_adc_read, }; int rt_hw_adc_init(void) { rt_err_t ret; static struct rt_adc_device adc_dev; adc_dev.ops ops; ret rt_hw_adc_register(adc_dev, adc0, RT_NULL, 0); return ret; } INIT_BOARD_EXPORT(rt_hw_adc_init);应用层要做的事情就非常简单了rt_adc_device_t adc_dev (rt_adc_device_t)rt_device_find(adc0); rt_adc_enable(adc_dev, 1); rt_uint32_t value rt_adc_read(adc_dev, 1); float voltage value * 3.0f / 4096.0f;这里有一个RT-Thread使用上的细节不同的版本对rt_adc_read()返回值有差异老版本直接返回读取的原始ADC值新版本4.1.0及以后把读取值通过指针参数value返回函数返回值是错误码。如果代码是从老版本迁移过来的一定要检查这个API的签名变化否则数据取出来全不对。2.5 数据漂移问题ADC采集值为什么会慢慢跑偏这个问题的典型现象是新板子上电ADC采样值很稳定但运行半小时后采集值会缓慢漂移几个LSB偶尔还会出现一个幅度较大的跳变。排查过好几块板子之后总结出三个最可能的原因第一个原因是发热引起的基准源温漂。REF5030虽然温漂指标不错但如果PCB布局让它紧挨着发热元件本体温升超过10°C输出就会有明显偏移。我后来把基准源芯片挪到离LDO和驱动芯片较远的位置并在底部铺了独立的地铜皮做热隔离漂移幅度立刻降下来了。第二个原因是采样电容的泄漏。GD32H759内部采样电容虽然很小但如果在PCB上走线过长且周围有潮湿空气或助焊剂残留等效泄漏电阻会降低导致保持阶段的电荷慢慢流失。解决方法是清洗板卡并在ADC输入引脚旁边加一个1nF到10nF的电容到地提供额外的电荷保持。第三个原因最隐蔽——DMA缓冲区和应用层读取之间的竞争。因为DMA在循环模式中会不断覆盖旧数据如果应用层读取缓冲区时正好DMA写入了一半数据拿到的就是半个旧值半个新值拼接出来的错误数据。虽然这不算真正的漂移但在示波器上看数据曲线会造成周期性跳变。解决方法是使用双缓冲区切换或者用rt_sem_take配合DMA中断做数据同步。3. DAC部分DHR寄存器的门道与波形输出的实现细节3.1 先搞懂DAC的数据通路DHR寄存器是必选项GD32H759的DAC模块结构上比ADC简单不少但有一个概念如果不搞清楚写代码时会被卡住——DHR寄存器数据保持寄存器。DAC的转换结构是软件把待转换数据写入DHR寄存器DHR寄存器的内容自动加载到DOR数据输出寄存器再由DOR驱动DAC输出引脚上的电压。DHR有多个地址映射方式对应不同的对齐模式寄存器名称位数用法说明DAC_DHR8R8位右对齐数据放在低8位用于8位输出模式DAC_DHR12R12位右对齐数据放在低12位常用DAC_DHR12L12位左对齐数据放在高12位较少用这三类寄存器的本质区别在于数据对齐方式和写入位宽而不是DAC的精度。12位模式写DHR12R数据范围是0-4095对应0-VREF的输出电压。如果你全程用8位数据写DHR8R即可但输出分辨率会降低到8位即256级。我在这块板子上使用12位模式写入DHR12R寄存器。标准电压换算公式DAC输出电压 (DHR12R值 / 4096) × VREF反向计算要把DAC输出3.3V参考电压的70%即2.31V那么DHR12R (2.31 / 3.3) × 4096 ≈ 2867。需要注意DAC的参考电压和ADC用的是同一个VFREF引脚所以在硬件设计上DAC和ADC共享同一个基准源这样做的优点很明显系统的输入和输出全部以同一参考电压为基准不会出现ADC侧参考是3.0V、DAC侧参考是3.3V导致数据错位的问题。3.2 输出缓冲模式与负载匹配输出电压为什么掉了一截DAC数据写入后最终输出到引脚上是经过输出缓冲的还是直接输出这决定了带负载能力和输出电压的范围。GD32H759的DAC有两种工作模式不带输出缓冲引脚直接连接DAC的核心输出内阻较大输出阻抗通常在几kΩ级别。优点是功耗低缺点是一接负载电压就掉。带输出缓冲内置运放做射极跟随器输出输出阻抗很低可以直接驱动小负载。优点是带负载能力强缺点是输出电压范围不是满摆幅——受内部运放限制最高只能到VREF-0.2V左右。这块板子的DAC输出用于驱动工业仪表输入阻抗约10kΩ我开启了输出缓冲模式。实测发现配置缓冲后空载时输出3.29VVREF3.3V但接上10kΩ负载后输出掉到了3.24V左右相差了50mV。对满量程3.3V来说这个偏差占1.5%性能要求高的场合根本不能接受。排查手法是用一个已知精确的电流表测量负载电流再测量DAC引脚的实际输出电压。计算得出驱动级内阻大约在10-15Ω之间虽然不高但对于要求0.1%精度的场合确实不够。后来在DAC输出和负载之间增加了一级**单位增益运放OPA2188**做隔离把DAC的输出经过运放缓冲后再送到外部接口输出电压偏差从50mV降到了2mV以内这属于硬件层面解决的输出缓冲不足问题。从软件角度如果不想改硬件也可以用校准的方式补偿输出压降。做法是在DAC驱动初始化后先输出0.5V和3.0V两个已知电压通过一个精密电阻分压后回到ADC采样用采样值反推实际输出电压得到一组两点校准系数后续所有DAC输出都通过这个系数修正。这个方案在精度要求不特别高的场合够用但稳定性不如硬件方案。3.3 用定时器触发DAC输出生成可控频率的三角波/正弦波工控场景中DAC经常需要输出特定波形比如给比例阀一个斜坡信号或者给振动台一个正弦扫频信号。如果用软件在循环里逐点写DHR寄存器波形频率会受CPU负载影响RT-Thread的调度延时会让波形出现毛刺。这块板子的DAC支持外部触发转换模式定时器溢出事件作为DAC触发源DAC在每次定时器溢出时自动把DHR寄存器的值加载到DOR并更新输出引脚电压。配合DMA可以实现一个完整的波形发生器准备一个波形数据表比如256点的正弦波数组。配置DMA把数据表从内存搬运到DAC的DHR寄存器。配置定时器设定触发频率。例如要输出1kHz正弦波256个点则每点更新频率为256kHz定时器的溢出频率设为256kHz。在RT-Thread中创建一个数据更新任务周期性修改波形数据表内存实现频率或幅度的调整。实际测试中用这种方案生成1kHz正弦波输出波形非常干净谐波分量很小因为波形更新完全由硬件定时器驱动与CPU无关。这个方法同样适用于三角波、锯齿波和自定义任意波形。定时器频率计算示例定时器时钟为120MHz预分频器(PSC)设为0自动重载值(ARR)设为468则溢出频率 120MHz / (01) / (4681) ≈ 255.86kHz差一点点是没问题的只要保证波形点数和触发频率的比值符合需求。若要求严格精确可以调整PSC或ARR并实测频率。3.4 RT-Thread DAC设备注册与电压转换逻辑RT-Thread的DAC设备注册与ADC高度对称。在驱动里实现dac_write()接口由rt_dac_write()调用。一个直接可用的DAC驱动实现static rt_err_t drv_dac_write(struct rt_dac_device *device, rt_uint32_t channel, rt_uint32_t value) { if (channel 2) { return -RT_EINVAL; } if (channel 0) { dac_data_set(DAC0, DAC_ALIGN_12B_R, value); } else { dac_data_set(DAC1, DAC_ALIGN_12B_R, value); } return RT_EOK; } static const struct rt_dac_ops dac_ops { .write drv_dac_write, }; int rt_hw_dac_init(void) { static struct rt_dac_device dac_dev; dac_dev.ops dac_ops; return rt_hw_dac_register(dac_dev, dac0, RT_NULL, 0); } INIT_BOARD_EXPORT(rt_hw_dac_init);应用层写入电压值rt_dac_device_t dac_dev (rt_dac_device_t)rt_device_find(dac0); rt_uint32_t dac_raw (rt_uint32_t)(voltage / 3.3f * 4095.0f 0.5f); rt_dac_write(dac_dev, 0, dac_raw);这里用voltage / 3.3f * 4095.0f而不是voltage / 3.3f * 4096.0f是有讲究的。虽然DHR12R寄存器能写入0-4095但实际的满量程输出对应的数字代码是4095而不是4096。如果按4096来算代码值最大只能到4095算出来的满量程电压会偏小约0.024%。对大多数场合来说无感知但要追求准确统一按4095做满量程换算更合理。4. 驱动层设计把模拟外设封装成可维护的外设服务4.1 多通道、多任务并发的访问控制ADC和DAC的驱动如果只做能读能写那还不算完。工控现场通常有多个任务同时访问模拟外设一个任务读取温度通道做PID控制一个任务读取压力通道做超限报警还有一个人机交互任务需要动态切换读取通道。这就带来一个并发问题——多个线程同时调用rt_adc_read()底层寄存器会打架。我遇到的实际案例是这样的系统里跑了三个任务任务A每10ms读一次温湿度PA0、PA1通道任务B每50ms读一次压力PA4通道任务C由用户手动触发读取任意一路通道做校准。结果在任务C读取通道时任务A的数据会发生偶发的跳变。分析之后确认不是硬件问题而是任务C修改了ADC的通道配置暂时占了ADC资源任务A读到的是被切换了一半的转换结果。解决办法是在驱动层加互斥锁把所有ADC通道访问串行化static struct rt_mutex adc_lock; static rt_err_t drv_adc_read(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value) { rt_err_t ret; rt_mutex_take(adc_lock, RT_WAITING_FOREVER); adc_channel_enable(channel); adc_software_trigger_enable(ADC0); while (adc_flag_get(ADC0, ADC_FLAG_STRC) RESET); *value adc_regular_data_read(ADC0); rt_mutex_release(adc_lock); return ret; }粗粒度锁虽然损失了点并发效率但在工控这种实时性要求高、数据吞吐量不大的场景保证数据一致性远比并发性能重要。4.2 数据回读、校准与满量程标定DAC还有一个容易被忽略的问题写入的值和实际输出电压不是严格线性对应的。GD32H759的DAC在出厂时并非绝对精密的器件存在偏移误差和增益误差。比如写0时理论输出0V实际可能输出10mV写4095时理论输出3.3V实际可能输出3.32V。为了达到工控现场要求的精度我在应用层加了一组软件校准参数在出厂调试阶段完成两步标定零点校准写DAC值为100避开真正的0点非线性区用万用表测实际电压Vz记录偏移量offset Vz - V理论(100)。增益校准写DAC值为4000测实际电压Vf计算增益系数slope (Vf - Vz) / (4095 - 100)。之后每次写电压值先做软件修正实际写入值 (目标电压 - offset) / slope。这套两点校准法把DAC的输出误差从±30mV降低到了±3mV以内对模拟量输出的控制精度有非常直接的好处。同理ADC侧也做了两点校准。给ADC输入一个用高精度信号源产生的2.000V电压读取采样值再输入0.100V读取采样值反推增益和偏移。校准后的电压换算从简单的value * 3.0 / 4096变成了value * gain offset形式。校准参数存放在片内Flash的用户配置区固件升级时不会丢失。4.3 应用层零修改切换模拟量通道最后一个设计上的建议不要把硬件通道号直接暴露给上层业务代码。我在驱动之上又封装了一层模拟量管理器维护一张通道映射表typedef struct { rt_uint8_t type; // 0: 电压, 1: 电流, 2: 电阻 rt_uint8_t adc_channel; // 底层ADC通道 float scale; // 标度系数 float offset; // 零位偏移 const char *name; // 通道名称如CH1_PT100 } analog_channel_t;上层业务代码只通过通道名称获取数据比如float temp analog_read_sensor(TEMP_IN);底层实际走的是ADC第几通道、量程系数是多少全部由配置数组决定。这样现场需要更换传感器类型或调整量程时只需要修改配置文件业务代码一行都不用动。这个抽象层带来的好处在后续维护时体现得很明显。有一次现场反馈某个通道的采集值普遍偏高排查后发现是变送器量程和配置不一致我直接在板子的调试接口把配置项从0-10V对应0-100kPa改成0-10V对应0-160kPa重新上电就正常了不需要重新编译固件。5. 实测数据与干扰排查从波形毛刺到点位标定的完整记录5.1 一组实测数据ADC/DAC精度到底能达到什么水平在硬件调试完成后我用一台六位半万用表Keysight 34461A和一台信号发生器对ADC和DAC做了整板标定测试。测试环境是实验室常温PCB完全裸露放置在桌面上供电用实验室直流电源。ADC侧用信号发生器分别输出0.500V、1.000V、1.500V、2.000V、2.500V、3.000V六个标准电压每个点连续采样1000次取平均得到的结果如下输入电压(V)采样均值(raw)换算电压(V)绝对误差(mV)0.50006450.4988-1.21.00012931.00020.21.50019401.50151.52.00025872.00121.22.50032332.50191.93.00038793.00121.2经过两点校准后所有测试点的绝对误差均小于2mV即不超过3个LSB12位条件下。对一个不带外部仪表放大器的MCU内置ADC来说这个精度在工控场景里是够用的。DAC侧向DAC分别写入代码值512、1024、2048、3072、4095测量实际输出写入代码理论电压(V)实测电压(V)绝对误差(mV)5120.41250.4089-3.610240.82500.8217-3.320481.65001.6471-2.930722.47502.4728-2.240953.30003.2982-1.8DAC的未校准输出误差约在-2至-4mV之间经过软件校准修正后误差可以压到±1mV以内。这个结果验证了硬件基础软件校准这套组合拳的有效性。5.2 时钟抖动与电源噪声PCB布局上直接影响采样的三个关键点如果说寄存器配置是软件层面的基本功那PCB布局就是硬件层面的决定因素。同样的驱动代码放在布局不同的两块板子上测出来的ADC数据质量可以差出一个数量级。这次调试中我对PCB布局做了三轮针对性修改总结出三个关键点第一个关键点ADC/DAC的模拟电源引脚必须独立走线且尽量靠近芯片放置去耦电容。GD32H759的AVDD引脚附近需要放置一个1μF陶瓷电容和一个10nF陶瓷电容两个电容都要直接连接到芯片的电源焊盘走线短而粗。我最初的设计中去耦电容放在了PCB背面经过两个过孔连接到电源引脚结果在高频干扰测试中ADC数据跳变明显增大。把电容移到正面电源引脚旁边后跳变幅度下降了一半以上。第二个关键点ADC输入走线要远离数字信号线特别是SPI、PWM这类高频翻转信号。数字信号的边沿跳变会产生强烈的电磁耦合如果ADC输入走线和一条PWM走线平行走了10mm以上采样结果的噪声底会显著抬高。原则是模拟信号走线尽量短、尽量宽、两侧用地铜皮包裹guard trace形成有效的回流路径。如果实在绕不开平行走线至少保证间距在10mil以上中间夹一条地线隔离。第三个关键点时钟源和ADC参考电压引脚之间保持物理距离。晶振的谐波和参考电压走线之间如果发生耦合会在采样值上叠加周期性干扰。高速晶振比如GD32H759外接的25MHz晶体下方尽量不要布置模拟走线时钟走线也不能和VREF走线平行。RTC低速晶振虽然频率低同样要注意与模拟区域的隔离。5.3 遇到ADC采样值周期性跳变时的排查路径有一个现象困扰了我一整天ADC采样在示波器上看不到明显问题但把数据通过串口发到上位机画波形时能观察到每隔一段时间出现一次毛刺宽度约几毫秒幅度能到几十个LSB。这种问题非常典型我把完整排查思路写出来方便大家对照排错。第一步先确认毛刺出现时系统正在做什么。我在RT-Thread中开启了线程级钩子把线程切换日志打出来发现毛刺出现的时间和CAN发送任务、SPI Flash擦写任务高度重合。到这里基本可以断定问题出在数字噪声耦合而非ADC自身。第二步用示波器测量VDDA引脚的纹波。把示波器探头接地环缩短直接点在VDDA测试点上发现在CAN收发瞬时VDDA上有约50mV的尖峰噪声频率和CAN波特率一致。这就是根源CAN收发器的电源没有和模拟电源完全隔离回流路径交叉。第三步硬件整改。把CAN收发器和隔离电源的GND与模拟地之间增加0Ω电阻作为单点连接同时将CAN收发器电源改用单独的LDO供电不再直接取自模拟VDDA网络。整改后复测毛刺幅度从几十个LSB降到2个LSB以内。这个案例给了一个经验总结ADC数据跳变优先查地回流其次查电源再查寄存器配置。很多人一看到采样值不对第一反应是改软件滤波算法其实大可不必。数据在源头就被干扰了软件滤波再强也只是修补。先解决硬件层面的耦合软件才能全力以赴处理真实信号。最后再分享一个小技巧GD32H759的ADC硬件均值模式非常实用。在ADC配置里开启硬件过采样设置8倍过采样和4次均值相当于用硬件自动做了24次叠加平均。实测下来纯静态输入下采样值的峰峰值从±6个LSB降到了±1个LSB对慢变模拟量来说几乎感觉不到延迟但数据质量好了不止一个档次。这个功能基本不需要额外占CPU强烈推荐在工控项目里默认开启。