在Ubuntu 22.04 LTS上编译CH341驱动前,需要确保系统环境配置正确。我遇到过不少新手因为漏装关键依赖导致编译失败的情况,这里把踩过的坑都列出来。
首先打开终端,更新软件源列表并升级现有软件包:
bash复制sudo apt update && sudo apt upgrade -y
安装编译所需的核心工具链:
bash复制sudo apt install build-essential linux-headers-$(uname -r) -y
这个命令会安装gcc编译器、make工具以及当前内核版本对应的头文件。有次我在树莓派上操作时发现linux-headers包找不到,后来发现是Raspberry Pi OS的包名不同,需要改用sudo apt install raspberrypi-kernel-headers。
额外建议安装的调试工具:
bash复制sudo apt install usbutils libusb-1.0-0-dev -y
usbutils包含lsusb等实用工具,libusb开发包则有助于排查USB设备识别问题。记得有次驱动加载失败,就是用lsusb -v查看到设备描述符异常,最终发现是USB线接触不良。
验证内核版本是否匹配:
bash复制uname -r
一定要确保安装的headers版本与这个输出完全一致。我在多台设备上测试时,就遇到过自动更新内核后headers未同步更新的情况,导致驱动无法加载。
官方CH341驱动源码获取方式有过几次变更。目前最可靠的方式是从GitHub克隆最新维护的版本:
bash复制git clone https://github.com/juliagoda/CH341SER.git
cd CH341SER
这个仓库相比老版本的优势是支持Linux 5.10+内核,实测在Ubuntu 22.04上更稳定。曾经用旧版源码编译时遇到implicit declaration of function警告,就是内核API变更导致的。
编译前建议先清理旧编译产物:
bash复制make clean
正式编译命令很简单:
bash复制make
但这里有个细节要注意:如果系统启用了Secure Boot,即使编译成功后续加载也会失败。我第一次遇到Operation not permitted错误时,花了半天才发现是Secure Boot的问题。
编译成功后会在目录下生成ch34x.ko文件(注意新版驱动文件名已从ch341变为ch34x)。可以用modinfo检查模块信息:
bash复制modinfo ch34x.ko
驱动加载看似简单,但实际可能遇到各种意外。先尝试手动加载:
bash复制sudo insmod ch34x.ko
常见问题及解决方案:
dmesg | grep ch34x查看详细错误sudo rmmod ch341移除旧驱动成功加载后,查看内核日志确认:
bash复制dmesg | tail -n 20
设备权限问题也很关键,否则普通用户无法访问串口。创建udev规则文件:
bash复制sudo nano /etc/udev/rules.d/99-ch34x.rules
写入以下内容:
code复制SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE="0666"
重载udev规则:
bash复制sudo udevadm control --reload-rules
sudo udevadm trigger
临时加载的驱动重启后会失效。实现开机自动加载有三种方案:
方案一:直接复制到内核模块目录
bash复制sudo cp ch34x.ko /lib/modules/$(uname -r)/kernel/drivers/usb/serial/
sudo depmod -a
然后创建conf文件:
bash复制sudo nano /etc/modules-load.d/ch34x.conf
写入模块名:
code复制ch34x
方案二:DKMS动态编译(推荐)
bash复制sudo apt install dkms
sudo mkdir -p /usr/src/ch34x/1.0
sudo cp -r * /usr/src/ch34x/1.0/
sudo dkms add -m ch34x -v 1.0
sudo dkms build -m ch34x -v 1.0
sudo dkms install -m ch34x -v 1.0
DKMS的优势是内核升级后会自动重新编译驱动。
方案三:Systemd服务(适合复杂场景)
创建服务文件:
bash复制sudo nano /etc/systemd/system/ch34x.service
内容示例:
code复制[Unit]
Description=CH34x Driver Load
[Service]
Type=oneshot
ExecStart=/sbin/insmod /path/to/ch34x.ko
[Install]
WantedBy=multi-user.target
然后启用服务:
bash复制sudo systemctl enable ch34x.service
驱动加载成功后,插入CH341设备,用多种方式验证:
基础检查:
bash复制lsusb | grep 1a86
应该能看到类似1a86:7523 QinHeng Electronics CH340 serial converter的输出。
内核模块检查:
bash复制lsmod | grep ch34x
设备节点查看:
bash复制ls /dev/ttyCH34*
正常情况下会出现/dev/ttyCH341USB0节点。
高级诊断工具:
bash复制sudo apt install putty screen
screen /dev/ttyCH341USB0 115200
如果看到空白终端且无错误,说明串口通信正常。
常见故障排查流程:
对于嵌入式开发,通常需要与各种IDE配合:
Arduino IDE配置:
/dev/ttyCH341USB0PlatformIO配置:
在platformio.ini中添加:
code复制upload_port = /dev/ttyCH341USB0
monitor_port = /dev/ttyCH341USB0
Python串口编程示例:
python复制import serial
ser = serial.Serial('/dev/ttyCH341USB0', 115200, timeout=1)
ser.write(b'Hello CH341\n')
print(ser.readline())
ser.close()
C语言示例:
c复制#include <fcntl.h>
#include <termios.h>
int fd = open("/dev/ttyCH341USB0", O_RDWR | O_NOCTTY);
struct termios options;
tcgetattr(fd, &options);
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
tcsetattr(fd, TCSANOW, &options);
write(fd, "AT\r\n", 4);
char buf[256];
read(fd, buf, sizeof(buf));
默认配置可能不适合高速通信,可以通过stty调整参数:
bash复制stty -F /dev/ttyCH341USB0 921600 raw -echo -echoe -echok
查看当前串口设置:
bash复制stty -F /dev/ttyCH341USB0 -a
提高USB传输效率的内核参数(添加到/etc/sysctl.conf):
code复制usbcore.usbfs_memory_mb=1000
usbcore.usbfs_max_buffers=32
对于需要低延迟的场景,可以禁用串口控制台:
bash复制sudo systemctl stop serial-getty@ttyCH341USB0.service
sudo systemctl disable serial-getty@ttyCH341USB0.service
当连接多个CH341设备时,可以通过udev规则创建固定设备名:
首先查看设备唯一标识:
bash复制udevadm info -a -n /dev/ttyCH341USB0 | grep '{serial}'
创建规则文件:
bash复制sudo nano /etc/udev/rules.d/99-ch34x-aliases.rules
示例规则:
code复制SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", ATTRS{serial}=="A1001", SYMLINK+="ttyArduino"
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", ATTRS{serial}=="B2002", SYMLINK+="ttyESP32"
重载规则后,设备将固定出现在/dev/ttyArduino和/dev/ttyESP32。我在做多节点机器人项目时,这个技巧特别有用,不用每次插拔都重新配置端口号。