Ubuntu系统安装CH340/CH341驱动
使用USB转UART模块连接设备时发现我的Ubuntu识别到设备了但是minicom链接乱码,排除了波特率的问题,查看使用的CH340芯片,在Windows主机中无乱码,所以初步判断为缺少ch340驱动。
自Linux内核版本2.6.24起,Linux主线内核已内置ch341串行驱动。其位置在:drivers/usb/serial/ch341.c,但是内置驱动无法保持更新,驱动官方建议客户安装使用新的驱动。
安装驱动
uname -r
这里我的内核版本是 6.8.0-40-generic
6.8.0-40-generic
随后在官网下载CH340的驱动文件压缩包:
压缩包下载地址:CH341SER_LINUX.ZIP - 南京沁恒微电子股份有限公司 (wch.cn)
下载好 scp 传入服务器
scp -r CH341SER_LINUX.ZIP root@192.168.11.78:/root/
在终端输入命令将压缩包解压缩到/usr/local/src文件夹:
unzip CH341SER_LINUX.ZIP -d /usr/local/src
文件解压后可以看到文件夹内有一个markdown的说明文档和driver文件夹,driver文件夹内存放着ch341的.c文件与.h、Makefile文件,其中Makefile文件主要用于自动化编译和构建程序。
driver文件夹内容如下:
编译驱动文件
cd /root/CH341SER_LINUX/driver
输入make命令构建文件:
sudo make
我这里报错了,因为CH341.C文件中在定义ch341_tty_write函数时与我的内核版本(6.8.0-40-generic)不兼容,可能是ubuntu24的问题,之前编译都没出过问题:
root@zl-QiTianM415-N000:~/CH341SER_LINUX/driver# make
make -C /lib/modules/6.8.0-40-generic/build M=/root/CH341SER_LINUX/driver
make[1]: Entering directory '/usr/src/linux-headers-6.8.0-40-generic'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
You are using: gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
CC [M] /root/CH341SER_LINUX/driver/ch341.o
/root/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,
| ^~~~~~~~~~~~~~~
/root/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: /root/CH341SER_LINUX/driver/ch341.o] Error 1
make[2]: *** [/usr/src/linux-headers-6.8.0-40-generic/Makefile:1926: /root/CH341SER_LINUX/driver] Error 2
make[1]: *** [Makefile:240: __sub-make] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-6.8.0-40-generic'
make: *** [Makefile:5: default] Error 2
自己懒得看了,直接丢给AI,给了解释
从编译错误来看,问题出在 `ch341.c` 文件的第 1460 行,`ch341_tty_write` 函数的签名与结构体中的 `write` 函数指针的类型不兼容。这可能是因为 Linux 内核 API 发生了变化,导致原来的代码在新的内核版本中不再兼容。
错误信息指出:
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]
- 期望的 `write` 函数类型为:
ssize_t (*)(struct tty_struct *, const unsigned char *, size_t)
- 而实际提供的函数签名为:
int (*)(struct tty_struct *, const unsigned char *, int)
解决方法
你可以修改 `ch341.c` 中的 `ch341_tty_write` 函数,使其参数类型与内核期望的类型一致。
1. 修改函数签名
找到 `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)
- 将 `int` 改为 `ssize_t`,表示返回写入的字节数。
- 将 `int count` 改为 `size_t count`,以匹配内核期望的参数类型。
2. 修改返回值类型
确保函数内部的返回值类型也与 `ssize_t` 匹配。如果返回的是 `int`,需要将其转换为 `ssize_t` 类型。
3. 重新编译
修改完成后,保存文件并重新运行 `make` 命令:其他注意事项
- 如果项目使用 `-Werror` 标志(将所有警告视为错误),你可以暂时禁用该标志以跳过警告。修改 `Makefile`,移除或注释掉 `-Werror`:
CFLAGS += -Werror
将其修改为:
# CFLAGS += -Werror
AI是很强大的,再次输入make构建命令后构建成功:
root@zl-QiTianM415-N000:~/CH341SER_LINUX/driver# make
make -C /lib/modules/6.8.0-40-generic/build M=/root/CH341SER_LINUX/driver
make[1]: Entering directory '/usr/src/linux-headers-6.8.0-40-generic'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
You are using: gcc-12 (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0
CC [M] /root/CH341SER_LINUX/driver/ch341.o
/root/CH341SER_LINUX/driver/ch341.c:646:9: warning: no previous prototype for ‘ch341_tty_write’ [-Wmissing-prototypes]
646 | ssize_t ch341_tty_write(struct tty_struct *tty, const unsigned char *buf, size_t count)
| ^~~~~~~~~~~~~~~
MODPOST /root/CH341SER_LINUX/driver/Module.symvers
CC [M] /root/CH341SER_LINUX/driver/ch341.mod.o
LD [M] /root/CH341SER_LINUX/driver/ch341.ko
BTF [M] /root/CH341SER_LINUX/driver/ch341.ko
Skipping BTF generation for /root/CH341SER_LINUX/driver/ch341.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-6.8.0-40-generic'
使用modprobe加载模块,modprobe可以自动处理内核模块
modprobe ch341
使用lsmod命令查看是否加载成功(太多了,我们grep一下),这个命令可以显示已加载到内核中的模块列表:
lsmod | grep ch
模块加载成功:
让驱动永久生效:
make install
查看串口设备信息及端口号
lsusb
这里可以看到连接到了一个CH340设备
输入以下命令可以显示特定USB设备的详细信息和树状结构显示USB设备层次
lsusb -v -d 1a86:7523 // 刚才检查出来的设备ID号
lsusb -t
输入命令列出所有以 /dev/tty
开头的设备文件,我们这里为/dev/ttyCH341USB0
官方说明文件
以下内容全部为官方的说明文件:
针对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设备。
目录 返回
首页
- 评论列表