用CD74HC4067扩展ADC通道:从原理到踩坑实战

发布时间:2026/9/4 15:48:02
用CD74HC4067扩展ADC通道:从原理到踩坑实战 1. 为什么我会在项目里选用CD74HC4067一个被成本逼出来的方案做嵌入式开发的朋友应该都有过这种经历产品定义阶段拍脑袋定了一堆模拟量采集需求等到画原理图的时候才发现MCU的ADC引脚根本不够用。我这次做的项目就是典型场景——一块主控板需要同时采集16路模拟电压信号包括多组电位器位置反馈、电流采样、温度传感器输出而主控芯片选的是STM32F103系列片上ADC只有10个输入通道还要留出几路给数字量复用检测直接采集16路根本不现实。换一颗ADC通道更多的MCU选型成本、采购周期、PCB改动全都要推倒重来。外挂一颗16通道ADC芯片比如ADS1115这类高精度ADC单颗价格十几块甚至几十块配合外围电路成本更高。这时候模拟多路复用器就成了最经济的选择。我选的就是TI的CD74HC4067一颗16通道单端模拟多路复用器/解复用器。它的原理可以理解成一个单刀16掷开关MCU通过4根地址线S0-S3选择16个通道中的任意一路把该通道的模拟信号导通到公共端再送进MCU的ADC引脚采样。价格便宜到可以忽略不计单颗几毛钱到一两块钱而且逻辑控制简单4根GPIO就能控制16路剩下的引脚资源开销非常小。这颗芯片的本质是HC系列CMOS模拟开关导通电阻Ron在供电5V时典型值大约70Ω信号带宽很高适合低频模拟信号切换。我先说结论如果你的项目里ADC输入源都是电压信号对采样率要求不算苛刻比如每路每秒几十次到几百次并且想用最低成本把ADC通道数量扩出来CD74HC4067是非常值得考虑的选择。但这里我要提前泼一盆冷水这颗芯片用起来简单坑也埋得不少。我这次调试过程中踩了两个比较隐蔽的坑一个把我折腾到怀疑芯片本身有问题另一个差点让我在ADC采集值不准的泥潭里出不来。这两个坑的排查过程和最终结论放到本文第四、五部分详细复盘先把基础设计吃透再说。2. 芯片底层的开关逻辑与选型思路搞懂导通特性和地址控制的细节2.1 内部结构和逻辑真值表CD74HC4067的封装一般是SOIC-24或TSSOP-24引脚包括16路输入/输出通道X0-X15、一个公共端X4根地址选择线S0-S3一个使能引脚E以及电源VCC和地GND。工作逻辑非常简单当E引脚为低电平时芯片正常工作S0-S3组成的4位二进制数对应选中相应通道当E引脚为高电平时所有通道全部断开公共端处于高阻状态。真值表就是0-15的二进制编码对应X0-X15这里不再整表罗列记住4根线16种组合就够了。有一个细节容易被人忽略CD74HC4067作为模拟开关信号是双向导通的。也就是说它不是只能从通道往公共端传反过来从公共端往某个通道传也完全没问题。这意味着这颗芯片不仅能做ADC输入扩展也能做多路DAC输出切换、多路通信信号切换等。我这次就是单纯用来做ADC前端切换但如果你后续有类似需求不用再去额外找芯片了。2.2 为什么需要关注导通电阻CD74HC4067的导通电阻不是一个固定值它和电源电压、输入信号电压范围都有关系。从芯片手册里的特性曲线可以看到VCC越低Ron越高输入信号电压越接近电源轨导通电阻也会明显上升。我实际测试的供电条件是5V供电输入信号范围0-3.3V这时候Ron大体在50Ω到80Ω之间波动。这个电阻对ADC采集的影响主要体现在分压效应上如果信号源内阻比较低比如运放输出、稳压源输出几十欧姆的Ron几乎不影响采样精度但如果信号源内阻很高比如直接接一个10KΩ电位器分压或者高阻传感器输出Ron和源内阻会形成分压导致ADC读到的电压偏低。我见过有人在低内阻源场景下完全忽略Ron也有人在高内阻场景下被这个电阻坑到读数偏差好几个百分点。具体到自己的项目务必估算一下信号源等效内阻再决定是否需要做阻抗匹配或者加一级运放缓冲。2.3 相邻通道之间的隔离度指标另一个值得关注的手册参数是通道间隔离度和关断隔离度。CD74HC4067在1MHz频率下的串扰隔离典型值大概在-50dB左右对于直流或低频慢变信号来说这个隔离度完全够用。但如果你的信号里混有高频噪声相邻通道之间可能会出现一定串扰。实测经验是如果两路信号幅度差异非常大比如一路5V一路0.1V通道切换后需要给信号一点稳定时间否则微小的电荷注入和串扰会影响采样值。解决办法后面详细讲这里先记住结论。3. 从原理图到PCB的完整接线方案16路信号怎么连才能少踩坑3.1 参考原理图的关键节点我这次的设计比较典型直接说接线要点VCC接5VGND接数字地。注意CD74HC4067是CMOS器件电源去耦电容尽量靠近VCC引脚我用的是100nF配合10μF电解电容组合。公共端X引脚通过一个RC滤波网络接MCU的ADC输入引脚。我用的RC是100Ω串联电阻加100nF对地电容截止频率大约16kHz对于慢变模拟信号足够同时能滤掉部分开关切换时产生的毛刺。16路通道引脚分别接各路传感器信号。值得注意的是如果某些通道暂时不用不要悬空最好通过10KΩ电阻下拉到地避免杂散耦合导致公共端信号异常。地址线S0-S3和使能E全部接MCU的GPIO推挽输出。由于CMOS输入阻抗极高不需要串联电阻但建议在靠近芯片处放置33Ω或47Ω的限流电阻防止热插拔或异常状态下损伤引脚这个属于老工程师的习惯性操作。这里有个常见的资源优化技巧如果你MCU引脚本身就紧张地址线和使能端可以挂在已有的SPI或I2C引脚上复用——前提是在采样期间这些总线不被占用GPIO模式切换用软件控制即可。我就是把S0-S3接到了原本用于调试的一组闲置引脚上省下了4根GPIO。3.2 PCB布局走线里的关键细节PCB设计上我最想强调的一点是一旦16路信号汇聚到一颗芯片公共端引脚到MCU ADC引脚的这段走线就成了整个系统最敏感的模拟通路。我的建议是公共端信号线尽量短而直线宽可以稍微放宽到10-15mil减少走线电阻和寄生电感。模拟信号区域的地平面必须完整不要在公共端下方走数字信号线。电源去耦电容的地端用独立过孔直接打到底层地平面不要和模拟走线共用回路。地址线和模拟输入通道之间保持距离避免数字翻转时通过邻近耦合干扰模拟信号。如果你在两层板上做这个设计至少保证模拟信号区域底层是完整地平面否则高频噪声很可能会从地回路串进ADC。3.3 多通道通道映射的软件规划硬件连好之后建议在代码里建立一张通道映射表。比如通道0对应电位器1、通道1对应电流采样A、通道3对应温度传感器B等等不要直接在业务代码里散落地写切到通道7等过两周你自己都记不住通道7接的是什么。我用的是一个简单的const数组// 通道映射表索引为逻辑通道号值为CD74HC4067地址 const uint8_t ch_map[16] { 0x00, // CH0 - 电位器1 0x01, // CH1 - 电位器2 0x02, // CH2 - 电流采样A 0x03, // CH3 - 温度传感器B 0x04, // CH4 - 备用 0x05, // CH5 - 电压监测VCC // ... 其他通道 };这样将来硬件改版换通道只改这一张表业务层完全不用动。这属于软件架构和硬件设计配合的基建工作前期多花10分钟后面省下半天排查时间。4. 实测踩坑之一切换通道之后ADC读到的值总是在异常跳动4.1 现象描述我把整个采集链路搭通之后第一轮在线调试就发现了问题。程序每100ms轮流采样16个通道通过串口把16路ADC值打印出来。现象是通道切换后读到的前几个原生值是乱的尤其是从大电压信号通道切到小电压信号通道时小电压通道的前几次采样值明显偏高而且不稳定跳动幅度能到满量程的2%-3%。等稳定几百毫秒后数值才会落到正常区间。一开始我以为是CD74HC4067质量不行甚至怀疑买到了假片。但换了三颗芯片、两块板子现象几乎一模一样。这时候我才冷静下来认真分析这个切换后前几帧数据异常到底是什么原因。4.2 根因定位输出建立时间和采样时机我拿示波器直接点到CD74HC4067公共端X引脚上观察通道切换瞬间的波形变化。真相其实不复杂——当S0-S3地址线变化时模拟开关的通道导通切换不是瞬间完成的。芯片内部存在一个开关动作时间和信号建立时间宏观看器件手册里给出的tON和tOFF大约在几十纳秒级别听起来非常快但问题在于你还要考虑信号源本身的驱动能力、公共端到ADC之间的RC滤波网络、以及ADC采样保持电容的充电需求。我当时在公共端X和MCU的ADC引脚之间串联了一个100Ω电阻和100nF电容构成的RC滤波器时间常数τRC10μs。MCU内部ADC采样保持电容充电也需要时间STM32F103的采样时间最短可以配1.5个ADC时钟周期大约0.3μs左右但由于外部RC的存在信号实际建立到稳定的时间被拉长了。这就意味着GPIO切换地址线之后我立刻启动ADC采样芯片开关导通了但公共端电压还在建立过程中这时候ADC采到的自然是一个过渡态的电压值而不是目标通道的稳定电压。4.3 解决手段通道切换后的延时与首帧丢弃策略找到问题后解决办法非常直接切换通道和启动ADC采样之间插入一段延时让信号稳定。具体延时多少取决于你的RC时间常数和需要的精度。我实测下来在100Ω加100nF的RC配置下延时1ms后采样已经非常稳定。如果你希望更保险可以延时5ms。但对于快变信号场景1ms的建立时间可能太奢侈那就需要减小RC值并在程序上做多帧采样取平均。另一个更加稳健的做法是丢弃首帧法每次切换通道后先做一次ADC转换并丢弃结果紧接着做第二轮转换才作为有效数据。这样既不会引入额外延时又能确保采样的是建立稳定后的信号。我最终采用的就是延时300μs 丢弃第一帧 连续采4帧取平均的策略效果非常好16路数据的稳定性达到了直接采集时同一水平。这个小坑的教训凡是切换-采样模式的架构切换动作和采样动作之间一定要留出建立时间不要相信引脚电平变了芯片输出就立刻稳定的直觉。RC网络、源阻抗、ADC采样电容三者叠加建立时间可能比你想象的慢得多。5. 实测踩坑之二16路ADC数值整体偏高换通道后通道间互相干扰5.1 奇怪的串扰现象第二个坑更隐蔽。在我把第一个坑解决之后数据表面上看已经相当稳定但拿万用表实测各通道输入电压和ADC显示值对比时发现所有通道的ADC读数普遍偏高大约比真实电压高出1.5%到2%。更诡异的是某些通道之间出现明显的串扰——通道0接了一个2.5V电压通道1接了一个0.5V电压单独测通道1时数据显示0.51V左右但通道0也接入后通道1的读数会飘到0.55V到0.57V。我当时第一个反应是CD74HC4067的通道隔离度指标不够但手册上的串扰数据明明不至于导致这么明显的误差。而且如果真是芯片隔离度问题往往频率越高越明显我这里全是直流低频信号为什么会出现这么显著的耦合5.2 排查过程从芯片隔离度到未选通通道的悬空效应顺着串扰这条线我花了不少时间甚至用示波器反复观察各通道波形但始终没找到根源。后来我把CD74HC4067从板子上拆下来直接飞线搭了一个简易测试电路才慢慢锁定了问题。问题出在未选通通道的引脚状态上。我的16路输入信号中有几路传感器输出源是高阻性质的比如某款温湿度传感器模块的输出端在未供电或输出模式下呈现高阻状态还有一路电位器分压是通过100KΩ电阻分压得到的等效源内阻非常高。这些高阻信号接在CD74HC4067的未选通通道上时由于芯片内部分布电容和通道间寄生电容的存在未选通通道上的电压会通过寄生电容耦合到公共端从而影响当前采样通道的电压。换句话说不是芯片通道间的隔离失效而是信号源本身太弱了——高内阻源的信号线就像一个浮空节点和公共端之间存在容性耦合路径最终串入了ADC采样结果。我再回头看自己的PCB布局公共端信号线在板子上走了比较长的路径和几路未选通通道的走线有一段是平行靠近的。这段走线间的寄生电容被高内阻源驱动形成了一条串扰路径。5.3 最终修复从高阻源头和走线布局两边同时下手修复方案分了两步。第一步也是立即见效的一步对于高内阻信号源在进入CD74HC4067通道引脚之前先做一级电压跟随缓冲。我用的是LMV321单运放接成同相跟随器输出阻抗降低到几十欧姆以下。这样即便通道未选通由于电压跟随器输出是低阻的寄生电容耦合的贡献也变得微乎其微。第二步修改PCB布局。把公共端信号线和通道走线之间的平行间距加大并且在公共端信号线两侧增加地线屏蔽。虽然改版还需要时间但第一版飞线上验证了效果数据串扰问题彻底消失所有通道的ADC读数回落到真实电压的0.5%误差范围内。这个坑让我最终总结出一个经验用多路复用器扩展ADC时信号源阻抗是比芯片通道隔离度更值得关注的参数。通道间的寄生电容是物理上必然存在的高压摆率信号和高阻源凑在一起串扰一定会出现。学到的应对策略就是高阻信号在入复用器之前必须做好阻抗变换这是绕不开的硬件底线。6. 多通道轮询采样的软件框架从裸机轮询到定时器触发第一个坑修的是信号建立时间第二个坑修的是硬件阻抗设计但既然16路通道是靠4根GPIO来选的软件层的轮询策略也必须设计得当。这部分我直接把实际工程中的代码逻辑分享出来。6.1 裸机轮询模式的实现要点如果系统的任务不复杂最简单的做法就是一个while循环里周期性地轮询16个通道。伪代码逻辑如下void adc_poll_all_channels(void) { for (uint8_t i 0; i 16; i) { // 1. 切换通道 set_mux_channel(ch_map[i]); // 2. 等待信号建立 delay_us(300); // 3. 丢弃第一帧不稳定数据 adc_read_raw(); // 4. 连续采样4次取平均 uint32_t sum 0; for (uint8_t j 0; j 4; j) { sum adc_read_raw(); } g_adc_result[i] sum / 4; } }这个实现思路很直白但需要注意一个细节如果系统里还有其他任务在跑比如LED刷新、按键扫描、通信协议处理这个轮询函数会被阻塞性调用拖住导致其他任务响应不及时。我的做法是把轮询逻辑放到定时器中断或RTOS的任务里保证采集周期稳定。6.2 在定时器中断中分时采集的思路更好的做法是不要在一个周期内一次性把16路全采完而是每个定时器周期只采集一路。比如配置一个1ms的定时器中断中断里负责切换通道、启动ADC转换ADC转换完成中断里读取结果并累加每16次定时器周期完成一轮全部通道的采集。这样系统的负载非常均衡不会出现一个长时间阻塞的采样函数。void timer_isr(void) { // 每1ms进入一次切换一个通道并触发ADC采样 current_channel (current_channel 1) % 16; set_mux_channel(ch_map[current_channel]); adc_start_conversion(); } void adc_complete_isr(void) { uint16_t value adc_get_result(); // 第一帧丢弃或做简单滤波 g_adc_raw[current_channel] value; }这种分布式的采样策略在实时性要求稍高的场合非常重要。比如某个通道需要每1ms更新一次而其他通道只需要每100ms更新一次那你完全可以动态调整每个通道的采样频率让定时器中断为不同通道设置不同的采样周期。用CD74HC4067之后采集时序的调度权完全掌握在软件手里这是比固定外部ADC芯片更灵活的一点。7. 精度优化与滤波处理ADC值不能直接用代码层还要做几件事7.1 原生ADC值的滤波方法16路模拟信号经过多路复用器和MCU的ADC转换后得到的原始值直接拿来做控制或显示往往是不够的。多路复用器切换瞬间的毛刺、电源纹波、传感器噪声都会在ADC结果里留下痕迹。我建议至少保留一级软件滤波。工程上我用得比较稳的方案是中位值平均滤波连续采样5到7次去掉一个最大值和一个最小值剩下的取平均值。这个滤波器对脉冲式干扰的抑制效果非常好代价是计算量稍微大一点但对于STM32F103主频72MHz来说完全不是问题。uint16_t median_average_filter(uint16_t *buf, uint8_t len) { // 简单选择排序去掉最大最小值后取平均 for (uint8_t i 0; i len - 1; i) { for (uint8_t j i 1; j len; j) { if (buf[j] buf[i]) { uint16_t tmp buf[i]; buf[i] buf[j]; buf[j] tmp; } } } uint32_t sum 0; for (uint8_t i 1; i len - 1; i) { sum buf[i]; } return (uint16_t)(sum / (len - 2)); }如果你的系统对响应速度要求高可以用一阶低通滤波也叫滑动平均滤波或惯性滤波g_filtered_value g_filtered_value * 0.8 new_value * 0.2;这个公式理解起来很简单新采样值只贡献20%的权重历史值占80%整体输出被平滑了。系数可以根据信号变化速度调整变化越快的信号新值权重可以调大一些。7.2 基准电压与校准ADC采集精度的一半取决于基准电压。STM32F103内部的参考电压漂移在温度和电压变化时比较明显如果你的应用对精度要求比较高建议使用外部基准电压源比如REF3025这类2.5V基准芯片接到VREF引脚。我做的是比较务实的方案板上留一路基准电压通过固定电阻分压得到精确的1.024V或1.25V接到CD74HC4067的一个通道。每次上电后MCU先采集这路参考电压反推当前的实际VREF和ADC增益误差然后在软件里做线性校准。校准公式// adc_ref_value为基准通道的实际ADC采样值 // true_voltage为已知基准电压例如1.024V float vref_actual (float)adc_ref_value * 3.3f / 4096.0f; float gain_scale true_voltage / vref_actual; float calibrated_voltage (float)adc_raw_value * 3.3f / 4096.0f * gain_scale;如果追求更精确可以在两个不同电压点做两点校准拟合出斜率和截距。但我的项目里单点校准已经能把误差控制在1%以内够用就好。8. 这个方案之外还有哪些可选的ADC扩展路线写到这里顺便把我在选型阶段对比过的几种方案列出来供你们参考。不同方案的取舍直接决定了产品成本和维护难度。对比维度方案成本通道数精度采样速度软件复杂度适用场景CD74HC4067 MCU内部ADC极低16可级联更多受限于内部ADC通常12bit每路轮流采集速度较慢低低频慢变信号、对成本敏感的批量产品外部独立ADC芯片如ADS1115中等通常4-8路差分/单端16bitI2C速度限制典型860SPS低需要高精度、对速度要求不高的场景外部SPI接口ADC如ADS1256较高8路差分/16路单端24bit高速最高30kSPS中等高精度高速采集、地震仪/工业仪表MCU内部多ADC轮流触发如STM32G4系列无额外成本取决于芯片型号12bit快低引脚够用、不想加外部器件的场景CD74HC4067方案的最大优势就是便宜大碗用极低成本把普通MCU变成一个16路甚至更多路的采集系统。缺点是单路采样率上不去而且通道切换需要软件打理建立时间。如果你的信号频率高比如音频采样、振动分析就该考虑直接换多通道ADC芯片不要在4067上硬撑。9. 关于CD74HC4067这个方案的完整经验总结回到最初的选型逻辑这个方案的灵魂是用GPIO换ADC通道数。如果你的产品形态允许接受逐通道轮询采样并且传感器信号本身是慢变的直流信号CD74HC4067几乎是最优解。我经历过的这些坑归纳起来就是三句话通道切换后要给模拟信号留出稳定时间通过延时加丢弃首帧的方法解决。高阻抗信号源入复用器之前一定要做阻抗变换否则通道间寄生电容会带来不明显的串扰。ADC原始值需要配合软件滤波和基准校准才能达到工程实用精度。这套配置在项目里稳定运行了几个月16路通道的数据一直保持在合理的误差范围内没有出现让我再回头动硬件的情况。如果你手头的项目也面临ADC通道不够用的问题先不要急着换主控或者外挂昂贵ADC芯片认真评估一下信号的频率和阻抗特性如果合适一块CD74HC4067就能把问题解决掉成本低效果直接只是在硬件设计细节上多下点功夫结果就不会让人失望。最后分享一个小技巧这颗芯片的第15通道是复用器地址码0x0F我喜欢把基准电压固定在15通道这样代码里每次上电校准的目标位置永远是确定的不会因为传感器通道增减而打乱校准逻辑。类似的灵巧用法你可以在实际项目中慢慢积累。