用CD74HC4067模拟多路复用器将ADC扩展至16路:原理、电路与调试
2026/9/5 5:35:40 网站建设 项目流程

裸板调ADC通道不够用,这大概是做嵌入式采集绕不开的坎。最近一个项目里MCU自带ADC只有8个外部通道,板子上偏偏要采16路模拟量,既不想换大封装芯片,也不想加一颗独立ADC芯片增加BOM成本。翻了翻库存,正好有一片CD74HC4067,16通道模拟多路复用器,几块钱的东西,直接用它把ADC口扩展成16路,效果还算理想。这块电路本身不复杂,真正费时间的反而是两个看起来不起眼的调试坑。这篇笔记把方案思路、接线方法、驱动逻辑和排查过程都整理出来,给后面要做类似扩展的朋友一个参考。

1. 方案选型与整体设计思路

1.1 为什么选择CD74HC4067而不是其他方案

扩展ADC通道的方案其实不止一种,常见的有三:换大资源MCU、加外部独立ADC芯片、用模拟多路开关切换。前两种成本高或者周期长,对于项目里只差几个通道的场景不划算。第三种方案里,CD74HC4067属于非常经典的16选1模拟开关,导通电阻典型值在70欧姆左右,带宽40MHz以上,开关切换时间在几十纳秒级别,用来切低频模拟信号完全够用。

和同为多路开关的CD4051(8通道)相比,CD74HC4067用两片才能凑够16路,控制复杂度和PCB面积都上去了;和带I2C接口的专用ADC扩展芯片比如ADS1115相比,CD74HC4067是纯模拟开关,不参与量化,只要后级ADC性能不变,整体采样精度就主要取决于开关的导通电阻和漏电流,可控性更强。几毛钱到一两块钱一片的单价,在量产项目里也有明显成本优势。

选型时的另一个考量点是它支持双向传输,输入输出可以互换使用,除了扩展ADC输入,还能用来复用GPIO、切换I2C或SPI总线等场景,属于比较通用的基础器件。虽然我这次只用在模拟采集通道扩展上,但后续如果项目里需要扩展其他接口,这块板子还能继续复用。

1.2 CD74HC4067的核心参数与引脚功能

先过一遍这颗芯片的关键参数,选型和排查问题时都用得上:

参数数值说明
通道数量16通道16选1结构,单端输入
导通电阻RON典型70Ω随供电电压和温度变化,5V供电时约40~80Ω
漏电流ILEAK典型±100nA高温下会增大,高阻源信号要注意
工作电压VCC2V~6V常用3.3V或5V供电
切换时间tON/tOFF典型20~40ns远快于ADC采样周期,几乎不用考虑
串扰CROSSTALK-50dB级别相邻通道在高频信号下会更明显
封装SOIC-24 / TSSOP-24TSSOP焊接难度稍高

引脚功能需要重点记几个:S0到S3是通道选择地址线,对应二进制0000到1111,比如S3S2S1S0=0000时选通COM与CH0,=1111时选通CH15。EN是使能脚,低电平有效,拉高时所有通道关闭,输出呈高阻。COM是公共端,接到MCU的ADC输入引脚,16路输入信号分别接到CH0~CH15。VCC和GND为芯片供电,另外还有VEE引脚,如果信号有负电压才需要接负电源,本项目只采单极性0~3.3V信号,VEE直接接地即可。

1.3 整体架构框图

整个采集链路的结构线如下:

传感器/外部信号(16路模拟电压)→ CD74HC4067的CH0~CH15 → COM端输出选通信号 → MCU ADC引脚 → DMA搬运结果 → 软件组包上报

MCU的3个普通GPIO(如果加上EN就是4个)分别接S0、S1、S2、S3,通过软件查表方式控制通道切换。DMA模式下,采集过程完全由定时器触发,每次切换通道后启动一次采样,全部16通道扫描一遍后再统一处理数据。当然,不用DMA也行,用软件轮询方式依次切换和读取,逻辑更简单,只是CPU占用高一些。后面会给出两种方式的对比。

2. 硬件电路连接与PCB设计要点

2.1 最小系统接线方案

我这次用的是STM32F103系列MCU,3.3V供电。连接方式如下:

  • VCC接3.3V,GND接系统地;
  • VEE接地(无负压信号);
  • S0、S1、S2、S3分别接MCU的四个GPIO,任意引脚即可,软件里用数组映射;
  • EN接MCU的另一个GPIO,低电平使能;
  • CH0~CH15接16路待测信号,信号幅度不超过VCC范围;
  • COM引脚接到MCU的ADC输入通道,比如PC0对应ADC_IN10。

硬件上要点不多,但有几个容易忽略的细节值得提一下。

2.2 输入信号阻抗匹配与缓冲问题

CD74HC4067的导通电阻不是理想0欧姆,后级ADC采样保持电容在采样瞬间会从信号源抽取电荷,如果信号源内阻较高,RON和信号源内阻串联分压会造成明显的采样误差。比如信号源内阻10k欧姆,RON取70欧姆时影响还不太明显,但如果信号源内阻到了100k甚至M欧姆级别,误差就大了。

处理办法有两个:一是信号进CD74HC4067之前先用运放跟随器做阻抗变换,低阻抗驱动开关,再从COM端直接进ADC;二是如果信号本身已经经过传感器调理电路输出,内阻通常在几百欧姆以内,可接受直接连接。我这次用的信号都是电压型传感器输出,内阻约1k欧姆,实测直接接没问题。

另外一个容易被忽视的点是COM端到MCU ADC引脚之间的走线长度尽量短,不要超过2厘米,并做包地处理,避免引入噪声。如果ADC引脚旁边还有其他高速数字信号线,建议在布局时保持距离,串扰会影响采样稳定性。

2.3 供电滤波与参考电压稳定性

模拟电路对电源纹波敏感,CD74HC4067的供电VCC纹波会通过开关导通特性耦合到信号链路上。建议在VCC引脚就近放一个0.1uF陶瓷电容,如果板子上有模拟电源和数字电源分区,VCC接模拟电源域更好。我这里由于板子已经定型,VCC和MCU的数字3.3V共用,实测在ADC采样数据低位有1~2个字跳动,后来在VCC处加了个磁珠隔离,效果有所改善,但不加也能接受。

ADC参考电压的稳定性同样重要,如果用MCU内部参考,建议确保VREF引脚电容到位(典型0.1uF+1uF组合),不要省。如果批量产品对精度有要求,可以考虑外置基准如REF3033或TL431,但这个取决于项目精度需求,不是必须项。

2.4 硬件焊接与常见连接错误检查

焊接这类引脚间距1.27mm或0.65mm的芯片时,焊接完毕务必用万用表蜂鸣档检查相邻引脚间有无锡连,尤其S0到S3四条地址线和COM线之间桥接会导致通道错乱,排查会非常费劲。

第一次调试时最容易犯的错误是EN引脚悬空。悬空状态下EN内部可能受到干扰,造成通道随机切换,表现就是读出来的ADC值随机跳变。实测中遇到过这个问题,把EN接低电平后症状消失。所以即使不需要控制使能,也建议将EN直接接地,别让它悬空。

3. 软件驱动设计与采集流程

3.1 四种GPIO组合控制16通道切换

CD74HC4067通道选择的本质是4位二进制码。实现时直接用数组建立通道号到GPIO状态的映射,避免每次切换都做位运算,代码可读性更好。以一个伪代码示例说明:

#define CH0 0 #define CH1 1 // ... 以此类推到 CH15 const uint16_t mux_channel_table[16] = { (0 << 12) | (0 << 8) | (0 << 4) | 0, // CH0: S3=0 S2=0 S1=0 S0=0 (0 << 12) | (0 << 8) | (0 << 4) | 1, // CH1: S3=0 S2=0 S1=0 S0=1 // ... 省略中间项 (1 << 12) | (1 << 8) | (1 << 4) | 1, // CH15: S3=1 S2=1 S1=1 S0=1 }; void mux_select_channel(uint8_t ch) { uint16_t val = mux_channel_table[ch]; GPIO_WriteBit(MUX_S3_PORT, MUX_S3_PIN, (val >> 12) & 0x01); GPIO_WriteBit(MUX_S2_PORT, MUX_S2_PIN, (val >> 8) & 0x01); GPIO_WriteBit(MUX_S1_PORT, MUX_S1_PIN, (val >> 4) & 0x01); GPIO_WriteBit(MUX_S0_PORT, MUX_S0_PIN, val & 0x01); }

工程上当然可以直接给GPIO的ODR寄存器赋值,但为了代码可移植性,使用标准库或HAL库的读写函数更稳妥。速度上每次通道切换不过几个微秒,对采集周期没影响,不必过度优化。

3.2 轮询采集和DMA采集两种实现对比

轮询方式适合采集频率不高、CPU资源充裕的场景。伪代码如下:

uint16_t adc_values[16]; void adc_scan_polling(void) { for (uint8_t i = 0; i < 16; i++) { mux_select_channel(i); delay_us(50); // 等待通道稳定 adc_values[i] = adc_read_single(); } }

DMA方式适合需要连续后台采集、CPU不被采集流程占用的场景。结合定时器触发ADC转换,每次转换完成通过DMA搬运到内存,任务只对数组做后处理。这种方式的时序思路是:

  1. 开启定时器更新中断或ADC转换完成中断;
  2. 在中断服务函数里切换MUX通道号,更新GPIO;
  3. 下一次定时器触发时,ADC采样的是刚切换好的通道;
  4. 16次采样完成后,软件置一个标志位,主循环处理这批数据。

这种方式要注意一点:S0~S3的GPIO切换需要一点点时间,定时器触发周期必须大于GPIO切换时间加上CD74HC4067开关建立时间,实际取50微秒以上非常稳妥,最大扫描周期就是16×50us=800us,即1kHz扫描频率,对多数传感器采集场景足够。

3.3 启动初始化流程细节

初始化顺序建议:先配置MUX控制GPIO为推挽输出,拉高EN引脚;再初始化ADC和DMA;最后将EN拉低使能MUX。这样可以避免上电瞬间或初始化过程中意外选通通道导致ADC读到不确定电平。

一个比较隐蔽的问题是:MCU复位后GPIO默认状态不确定,S0~S3可能处于任意组合,此时COM端已经随机选通某一路。如果EN默认低电平使能,外部待测信号会直接灌入ADC引脚,轻则采到错误值,重则影响ADC内部采样电容的初始状态。所以EN默认拉高、后使能这个顺序建议严格遵守。

3.4 多通道数据排序与对齐

DMA采集完成后,内存里的数据是按一次触发一个值的顺序排列的,但通道切换和ADC转换之间有固定延迟,需要在软件里维护一个当前通道号变量,每次DMA传输完成或转换完成中断时自增并回绕到0。数据排列顺序和通道映射表保持一致,组包时直接按顺序填充即可。

如果采集过程中还混入了其他中断或任务延迟,通道号变量和实际采样值之间可能出现错位。排查方法是人为给某一通道加一个偏移电压(比如CH5接3.0V),看数据里对应位置是否稳定出现大值。这个方法在调试多通道时非常好用。

4. 实操过程与关键验证记录

4.1 我的实际电路搭建与测量数据

我把CD74HC4067用在了一块基于STM32F103C8T6的采集板上,电源3.3V,VEE接地,COM接PC0(ADC_IN10),S0~S3接PB0~PB3,EN接PB4。16路输入信号分别用可调稳压源给不同电压,范围从0.1V到3.2V。

测量结果(每路采样64次取平均):

通道设定电压(V)ADC原始值计算电压(V)误差(mV)
CH00.1001320.1066
CH10.5006210.497-3
CH21.00012470.998-2
CH31.60020051.6044
CH42.20027462.197-3
CH53.20040003.2000

12位ADC满量程电压按3.3V计算,每位约0.806mV,实测误差在±6mV以内,折算约±7个LSB。这个结果对普通电压采集完全可用。

4.2 通道切换稳定时间实测

我专门用逻辑分析仪测过从GPIO切换信号发出到ADC引脚电平稳定的时间。实测在1k欧姆信号源内阻条件下,通道切换后约10微秒左右电平稳定在最终值1LSB范围内。如果信号源内阻增大到100k欧姆,稳定时间会超过50微秒,此时50微秒的延时就不太够了,建议加长到200微秒或者加运放缓冲。

考虑到不同的信号源阻抗差异非常大,建议在固件里保留一个可配置的稳定延时参数,产品定型后根据实测标定。不要图省事直接把延时写死成最小值,一旦换了传感器类型就要改固件,不利于维护。

4.3 长时间运行稳定性测试结果

连续运行24小时采集中,观察16路数据最大漂移约±12mV,大部分通道在±8mV以内,漂移来源主要是环境温度和参考电压的缓慢变化,不影响系统功能。中间有段时间数据偶发出现单次大的跳变(超过50mV),后来定位为附近大功率设备启停导致的电源干扰,加粗了地线并优化了滤波电容布局后明显改善。

如果产品对长期漂移敏感,建议在固件里加入周期性自校准逻辑,在板子上留一路精准参考电压输入(比如2.5V基准),每次开机或定时采集这路并修正增益误差。

5. 两个调试坑的完整排查过程

5.1 坑一:采样值随机跳变,通道对不上

现象是最开始调试时无论选择哪一路,ADC读到的值都频繁在几个数值之间跳变,而且切换通道后读到的数据像是混了前一通道的残余值,感觉通道之间有串扰。排查过程:

第一步用万用表直接量CH0输入电压和COM引脚电压,发现COM引脚电压确实等于CH0电压,说明硬件链路是通的。第二步把S0~S3用杜邦线直接接地或接3.3V固定组合,发现ADC读数稳定了,说明问题出在GPIO切换过程。

第三步示波器挂到S0引脚上观察切换瞬间波形,发现GPIO从低到高跳变后,S0引脚电平出现了一串明显振铃,幅度接近1V,持续约300纳秒。虽然CD74HC4067本身能容忍一定振铃,但ADC触发采样的时刻如果正好落在振铃区间,采集到的信号就不是稳定信号。

根因找到了:S0~S3的GPIO线走线过长且没有串接电阻,走线电感和引脚电容形成LC振铃。解决办法是在每个控制引脚靠近MUX处串联33欧姆电阻,并在S0~S3引脚与GND之间加10pF小电容,把振铃抑制到200mV以内。实际改动后采样值变得稳定,通道间串扰也基本消失。

如果复现这类问题,建议先去查控制信号波形,不要一开始就怀疑ADC配置。ADC采到的数据只是结果的表象,控制信号上的毛刺往往才是幕后元凶。

5.2 坑二:首采数据异常,之后正常

第二个问题更隐蔽,通道切换后第一次采样值总是不对,偏小或偏大几十个LSB,但同一通道第二次、第三次采样就恢复了正常值。一开始以为是延时不够,把延时从50微秒调到200微秒,仍然只有第一次值异常。

后来翻了MCU参考手册,注意力落到ADC采样保持电容的充放电过程上。ADC在每次采样时,内部采样电容需要从外部信号源抽取电荷,如果外部信号源内阻较大,采样电容无法在前一次转换结束后完全恢复到与当前输入信号一致的电平,首次采样的结果就会实际偏小。多采几次后电容逐步逼近稳定值,表现就是首采异常。

解决办法也很直接:通道切换后连续启动两次ADC转换,第一次结果直接丢弃,保留第二次结果。由于第二次采样时采样电容已经预充到接近当前通道电压,结果就会准确。这个方法在STM32的多个型号上都适用,实现成本几乎为零。

5.3 现场调试其他小坑记录

还有一个容易忽略的是S0~S3的线序反接问题。我们画PCB时,S0、S1、S2、S3从MCU引出后在板子上交叉了一次,导致通道选择表和实际选通的通道错位。排查时用固定电压逐通道扫描,画出一张实际通道对应关系表,反过来就定位了。这类问题原理上很简单,但因为PCB丝印不明显,往往会浪费不少时间。

建议在硬件调试文档里专门建一个表格,记录MCU GPIO、MUX S0~S3、通道号三者之间的实际映射关系,后续写固件时直接照表填写。

5.4 常见问题速查表

现象可能原因解决措施
某几个通道读值恒为0CH引脚漏焊、信号未接入检查硬件连接,用万用表量CH引脚与信号源之间通断
所有通道读数都偏大MUX导通电阻形成分压,后级输入阻抗不匹配增加运放缓冲,或减小信号源内阻
通道切换后首采值异常ADC采样电容未预充到位连续采集两次,丢弃第一次结果
随机跳变不稳定控制信号振铃、电源纹波、信号源阻抗过高控制线串电阻、加滤波电容、检查地线
某个通道偶尔串到相邻通道PCB布线间距过近、串扰调整布局走线,增加屏蔽地线
EN悬空导致行为随机EN输入浮空EN引脚接固定电平(接地或拉高)
与逻辑分析仪共地异常地电位不一致确保共地,必要时隔离供电

6. 效果对比与扩展思路

6.1 扩展前后采集性能对比

方案改完后的整体数据:16通道单轮扫描时间最快1.2ms(含两次采样去首采逻辑),换算扫描频率约830Hz;每通道ADC值稳定,跳变范围保持在±3LSB以内;CPU占用率在DMA模式下几乎可以忽略。相比直接用MCU内置ADC通道,这种方法用3个GPIO加一颗几块钱的芯片,就拿到了16路模拟采集能力,性能够用,成本控制得很理想。

如果后续需要更高精度,可以考虑在COM端和MCU ADC之间加一级运放跟随器,把MUX的RON影响彻底隔离掉。这样12位ADC采集的精度发挥会更充分。

6.2 后续还可以扩展的方向

CD74HC4067不止能做ADC扩展,几个思路可以参考:

  • 反向使用:把COM当输入,CH0~CH15当输出,可以用少量GPIO控制多路模拟信号输出(比如通过PWM滤波后的模拟电压);
  • 数字信号扩展:切换I2C总线上挂载的多颗同地址传感器,轮询访问;
  • 把多片CD74HC4067级联,用EN控制不同芯片的使能,理论上可以扩展到更多通道;
  • 测试治具上用于自动化测试信号路由,一个MCU控制所有开关切换不同的被测信号。

我个人在实际操作中的一个体会:模拟多路开关是那种看起来简单、用好了很省事、但细节决定成败的器件。整个调试过程下来,真正让我印象深刻的不是芯片本身多难用,而是排查采样抖动、首采异常这些现象时,对MCU ADC采样原理和信号完整性的理解才是解决问题的关键。如果你正在做类似的扩展方案,建议先把信号源内阻、延时设置、控制信号质量这些基础参数用示波器和万用表摸清楚,再动手调软件,能省掉非常多来回折腾的时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询