今天在使用USB转UART模块连接设备时发现我的Ubuntu虚拟机无法识别USB设备,这个模块使用的CH340芯片,在Windows主机中可以识别到串口并连接,所以初步判断为虚拟机中缺少ch340驱动。实际上自Linux内核版本2.6.24起,Linux主线内核已内置ch341串行驱动。其位置在:drivers/usb/serial/ch341.c,但是内置驱动无法保持更新,驱动官方建议客户安装使用新的驱动。
安装驱动
在安装前先查看下内核版本,有些内核版本可能与下载的驱动文件存在不兼容现象:
uname -r
这里我的内核版本是 6.8.0-39-generic
6.8.0-39-generic
随后在官网下载CH340的驱动文件压缩包:
压缩包下载地址:CH341SER_LINUX.ZIP - 南京沁恒微电子股份有限公司 (wch.cn)
我的Ubuntu虚拟机是带图形显示的版本,无头模式的系统可以使用FTP或者SSH的方式把下载好的压缩包上传到服务器或虚拟机,也可以尝试下其他方式:
下载完成后cd到压缩包所在的路径,在终端输入命令将压缩包解压缩到/usr/local/src文件夹:
sudo unzip CH341SER_LINUX.ZIP -d /usr/local/src
文件解压后可以看到文件夹内有一个markdown的说明文档和driver文件夹,driver文件夹内存放着ch341的.c文件与.h、Makefile文件,其中Makefile文件主要用于自动化编译和构建程序。
driver文件夹内容如下:
编译驱动文件
随后在终端切换到解压后的驱动目录:
cd /usr/local/src/CH341SER_LINUX/driver
输入make命令构建文件:
sudo make
这里有可能会报错,因为CH341.C文件中在定义ch341_tty_write函数时与我的内核版本(6.8.0-39-generic)不兼容:
make -C /lib/modules/6.8.0-39-generic/build M=/usr/local/src/CH341SER_LINUX/driver
make[1]: 进入目录“/usr/src/linux-headers-6.8.0-39-generic”
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc-13 (Ubuntu 13.2.0-23ubuntu4) 13.2.0
You are using: gcc-13 (Ubuntu 13.2.0-23ubuntu4) 13.2.0
CC [M] /usr/local/src/CH341SER_LINUX/driver/ch341.o
/usr/local/src/CH341SER_LINUX/driver/ch341.c:1460:18: error: initialization of ‘ssize_t (*)(struct tty_struct *, const u8 *, size_t)’ {aka ‘long int (*)(struct tty_struct *, const unsigned char *, long unsigned int)’} from incompatible pointer type ‘int (*)(struct tty_struct *, const unsigned char *, int)’ [-Werror=incompatible-pointer-types]
1460 | .write = ch341_tty_write,
| ^~~~~~~~~~~~~~~
/usr/local/src/CH341SER_LINUX/driver/ch341.c:1460:18: note: (near initialization for ‘ch341_ops.write’)
cc1: some warnings being treated as errors
make[3]: *** [scripts/Makefile.build:243:/usr/local/src/CH341SER_LINUX/driver/ch341.o] 错误 1
make[2]: *** [/usr/src/linux-headers-6.8.0-39-generic/Makefile:1926:/usr/local/src/CH341SER_LINUX/driver] 错误 2
make[1]: *** [Makefile:240:__sub-make] 错误 2
make[1]: 离开目录“/usr/src/linux-headers-6.8.0-39-generic”
make: *** [Makefile:5:default] 错误 2
使用Vim修改下ch341.c文件,这里我的Ubuntu系统并非实际的生产环境,如果是生产环境还得考虑一些其他因素
sudo vi /usr/local/src/CH341SER_LINUX/driver/ch341.c
找到 ch341_tty_write 函数,将函数定义修改一下,原函数定义如下:
int ch341_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
修改后的函数定义为:
ssize_t ch341_tty_write(struct tty_struct *tty, const unsigned char *buf, size_t count)
原代码中,count
被定义为int
类型;修改后count
被定义为size_t
类型。使用size_t
作为count
的类型,更明确地表示count
应该是一个非负整数,表示要处理的数据量的大小。最后函数返回类型是ssize_t
,是一个允许函数返回负值以表示错误或特殊情况的有符号类型。
再次输入make构建命令后构建成功:
使用modprobe加载模块,modprobe可以自动处理内核模块
sudo modprobe ch341
使用lsmod命令查看是否加载成功,这个命令可以显示已加载到内核中的模块列表:
lsmod
模块加载成功:
输入命令让驱动永久生效:
sudo make install
查看串口设备信息及端口号
使用lsusb命令查看USB连接设备,可以显示本机USB设备列表及其详细信息:
lsusb
这里可以看到连接到了一个CH340设备
输入以下命令可以显示特定USB设备的详细信息和树状结构显示USB设备层次
lsusb -v -d 1a86:7523 // 刚才检查出来的设备ID号
lsusb -t
输入命令列出所有以 /dev/ttyUSB
开头的设备文件
ls /dev/ttyUSB*
这里可以看到USB串行设备的端口号了
官方说明文件
以下内容全部为官方的说明文件:
针对USB转UART芯片ch340、ch341等的USB串行驱动。事实上,自Linux内核版本2.6.24起,Linux主线内核已内置ch341串行驱动。其位置在:drivers/usb/serial/ch341.c,遗憾的是,内置驱动无法保持更新。我们建议客户使用此驱动。
- 打开“终端”
- 切换到“driver”目录
- 使用“make”编译驱动,如果成功,你将看到模块“ch341.ko”
- 输入“sudo make load”或“sudo insmod ch341.ko”动态加载驱动
- 输入“sudo make unload”或“sudo rmmod ch341.ko”卸载驱动
- 输入“sudo make install”使驱动永久生效
- 输入“sudo make uninstall”移除驱动
- 你可以参考以下链接获取uart应用程序,你可以使用gcc或交叉编译工具cross-gcc进行编译
GitHub - WCHSoftGroup/tty_uart: linux tty uart application
在驱动工作之前,你应确保USB设备已插入并正常工作,你可以使用shell命令“lsusb”或“dmesg”进行确认,这些设备的USB VID为[1a86],你可以在“ch341.c”中定义的id表中查看所有ID。
如果设备工作正常,驱动将在/dev目录下创建名为“ttyCH341USBx”的tty设备。