十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

开发板连Ubuntu没反应?设备文件消失排查与串口调试实战

开发板连Ubuntu没反应?设备文件消失排查与串口调试实战 开发板插上 Ubuntu 没反应一次讲清设备文件消失的排查思路做嵌入式开发的朋友几乎都遇到过这个场景拿出开发板用 USB 线连上电脑打开终端准备用串口调试结果在/dev目录下等了个寂寞——既没有ttyUSB0也没有ttyACM0。网上搜一圈全是零零散散的答案装驱动、改权限、重启服务一顿操作下来还是不行。这篇文章就是为解决这个问题写的。我会从设备文件为什么没生成讲起覆盖驱动、权限、虚拟机透传、udev 规则这几大核心原因最后附带串口调试和 Qt 串口编程的衔接经验。无论你用的是 CH340、CH341 这类国产芯片还是 FTDI、CP210x还是 t113、ESP32、泰山派这类常见开发板排查思路基本通用。1. 问题根源分析设备文件为什么没出现设备文件不是凭空冒出来的。Linux 下/dev/ttyUSB0、/dev/ttyACM0这类节点是内核驱动检测到 USB 转串口芯片后自动创建的。换句话说设备文件没出现本质上就两种情况内核根本没识别到你的芯片或者识别到了但因为某种原因没有生成对应节点。1.1 芯片型号决定驱动先搞清楚你用的是哪颗芯片开发板上那颗负责 USB 转串口的芯片直接决定了系统需要用哪个驱动。最常见的几类CH340 / CH341国产低成本方案淘宝上十几块的 USB 转 TTL 小板基本都是它。对应驱动是ch341内核 3.x 以后通常已经自带但部分精简版 Ubuntu 需要手动装。FTDI FT232老牌芯片驱动是ftdi_sioUbuntu 默认自带稳定性极好。缺点是贵正品 FT232 芯片的线要三四十块。CP210x Silicon Labs 的方案驱动是cp210x默认也自带。很多开发板自带的串口芯片用的是它。其他比如 CH9102、CH343 这些新出的芯片老内核可能不认识需要去厂商官网下驱动源码自己编译。注意芯片型号不是看开发板的品牌而是看板子上那颗小芯片丝印。比如 T113 开发板上可能印着 CH340C也可能印着 CH343P两者驱动不一样。用lsusb命令可以快速确认。1.2 设备文件的命名规律ttyUSB 和 ttyACM 的区别很多新手看到别人的博客里写ttyUSB0自己却只有ttyACM0就以为出问题了。其实这是两种不同的 USB 通信模式ttyUSB走传统的 USB 转串口桥接方案芯片内部有 UART 控制器模拟出一个串口设备。CH340、FT232、CP210x 都是这种。ttyACM走 USB CDC ACM 协议设备把自己伪装成一个虚拟串口。很多开发板原生 USB 口、Arduino Uno、ESP32 的板载 USB 口都是这种。两种都能用来调试只是协议不同。如果系统里同时插了多个串口设备命名会依次递增比如第一个是ttyUSB0第二个是ttyUSB1以此类推。但这里有个坑后面会详细说——USB 枚举顺序不固定插拔几次之后设备名可能乱掉。1.3 为什么有人说要装驱动有人说不用装这个问题的答案取决于你用的 Ubuntu 版本和芯片型号。以 CH340 为例Ubuntu 18.04 及以后的内核自带ch341驱动即插即用什么都不用装。某些精简版、国产定制版 Ubuntu或者老版本内核确实没有这个驱动需要手动装。新版芯片如 CH343、CH9102老内核比如 4.x可能不支持必须装驱动。判断方法很简单插上开发板终端执行dmesg | tail -20看有没有类似ch341-uart now attached to ttyUSB0的信息。有就是驱动正常没有就是没识别到。2. 实操检查流程先定位再动手设备文件没出现不要急着装驱动先按顺序做一轮快速检查。这套流程我用了很多年能解决八九成的问题。2.1 用 lsusb 和 dmesg 快速确认系统是否识别到芯片第一步插上开发板之前先跑一遍lsusb记住当前列表。然后插上开发板再跑一遍。对比两次输出看多出来了什么lsusb Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 001 Device 005: ID 1a86:7523 QinHeng Electronics CH340 serial converter看到1a86:7523就是 CH3400403:6001是 FTDI FT23210c4:ea60是 CP210x。如果lsusb里根本找不到新设备说明 USB 物理链路就有问题——换线、换口、换电脑验证。lsusb能看到之后再配合dmesg看内核日志dmesg | tail -20正常情况会看到类似这几行usb 1-1: new full-speed USB device number 5 using xhci_hcd usb 1-1: New USB device found, idVendor1a86, idProduct7523 usb 1-1: New USB device strings: Mfr1, Product2, SerialNumber3 ch341-uart ttyUSB0: ch341-uart converter now attached to ttyUSB0看到最后一行就说明驱动加载成功/dev/ttyUSB0应该已经存在了。如果lsusb有设备但dmesg没有最后那行 attach 信息多半是驱动没加载或者芯片不被内核支持。2.2 临时验证方案手动加载内核模块如果确认内核没有自动加载驱动可以先手动加载试试。以 CH34x 系列为例# 检查模块是否已加载 lsmod | grep ch341 # 手动加载 sudo modprobe ch341 # 如果模块不存在检查系统是否有这个驱动文件 find /lib/modules/$(uname -r) -name *ch34*不同芯片对应的模块名不太一样ch341对应老款 CH340/CH341ch343对应新版ftdi_sio对应 FT232cp210x对应 CP2102/CP2103。手动modprobe只是临时方案重启后就没了但可以用来确认问题是否出在驱动加载环节。如果/lib/modules下找不到对应驱动文件那就需要自己编译安装驱动了。CH340 的官方驱动源码可以去 WCH 官网下载解压后是个文件夹进去执行make sudo make install sudo modprobe ch341编译环境需要先装好 build-essential 和 linux-headerssudo apt update sudo apt install build-essential linux-headers-$(uname -r)装完驱动重启一下或者重新插拔一次 USB 线再看/dev下有没有设备文件。这一步能解决绝大多数系统不认识这颗芯片的问题。2.3 常见芯片驱动安装实测记录说几个实测过的场景给大家参考T113 开发板配套的串口模块有批板子用的 CH340CUbuntu 20.04 插上直接识别不需要任何操作。后来换了个 CH343P 的版本20.04 内核 5.4 识别不了lsusb 能看到1a86:55d4但 dmesg 报unknown device最后去 WCH 官网下载了 CH343 驱动源码编了一下才解决。FTDI FT232 线Ubuntu 16.04 到 22.04 都用过全部免驱插上就是ttyUSB0。唯一的坑是市面上假货太多假芯片用ftdi_sio驱动有时会报device descriptor read/64, error -71这种只能换线。ESP32 开发板板载 USB 口走的是 CDC ACM识别为ttyACM0。有些板子用的桥接芯片是 CP2102识别为ttyUSB0。两种都有可能不要死等ttyUSB0。3. 权限问题与 udev 规则设备文件有了但打不开设备文件明明存在但一打开就报Permission denied这又是另一类高频问题。原因很简单Linux 下访问串口设备需要权限普通用户默认不在dialout组里自然没权限读写。3.1 dialout 权限组设置让普通用户也能用串口Ubuntu 下串口设备文件的属组是dialout普通用户想直接访问把自己加进这个组就行sudo usermod -aG dialout $USER改完要重新登录或者重启才能生效。这个操作在 Ubuntu 18.04、20.04、22.04、24.04 上都验证过一次搞定永不再犯。发行版可能略有不同但 Ubuntu 系的思路就是这个。如果你临时不想重新登录可以用 sudo 直接跑串口工具但这样每次都要 sudo很麻烦。还是建议把用户加到组里一劳永逸。3.2 自定义 udev 规则固定设备名解决插拔乱序问题插一个串口设备是ttyUSB0插两个就可能变成ttyUSB0和ttyUSB1但谁对应谁完全看 USB 枚举顺序可能导致脚本里写死了ttyUSB0却没连对设备。解决办法是用 udev 规则根据设备的 VID/PID 甚至序列号固定一个稳定、好记的设备名。创建一个规则文件sudo vim /etc/udev/rules.d/99-usb-serial.rules写入类似内容SUBSYSTEMtty, ATTRS{idVendor}1a86, ATTRS{idProduct}7523, SYMLINKttyCH340, MODE0666保存后用sudo udevadm control --reload-rules sudo udevadm trigger重新加载规则再插拔一次设备/dev/ttyCH340这个链接就会出现而且不管后面插什么设备这个名字都不会变。这对写自动化脚本、配置串口服务器来说非常实用。写规则的时候有几个细节用ATTRS带 S而不是ATTR它会在设备树的父节点上递归查找匹配属性实际使用中更稳。VID/PID 要和lsusb里的结果保持一致字母要小写。MODE0666设了之后其他用户也可以直接访问设备了安全性要求高的场合慎用还是建议用dialout组的方式。3.3 权限踩坑实录udev 规则写错导致设备消失曾经踩过一个很隐蔽的坑写 udev 规则时把SYMLINK写成了NAME结果设备文件被重命名走了/dev/ttyUSB0直接消失搞了半天以为是驱动问题。注意NAME会改变内核创建的设备节点名称而SYMLINK只是创建一个软链接两者区别很大实际中不要随便用NAME。另外规则里如果用了KERNEL匹配名字比如KERNELttyUSB*要格外小心可能匹配到其他不该匹配的设备。4. 虚拟机与开发板联动设备文件被宿主机抢走了开发板插在电脑上但虚拟机里的 Ubuntu 找不到设备文件这属于另一类问题——USB 设备根本没被透传到虚拟机里而是被宿主机Windows/macOS 等接管了。4.1 VMware/VirtualBox USB 直通设置无论是 VMware Workstation 还是 VirtualBox想让虚拟机访问 USB 设备都需要做两件事一是安装虚拟机增强工具。VMware 是 VMware ToolsVirtualBox 是 Guest Additions。不装的话 USB 直通功能时好时坏尤其是新版 Ubuntu 下很容易出错。二是在虚拟机设置里把 USB 控制器打开然后在可移动设备菜单里勾选你的串口设备。勾选时设备会从宿主机断开同时出现在虚拟机里。有些版本的 VMware 需要先在USB 兼容性里选 USB 3.1 或 USB 2.0开发板串口芯片大部分是低速 USB 2.0 全速设备兼容性一般选 USB 2.0 更稳。直通成功后在虚拟机里执行lsusb就能看到对应芯片的制造商信息dmesg也会显示驱动 attach 信息。如果虚拟机里还是找不到可以检查一下是不是宿主机上某个软件占用了设备——Windows 上常见的串口工具会抢占 COM 口导致设备无法被虚拟机透传。解决办法是在宿主机上关闭对应程序或者在 VMware 的设备管理里把自动连接勾上并重启虚拟机。4.2 开发板挂载 Ubuntu串口之外的网络连接方式很多时候我们不只是要一个串口调试终端还想让开发板直接挂载 Ubuntu 上的文件系统或者通过网络访问 Ubuntu 里的服务。这已经超出串口设备文件的范畴了但经常和串口问题一起出现。比如 t113 开发板跑 Linux通过串口连上之后可以用NFS挂载 Ubuntu 下的根文件系统这样代码编译在 Ubuntu运行在开发板中间通过以太网连接# Ubuntu 侧配置 NFS 共享 sudo apt install nfs-kernel-server sudo vim /etc/exports # 添加一行把 /home/user/nfsroot 共享给开发板网段 /home/user/nfsroot *(rw,sync,no_subtree_check,no_root_squash) sudo systemctl restart nfs-server开发板那边挂载mount -t nfs -o nolock 192.168.1.100:/home/user/nfsroot /mnt这种情况下串口的角色就只是控制台和调试信息输出数据交互走网络更快更稳。要注意的是如果 Ubuntu 有防火墙ufw需要放行 NFS 相关端口sudo ufw allow from 192.168.1.0/24 to any port nfs4.3 设备树与串口调试的关系热词里有 Xilinx SDK 生成设备树、t113 设备树文件这些内容。简单说在 Linux 设备树中UART 节点的存在与否直接影响该串口是否被内核初始化。如果你用的是主线内核 设备树的开发板串口设备节点没有在设备树里打开或者status disabled那即使驱动没问题串口也不会有输出。典型的设备树 UART 节点长这样uart3 { pinctrl-names default; pinctrl-0 uart3_pins; status okay; };修改设备树后编译 dtb替换开发板上的对应文件再重启串口设备就会出现在/dev下。不同开发板改法不同但有几点通用确认你用的是哪个 UART 外设/dev/ttyS0、ttyS1对应哪个节点查看板卡原理图或者 SDK 文档最直接。有些板子 UART 和 console 是同一个改了 console 设置会导致开机信息没了先从 bootargs 里确认console参数。设备树改动要配合 pinctrl 配置只有 UART 节点而引脚没有复用成 UART 功能一样不通。5. 串口通信验证与常见坑设备文件有了权限也对下一步就是测试能不能正常通信。这里同样有几个高频坑等着你。5.1 串口调试工具从 minicom 到 cutecom 再到 Python命令行调试最常用的是 minicom 和 screen各有优劣。minicom 功能全但配置麻烦screen 简单粗暴适合快速测试# screen 连接串口波特率 1152008N1 数据格式 screen /dev/ttyUSB0 115200 # 退出 screen按 CtrlA 然后按 K 再按 Yminicom 首次使用需要配置sudo minicom -s进入配置界面后选Serial port setup把串口设备改成/dev/ttyUSB0或你实际的设备名波特率改成 115200然后保存为默认配置。下次运行直接sudo minicom就行。新手有时候会觉得 minicom 不好用raw 模式下容易丢控制字符。可以试 cutecom图形界面更直观还能按十六进制发送接收调试一些特殊协议很好用sudo apt install cutecom cutecom如果你想做自动化Python 的 pyserial 库是标配import serial ser serial.Serial( port/dev/ttyUSB0, baudrate115200, bytesizeserial.EIGHTBITS, parityserial.PARITY_NONE, stopbitsserial.STOPBITS_ONE, timeout1 ) ser.write(bhello\r\n) data ser.readline() print(data) ser.close()Python 做串口调试的最大好处是方便组合各种协议逻辑读取传感器数据、发送指令、自动解析日志都不在话下。5.2 乱码、数据丢失、DMA 接收这些进阶问题串口通信中常见的问题不止设备文件找不到以下这些也值得留意乱码最常见是波特率不匹配。开发板输出 115200你终端设置成 9600必乱码。还有一种可能是电平问题TTL 电平的板子直接接 RS232 电平的接口也可能出现乱码需要加转换模块。数据丢字节这很可能是缓冲溢出。如果开发板一次性发大量数据上位机处理不过来内核缓冲区会被填满新数据就被丢弃。解决办法在代码里加快读取速度或者用pyserial设置更大的缓冲区或者数据量特别大时改用 DMA 空闲中断的方式接收。PY32F003 这类 MCU 用 DMA IDLE 中断接收数据能大幅减少 CPU 开销和丢数据概率。换行问题开发板输出\r\n上位机只按\n处理可能出现串口怎么换行的困惑。minicom 里可以设置Add Carriage Return来适配代码里最好统一按\r\n或\n两种都处理。读取阻塞pyserial设置了timeoutNone时会一直阻塞等待数据程序无响应。把 timeout 设成 0.5 或 1 秒就能避免。5.3 硬件层面的坑线材、供电、共地问题软件排查一圈都没问题还是不通那就要怀疑硬件了。几个比较典型的硬件问题USB 线质量差有些线看着是 USB 线里面只有电源正负极没有数据线插上只能供电不能通信。换一根线是最快的验证方法。ESD 静电击穿干燥环境下插拔串口线静电可能把串口芯片打坏。芯片坏了之后lsusb还能识别但发数据没反应。用手摸一下板子金属部分或使用带屏蔽的线缆能减少这个风险。供电不足USB 口供电能力有限开发板外接屏幕、传感器、舵机等大功率外设后电流不够可能导致串口芯片工作异常。用外部电源给开发板单独供电同时共地可以有效规避这个问题。共地问题USB 转 TTL 的设备两个设备之间除了 TX/RX还需要接 GND。不共地的话数据信号没有参考电平收发的数据可能全是乱码或者根本没反应。这种问题在串口调试里非常常见网上经常看到有人问为什么接上没反应、灯亮但数据不对十有八九是没共地。6. 与 Qt/Python 串口编程及交叉编译的衔接设备文件和权限问题解决之后下一步往往就是写上位机程序了。这里重点说下 Qt 串口编程和交叉编译。热词里有qt如何交叉编译生成能在开发板运行的文件这个和串口问题经常一起出现。6.1 Qt 串口编程从工程配置到读写操作Qt 自带QtSerialPort模块用起来比纯 Python 更贴近桌面应用场景。在.pro文件里加上QT serialport典型的初始化代码#include QSerialPort #include QSerialPortInfo QSerialPort serial; serial.setPortName(/dev/ttyUSB0); serial.setBaudRate(QSerialPort::Baud115200); serial.setDataBits(QSerialPort::Data8); serial.setParity(QSerialPort::NoParity); serial.setStopBits(QSerialPort::OneStop); serial.setFlowControl(QSerialPort::NoFlowControl); if (!serial.open(QIODevice::ReadWrite)) { qDebug() 打开串口失败: serial.errorString(); }用QSerialPort可以接readyRead信号来收数据配合定时器做超时处理写一个简单的串口调试助手大概两三百行代码就够了。界面还是你熟悉的 Qt Widgets 风格事件驱动比写命令行工具舒服很多。Qt 串口编程有个容易踩的坑枚举串口时会看到/dev/ttyS0这类系统串口和/dev/ttyUSB0这类 USB 转串口要区分开。系统串口通常没有权限也不可用枚举时有SerialNumber或description可以过滤最好做一次可用性测试foreach (const QSerialPortInfo info, QSerialPortInfo::availablePorts()) { qDebug() Port: info.portName(); qDebug() Description: info.description(); qDebug() Manufacturer: info.manufacturer(); }6.2 交叉编译与串口程序部署到开发板如果你是在 Ubuntu 上开发开发板的程序交叉编译是绕不开的一环。以 t113 开发板为例官方 SDK 通常会附带交叉编译工具链比如arm-linux-gnueabihf-gcc或者专门定制版。环境搭好后编译 Qt 项目/path/to/交叉编译工具链/bin/qmake your_project.pro make编译产物是一个可执行文件通过串口或网络传到开发板上运行。Qt 程序还需要把相应的 Qt 库拷贝到开发板否则运行时报libQt5SerialPort.so.5: cannot open shared object file。这种情况后续可以用find / -name libQt5SerialPort*找到真实依赖路径再配合LD_LIBRARY_PATH设置库路径即可。交叉编译有几个常见问题qmake 版本不对直接执行qmake用的是宿主机的 Qt编译出来的是 x86 程序开发板跑不了。一定要用交叉编译工具链里的 qmake路径通常在工具链的bin目录下。头文件和库文件缺失需要交叉编译版本的 Qt 库不能直接用宿主机上的。这也是新手最爱踩的坑之一。运行库不匹配开发板上缺少 Qt 运行库把宿主机/usr/lib/...下的库拷过去大概率不行架构不一样。正确做法是从交叉编译 SDK 里拷贝。6.3 串口调试与交叉编译联动排查实录一次实际经历用 Qt 写了个简单的串口调试助手宿主机上跑得好好的放到 t113 开发板上就报 Cannot connect: Permission denied。排查过程开发板上执行ls -l /dev/ttyS0发现属组是root:root普通用户访问不了。开发板上没有dialout组用户权限不好处理所以直接改设备节点权限chmod 666 /dev/ttyS0程序正常运行。这里要注意开发板上的权限策略和 Ubuntu 不完全一样很多精简根文件系统没有dialout组的概念直接改权限是最快的方案。如果想开机自动生效可以写一个开机脚本或者在/etc/rc.local里加一行chmod 666 /dev/ttyS0。7. 多设备插拔场景与进阶调试技巧解决了单个设备的问题接下来往往会遇到更复杂的场景同时接多块开发板或者开发板之外还有 USB 转串口工具、仿真器、USB 摄像头等设备混插。设备文件对应的设备可能发生变化以下几点建议值得留意。7.1 用 symlink 和脚本管理多个开发板多板卡调试时我的做法是每块板子按串口芯片的序列号如果芯片支持或者 USB 接口物理位置固化成独立的软链接。CH340 系列的芯片通常没有唯一序列号不同板子用同一芯片时lsusb看到的 VID/PID 一样只能通过ATTRS{devpath}绑定物理端口来区分。例如一台机器同时插了四块 t113可以写一个 udev 规则绑定 USB 端口位置SUBSYSTEMtty, KERNELttyUSB*, ATTRS{devpath}1-1.1, SYMLINKt113_board_01 SUBSYSTEMtty, KERNELttyUSB*, ATTRS{devpath}1-1.2, SYMLINKt113_board_02运行udevadm info -a -n /dev/ttyUSB0 | grep devpath可以查到当前设备挂在哪个端口规则写好后用插拔验证一下即可。这样不管/dev/ttyUSB后面数字怎么变你的脚本和 IDE 里始终用的是固定的软链名省心很多。7.2 日志分析与内核调试技巧如果dmesg里没有任何报错但设备就是不可用试着插上设备后立刻执行sudo dmesg -w这样可以实时看内核日志观察设备从插入到被处理的全过程。如果出现cannot enqueue或者timeout这类 USB 层错误大概率是硬件问题比如线缆接触不良、USB 口供电不足、芯片被静电打坏了等。另外串口设备文件消失还有一个容易被忽略的原因一个设备同时只能被一个程序占用。如果 Ubuntu 上已经有一个进程打开了/dev/ttyUSB0另一个进程打开时会报 Device or resource busy。遇到这种情况用lsof /dev/ttyUSB0就能看到是哪个进程占用了。lsof /dev/ttyUSB0这是排查设备文件在但打不开时非常实用的一条命令。7.3 为什么我建议用 USB 转 TTL 而不是板载 USB 串口这个问题有点争议但我的实际感受是开发板自带的 USB 转串口芯片确实方便但出问题后排查链路太长。板载 USB 口往往还承担了供电、烧录、MSC 等多种功能驱动环境一复杂就容易出幺蛾子。而外接一个 CH340 USB 转 TTL 小板插上去就是纯粹的一个串口逻辑简单问题定位快。尤其是做低功耗调试或者需要调试低电平 UART 的场合外置串口工具加个逻辑分析仪比板载 USB 转串口可靠得多。而且坏了一个换一根线几块钱就解决不用拿着整块开发板去返修。8. 完整的排查清单和实测体会最后给出一份可以直接复制使用的排查清单按顺序执行大概率能解决你开发板串口连接到 Ubuntu 后未找到对应的设备文件的问题lsusb对比插拔前后的设备列表确认 USB 层是否识别到芯片。dmesg | tail -20看内核日志确认驱动是否加载成功。ls -l /dev/ttyUSB* /dev/ttyACM*查看设备文件是否创建成功注意不要死等ttyUSB0有的芯片是ttyACM0。如果设备文件出现但打不开执行usermod -aG dialout $USER并重新登录。如果设备文件没出现检查驱动lsmod | grep ch341、modprobe ch341、find /lib/modules/... -name *ch34*。虚拟机上使用确认 USB 直通已开启增强工具已安装设备没有被宿主机软件占用。写 udev 规则固定设备别名防止多设备插拔后混乱。还是不行换线、换口、换电脑交叉验证排除硬件问题。确认共地、确认波特率、确认没有别的进程占用串口。这份清单我打印出来贴在工作台旁边踩过无数次坑之后整理出来的。嵌入式开发这东西80% 的问题都出在环境配置上真正复杂的业务逻辑反而占少数。串口作为开发板最主要的调试通道把这一套吃透了后面遇到网络、仿真器、烧录器的问题排查思路也是相通的。有一说一设备和系统之间的问题从来没有玄学每一步都能找到对应的日志或者现象。搞不定往往只是信息缺一环而不是真的无解。
返回列表