最近在调试ELF2开发板时,发现系统默认没有预装CH341 USB转串口驱动。这让我想起去年做另一个项目时,也遇到过类似问题——当时只是简单用insmod临时加载了驱动,结果每次重启都要重新操作,给现场部署带来不少麻烦。这次我决定从产品化角度,彻底解决这个问题。
对于嵌入式开发者来说,驱动加载只是第一步。真正的挑战在于如何让驱动成为系统的一部分,稳定可靠地工作。本文将分享从驱动编译到系统集成的完整流程,特别适合需要构建稳定产品原型的工程师参考。
在开始之前,我们需要确认几个关键点:
首先检查开发板当前运行的内核版本:
bash复制uname -r
记录下输出的内核版本号,比如5.10.66。然后,在开发主机上进入内核源码目录,验证配置:
bash复制cd /path/to/kernel/source
make menuconfig
关键点:如果之前清理过编译中间文件,可能需要重新生成.config。ELF2开发板通常会提供默认配置文件,位置一般在arch/arm64/configs/目录下。
获取CH341驱动源码后,我们需要修改Makefile以适应交叉编译环境。以下是经过验证的Makefile模板:
makefile复制CONFIG_MODULE_SIG=n
KERNELDIR := /path/to/elf2/kernel/source
PWD := $(shell pwd)
obj-m := ch341.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules \
ARCH=arm64 \
CROSS_COMPILE=aarch64-none-linux-gnu-
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
需要特别注意的两个参数:
编译完成后,会生成ch341.ko文件。将其传输到开发板进行测试:
bash复制insmod ch341.ko
ls /dev/tty*
如果看到/dev/ttyUSB0等设备节点出现,说明驱动加载成功。
临时加载驱动虽然简单,但不符合产品化需求。下面比较三种系统集成方案:
| 方案 | 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 手动加载 | insmod命令 | 简单直接 | 重启失效 | 临时调试 |
| modprobe | /lib/modules配置 | 自动处理依赖 | 需要完整配置 | 常规部署 |
| 内置内核 | 编译进内核镜像 | 开机即用 | 需要重新编译内核 | 量产固件 |
对于大多数产品原型,modprobe方案是最佳平衡点。它不仅支持开机自动加载,还能处理模块依赖关系。
正确的驱动部署位置应该是:
code复制/lib/modules/$(uname -r)/kernel/drivers/usb/serial/
执行以下命令完成标准安装:
bash复制sudo cp ch341.ko /lib/modules/$(uname -r)/kernel/drivers/usb/serial/
sudo depmod -a
注意:
depmod -a命令会重新生成modules.dep等依赖文件,这一步绝对不能省略
现代Linux系统通常使用systemd管理模块加载。创建配置文件:
bash复制sudo vi /etc/modules-load.d/ch341.conf
文件内容只需要一行:
code复制ch341
重启后,可以通过以下命令验证:
bash复制lsmod | grep ch341
如果需要特定权限或别名,可以创建udev规则:
bash复制sudo vi /etc/udev/rules.d/99-ch341.rules
添加如下内容:
code复制SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE="0666", SYMLINK+="ttyCH341"
这样设备会同时出现在/dev/ttyCH341,方便应用程序固定访问。
对于需要构建完整根文件系统的项目,可以考虑更彻底的集成方案:
在Buildroot配置中:
code复制make menuconfig
进入:
code复制Target packages → Hardware handling → ch341
标记为[*]表示编译为模块,[M]表示编译进内核。
在recipe中增加:
bitbake复制SRC_URI += "file://ch341.c \
file://Makefile"
do_compile() {
oe_runmake KERNEL_PATH=${STAGING_KERNEL_DIR} \
KERNEL_VERSION=${KERNEL_VERSION}
}
do_install() {
install -d ${D}${base_libdir}/modules/${KERNEL_VERSION}/kernel/drivers/usb/serial
install -m 0644 ${S}/ch341.ko ${D}${base_libdir}/modules/${KERNEL_VERSION}/kernel/drivers/usb/serial
}
确保内核配置包含以下选项:
code复制CONFIG_USB_SERIAL=y
CONFIG_USB_SERIAL_CH341=y
可以通过make menuconfig在以下路径找到:
code复制Device Drivers → USB support → USB Serial Converter support → USB Winchiphead CH341 Single Port Serial Driver
问题1:模块加载失败,提示"Invalid module format"
解决方案:
bash复制modinfo ch341.ko | grep vermagic
uname -r
确保两者显示的kernel版本完全一致
问题2:设备节点没有生成
排查步骤:
bash复制dmesg | grep ch341
bash复制lsusb
bash复制lsmod | grep ch341
问题3:开机自动加载失败
检查流程:
bash复制journalctl -u systemd-modules-load
对于工业级应用,还需要考虑:
一个增强版的Makefile示例:
makefile复制EXTRA_CFLAGS += -DDEBUG -DCH341_USE_WATCHDOG
obj-m := ch341.o
ch341-objs := ch341_main.o ch341_watchdog.o
# 添加看门狗超时设置(单位:秒)
EXTRA_CFLAGS += -DCH341_WATCHDOG_TIMEOUT=30
在实际项目中,我们团队发现CH341芯片在连续工作72小时后可能出现不稳定。通过添加硬件看门狗和定期重置机制,最终实现了2000+小时的稳定运行。