1. 为什么在 Ubuntu 18.04 上折腾 CH340/CH341 驱动如果你手头有一块 Arduino 兼容板、ESP8266/ESP32 开发板、STM32 最小系统板或者某个用串口通信的传感器模块大概率会碰到 CH340 或 CH341 这两颗芯片。它们便宜、量大、兼容性好几乎成了国产 USB 转串口方案里的“默认选项”。但问题也恰恰出在这里——Ubuntu 18.04 这个 LTS 版本自带的ch341内核模块对 CH340 和 CH341 的支持并不总是开箱即用尤其是遇到某些批次芯片或者需要特定工作模式时插上设备后/dev/ttyUSB0死活不出现或者出现了却无法正常收发数据。这篇文章就是围绕“在 Ubuntu 18.04 上把 CH340 和 CH341 驱动彻底跑通”这件事展开的。我会从内核模块机制讲起把识别、编译、加载、权限、排查这一整条链路拆开揉碎给出可以直接复制执行的命令和配置。无论你是刚接触 Linux 的嵌入式新手还是被某个串口设备卡了半天的老手都能在这里找到能落地的方案。核心关键词就三个Ubuntu 18.04、CH340 驱动、CH341 驱动全文围绕它们展开不跑题。需要先说明一点CH340 和 CH341 虽然名字接近但在 Linux 内核里的处理方式有差异。CH341 本身是一个多功能芯片可以工作在串口、并口、I2C、SPI 等多种模式CH340 则基本只做 USB 转串口。Ubuntu 18.04 内核里已经带了ch341模块它同时覆盖了 CH340 和 CH341 的串口功能。所以大多数情况下你不需要去网上找什么“CH340 驱动安装包”而是要把系统自带的这个模块用对、用稳。这一点如果搞反了后面会走很多弯路。2. 先搞清楚内核模块与设备节点的关系2.1 ch341 模块到底管了什么Linux 内核从很早的版本开始就包含了ch341这个 USB 串口驱动源码位于drivers/usb/serial/ch341.c。它注册的 USB 设备 ID 覆盖了 CH340、CH341 以及一些衍生型号。当你把设备插到 USB 口上内核的 USB 子系统会读取设备描述符如果 VID/PID 匹配上了ch341驱动就会自动加载该模块并在/dev下创建ttyUSBx节点。你可以用下面这条命令确认当前内核是否已经识别并加载了模块lsmod | grep ch341如果输出里有ch341这一行说明模块已经在内核里了。再用dmesg | grep -i ch341看内核日志里有没有类似ch341-uart 1-1:1.0: ch341-uart converter detected的记录。有的话设备节点通常就是/dev/ttyUSB0。这一步是判断“驱动到底有没有工作”的最直接依据比任何图形化工具都靠谱。2.2 为什么有时候模块在却不出节点模块加载了、日志也认了但/dev/ttyUSB0就是不存在这种情况我遇到过好几次。常见原因有三个一是设备被其他驱动抢占了比如usbserial通用驱动先一步绑定二是内核模块版本和当前运行内核不匹配常见于你手动升级过内核但没重启三是 USB 线本身只有供电能力没有数据能力这种线插上后连设备描述符都读不到自然不会有任何日志。排查顺序建议是先换一根确认能传数据的 USB 线再看dmesg末尾有没有 USB 枚举失败的报错最后检查lsusb能不能看到设备的 VID/PID。lsusb能看到设备但dmesg没有 ch341 相关记录才说明是驱动绑定问题这时候才需要往下走编译或手动绑定的流程。提示不要一上来就急着重装驱动。先确认硬件链路和内核日志能省掉至少一半的无用操作。3. 确认系统环境与必要依赖3.1 检查内核版本和头文件Ubuntu 18.04 默认内核是 4.15 系列后续通过apt升级可能会到 4.15.x 的更高小版本。编译内核模块必须保证linux-headers和当前运行内核版本完全一致否则编译出来的.ko文件加载时会报invalid module format。先看当前内核uname -r然后确认对应的头文件是否已安装dpkg -l | grep linux-headers-$(uname -r)如果没有输出就装sudo apt update sudo apt install linux-headers-$(uname -r) build-essentialbuild-essential提供gcc、make等编译工具是编译任何内核模块的前提。这两步做完环境基本就齐了。3.2 确认设备 VID/PID不同批次的 CH340 芯片 VID/PID 可能不同常见的是1a86:7523CH340和1a86:5523CH341 串口模式。用lsusb找到类似QinHeng Electronics HL-340 USB-Serial adapter的行记下冒号前后的两个十六进制数。这个信息在后面手动绑定驱动或者写 udev 规则时会用到。如果lsusb里根本看不到设备那问题在硬件或 USB 口不在驱动先解决物理连接。4. 驱动安装的三条路线与选型逻辑4.1 路线一直接用内核自带模块这是首选也是 90% 以上场景应该走的路。只要lsmod能看到ch341dmesg能认到设备/dev/ttyUSB0能出现就什么都不用装。很多人被网上各种“CH340 驱动下载”误导去装了一个来路不明的.ko结果和内核自带模块冲突反而把原本能用的设备搞坏了。判断标准很简单插上设备dmesg | tail有 ch341 识别日志ls /dev/ttyUSB*有节点用minicom或screen能打开就说明自带模块完全够用。这时候你要做的只是权限配置不是驱动安装。4.2 路线二内核模块存在但绑定失败时手动绑定有时候模块在但设备被usbserial抢先绑定了。表现是dmesg里出现usbserial而不是ch341。这时候可以先把usbserial从设备上解绑再让ch341接管。操作如下# 查看设备对应的驱动绑定情况 ls /sys/bus/usb-serial/drivers/ # 如果设备在 usbserial 下先解绑把 X-Y 换成实际总线号 echo -n 1-1:1.0 | sudo tee /sys/bus/usb/drivers/usbserial/unbind # 再绑定到 ch341 echo -n 1-1:1.0 | sudo tee /sys/bus/usb/drivers/ch341/bind总线号1-1需要根据dmesg或lsusb -t的实际输出替换。这种手动绑定是临时的重启后失效适合用来验证问题根源。确认有效后再考虑用 udev 规则固化。4.3 路线三编译安装较新版本驱动只有当自带模块确实不支持你的芯片批次或者你需要 CH341 的并口/I2C 等非串口模式时才考虑编译新驱动。Ubuntu 18.04 自带的ch341对纯串口场景支持已经足够编译新驱动更多是为了功能扩展而不是“修复不能用”。编译前务必确认新驱动源码与当前内核版本兼容否则会出现符号找不到、结构体定义变化等一堆编译错误。我的建议是除非你明确知道自带模块缺了什么功能否则不要轻易走这条路。5. 完整实操流程从插上设备到能收发数据5.1 第一步插入设备并观察内核反应把 CH340/CH341 设备插到 USB 口立刻执行dmesg | tail -20正常输出应该包含usb 1-1: new full-speed USB device、ch341-uart、ttyUSB0等关键字。如果只看到new full-speed USB device后面没有驱动绑定信息说明驱动没匹配上记下此时的 VID/PID进入下一步排查。5.2 第二步确认设备节点并设置权限节点出现后默认属主是root普通用户无法直接读写。最省事的做法是把当前用户加入dialout组sudo usermod -aG dialout $USER执行完必须重新登录才生效这一点很多人会忽略然后抱怨“加了组还是没权限”。重新登录后用id命令确认dialout在组列表里。如果不想重新登录也可以临时改权限sudo chmod 666 /dev/ttyUSB0但这只是临时的拔插设备后会重置。长期方案还是dialout组或者 udev 规则。5.3 第三步用工具验证串口通信权限搞定后用screen或minicom打开串口。以screen为例sudo apt install screen screen /dev/ttyUSB0 115200115200是波特率按你设备的实际参数改。打开后如果能看到设备输出的数据说明整条链路通了。退出screen的快捷键是CtrlA然后按K再按Y确认。如果打开后没有任何数据先确认波特率、数据位、停止位、校验位是否和设备匹配。串口通信参数不匹配是最常见的“连上了但没数据”的原因比驱动问题更常见。5.4 第四步固化配置避免每次折腾设备节点名可能会因为插入顺序变化而变成ttyUSB1、ttyUSB2。如果你的项目依赖固定节点名可以写一条 udev 规则。先获取设备的序列号或物理端口路径udevadm info -a -n /dev/ttyUSB0 | grep -E KERNEL|SUBSYSTEM|ATTRS{serial}然后创建规则文件/etc/udev/rules.d/99-ch340.rulesSUBSYSTEMtty, ATTRS{idVendor}1a86, ATTRS{idProduct}7523, SYMLINKttyCH340重新加载规则sudo udevadm control --reload-rules sudo udevadm trigger之后无论设备插在哪个口都可以用/dev/ttyCH340访问项目代码里写死这个路径就不会因为节点漂移而失败。6. 常见问题与排查速查6.1 插上没反应lsusb 也看不到设备这种情况基本可以判定是硬件或线缆问题。先换 USB 线再换 USB 口最后换一台电脑交叉验证。CH340 模块本身损坏的概率不高但劣质 USB 线只有供电没有数据线的比例相当大。我手里就有一根这样的线插U盘能供电但认不到专门用来做“排除法”测试。6.2 能看到设备但 dmesg 报错常见报错有ch341-uart: probe of 1-1:1.0 failed with error -110这通常是 USB 供电不足或者设备固件异常。可以尝试换到主板后置 USB 口避开前面板或 USB Hub。如果是自己画的板子检查 CH340 外围电路尤其是晶振和去耦电容。6.3 节点出现但打开报 Permission denied回到 5.2 节确认用户在dialout组且已重新登录。用ls -l /dev/ttyUSB0看属主和权限。如果属主是root:dialout且权限是crw-rw----那么dialout组成员就有读写权限问题一定出在组没生效上。6.4 数据收发乱码乱码几乎都是波特率不匹配。确认设备实际波特率注意有些设备用的是9600而不是115200。另外检查数据位是否为 8、停止位是否为 1、校验位是否为 None这是最常见的8N1配置。如果设备要求7E1之类也要相应调整。6.5 问题速查表现象可能原因处理方式lsusb 无设备线缆/供电/硬件换线换口交叉验证有设备无 ttyUSB驱动未绑定检查 dmesg手动 bind节点存在但无权限用户不在 dialout加组并重新登录打开无数据参数不匹配核对波特率与帧格式数据乱码波特率错误改为设备实际波特率节点名漂移插入顺序变化写 udev 规则固定符号链接7. 几个容易被忽略的实操心得第一不要迷信“驱动安装包”。Ubuntu 18.04 内核自带的ch341模块对 CH340/CH341 串口场景的支持已经非常成熟绝大多数“装驱动”的需求实际上是权限配置或参数配置问题。把精力放在dmesg和lsusb上比到处找安装包高效得多。第二dmesg 是你最好的朋友。每次插拔设备后第一时间看dmesg | tail里面会明确告诉你内核认到了什么、绑定了哪个驱动、创建了什么节点。养成这个习惯排查效率会提升一个档次。第三udev 规则要早写。只要你的项目超过一块串口设备节点名漂移迟早会找上门。与其到时候改代码不如一开始就用符号链接固定下来。规则文件里的ATTRS{serial}如果设备没有序列号可以改用KERNELS匹配物理端口路径效果一样。第四权限问题优先于驱动问题。我见过太多人把“Permission denied”当成驱动没装好折腾半天编译模块最后发现只是没加dialout组。遇到打不开设备先看权限再看驱动这个顺序能省大量时间。第五保留一个已知能用的测试环境。比如手头留一块确认正常的 CH340 模块和一根确认能传数据的线遇到问题时用它做对照能快速判断是设备问题还是环境问题。这个习惯在嵌入式调试里价值极高。8. 关于 CH341 非串口模式的补充说明CH341 除了串口还支持并口、I2C、SPI 等模式但这些模式在 Linux 下的支持并不像串口那么直接。内核自带的ch341模块主要面向串口其他模式往往需要配合i2c-ch341、spi-ch341等独立驱动或者使用厂商提供的用户态库。如果你只是做 USB 转串口完全不用关心这些如果你确实需要 I2C 或 SPI 模式建议先确认内核版本是否包含对应驱动再决定是否编译。需要强调的是非串口模式的驱动配置涉及更多内核选项和硬件时序细节已经超出“装个串口驱动”的范畴。如果你的项目只是用 CH340 下载程序或打印日志把串口这条路走稳就足够了不必为了“功能齐全”去折腾用不到的模式。9. 串口调试工具的选择与配置除了screen和minicompicocom也是一个轻量好用的选择sudo apt install picocom picocom -b 115200 /dev/ttyUSB0退出用CtrlA再按CtrlX。picocom的好处是默认不做流控对很多 CH340 设备更友好。如果你用minicom记得进配置菜单把硬件流控关掉否则可能出现能连上但发不出数据的情况。对于需要脚本化测试的场景Python 的pyserial库非常方便import serial ser serial.Serial(/dev/ttyUSB0, 115200, timeout1) ser.write(bhello\n) print(ser.readline()) ser.close()这段代码可以直接用来验证串口是否真的能收发。如果pyserial能正常读写说明驱动、权限、参数全部正确问题就不在底层了。10. 内核升级后驱动失效的处理Ubuntu 18.04 通过apt upgrade升级内核后之前手动编译的模块会失效因为模块是按旧内核版本编译的。表现是重启后lsmod里没有ch341或者加载时报版本不匹配。处理方式很简单重新安装新内核对应的头文件然后重新编译模块。如果用的是内核自带模块则不受影响因为新内核会自带对应版本的ch341。这也是我反复建议优先使用自带模块的原因之一——它跟着内核走升级内核不会导致驱动失效。只有自带模块确实不满足需求时才值得承担手动编译带来的维护成本。11. 一个完整的验证脚本把前面零散的命令串起来可以写一个简单的验证脚本插上设备后一键检查#!/bin/bash echo 内核模块 lsmod | grep ch341 || echo ch341 模块未加载 echo USB 设备 lsusb | grep -i 1a86 || echo 未发现 CH340/CH341 设备 echo 内核日志 dmesg | grep -i ch341 | tail -5 echo 设备节点 ls -l /dev/ttyUSB* 2/dev/null || echo 无 ttyUSB 节点 echo 当前用户组 id | grep dialout || echo 当前用户不在 dialout 组这个脚本不解决任何问题但能让你在几秒内看清当前状态快速定位是哪一环出了问题。排查问题的第一步永远是“看清现状”而不是盲目尝试各种修复命令。12. 关于驱动稳定性的个人体会CH340 和 CH341 在 Ubuntu 18.04 上的稳定性很大程度上取决于硬件本身的质量和 USB 供电环境。我遇到过同一批模块有的插上就认有的需要重新插拔几次才出节点还有的在长时间大数据量传输时会掉线。这些现象大多和芯片外围电路、USB 线质量、主板供电有关而不是驱动代码的问题。所以如果你发现驱动明明装好了、权限也对了但设备偶尔抽风先别怀疑 Linux去检查硬件。换一根带屏蔽的短 USB 线、换一个供电充足的口、给模块单独供电往往比改任何软件配置都有效。这个经验在嵌入式和硬件调试里几乎是通用的软件问题有迹可循硬件问题千奇百怪而串口通信恰好是两者交界的地方。