我把这块经验完整写出来了,从驱动到底层调试一条线捋清楚,你照着排查基本都能解决。
1. 问题现象:插上开发板,Ubuntu里就是找不到设备文件
我一直觉得,串口这玩意儿是嵌入式开发里最“基础但也最磨人”的一环。很多朋友拿到开发板,第一步不是点灯,而是先想办法把串口打通。因为只有串口通了,你才能看到bootloader的打印、内核的日志,才能跟板子“对话”。
但麻烦也往往出在这一步:把开发板的USB转串口线插到电脑上,Windows那边设备管理器里还能看到“COM3”“COM4”,换到Ubuntu虚拟机或者实体机里,敲ls /dev/ttyUSB*或者ls /dev/ttyACM*,结果一片空白,什么都找不到。
这种时候,新手的第一反应是“驱动没装吧”。但实际上,在Linux系统里,串口设备找不到对应的设备文件,原因往往比“驱动没装”复杂得多。可能是线的问题,可能是内核模块没加载,也可能是权限不够,甚至是你压根看错了设备名。
我最早在给一块全志T113开发板调试时,也踩过这个坑。当时手头用的是一条CH340芯片的USB转串口线,插上去之后lsusb能看到设备,但/dev下就是没有ttyUSB0。折腾了一下午,最后才发现是内核模块没自动加载,手动modprobe ch340之后,设备文件立刻出现了。
这种经历我想很多人都有过。所以这篇就把“开发板串口连接Ubuntu后找不到设备文件”这个问题的完整排查思路整理出来,从底层原理到实际操作,从驱动安装到权限配置,一次性讲透,下次遇到直接照着排查就行。
适合谁看?刚接触嵌入式Linux的新手、在VMware里装Ubuntu做开发的朋友、以及那些被CH340、CP210x、FTDI这些USB转串口芯片折腾过的老哥。
2. 先搞懂串口设备文件的本质,再谈解决方案
2.1 /dev目录下到底发生了什么
很多人一上来就敲命令,敲完发现没有就慌。但我建议先花两分钟理解一下Linux的设备和文件的关系。
在Linux世界里,一切皆文件。串口这种硬件设备,在系统里也会被抽象成一个个文件,放在/dev目录下。你操作串口,本质上就是读写这个文件。
常见的串口设备文件名有这么几类:
/dev/ttyS0、/dev/ttyS1——这是主板上原生串口,对应PC主板的COM口,嵌入式开发里很少用;/dev/ttyUSB0、/dev/ttyUSB1——这是USB转串口芯片(比如CH340、CP210x、FTDI)枚举出来的设备,绝大多数开发板调试串口走的是这个;/dev/ttyACM0、/dev/ttyACM1——这是USB CDC ACM类设备,比如STM32的USB虚拟串口、某些ESP32板载串口、Arduino等,走的是这个。
关键点在于:这个设备文件不是天生的,而是当USB转串口芯片被内核识别并绑定驱动之后,由驱动动态创建出来的。
所以,你找不到设备文件,本质上就是:芯片没有被内核识别,或者识别了但没绑定正确的驱动,或者驱动创建了节点但你的用户权限不够压根看不到。
理解了这一层,后面所有排查步骤就都顺理成章了。
2.2 为什么Windows下正常,Ubuntu下不行
这是一个特别经典的困惑。很多人在Windows设备管理器里能看到COM口,说明硬件和线材本身是没问题的。但是一到Ubuntu就找不到,原因通常是这么几个:
第一,Windows的驱动是芯片厂商提供的,安装包一般会自动装好。而Linux多数情况下用的是内核自带的驱动模块,但模块可能没启用、没加载,或者被你系统里某个操作给屏蔽了。
第二,虚拟机场景下,USB设备的“直通”没有配置好。你用的是VMware或者VirtualBox,宿主机的USB设备默认是不会自动“送”进虚拟机的,需要在虚拟机设置里手动添加USB控制器和过滤器,把USB设备挂载到虚拟机里。很多人忽略这一步,自然在虚拟机里看不到设备。
第三,权限问题。设备节点创建出来了,但默认属于dialout组,而当前用户不在这个组里,ls /dev的时候可能因为权限不够而无法访问,甚至某些终端配置下直接不显示(后者少见,但确实有)。
搞清楚这三个方向,排查就有了头绪。
3. 三板斧快速定位:lsusb、dmesg、ls /dev
3.1 第一步:确认USB设备是否被系统识别
把USB转串口线插上开发板,再连到电脑USB口之后,第一步不是去/dev目录翻,而是先确认USB总线层面是否认到了这个设备。
打开终端,输入:
lsusb这个命令会列出当前USB总线上所有设备。你需要找的是类似这样的行:
Bus 001 Device 004: ID 1a86:7523 QinHeng Electronics CH340 serial converter或者是:
Bus 001 Device 003: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port再或者:
Bus 001 Device 005: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC如果你能在lsusb里看到芯片厂商和型号,恭喜,硬件层面是通的。这一步同时也告诉了你芯片用的是哪家的方案,后面查驱动就有方向了。
如果lsusb里什么都看不到,说明USB枚举都没成功,那可能是:
- USB线是纯充电线,没有数据线芯;
- 开发板没有上电;
- 开发板的USB转串口电路有问题;
- USB口供电不足。
这种情况基本跟软件无关,先换线、换USB口、检查板上供电。
3.2 第二步:查看内核日志,驱动是否加载成功
接下来看看内核有没有为这个设备加载对应的驱动。用dmesg直接翻内核环形缓冲区,这里能看到设备从插入到枚举、到绑定驱动的全过程。
dmesg | tail -n 30正常情况你会看到:
usb 1-2: new full-speed USB device number 4 using xhci_hcd usb 1-2: New USB device found, idVendor=1a86, idProduct=7523 usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-2: Product: USB2.0-Serial usb 1-2: Manufacturer: QinHeng Electronics usb 1-2: SerialNumber: 00A00000000 ch341 1-2:1.0: ch341-uart converter detected usb 1-2: ch341-uart converter now attached to ttyUSB0看到最后一行now attached to ttyUSB0,说明驱动已经加载,设备节点也创建好了。这时候ls /dev/ttyUSB0肯定是存在的。
但如果你只看到枚举成功,却没有“usb 1-2: ch341-uart converter now attached to ttyUSB0”这一行,说明内核没能成功绑定驱动。常见情况是dmesg里出现类似:
usb 1-2: ch341-uart converter now attached to ttyUSB0根本没有,或者:
usb 1-2: usbfs: interface 0 claimed by 'cdc_acm' while 'ch341' sets config #1这说明驱动冲突了,芯片被另一个驱动抢占。
3.3 第三步:查设备节点,确认最终结果
如果没有问题,这时候再执行:
ls -l /dev/ttyUSB*或者:
ls -l /dev/ttyACM*看到类似这样的输出:
crw-rw---- 1 root dialout 188, 0 7月 19 10:30 /dev/ttyUSB0就说明设备节点已经创建好了。
注意这里有一个细节:这个文件的权限是crw-rw----,所有者是root,所属组是dialout。也就是说,如果不是root用户,且不在dialout组里,你是无法读写这个设备的。
那么下一步就来了:把当前用户加入dialout组。
sudo usermod -a -G dialout $USER执行完需要重新登录一次,或者重启虚拟机,组权限才会生效。这里多说一句:这一步很多人都漏了,导致minicom或串口助手能打开设备但发不出数据,或者直接报permission denied。
4. 驱动层面深挖:CH340、CP210x、FTDI、CDC ACM
4.1 CH340/CH341:最常见也最容易出幺蛾子
CH340几乎是国内开发板的标配,正点原子、野火很多板载USB转串口都是这个方案。它对应的Linux内核驱动叫ch341(对,就是少了一个0),从内核2.6.24开始就内置了。
但问题在于:某些精简版内核或者自行裁剪的内核里,这个模块可能没有被编译进去,或者被设为m(模块)但没自动加载。
确认方法:
lsmod | grep ch341如果没有任何输出,说明模块没加载。手动加载:
sudo modprobe ch341再查lsmod,或者直接看/dev/ttyUSB0是否出现。
如果modprobe报错找不到模块,说明内核里压根没这个驱动。这时候要么换一个完整版内核,要么手动编译驱动。好在,对绝大多数桌面版Ubuntu(22.04、24.04)来说,ch341驱动都是自带的,不太会遇到编译驱动这种终极玩法。
顺带提一嘴,如果插上去之后,dmesg里出现“usbfs: interface 0 claimed by 'cdc_acm' while 'ch341' sets config #1”这种冲突,说明内核把设备识别成了CDC ACM类。可以试着禁用cdc_acm模块再重新加载ch341:
sudo modprobe -r cdc_acm sudo modprobe ch341但这种情况不常见,一般出现在某些特殊的USB转串口线,芯片内置了CDC ACM固件。
4.2 CP210x:Silicon Labs的方案,模块名容易被忽略
CP2102、CP2104这类芯片在ESP32开发板、以及不少工业级调试工具里很常见。对应的内核模块叫cp210x。
排查方法跟CH340一样:
lsmod | grep cp210x sudo modprobe cp210x加载成功之后,设备文件同样是/dev/ttyUSB0。
有一个细节:如果你同时插了多片USB转串口芯片,设备文件编号不一定是ttyUSB0,可能是ttyUSB1、ttyUSB2。这时候用ls -l /dev/ttyUSB*看,或者用:
udevadm info /dev/ttyUSB1查看具体是哪个设备。
4.3 FTDI:工业级的选择,驱动一般没问题
FT232、FT2232这些是FTDI家的经典芯片,在高端开发板、仿真器、以及一些老设备上用得很多。对应的内核模块叫ftdi_sio:
lsmod | grep ftdi_sio sudo modprobe ftdi_sioFTDI芯片在Linux下的兼容性通常是最好的,基本插上就能用。但要注意,市面上有不少“山寨FTDI”芯片,某些内核版本会对这类芯片做识别拦截——插上去之后设备直接不被枚举,因为内核装了“黑名单”。这个比较坑,但如果你用的是原装芯片或者正规开发板,基本不会遇到。
4.4 CDC ACM:STM32虚拟串口、ESP32-C3等原生USB场景
如果你用的不是USB转串口线,而是开发板上的USB口直接引出虚拟串口功能(比如STM32的USB_CDC例程、ESP32-S3的原生USB),那么对应的设备节点不是ttyUSB0,而是ttyACM0。
对应的驱动模块是cdc_acm:
lsmod | grep cdc_acm这个模块绝大多数发行版默认是编译进内核的,一般不需要手动处理。唯一需要注意的是,ttyACM0和ttyUSB0的读写行为在某些串口工具下有差异,比如minicom里需要手动配置硬件流控为None,否则可能无法正常通信。
5. 权限、虚拟机和udev规则:三个隐藏大坑
5.1 dialout组权限,别等出了问题才想起来
前面提到了dialout组,这里再展开一下。
Linux下串口设备默认属于dialout或者uucp组(不同发行版不一样,Ubuntu是dialout)。用户在访问设备之前,必须在组里。否则用minicom打开会直接报错:
Cannot open /dev/ttyUSB0: Permission denied解决方式:
sudo usermod -a -G dialout $USER但重点来了:执行完这个命令之后必须注销重新登录,或者重启系统,组权限才会重新生效。很多人卡在这一步,就是因为没重新登录,一直以为自己没加成功。
如果你实在不想重新登录,有一个临时的替代方案,测试用够了:
sudo chmod 666 /dev/ttyUSB0这样所有用户都有读写权限了。但注意,设备节点重新插拔之后权限会恢复默认,这只适合临时测试。
5.2 VMware虚拟机:别忘了把USB设备“送”进去
很多人的Ubuntu是装在VMware里的,这时候遇到找不到设备,首先要排查的,反而是虚拟机有没有把USB设备直通进去。
VMware默认情况下,宿主机插入的USB设备会优先被宿主机捕获,只有当你手动把设备“连接”到虚拟机里,虚拟机才能看得到。
操作步骤:
- 打开VMware,在虚拟机窗口右下角找到USB图标(或者菜单栏“虚拟机”->“可移动设备”);
- 下拉菜单里找到你的USB转串口设备(比如"USB2.0-Serial");
- 点击“连接”;
- 此时设备会从宿主机“断开”,转而被虚拟机接管。
连接成功之后,lsusb里就能看到设备了,然后继续走3.1的步骤。
还有一个前置条件:虚拟机的USB控制器必须开启。在“虚拟机设置”里,选择“USB控制器”,确认勾选了“USB 2.0”或“USB 3.0”。如果用的是USB2.0的芯片,2.0和3.0都能兼容;但某些老芯片在USB3.0控制器下可能有兼容性问题,遇到插了不识别,可以试试改成USB2.0。
5.3 udev规则:让设备名固定,写自动化脚本不再玄学
等你调通了多个串口设备,就会发现另一个痛点:ttyUSB0这个编号是内核按枚举顺序分配的,不固定。今天插A板是ttyUSB0,明天插B板可能就变了,写自动化脚本时特别头疼。
解决办法是通过udev规则,根据USB设备的序列号、供应商ID、产品ID,绑定一个固定的软链接。
先查看设备的属性:
udevadm info -a -n /dev/ttyUSB0找到类似ATTRS{serial}、ATTRS{idVendor}、ATTRS{idProduct}的值,然后新建一个udev规则文件:
sudo vi /etc/udev/rules.d/99-usb-serial.rules内容格式:
KERNEL=="ttyUSB*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE="0666", SYMLINK+="t113_debug"保存后重新加载规则:
sudo udevadm control --reload-rules sudo udevadm trigger再插拔一次设备,/dev下就会出现一个/dev/t113_debug软链接,直接指向实际的ttyUSB节点。以后用这个固定路径操作串口,脚本就稳定多了。
6. 应用层验证:串口通了没有,用工具实测一把
6.1 minicom:最经典,但要先配置
找到了设备文件,权限也搞定了,接下来就是实测串口能不能正常通信。
最常用的工具是minicom。安装:
sudo apt install minicom先启动配置界面:
sudo minicom -s进入“Serial port setup”,按A修改串口设备路径为/dev/ttyUSB0,按E修改波特率。开发板调试串口常用的波特率是115200,但也有些老型号是57600,具体看板卡手册。
关键点是:如果你用的是USB转串口芯片(比如CH340),硬件流控必须设为No。否则可能出现能打开设备但是没有任何输出,或者输出乱码的情况。
设置完成后,选择“Save setup as dfl”保存为默认配置,然后退出设置界面,回到minicom主界面。此时给开发板上电,复位,正常就能看到bootloader的打印信息了。
如果打开设备报错权限不够,回到5.1节把权限搞定。如果打开设备后没有任何输出,检查以下几点:
- 波特率是否和板子匹配;
- 开发板的串口TX/RX是否交叉连接(如果用的是USB转串口线,TX接板子的RX,RX接板子的TX);
- 开发板是否已经上电启动;
- GND是否共地(不共地是新手最常见的翻车原因,USB转串口线必须和开发板共地)。
6.2 串口调试助手图形界面:不习惯命令行的人有福了
虽然嵌入式开发者大多习惯了命令行,但偶尔还是会觉得minicom操作不够直观。Ubuntu下有几个图形化的串口调试工具,我常用的是cutecom:
sudo apt install cutecom cutecom界面比较简洁,左边可以选设备、设波特率、数据位/停止位/校验位,右边是收发区,类似Windows下的串口助手。用起来顺手,适合刚开始接触Linux串口的同学。
除此之外,还有一个叫serialplot的工具,可以实时把串口收到的数据画成波形,做传感器调试的时候特别好用。如果你在做姿态传感器、GPS模块之类的数据采集,可以装一个试试。
6.3 用Python直接练手:绕过工具,代码才是最底层的控制
工具始终是别人封装好的,真正要理解串口,还得用代码直接操作。这里推荐用Python的pyserial库,几行代码就能打开串口收发数据:
pip install pyserial然后写个最简单的测试脚本:
import serial ser = serial.Serial( port='/dev/ttyUSB0', baudrate=115200, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1 ) # 发送数据 ser.write(b'hello\r\n') # 读取响应 while True: data = ser.readline() if data: print(data.decode('utf-8', errors='ignore'), end='')这个脚本可以快速验证串口是否能收能发。如果发送后没有任何回包,先确认开发板的串口是否配置为回显模式,或者是否真的在等外部数据。
这里我有一个使用习惯:调串口时永远先做“回环测试”。把USB转串口线的TX和RX短接在一起,然后在终端里发什么就能收到什么。如果自发自收成功,说明串口链路没问题;不成功,说明设备/驱动/权限某个环节还有问题。用这种排除法,能帮你快速划分责任边界。
6.4 stty命令:直接查看和修改串口参数
有时候不想装额外工具,纯命令行里也可以快速配置串口参数。用stty:
stty -F /dev/ttyUSB0 115200 cs8 -cstopb -parenb查看当前串口配置:
stty -F /dev/ttyUSB0 -a然后用cat直接接收:
cat /dev/ttyUSB0再用echo发送:
echo "hello" > /dev/ttyUSB0这种方法特别适合快速确认串口是否通,不依赖任何第三方工具。
7. 常见问题速查表与终极排查清单
说了这么多,最后整理一个速查表,你在实际调试中可以直接对照着排查。
| 问题现象 | 可能原因 | 排查/解决方式 |
|---|---|---|
lsusb看不到USB设备 | 线材问题/USB口接触不良/未上电 | 换线、换USB口,确认开发板供电正常 |
lsusb能看到,/dev下无ttyUSB | 内核驱动未加载 | lsmod | grep ch341,sudo modprobe ch341 |
dmesg显示驱动被占用 | cdc_acm与ch341冲突 | sudo modprobe -r cdc_acm后重新插拔 |
| 设备文件存在但权限拒绝 | 用户不在dialout组 | sudo usermod -a -G dialout $USER后重新登录 |
| VMware里找不到设备 | USB设备未直通到虚拟机 | 虚拟机右下角USB图标手动“连接”设备 |
| 设备有,但无任何串口输出 | TX/RX接反/GND不共地/波特率错误 | 交叉接线、确认共地、核对波特率 |
| minicom打开设备失败 | 硬件流控未关闭/权限不足 | 关闭流控,检查用户组权限 |
| 设备文件编号不固定 | udev默认枚举顺序 | 添加udev规则,绑定固定软链接 |
排查时我建议按这个顺序走:
lsusb确认USB枚举成功;dmesg | tail确认驱动加载成功;ls -l /dev/ttyUSB*确认设备节点存在;groups确认当前用户权限;- 回环测试确认串口链路。
走完这五步,99%的“找不到设备文件”问题都能定位出来。
8. 从串口再往下走一步:设备树、Qt交叉编译和板卡挂载
串口通了之后,开发工作才算真正开始。很多用T113、K230这类板卡的朋友,下一步往往是配置网络、挂载文件系统、用Qt交叉编译开发界面程序。
这里我补充一点和串口强相关的两个场景。
第一个是设备树。如果你的板子上有多个串口,你要在Linux里使用其中某一个,往往需要查看设备树里对应的uart节点是否被启用。比如T113的设备树文件里,sunxi-uart0默认用于调试串口,其他的UART默认可能处于disabled状态。你需要把对应的status = "okay";加上,然后重新编译设备树、更新到boot分区,重启之后才会出现/dev/ttyS1、/dev/ttyS2这些节点。
所以,如果你已经摸清了USB转串口的设备节点问题,但发现板子上板载的串口(ttyS1之类的)打不开,那就要往设备树方向查了。
第二个是Qt交叉编译。这个和串口不是直接相关,但几乎是同一批人要做的事。很多朋友问“Qt如何交叉编译生成能在开发板运行的文件”,其实核心就三件事:交叉编译工具链选对、Qt库用同版本交叉编译、编译产物拷贝到板子上时库路径配置好。用命令行构建时,-prefix参数指向sysroot,-xplatform指定为linux-arm-gnueabi-g++之类的平台,这样生成的二进制才能在开发板架构上跑。
还有一点,在开发板上跑Qt程序时,如果要用到串口,记得链接Qt5SerialPort模块:
QT += serialport交叉编译的坑更多,但那是另一个话题了,这里先提个醒:只要你的主机Ubuntu串口真正调通了,后面通过串口终端在板子上执行命令、挂载主机目录这些操作,都会顺利很多。
再补充一个和开发板挂载有关的技巧:板子跑起来之后,很多时候不用来回插拔SD卡或者U盘,直接用网络挂载。开发板通过网线和主机连到同一网段后,在板子上跑:
mount -t nfs 192.168.1.100:/home/user/share /mnt这样板子就能直接访问你Ubuntu主机上的目录,交叉编译出来的可执行文件放到共享目录里,板子上直接运行,调试效率高一大截。当然NFS服务端需要先在Ubuntu上装好nfs-kernel-server并配置exports文件,这个操作相对成熟,网上也有很多教程,不多展开了。