ARTICLE DETAIL

资讯详情

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

SWM181 ADC评估板采样不稳定?从RC滤波到DMA与三点校准全流程解析

SWM181 ADC评估板采样不稳定?从RC滤波到DMA与三点校准全流程解析 简介基于国产SWM181CBT6-50Cortex-M0内核单片机设计的ADC评估板完整工程资料面向嵌入式硬件工程师、电子爱好者和相关专业学生既可作为学习国产MCU评估板设计的样例也适合在此基础上进行二次开发或产品预研。压缩包大小约40.73MB内含Altium Designer原理图与PCB文件、软件代码及应用文档硬件为97x58mm的双面2层板布局布线涉及LDO低压差线性稳压、可调升压、可调基准电压、按键输入、LED指示等常见功能模块设计。其中给出了从电源电路到信号调理的完整器件选型参考如贴片电阻电容及翠绿指示灯等并配有相应软件代码帮助理解硬件与固件的协同工作方式。目前已有314人学习适合需要快速上手SWM181系列ADC应用开发的工程师参考。所有工程均可用Altium Designer直接打开或修改便于对照原理图、PCB和代码进行系统性学习为实际产品设计提供可靠参考。1. 一块 SWM181CBT6-50 的 ADC 评估板真正难住人的是采样数据怎么才不漂调试这种评估板时最常遇到的画面是面板上的电位器拧到固定位置万用表测出 1.650V板上 SWM181CBT6-50 的串口却反复跳 1.610~1.720V12 位 ADC 的理论分辨率不到 1mV实测波动却大到没法用。问题往往不在芯片本身而在原理图上的前端 RC、PCB 上的 guard ring 和软件里的采样周期上。这个评估板资料包把这几样凑齐了Altium 工程里是完整的 ADC 采集前端与板级布局软件 code 里是初始化、滤波、DMA 和校准例程应用文档记录调试方法。下面顺着这三块内容讲清楚怎么从原理图一直推到校准后的稳定读数适合正在做信号采集、传感器接口或电池监测的单片机工程师。2. SWM181CBT6-50 的 ADC 资源与 Altium 原理图外围设计拿到资料先打开原理图不要急着看 PCB。SWM181CBT6-50 内部是一个 12 位逐次逼近型 ADC采样保持电容通常在几 pF 到十几 pF对外接口看起来像高阻但一旦采样开关导通瞬间电流由外部电路提供。因此原理图上每个 ADC 输入引脚前面的阻容网络决定了实际转换精度。2.1 12 位 SAR ADC 的采样原理与通道分配逐次逼近型 ADC 的采样过程是短暂的采样开关闭合内部电容充电然后断开进行逐位比较。如果外部源阻抗过大RC 充电时间不够采样到的电压就不是输入电压实际值表现为“高阻抗通道读数偏低、随采样周期变化”。这就是 adc 采样原理里最容易被忽略的一点。评估板一般会引出 4 到 8 个模拟通道。下面是一份常见的通道分配表具体网络名以原理图里的 net 为准ADC 通道板载功能网络名示例输入范围备注AIN0板上电位器分压VR_ADC0 ~ 3.3V用于自检AIN1外部电流转电压EXT_4_20MA0.4 ~ 2V250Ω 采样电阻AIN2电池电压分压BAT_ADC0 ~ 3.0V分压比 1/3AIN3温度传感器NTC_ADC0 ~ 3.0VNTC 上拉分配通道时要注意相邻通道的串扰如果 AIN0 走线紧挨着一路 PWM 输出的 GPIO切换采集时容易在转换结果里看到固定跳变。SWM181 的 ADC 采样周期寄存器一般以 ADC 时钟为单位范围从几个到几十个时钟。增大采样周期可以直接补偿高源阻抗造成的采保误差但代价是总吞吐率下降。2.2 原理图里需要优先确认的前端 RC 参数每个 ADC 输入前建议都加一节 RC 低通。RC 的作用一是滤除高频噪声二是在采样瞬间由电容 C 提供电荷。常用做法是串联 100Ω~1kΩ并联 1nF~10nF。R 和 C 的乘积决定时间常数 τ选择时考虑两个约束截止频率要高于信号最高频率避免想采的信号被衰减。对于 10Hz 温度的 NTC1kΩ 1μF 的 τ1ms截止频率 159Hz够用。τ 要远小于 ADC 采样周期否则采样电容充电还没稳定开关就断开了。一般要求 τ Tsamp/20。下面的表给出几组常用参数方便抄作业时做取舍RCτ_rc截止频率适合场景100Ω10nF1 μs159 kHz低速直流电压采集1kΩ10nF10 μs15.9 kHz传感器信号10kΩ100nF1 ms159 Hz大时间常数抗混叠注意R 过大会带来直流压降吗ADC 输入漏电流很小但在高温高湿下不能忽略。评估板一般把前级运放输出端直接接这个 RC以运放的低输出阻抗驱动 RC 网络这是更稳妥的做法。前端 rc 滤波设计里还要看参考电压SWM181CBT6-50 的 VREF 引脚不能直接挂在 3.3V 电源上板上通常会放一个 1μF 钽电容加 100nF 陶瓷电容如果应用对绝对精度有要求会换低温漂基准源并把 100nF 放在离 VREF 引脚最近的位置。2.3 用 Python 检查网络表避免原理图引脚接错拿到 Altium 的原理图导出 Netlist 后不要只肉眼对引脚。可以用一段几十行的 Python 脚本把 ADC 通道的网络名提取出来和分配表对照防止通道悬空或者接错电源。下面是解析 Protel 格式网表的例子import re netlist_file SWM181_ADCB_V1.0.net pin_map {} with open(netlist_file, r, errorsignore) as f: lines f.read().splitlines() cur_net None for line in lines: m re.match(r\[(.*)\], line.strip()) if m: cur_net m.group(1).strip() continue if cur_net and re.match(r^SWM181, line.strip()): # 行末括号里是引脚号这里按实际格式调整 pin_map.setdefault(cur_net, []).append(line.strip()) for net in [VR_ADC, EXT_4_20MA, BAT_ADC, NTC_ADC]: print(net, pin_map.get(net, []))这个脚本不复杂核心是按括号分行识别网络再按器件前缀筛选。跑完能看到每个模拟网络接在 SWM181 的哪个引脚上。如果某一路网络同时出现在两处 ADC 输入或没出现在目标引脚可以立刻发现。实际应用中Altium 导出的网表格式可能带更多转义字符正则要按自己的格式微调思路不变。3. 软件 code采样周期、滤波函数和四通道 DMA 搬运硬件只是基础软件上最容易栽跟头的是初始化顺序和滤波时机的选择。很多人直接把 ADC_Init 放在 while(1) 外面然后循环读得到的值没有经过滤波就直接用于 PID 或通信结果上机就跑飞。先把周期、DMA、滤波放在一起看。3.1 主频与 ADC 时钟的匹配sampleCycle 该怎么调SWM181CBT6-50 主频为 50MHz 时ADC 外设时钟还需要分频。示例代码中常见的是配置 ADC 时钟为 12.5MHz 或 25MHz然后设置采样周期寄存器。下面是基于官方寄存器风格改写的初始化代码函数名以实际 SDK 为准void ADC_SampleCycle_Init(void) { // 1. 使能 ADC 时钟前先复位避免上一次配置残留 SYS_ClockEnable(SYS_CLK_ADC); ADC_InitTypeDef cfg; cfg.channel ADC_CHANNEL0; // 单通道 cfg.sampleCycle ADC_SAMPLE_24CLK; // 采样 24 个 ADC 时钟 cfg.convMode ADC_CONV_SOFTWARE; // 软件触发 cfg.vrefSel ADC_VREF_EXTERNAL; // 使用板上外部基准 ADC_Init(cfg); ADC_ChannelEnable(ADC_CHANNEL0); ADC_Start(); }sampleCycle 参数单位是 ADC 时钟个数。对 50MHz 主频、2 分频得到 25MHz ADC 时钟24 个时钟就是 960ns 采样时间足以应付源阻抗 10kΩ 以下的输入。如果源阻抗是 100kΩ需要把 sampleCycle 提到 64 甚至 128。对应关系可以做成一张速查表源阻抗范围ADC 时钟分频sampleCycle等效采样时间1kΩ4 分频12480ns1kΩ ~ 10kΩ2 分频24960ns10kΩ ~ 100kΩ1 分频1002μs注意采样周期加大会降低最大采样率。如果项目既要高阻抗输入又要高同步速率更实际的做法是加一级运放跟随器而不是一味拉长采样周期。外部基准的精度和温漂同样会影响结果片上内部参考电压温度系数一般够用但不够好评估板上留有外部基准焊盘就是为校准留余地。3.2 c 语言 adc 值滤波函数滑动平均与中值组合评估板资料里的软件 code通常不会只给一个裸读函数。对工业采集场景一个稳定的 adc 值滤波函数比多几路通道更有用。我一般推荐先做一次中值滤波剔除毛刺再做滑动平均平滑。#define ADC_MEDIAN_WIN 5 #define ADC_AVG_WIN 8 static uint16_t median_buf[ADC_MEDIAN_WIN]; static uint16_t avg_buf[ADC_AVG_WIN]; static uint8_t avg_index 0; uint16_t ADC_MedianFilter(uint16_t new_value) { for (int i 0; i ADC_MEDIAN_WIN - 1; i) median_buf[i] median_buf[i 1]; median_buf[ADC_MEDIAN_WIN - 1] new_value; // 拷贝并排序 uint16_t tmp[ADC_MEDIAN_WIN]; for (int i 0; i ADC_MEDIAN_WIN; i) tmp[i] median_buf[i]; for (int i 0; i ADC_MEDIAN_WIN - 1; i) for (int j i 1; j ADC_MEDIAN_WIN; j) if (tmp[j] tmp[i]) { uint16_t t tmp[i]; tmp[i] tmp[j]; tmp[j] t; } return tmp[ADC_MEDIAN_WIN / 2]; } uint16_t ADC_MovingAverage(uint16_t filtered) { avg_buf[avg_index] filtered; avg_index (avg_index 1) % ADC_AVG_WIN; uint32_t sum 0; for (int i 0; i ADC_AVG_WIN; i) sum avg_buf[i]; return (uint16_t)(sum / ADC_AVG_WIN); }中值窗口选 5适合电力工频干扰和偶然尖峰滑动平均的窗口选 8是 2 的幂可以直接移位。两者合在一起后数据流看起来平滑但阶跃响应会慢用于保护逻辑时要额外判断原始值。adc 数据漂移还有一个常见来源参考电源随温度缓慢变化滤波函数解决不了只能在硬件上选低温漂基准或者用第 5 章的三点校准补偿。3.3 adc 四通道使用 dma 的初始化与缓冲切换当需要连续扫描多通道时轮询模式会卡在等待转换结束上比较合适的是 adc 四通道使用 dma。DMA 把 ADC 数据寄存器按顺序搬到内存缓冲区不占用 CPU。这里的关键是缓冲区的长度设计#define ADC_DMA_CH_COUNT 4 #define ADC_DMA_BUF_LEN 64 uint16_t adc_dma_buf[ADC_DMA_CH_COUNT * ADC_DMA_BUF_LEN]; uint16_t adc_dma_buf_backup[ADC_DMA_CH_COUNT * ADC_DMA_BUF_LEN]; volatile uint8_t dma_active_buf 0; void ADC_Setup_DMA_4Ch(void) { ADC_InitTypeDef cfg; cfg.channelMask ADC_CH_MASK0 | ADC_CH_MASK1 | ADC_CH_MASK2 | ADC_CH_MASK3; cfg.convMode ADC_CONV_DMA; cfg.sampleCycle ADC_SAMPLE_24CLK; ADC_Init(cfg); DMA_ConfigTypeDef dma_cfg; dma_cfg.periphAddr (uint32_t)ADC-DATA; dma_cfg.memAddr (uint32_t)adc_dma_buf; dma_cfg.length ADC_DMA_CH_COUNT * ADC_DMA_BUF_LEN; dma_cfg.loopMode DMA_LOOP_ENABLE; DMA_Init(ADC_DMA_CH, dma_cfg); DMA_Start(ADC_DMA_CH); } void ADC_DMA_IRQHandler(void) { if (DMA_GetFlag(DMA_CH_TRANSFER_DONE)) { DMA_ClearFlag(DMA_CH_TRANSFER_DONE); // 双缓冲切换 if (dma_active_buf 0) { DMA_SetMemAddr(ADC_DMA_CH, (uint32_t)adc_dma_buf_backup); dma_active_buf 1; } else { DMA_SetMemAddr(ADC_DMA_CH, (uint32_t)adc_dma_buf); dma_active_buf 0; } } }代码里的长度是4 * 64等于每次 DMA 中断处理 256 次转换结果。ADC_DMA_BUF_LEN最好按采样率和中断频率取整避免在中断里做除法。双缓冲切换能防止数据处理期间缓冲被覆盖。若串口还要发 modbus 帧处理完的adc_dma_buf[]再交给上层的帧发送函数不要在中断里直接调用串口发送否则长帧会拖长中断时间导致下一次 DMA 切换不及时。4. PCB 布局guard ring、铺铜挖空和 Altium 的 DRC 规则原理图和代码只是让 ADC 工作的第一步实际稳定度大部分由 PCB 决定。SWM181CBT6-50 的 ADC 属于逐次逼近型对参考电压、时钟走线和地平面敏感。这一章把评估板里最值得抄的三个布局手段拆开来说。4.1 规避时钟抖动与电源噪声的 3 个 pcb 布局要点第一个要点ADC 参考电压的 VREF 引脚不能直接接在主电源铜皮上。评估板一般用单独的参考电压芯片或从 AGND 分出来的星型网络。VREF 的 1μF 和 100nF 去耦电容要放在距离 VREF 引脚不超过 3mm 的位置GND 过孔垂直落到模拟地平面。第二个要点时钟线、PWM 线和 ADC 输入线不能在相邻层平行超过 5mm。SWM181 的 ADC 时钟来自内部 PLL 或外部晶振路径如果在 Top 层把晶振走线从 ADC 输入旁边穿过时钟抖动会耦合进采样电容转换结果低位持续跳。第三个要点模拟地和数字地单点连接且连接点选在 ADC 地引脚附近。评估板通常用 0Ω 电阻或磁珠把 AGND 和 DGND 隔开连接点下方不要铺整片铜而是露出一小块铜皮让返回电流唯一路径。下面这张表可以贴在工位上看检查项推荐做法判定标准VREF 去耦距离1μF100nF 靠近 VREF 引脚 3mmADC 输入线宽0.2mm~0.3mm尽量短避开高频总线模拟地分界单点 0Ω/磁珠连接连接点靠近 ADC 地采样电容位置靠近 MCU 引脚放置控制 RC 回路面积4.2 Altium 里加 guard ring 的操作步骤与常见误区“guard ring” 不是简单画一圈线它的作用是让模拟输入引脚外围被同电位的地包围减小表面漏电流和耦合电流。Altium 里加 guard ring 的常见做法是在 Top Layer 用 Track 沿 ADC 输入网络外围画一圈闭合图形图形距信号线至少 0.3mm。把这圈 Track 的网络改成 AGND宽度设 0.4mm 左右。在环上间隔 0.5mm 放置过孔过孔网络同样设为 AGND让环和地层形成低阻屏蔽。如果环内有 3.3V 电源走线穿过需要将电源走线移出Guard Ring 内只允许模拟信号和地。需要提醒Guard ring 不能同时作为多路 ADC 输入间的隔离带。多路模拟输入之间更应该用独立的接地走线分开而不是把所有信号都包在一个大环里否则会引入通道间寄生电容。另一个常见误区是把环放在 Keep-Out 层Keep-Out 只影响布线铜箔层上的 guard ring 还是要在 Top/Bottom 层绘制。对于已经铺了大面积 AGND 的情况可以用 Polygon Pour Cutout 在 ADC 输入信号周围挖出一道狭窄的隔离带避免数字返回电流从模拟输入下方穿过这种铺铜挖空手法在混合信号板里比单纯画线更可靠。4.3 用 AD20 的 DRC 报告脚本统计规则冲突在 Altium 里跑 DRC 只是第一步报告密密麻麻眼睛看容易漏。建议直接导出drc_report.txt再用 Python 按规则名统计。DRC 规则本身也要预先设置在 PCB 编辑器的 Design Rules 里把 Clearance 设为 0.2mm信号线宽最小 0.2mm电源铜皮线宽最小 0.5mm。AD20 默认的规则偏保守但模拟区域与数字走线的间距可以单独加一条规则强制到 0.5mm。我一般用下面这个脚本快速核对import re, collections rule_counter collections.Counter() with open(drc_report.txt, r, errorsignore) as fp: for line in fp: # 典型行Clearance Constraint - 12 violation(s) m re.search(r^([^:]?)\s-\s(\d)\sviolation, line) if m: rule_counter[m.group(1).strip()] int(m.group(2)) for rule, total in rule_counter.most_common(): print(f{rule}: {total})实际导出报告格式可能含有制表符这里用-做分隔符读出来就是每个规则对应错误数。比如看到Silk To Solder Mask: 5就说明丝印压焊盘这类问题对 ADC 性能没影响可以最后处理如果Clearance Constraint计数高则要先查 AGND 环路是否足够宽。5. 用外部 adc 三点校准把数据漂移压到 0.1% 以内校准是对 ADC 的偏移、增益和非线性做一次“就地修正”。用高精度电压源分别输入三个标准电压记下对应原始码然后分段线性插值。选点时不要选 0% 和 100%而选量程的 10%、50%、90%避免基准饱和区。SWM181CBT6-50 的 ADC 量程常用 0~3.3V。5.1 三点校准的选点和分段线性插值实现标准点输入电压预期原始码实际原始码低点0.330V409405中点1.650V20472040高点2.970V36843670实现时用一个结构体保存校准表计算阶段直接把原始码映射到电压不做线性拟合再反推省一步浮点运算。typedef struct { uint16_t raw[3]; float volt[3]; } ADC_TripleCal_t; float ADC_TripleCal(ADC_TripleCal_t *cal, uint16_t raw) { if (raw cal-raw[0]) return cal-volt[0]; if (raw cal-raw[2]) return cal-volt[2]; for (int i 0; i 2; i) { if (raw cal-raw[i] raw cal-raw[i 1]) { float k (cal-volt[i 1] - cal-volt[i]) / (float)(cal-raw[i 1] - cal-raw[i]); return cal-volt[i] k * (raw - cal-raw[i]); } } return cal-volt[0]; }代码核心是两段斜率注意(float)强制转换要放在差值上别放在整数除法里否则斜率直接变成 1。校准最多能做三段适合实际曲线“S”形比较明显的器件。如果系统不允许浮点运算可以把斜率放大 1024 倍存成 int最后右移 10 位代价是精度损失不到 0.1%。验证方法输入 1.000V 和 2.000V 两个校验点分别采集 100 次最大值减最小值除以中值如果小于 0.1%就说明采样周期、PCB 和校准这三层都对了。如果还差一点可以先把滤波窗口加回中值滑动平均再跑一次同一个校验点。整个评估板的调试顺序就是先看原理图 RC再调采样周期然后守好 PCB 布局最后校准。本文还有配套的精品资源点击获取
返回列表