使用USB转串口模块,并不可免的需要安装驱动,目前主要涉及的是CH340以及CH9344驱动。其安装方法、验证方法以及其中有可能出现的问题看本博文就够了。
驱动安装
串口驱动基本上在
https://www.wch.cn/downloads/
网站上可以找到
CH340驱动
-
源码编译安装
驱动下载地址:``http://www.wch.cn/download/CH341SER_LINUX_ZIP.html`
包含如下文件:ch34x.c、Makefile、readme.txt
源码安装步骤为:
unzip CH341SER_LINUX.zip cd CH341SER_LINUX/driver make make load sudo make install
-
如果make报异常:/lib/modules/4.19.75-v7+/build: 没有那个文件或目录
是linux-headers的问题。安装
sudo apt-get install linux-headers ls /usr/src #查看linux-headrs的版本 sudo ln -s /usr/src/linux-headers-4.19.66-v7+ /lib/modules/4.19.75-v7+/build #形成链接即可
-
报错:
unknown type name ‘wait_queue_t’
wait_queue_t wait 并没有用,打开driver/ch34x.c查看源码,直接注释掉
-
报错:
implicit declaration of function ‘signal_pending’
这个错误是因为没有包含一个头文件
signal.h
,在文件包含头文件的地方加入#include <linux/sched/signal.h>
-
报错:
.write = ch341_tty_write
详细的错误截图:
make -C /lib/modules/6.6.47+rpt-rpi-2712/build M=/home/pi/Downloads/CH341SER_LINUX/driver make[1]: Entering directory '/usr/src/linux-headers-6.6.47+rpt-rpi-2712' CC [M] /home/pi/Downloads/CH341SER_LINUX/driver/ch341.o /home/pi/Downloads/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, | ^~~~~~~~~~~~~~~ /home/pi/Downloads/CH341SER_LINUX/driver/ch341.c:1460:18: note: (near initialization for ‘ch341_ops.write’) cc1: some warnings being treated as errors make[3]: *** [/usr/src/linux-headers-6.6.47+rpt-common-rpi/scripts/Makefile.build:248: /home/pi/Downloads/CH341SER_LINUX/driver/ch341.o] Error 1 make[2]: *** [/usr/src/linux-headers-6.6.47+rpt-common-rpi/Makefile:1946: /home/pi/Downloads/CH341SER_LINUX/driver] Error 2 make[1]: *** [/usr/src/linux-headers-6.6.47+rpt-common-rpi/Makefile:246: __sub-make] Error 2 make[1]: Leaving directory '/usr/src/linux-headers-6.6.47+rpt-rpi-2712' make: *** [Makefile:5: default] Error 2
具体来说,错误发生在
ch341.c
文件的第 1460 行,这里指定了一个名为ch341_tty_write
的函数作为tty_struct
的write
方法。编译器报告说这个函数的返回类型int
与期望的ssize_t
类型不一致。在 Linux 内核编程中,
tty_struct
的write
方法预期返回一个ssize_t
类型,这是为了表示写入的字节数或者一个负值来指示错误。而ch341_tty_write
函数似乎返回的是int
类型,这导致了编译器报错。处理:主要是修改
ch341_tty_write
函数的参数,包括static int ch341_tty_write(struct tty_struct *tty, const unsigned char *buf, size_t count)
改成static ssize_t ch341_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
int stat;
改成ssize_t stat;
完整源码:
static ssize_t ch341_tty_write(struct tty_struct *tty, const unsigned char *buf, size_t count) { struct ch341 *ch341 = tty->driver_data; ssize_t stat; unsigned long flags; int wbn; struct ch341_wb *wb; if (!count) return 0; spin_lock_irqsave(&ch341->write_lock, flags); wbn = ch341_wb_alloc(ch341); if (wbn < 0) { spin_unlock_irqrestore(&ch341->write_lock, flags); return 0; } wb = &ch341->wb[wbn]; if (!ch341->dev) { wb->use = 0; spin_unlock_irqrestore(&ch341->write_lock, flags); return -ENODEV; } count = (count > ch341->writesize) ? ch341->writesize : count; memcpy(wb->buf, buf, count); wb->len = count; stat = usb_autopm_get_interface_async(ch341->data); if (stat) { wb->use = 0; spin_unlock_irqrestore(&ch341->write_lock, flags); return stat; } if (ch341->susp_count) { usb_anchor_urb(wb->urb, &ch341->delayed); spin_unlock_irqrestore(&ch341->write_lock, flags); return count; } stat = ch341_start_wb(ch341, wb); spin_unlock_irqrestore(&ch341->write_lock, flags); if (stat < 0) return stat; return count; }
-
-
通过Git安装
sudo apt-get update sudo apt-get install dkms sudo chmod +x ./install.sh sudo ./install.sh
这个方法不知道为啥,不正确,首先安装dkms后,chmod这个install.sh在哪,也不知道
但是可以git安装CH340:
sudo apt-get install git git clone https://github.com/juliagoda/CH341SER.git cd CH341SER make sudo make load sudo make install
-
测试
- 命令查看是否识别硬件:
lsusb
,正常是看到:
-
检查硬件连接信息:
dmesg | grep USB
,正常的话可以看到: -
检查加载的模块:
lsmod | grep ch34
orlsmod | grep ch
,正常是看到: -
手动加载模块:
sudo modprobe ch341
-
获取更详细的设备连接信息:
usb-devices
如果是获得信息如下:
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=ff(vend.) Sub=80 Prot=37 MxPS=64 #Cfgs= 1 P: Vendor=1a86 ProdID=55d9 Rev=01.36 S: Manufacturer=wch.cn S: Product=USB2.0 To Multi Serial Ports C: #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=200mA I: If#= 0 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=80 Prot=37 Driver=(none) E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
确实识别到了您的多串口设备,但重要的一点是在设备信息行
I: If#= 0 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=80 Prot=37 Driver=(none)
中显示"Driver=(none)"
。这意味着没有为这个设备加载适当的驱动。这可能是为什么/dev/ttyUSB*
端口没有被创建的原因。suo -
查看端口号(检查设备节点):
dmesg | grep ttyUSB* # 或者 ls /dev/ttyUSB*
- 命令查看是否识别硬件:
安装CH9344驱动
这个驱动是给芯片CH384
以及CH9344
的。
安装步骤类似CH341
,在安装时常见的错误也是在修改chXXX_tty_write
函数,这里是ch9344_tty_write
,需要进行修改:
static int ch9344_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
改成static ssize_t ch9344_tty_write(struct tty_struct *tty, const unsigned char *buf, size_t count)
int stat;
改成ssize_t stat;
int packlen, total_len, sendlen;
改成ssize_t packlen, total_len, sendlen;
该函数的完整源码:
static ssize_t ch9344_tty_write(struct tty_struct *tty, const unsigned char *buf, size_t count)
{
struct ch9344 *ch9344 = tty->driver_data;
ssize_t stat;
unsigned long flags;
int wbn;
struct ch9344_wb *wb;
int portnum = ch9344_get_portnum(tty->index);
int timeout;
int maxep = ch9344->writesize / 20;
int packnum, maxpacknum;
ssize_t packlen, total_len, sendlen;
if (!count)
return 0;
if (ch9344->writesize % (maxep - 3))
maxpacknum = ch9344->writesize / (maxep - 3) + 1;
else
maxpacknum = ch9344->writesize / (maxep - 3);
count = count > (ch9344->writesize - maxpacknum * 3) ? (ch9344->writesize - maxpacknum * 3) : count;
total_len = count;
sendlen = 0;
if (count % (maxep - 3))
packnum = count / (maxep - 3) + 1;
else
packnum = count / (maxep - 3);
transmit:
spin_lock_irqsave(&ch9344->write_lock, flags);
wbn = ch9344_wb_alloc(ch9344, portnum);
if (wbn < 0) {
spin_unlock_irqrestore(&ch9344->write_lock, flags);
return 0;
}
wb = &ch9344->wb[portnum][wbn];
if (!ch9344->dev) {
wb->use = 0;
spin_unlock_irqrestore(&ch9344->write_lock, flags);
return -ENODEV;
}
/* packets deal */
if (total_len > maxep - 3) {
packlen = maxep - 3;
total_len -= packlen;
} else {
packlen = total_len;
total_len = 0;
}
*(wb->buf) = ch9344->port_offset + ch9344_get_portnum(tty->index);
*(wb->buf + 1) = packlen;
*(wb->buf + 2) = packlen >> 8;
memcpy(wb->buf + 3, buf + sendlen, packlen);
wb->len = packlen + 3;
sendlen += packlen;
wb->portnum = portnum;
stat = usb_autopm_get_interface_async(ch9344->data);
if (stat) {
wb->use = 0;
spin_unlock_irqrestore(&ch9344->write_lock, flags);
return stat;
}
if (ch9344->susp_count) {
if (ch9344->putbuffer) {
usb_anchor_urb(ch9344->putbuffer->urb, &ch9344->delayed);
ch9344->putbuffer = NULL;
}
usb_anchor_urb(wb->urb, &ch9344->delayed);
spin_unlock_irqrestore(&ch9344->write_lock, flags);
return sendlen;
} else {
if (ch9344->putbuffer) {
ch9344_start_wb(ch9344, ch9344->putbuffer);
ch9344->putbuffer = NULL;
}
}
if (!ch9344->ttyport[portnum].write_empty) {
spin_unlock_irqrestore(&ch9344->write_lock, flags);
timeout = wait_event_interruptible_timeout(ch9344->ttyport[portnum].wioctl,
ch9344->ttyport[portnum].write_empty,
msecs_to_jiffies(DEFAULT_TIMEOUT));
if (timeout <= 0) {
wb->use = 0;
ch9344->ttyport[portnum].write_empty = true;
return sendlen ? sendlen : -ETIMEDOUT;
}
spin_lock_irqsave(&ch9344->write_lock, flags);
}
ch9344->ttyport[portnum].write_empty = false;
stat = ch9344_start_wb(ch9344, wb);
if (stat < 0) {
ch9344->ttyport[portnum].write_empty = true;
spin_unlock_irqrestore(&ch9344->write_lock, flags);
return stat;
}
spin_unlock_irqrestore(&ch9344->write_lock, flags);
if (total_len != 0)
goto transmit;
if (sendlen > 0)
ch9344->ttyport[portnum].iocount.tx += sendlen;
return sendlen;
}
安装minicom
Minicom 是一个基于文本的串行通信程序,广泛用于 Unix 和 Unix-like 系统(包括 Linux)。它非常适合用来管理通过串行端口连接的设备,如调制解调器、路由器和交换机。Minicom 类似于 Windows 上的 HyperTerminal,但具有更多功能和灵活性。在树莓派,可以直接在software中进行添加:
-
使用 Minicom 时,你需要知道你想要连接的串行设备的设备文件名(通常是
/dev/ttyUSB0
、/dev/ttyS0
等)以及通信参数(如波特率、数据位、停止位等)。可以使用以下命令配置串行端口:sudo minicom -s
这将打开 Minicom 的设置菜单。在这里,你可以设置:
-
串行端口配置:选择 “Serial port setup” 来配置串行端口。
- 更改串行设备(默认为
/dev/modem
,通常需要更改为/dev/ttyUSB0
或其他正确的设备文件)。 - 设置波特率和其他串行参数(如 9600、19200、115200 等)。
- 更改串行设备(默认为
-
保存设置:配置完毕后,选择 “Save setup as dfl” 来保存设置为默认配置。
-
退出设置:选择 “Exit” 来退出设置菜单并启动串行会话。
-
连接到设备:一旦设置正确,你可以直接通过运行 Minicom 来连接设备:
sudo minicom
这将使用之前保存的设置启动串行通信会话。
-
使用 Minicom
在 Minicom 中,你可以发送文本命令到连接的设备,并查看来自设备的响应。Minicom 提供了一套控制命令,通常通过按
Ctrl-A
后跟另一个字符来调用。常见的控制命令包括:-
Ctrl-A Z:显示帮助菜单,列出所有 Minicom 命令。
-
Ctrl-A X:退出 Minicom。
-
Ctrl-A E:打开或关闭本地回显。
-
Ctrl-A L:记录会话到文件。
-