新闻详情

新闻详情

首页 / 资讯中心 / 详情

CD74HC4067扩展16路ADC采集:模拟开关驱动代码与调试经验

发布时间:2026/9/8 14:15:50来源:尧图网络
CD74HC4067扩展16路ADC采集:模拟开关驱动代码与调试经验
做嵌入式项目的人十个里有八个都抱怨过同一件事MCU自带的ADC通道根本不够用。这个“CD74HC4067扩展16路ADC采集”项目笔记就是专门解决这个痛点的。项目核心是用一颗几块钱的16通道模拟开关芯片CD74HC4067把单片机自带的ADC扩展出最多16路模拟输入通道我在实际调板过程中还踩了两个特别典型的坑一个是通道切换后采样数据漂移一个是悬空通道串扰殃及邻路这篇笔记会把驱动代码、硬件设计要点和排查思路全部摊开讲适合正在做多路传感器采集、电池巡检、数据采集板这类项目的嵌入式工程师参考也适合刚入门想搞懂模拟开关到底怎么用的朋友。先说结论用CD74HC4067扩展ADC通道这件事本身并不复杂核心就三句话——4个IO口控制16路开关一进一出轮流导通软件上按需切换采样。但真正做到每路数据稳定可靠你会发现在通道切换时序、未用通道处理这些细节上藏着不少门道。1. 项目背景与整体设计思路1.1 为什么需要扩展ADC通道很多实际项目里MCU选型往往不是只考虑ADC资源这一个指标。我当时手头的主控是STM32F103系列自带16个ADC通道听起来不少但分配到项目需求里就捉襟见肘了——两路电压监测、四路NTC温度、一路光敏、一路气压计还没算上预留的电位器校准通道板载ADC已经被占得干干净净。这时候摆在我面前的选择有三个换一颗带更多ADC通道的MCU、外挂独立的ADC芯片、或者用模拟开关做通道扩展。换MCU方案最直接但要重新画板、改代码、重新验证外设兼容性开发周期至少多一周独立ADC芯片方案效果好比如ADS1115这种16位芯片但一颗要十几块钱多颗的话成本直接上去而且I2C总线上挂多颗从设备还要处理地址冲突。对比之下CD74HC4067这个方案成本最低——芯片一片也就一两块钱几乎不占MCU资源4个GPIO就能换16路模拟输入通道性价比非常明显。1.2 CD74HC4067的工作原理与核心特性CD74HC4067本质上是一颗16选1的模拟多路复用器或者叫模拟开关阵列。封装里面有16个模拟开关管一端分别接CH0到CH15这16个外部输入引脚另一端全部并在一起接到公共引脚COM。芯片上有4根地址线S0到S3再加上一根使能脚EN工作原理跟早期的拨码开关很像S0到S3的四位二进制组合决定了当前哪个通道和COM导通比如S3S2S1S0等于0000时选通CH0等于0001时选通CH1以此类推到1111就是CH15。这颗芯片在5V供电下的典型导通阻抗在70欧姆到120欧姆之间工作电压范围覆盖2V到6V支持双向信号传输——意思是不光可以用它来选通模拟信号送给ADC也可以反过来把一路信号分发到16个目标端。这正好符合我项目里的需求外部传感器信号先进入CH0到CH15再通过COM引脚送到MCU的ADC输入口。选这颗芯片而不是它的兄弟型号CD4051或者CD4052原因也简单4051只有8个通道4052虽然也是双通道但它是2组8选1的结构都不如4067这样一片搞定16路来得干净。当然如果你只需要8路用4051会更省钱不过考虑到扩展余量和BOM统一我直接选了16路的。2. 硬件电路设计要点2.1 引脚连接与最小系统搭建CD74HC4067是标准的24脚DIP或SOP封装引脚排布很规整。最常用的连接方式是这样的S0到S3接MCU的4个GPIO口EN接另一个GPIO口COM接MCU的ADC输入引脚CH0到CH15分别接对应的传感器信号输出端。电源部分VCC接3.3V或5VVEE接GNDGND和系统共地。这里有个细节要特别注意CD74HC4067芯片手册上要求所有模拟输入引脚CH0到CH15的电压不能超出VCC以上0.5V或者低于VEE以下0.5V一旦超出芯片内部的ESD保护二极管就会正向导通。平时还好但如果传感器信号是从别的供电系统引过来的万一上电时序先后不一致某个通道上瞬间窜过来一个高于VCC的电压轻则采样不准重则把芯片甚至后续的MCU ADC引脚打坏。所以我在所有外部模拟输入通道入口都串了一个1K欧姆的保护电阻虽然会给信号引入一点点误差但换来的安全性非常值得。2.2 电平匹配与使能脚处理使用CD74HC4067时最容易忽略的是电平匹配问题。如果芯片用5V供电那么S0到S3地址引脚的高电平阈值是0.7倍的VCC也就是3.5V。很多3.3V供电的MCUGPIO输出高电平也就是2.8V到3.2V之间的水平直接驱动这颗5V供电的芯片很可能触发不了逻辑高电平状态导致通道选择和预期完全错乱。我当时的处理办法有两条路可以选要么整个4067直接用3.3V供电这样地址阈值降到2.31V3.3V的MCU高电平稳稳超过阈值要么改用CD74HCT4067这颗TTL兼容版本它的输入阈值是固定的0.8V和2.0V跟5V或3.3V的IO都能匹配。实测对比下来3.3V供电的方案在模拟开关导通电阻上比5V供电略大一些大概高十几欧姆但在我的电路里后级ADC输入阻抗足够大这个差异影响很小我就采用了3.3V统一供电的方案省去一颗电平转换芯片。EN使能脚的接线也值得专门说一下。这颗芯片的EN是低电平有效EN1时所有通道全部断开EN0时通道切换才真正生效。很多人的做法是直接把这根脚接地让芯片永远处于使能状态这在大批量生产中或许没问题但调试阶段非常不推荐。我把EN接到了MCU的GPIO上并让程序在初始化时先把EN置成高电平等所有控制引脚和系统状态确定后再拉低使能。这样做的目的很明确防止MCU在上电复位过程中GPIO电平不定导致芯片瞬间选通了某个随机通道把乱七八糟的信号灌进ADC引脚。2.3 PCB布局走线的几个经验模拟信号采样电路里PCB布局对结果的影响比很多人想象中大得多。我在这版设计里踩过一次数字信号干扰模拟信号的坑——通道切换用的S0到S3地址线一旦跳变沿太陡峭会产生高频噪声通过走线之间的寄生电容耦合到COM引脚上。解决办法有几个我在硬件上给S0到S3每组信号串联了一个33欧姆的电阻用来降低跳变沿的斜率效果等同于把高频成分的能量衰减掉一部分另外在模拟信号走线的两侧尽量铺地隔离模拟地区域和数字地区域单点相连避免数字电流流过模拟地平面。3. 软件驱动与采样流程实现3.1 通道切换与读取的驱动代码软件部分我用的是STM32标准库HAL库思路完全一致。先把控制引脚封装成宏定义这样代码可读性会好很多#define MUX_S0_GPIO GPIOA #define MUX_S0_PIN GPIO_PIN_0 #define MUX_S1_GPIO GPIOA #define MUX_S1_PIN GPIO_PIN_1 #define MUX_S2_GPIO GPIOA #define MUX_S2_PIN GPIO_PIN_2 #define MUX_S3_GPIO GPIOA #define MUX_S3_PIN GPIO_PIN_3 #define MUX_EN_GPIO GPIOA #define MUX_EN_PIN GPIO_PIN_4 #define MUX_ENABLE() GPIO_ResetBits(MUX_EN_GPIO, MUX_EN_PIN) #define MUX_DISABLE() GPIO_SetBits(MUX_EN_GPIO, MUX_EN_PIN)通道选择的核心逻辑就是一个4位二进制译码的过程用宏定义加位运算展开S0到S3的电平组合void MUX_SelectChannel(uint8_t channel) { if (channel 15) { return; } if (channel 0x01) { GPIO_SetBits(MUX_S0_GPIO, MUX_S0_PIN); } else { GPIO_ResetBits(MUX_S0_GPIO, MUX_S0_PIN); } if (channel 0x02) { GPIO_SetBits(MUX_S1_GPIO, MUX_S1_PIN); } else { GPIO_ResetBits(MUX_S1_GPIO, MUX_S1_PIN); } if (channel 0x04) { GPIO_SetBits(MUX_S2_GPIO, MUX_S2_PIN); } else { GPIO_ResetBits(MUX_S2_GPIO, MUX_S2_PIN); } if (channel 0x08) { GPIO_SetBits(MUX_S3_GPIO, MUX_S3_PIN); } else { GPIO_ResetBits(MUX_S3_GPIO, MUX_S3_PIN); } }这段代码的本质就是把传入的通道号0到15拆成4个二进制位每一位对应一根地址线的电平。你可能觉得这样写成4个if语句有点啰嗦图省事也可以用switch-case不过我个人更推荐这种位运算的写法——当以后需要移植到其他平台时只需要改宏定义逻辑层完全不用动。3.2 ADC采样函数与稳定延时设计通道切换完毕之后接下来的操作就是等信号稳定然后启动ADC转换。我在项目里的标准采样函数长这样uint16_t MUX_ReadChannel(uint8_t channel) { uint16_t adc_value 0; MUX_SelectChannel(channel); Delay_us(200); //关键延时等模拟开关电容充放电稳定 ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) RESET); adc_value ADC_GetConversionValue(ADC1); return adc_value; }这个200微秒的延时不是我随手拍的也不是网上抄来的它是根据实际电路参数算出来的。我把信号链路等效成了一条RC充电回路电阻部分包括信号源内阻、4067的导通电阻以及PCB走线的寄生电阻电容部分则是4067输出端的寄生电容加上STM32 ADC内部采样电容。以我这边NTC温度传感器的信号源内阻约10K欧姆为例加上导通电阻120欧姆和杂散电阻总共约10.5K欧姆ADC输入引脚外接了一个0.1uF的滤波电容那么时间常数τ就是10.5K乘以0.1uF大约等于1.05毫秒。要让电容充到99.9%以上的稳定电压需要大约7倍时间常数也就是7.35毫秒。所以你看到了200微秒延时其实对NTC这种传感器是不太够的我真正生产版本里改成了10毫秒保证精度。但如果你像热电偶一样信号源内阻只有几百欧姆那200微秒甚至更短就足够了。这个延时必须根据你实际的信号源阻抗来定一劳永逸地套一个固定值很容易出问题。3.3 16路通道轮流扫描的实现方式实际项目中往往需要周期性地把16路信号都采一遍。最直接的思路就是循环调用MUX_ReadChannel函数void MUX_SampleAllChannels(uint16_t *adc_buffer, uint8_t len) { uint8_t i 0; for (i 0; i len i 16; i) { adc_buffer[i] MUX_ReadChannel(i); } }这个函数跑起来以后得到的是从通道0到通道15各自ADC转换后的原始值。后续如果需要换算成实际电压再乘上参考电压除以ADC分辨率即可。这里顺便提一句如果采样通道数量比较多单次ADC转换加延时的总耗时可能会让整个采样周期变得很长16路每路10毫秒延时就要160毫秒。如果你的系统不允许这么慢可以换一种思路不是每路都延时而是先把某一路切换好利用延时的这段时间去处理其他任务用轮询或者定时器回调的方式来驱动采样状态机。4. 两个调试坑实录与排查思路4.1 坑一切换通道后立即采样数据飘忽不定第一个让我折腾了整整一晚上的问题发生在第一次把电路板焊好、程序跑起来的时候。现象是读取通道0时电压读数正常紧接着读通道1读回来的数字明显偏小但如果连续多读几次通道1数据又会慢慢爬到正常值。更诡异的是从通道1切回通道0第一次读回来的数值也不是通道0的真实电压而是介于通道0和通道1之间的某个值。我一开始怀疑是ADC自身的问题于是把程序改成只固定读一个通道结果数据非常稳定ADC本身没有任何毛病。后来又怀疑4067通道选择逻辑出错我用万用表量S0到S3引脚的电平发现完全正确通道确实切过去了。真正定位到问题是在示波器探头搭到COM引脚上的那一刻——通道切换的瞬间COM引脚的波形不是立即跳到目标电压而是经过了一个明显的指数爬升过程爬升时间大概在几百微秒到几毫秒不等。这个现象背后的原理我前面已经推导过整个信号链路是一个RC低通网络4067的导通电阻、信号源内阻和寄生电容共同决定了充电时间常数。通道切换完成并不代表信号已经稳定芯片内部残留电荷以及外部电容上的旧电压都需要时间泄放。如果信号源内阻很大这个稳定过程就会明显变慢甚至长达几十毫秒。解决办法我从两个维度同时下手。软件层面增加了稳定延时并且采用了二次采样法也就是切换通道后延时等待稳定然后启动第一次ADC转换并丢弃结果紧接着做第二次转换这次才作为有效数据。硬件层面则把所有高阻抗的信号源前面都加了一级电压跟随器用运放把信号源内阻降下来。这两个措施叠加之后采样数据纹丝不动再也没有出现过通道间串数据的问题。4.2 坑二没接传感器的悬空通道把邻路采样带跑了第二个坑比第一个更隐蔽。当时板子上16路通道只用了11路剩下5路暂时空着结果软件每轮扫描出来的数据表中通道10到通道15的值毫无规律地跳来跳去甚至连带着通道9的值也不稳定。通道9明明接了传感器而且信号很干净怎么会被影响排查过程比第一个坑曲折得多。我先用示波器观察通道9的输入信号波形很干净没有叠加噪声。然后看COM引脚发现波形上覆盖了一层高频毛刺。这时候我把飞线从悬空的通道引脚上断开毛刺立刻消失通道9的读数也恢复正常了。问题就出在悬空通道上。原理是这样的CD74HC4067内部虽然在同一时刻只有一个通道的开关管导通但未导通的通道引脚与公共端COM之间仍然存在寄生电容典型的数值在几个皮法到十几皮法。悬空状态的引脚相当于一根天线电磁环境中的噪声信号会感应出微弱电压这个电压经过寄生电容耦合到COM引脚上对正在采样的通道造成干扰。如果悬空引脚离某个正在导通的通道在芯片内部物理位置上比较近耦合串扰会更明显。解决思路也很直接绝对不能允许模拟输入引脚悬空。我把暂时用不到的几个通道全部通过10K欧姆电阻接到了地上让它们保持确定的电位寄生电容上不会再积累随机电荷。事后我查了芯片手册官方确实在应用注意事项里提到过未使用输入要接地的建议只是藏得比较深很多人根本注意不到。如果你不想在板子上预留这么多电阻位也可以把悬空通道在软件层面跳过不去选中它们但硬件上接地的方案才最稳妥。4.3 调试过程中用到的排查思路这两个坑排查下来我自己总结出一套调试思路。第一怀疑硬件问题前先在纯固定场景下做对照实验——比如把ADC固定接到某个已知电压排除软件逻辑干扰第二善用示波器观察关键节点的真实波形不要只盯着打印出来的数值猜来猜去第三一次只改变一个变量比如这次把悬空通道全部接地那次把延时加长改完一项看数据变化再做下一步判断同时改好几处的话根本分不清是哪个改动起的作用。5. 常见问题速查与扩展建议5.1 常见问题与解决方案速查表调试过程中我把遇到过的各种问题整理成了下面这个表格后面新项目再碰到类似问题我总是先翻一遍这张表现象可能原因解决措施切换通道后第一次采样值偏小或偏大RC充放电未完成采样时序过快加大稳定延时或丢弃第一次采样结果悬空通道让邻路数据跳变引脚悬空引入噪声寄生电容耦合未用通道通过10K电阻接地通道选择错乱选了A路读到B路地址线高电平不满足阈值要求检查4067供电电压与MCU电平匹配所有通道读数系统性偏大或偏小导通电阻与后级电路分压引起误差后级加电压跟随器或改用低导通电阻的多路开关上电瞬间异常数据EN引脚悬空或电平不稳定EN接GPIO受控上电先禁用再启用高速切换时通道间串扰数字控制信号耦合到模拟链路地址线串33欧电阻模拟走线铺地隔离5.2 在16路基础上继续扩展的思路如果你的项目比16路还变态要三四十路甚至更多CD74HC4067也扛不住贪心但扩展思路还是有的。最简单粗暴的方法是级联多片4067每一片的COM端再接一级前端的模拟开关构成树形结构不过这样会叠加导通电阻采样精度会损失多一些。还有一种做法是每片4067的EN引脚接不同的GPIO用两个GPIO做片选一次只使能一片芯片其他片的输出处于高阻态这样等于把通道数量按倍数往上翻。网上有开源项目做到64路ADC采集用的就是这种片选并行的设计思路。另外要注意的是单片4067的导通电阻70到120欧姆并不算小如果你需要采集的是非常微弱的信号比如毫伏级别的电桥输出这个导通电阻造成的误差占比会变得可观。这种情况我的建议是先放大信号再做多路切换或者选用导通电阻更低的专用模拟开关芯片比如导通电阻只有几欧姆的型号。一句话总结4067这种器件适合的是信号幅度和阻抗都在合理范围内的传感器采集它是个实用的“通道扩展开关”不是万能的精密信号调理核心。我在实际使用中最大的体会是用CD74HC4067扩展ADC通道并不是难点难的是真正理解你信号链路里的每一个电阻和电容对采样精度的影响。我把这两个调试坑记下来也是希望后面做类似项目的朋友能少走这段弯路。如果调试时再遇到通道间串扰或者数据漂移的问题不妨先看一眼你的悬空引脚处理好了没有再看一眼通道切换后的采样延时给够了没有大概率问题就出在这两处。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

DMA实战指南:从串口接收到PWM输出,一文搞定配置与避坑 2026/9/8 14:54:58

DMA实战指南:从串口接收到PWM输出,一文搞定配置与避坑

DMA这名字听起来像是什么高深莫测的硬件黑魔法,但说白了,它就是一块专门帮你搬数据的“肌肉劳工”。干嵌入式这行越久,越会发现一个真理:CPU最宝贵的是算力,而不是做搬运工。把内存、外设之间的数据挪动交给DMA去办&am…

阅读更多 →
AI编程助手opencode实战:终端AI Agent配置与IDE集成指南 2026/9/8 14:54:58

AI编程助手opencode实战:终端AI Agent配置与IDE集成指南

聊一个我最近天天在用的工具:opencode。它是一个开源 AI 编程助手(AI coding agent),运行在终端里,能读你的项目代码、定位问题、修改文件、跑测试,然后给出可审查的改动结果。如果你折腾过 Claude Code&am…

阅读更多 →
Python编写网站API抓取教程,轻松实现数据爬取 2026/9/8 14:54:58

Python编写网站API抓取教程,轻松实现数据爬取

当前处于互联网时代, 数据成了极为宝贵的资源当中的一个。在大数据时代来临之后, 数据的获取以及利用这件事变得越发重要起来。对于身为程序员的人而言, API是一种相当不错的选择。API它属于是一种软件接口, 它能够让应用程序跟其他软件展开交互, 并且还提供了一组有着明确界定…

阅读更多 →
opencode 实战指南:终端里的开源 AI 编码代理完全上手 2026/9/8 14:54:58

opencode 实战指南:终端里的开源 AI 编码代理完全上手

最近几天我在几个技术社群里反复看到 opencode 这个词,一开始以为是某个新出的编辑器主题皮肤,点进去才发现是个终端里的 AI 编码代理。说实话,我已经在 Claude Code、Codex、opencode 之间来回换了好几轮,最后把日常开发的主力场…

阅读更多 →
嵌入式启动流程与OTA升级实战:从MCU到SoC的故障定位与容错设计 2026/9/8 14:54:58

嵌入式启动流程与OTA升级实战:从MCU到SoC的故障定位与容错设计

做嵌入式固件这行,最怕的不是需求改版,而是设备上电之后毫无反应、升级升到一半变砖、极端环境下一觉醒来发现远程设备集体失联。最近我把这几年做启动流程、故障定位和OTA升级的工程经验整理成付费专栏连载,本篇是衔接上篇思考题的一期&…

阅读更多 →
价格合理的电子签章系统推荐厂商 成本收益测算指南 2026/9/8 14:51:57

价格合理的电子签章系统推荐厂商 成本收益测算指南

电子签章的价格构成与常见误区年底3000份人事职称聘书,3名行政人员连续加班1周才勉强盖完,这是不少企事业单位年末都会遇到的典型场景,更棘手的是医疗、政务等涉密场景下,签署文件还不能出单位内网,不少采购方选型时只…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞