你可以帮我写一个程序吗,使用qt5.12.8,在银河麒麟下运行,你有什么思路吗,可以知道它是什么
QSerialPortInfo 枚举(推荐,Qt 原生)
Qt5 自带`QSerialPortInfo` ,可以列出所有串口的详细信息:
for (const QSerialPortInfo &info : QSerialPortInfo::availablePorts()) { qDebug() << info.portName(); // 如 "ttyUSB0" qDebug() << info.manufacturer(); // 厂商,如 "FTDI" qDebug() << info.description(); // 描述,如 "FT232R USB UART" qDebug() << info.vendorIdentifier(); // VID(十六进制) qDebug() << info.productIdentifier(); // PID(十六进制) qDebug() << info.serialNumber(); // 序列号 }只要你的设备芯片是常见型号,`description` +`manufacturer` 基本就能认出来。
admin123@admin123-pc:~/桌面$ ls /dev/ttyS* /dev/ttyS0 /dev/ttyS14 /dev/ttyS2 /dev/ttyS25 /dev/ttyS30 /dev/ttyS8 /dev/ttyS1 /dev/ttyS15 /dev/ttyS20 /dev/ttyS26 /dev/ttyS31 /dev/ttyS9 /dev/ttyS10 /dev/ttyS16 /dev/ttyS21 /dev/ttyS27 /dev/ttyS4 /dev/ttyS11 /dev/ttyS17 /dev/ttyS22 /dev/ttyS28 /dev/ttyS5 /dev/ttyS12 /dev/ttyS18 /dev/ttyS23 /dev/ttyS29 /dev/ttyS6 /dev/ttyS13 /dev/ttyS19 /dev/ttyS24 /dev/ttyS3 /dev/ttyS7你看到的这些/dev/ttyS*是 Linux 下的串口设备节点(传统8250/16550兼容串口)。你的系统里出现了ttyS0~ttyS31,说明内核最多枚举了32 个串口。
传统 8250/16550 兼容串口这是什么?
这是PC 串口通信硬件的两个经典芯片型号,后来成了"标准串口"的代名词。
名字的来源
8250:1980 年代 IBM PC 上使用的第一代UART 芯片(National Semiconductor 出品)
16550:1987 年推出的改进版,增加了16 字节 FIFO 缓冲区,是后来所有PC 串口的"标准"
UART =Universal Asynchronous Receiver/Transmitter(通用异步收发器),负责把并行的 CPU 数据转成串行信号发出去,反之亦然。
为什么叫"兼容"
后来几十年的串口芯片(无论哪家厂商生产),寄存器布局、编程接口都向后兼容 8250/16550。所以Linux 驱动只要认这个接口,就能驱动几乎所有传统串口。
应用层 → /dev/ttyS0 ↓ Linux 8250 驱动 (serial8250) ↓ 硬件 UART 芯片(16550 兼容) ↓ RS-232 电平 → DB9 接口 → 外部设备
典型特征
| 特征 | 说明 |
|---|---|
| 接口形式 | DB9 / DB25针脚,或主板 9 针排针 |
| 电平标准 | RS-232(±12V)或 TTL(3.3V/5V) |
| 地址 | 传统固定 I/O 地址,如COM1=0x3F8, COM2=0x2F8 |
| 中断 | IRQ 4、IRQ 3 等 |
| 波特率 | 通常到 115200,部分到 921600 |
| FIFO | 16550 有 16 字节,8250 没有 |
和你/dev/ttyS*的关系
Linux 的8250驱动(代码在drivers/tty/serial/8250/)负责管理这些设备。它为每个可能的 I/O 地址/端口都建了一个/dev/ttyS*节点,所以你看到 ttyS0~ttyS31 一大串,但:
真正插了硬件的 →驱动能读到芯片,显示
16550A没插硬件的 → 显示
unknown,纯粹是占位节点
这就是为什么你ls /dev/ttyS*看到 32 个,但实际能用的可能只有 1~2 个。
如何判断哪些是实际能用的?
.....................................待写....................................
讲讲/dev/ttyUSB*
/dev/ttyUSB*是USB 转串口设备在 Linux 下的设备节点。它和/dev/ttyS*最大的区别是:硬件不是主板上的,而是通过USB 接口热插拔接入的。
为什么会有 ttyUSB*
很多设备(单片机、PLC、传感器、路由器、Arduino等)本身只有 UART 串口,但现代电脑没有物理串口了,于是用一根USB 转串口线把UART 信号转换成 USB 信号接入电脑。
Linux 内核里的usbserial驱动负责识别这些转换芯片,并为每个转换出来的串口创建/dev/ttyUSB0、/dev/ttyUSB1……的节点。
设备 UART → USB 转串口芯片 → USB 总线 → usbserial 驱动 → /dev/ttyUSB0常见的 USB 转串口芯片
| 芯片 | 常见设备 | 驱动模块 | 特点 |
|---|---|---|---|
| CH340 / CH341 | 廉价Arduino、ESP开发板 | ch341 | 便宜,最常见 |
| CP2102 / CP2104 | ESP32、NodeMCU | cp210x | 稳定,Silicon Labs |
| FT232 / FT2232 | 工业设备、调试板 | ftdi_sio | 老牌,质量好 |
| PL2303 | 老式转接线 | pl2303 | 早期常用,兼容性有坑 |
| CDC ACM(非 ttyUSB) | STM32、Arduino原生 USB | cdc_acm | 节点是/dev/ttyACM* |
注意:CDC 类设备不会出现在
ttyUSB*,而是/dev/ttyACM*。
查看有哪些 ttyUSB:
ls /dev/ttyUSB*admin123@admin123-pc:~/桌面$ ls /dev/ttyUSB* ls: 无法访问'/dev/ttyUSB*': 没有那个文件或目录admin123@admin123-pc:~/桌面$ ls /dev/ttyACM* ls: 无法访问'/dev/ttyACM*': 没有那个文件或目录使用上面的代码:
我可以识别到ttyAMA0---ttyAMA3还有ttyUSB0
你识别到的端口是什么
| 端口 | 类型 | 说明 |
|---|---|---|
ttyAMA0~ttyAMA3 | ARM SoC的 PL011 UART | 板载串口,常见于树莓派、瑞芯微、全志等 ARM 平台。固定编号,不随插拔变化 |
ttyUSB0 | USB 转串口 | 你插的外接设备,通常是FTDI/CH340/CP2102等芯片。编号会变 |
ttyAMA*是ARM 平台特有的命名。AMA来自 ARM 的AMBA(Advanced Microcontroller Bus Architecture)总线,PL011 是这个总线上的 UART 控制器。它们对应开发板/工控板上的物理串口排针或板载接口。
打开前的检查
Qt 提供了几个有用的检查方法:
QSerialPortInfo info = /* 你选中的端口 */; // 1. 是否被占用 if (info.isBusy()) { qDebug() << "端口已被其他程序占用"; } // 2. 是否有效(有真实硬件) if (!info.isValid()) { qDebug() << "无效端口"; }ttyAMA0 常被系统占用
在很多ARM 开发板上,ttyAMA0默认是串口控制台(内核打印、登录 shell 都走它)。程序直接打开会失败或干扰系统输出。
# 看是否被 getty 占用 systemctl status serial-getty@ttyAMA0.service # 检查内核命令行是否把 console 指向了它 cat /proc/cmdline | grep console如果确实被占用,建议改用ttyAMA1/2/3或停掉 getty。
我看我的设备上有两个串口相关的物理接口,一个写着RS232,一个写着RS422,但我Qt程序识别出来的是ttyAMA0---ttyAMA3,感觉有点怪怪的???
你看到的“奇怪”其实是两个不同层面的东西被混淆了。你设备上物理接口写的RS232和RS422,指的是电气标准(物理层);而 Qt 识别出的ttyAMA0~3,是内核给UART 控制器分配的软件设备名。它们不是同一层面的对应关系。
为什么会有“两个物理接口,四个 ttyAMA”?
你的设备大概率是ARM 架构的开发板或工控机。ttyAMA是ARM PL011 UART 控制器的标准命名
关键在于:物理接口(RS232/RS422)和 UART 控制器(ttyAMA)之间,通常隔着一层电平转换芯片或跳线配置。
一个物理接口,背后可能对应一个或多个 UART
RS232 和 RS422 都是电气标准。RS422 需要4 根信号线来实现全双工差分传输,而 RS232 只需要2 根信号线。有些设备的 RS422 接口会占用两个 UART 控制器(一个负责发送,一个负责接收),或者通过硬件跳线将某个 UART 配置为RS422 模式。
你识别到的 4 个 ttyAMA,只有部分真正连接到了物理接口
你的设备虽然有 RS232 和 RS422 两个物理接口,但板载的ARM SoC通常有多个UART 控制器(如 4 个)。这些ttyAMA0~3可能对应 SoC 内部的4 个 UART 外设,但只有 1~2 个被引出到了物理接口上。
典型的树莓派就是
/dev/ttyAMA0引出到GPIO 针脚。对于工控板,厂商可能会把
ttyAMA1和ttyAMA2通过板载的RS422 收发器引出。
“RS232”和“RS422”标签是给使用者看的接线指引
设备厂商在物理接口旁印上 RS232/RS422,是告诉你“这个 DB9 或端子排,电气特性符合 RS232/RS422 标准,请按对应方式接线”。它不会直接出现在 Linux 的设备节点命名里。内核只认 UART 控制器,不关心你外面接的是MAX232 芯片还是MAX490 芯片。
怎么确定哪个 ttyAMA 对应哪个物理接口?
最可靠的方法是查设备手册,因为UART到物理接口的映射是硬件设计决定的,没有通用规律。
那我应该问厂商要一下设备手册,或者直接问设备上的RS422对应着ttyAMA中的哪一个,亦或者我该从/dev目录中访问谁才能读取到另一个设备通过RS422接口发过来的串口信息?
最优先:直接问厂商
问厂商是最快、最可靠的路径。你可以直接这样问:
“我设备上有RS232 和 RS422 两个物理串口接口,请问:
它们分别对应 Linux 下的哪个
/dev/ttyAMA*设备节点?
自己动手验证:从/dev里试
确认哪些 ttyAMA 是真实的
# 看每个 ttyAMA 是否有效 for i in 0 1 2 3; do echo "=== ttyAMA$i ===" stty -F /dev/ttyAMA$i -a 2>&1 | head -3 done能正常输出参数的就是真实可用的 UART;报No such device或Invalid argument的可能未启用。
stty -F /dev/ttyAMA$i -a 2>&1 | head -3
这行是核心,拆成几部分:
| 部分 | 含义 |
|---|---|
stty | set tty,查看/设置终端(串口)参数的命令 |
-F /dev/ttyAMA$i | 指定要操作的设备文件,$i替换后如/dev/ttyAMA0 |
-a | all,显示该串口的全部当前配置 |
2>&1 | 把标准错误重定向到标准输出,这样错误信息也能被管道处理 |
| head -3 | 只显示输出的前 3 行,避免刷屏 |
今天联合调试的时候,就遇到了这种问题,我跟搞硬件的人,中间差了一些东西,让我们的沟通很艰难,我眼里只有4个串口名称:ttyAMA0---ttyAMA3,他们眼里是:我已经把线接到RS422口上了,你能不能收到数据。有点烦。。。
这种“鸡同鸭讲”的场面在软硬件联调里太常见了,你烦是完全正常的。根本原因是你们各自站在不同的抽象层说话,中间缺了一张“翻译表”。下面帮你把这个问题彻底理清。
你们各自在说什么
| 角色 | 眼里的世界 | 关心的东西 |
|---|---|---|
| 你(软件) | /dev/ttyAMA0~3 | 打开哪个设备节点、波特率、能不能读到字节 |
| 硬件同事 | RS422 接口、TX+/TX-、A/B 线 | 线接对了没、电平对不对、收发器工作没 |
你们说的都对,但不在同一个层面。
他说“我接到 RS422 口了” → 这是物理层
你说“我只有 ttyAMA0~3” → 这是软件设备层
中间缺的是:RS422 物理接口 ↔ 哪个 ttyAMA的映射关系
为什么沟通这么难
因为这个映射关系是硬件设计决定的,软件无法自动知道。
硬件同事知道:他把线接到了 RS422 端子
你知道:系统里有 4 个 ttyAMA
你们都不知道(或没对齐):RS422 端子背后连的是ttyAMA ?
这个信息只存在于硬件原理图或厂商手册里,软件侧dmesg、/sys、lsusb都查不出来,因为电平转换芯片对软件是透明的。
可以尝试的方法:
现场最快的方法:你写一个最简单的测试程序,逐个 ttyAMA 监听,让硬件同事从 RS422 发数据。
用 Qt 写个最小 demo:
for (const QString &name : {"ttyAMA0", "ttyAMA1", "ttyAMA2", "ttyAMA3"}) { QSerialPort port; port.setPortName(name); port.setBaudRate(QSerialPort::Baud115200); if (port.open(QIODevice::ReadOnly)) { qDebug() << name << "打开成功,等待数据..."; // 连 readyRead 信号,收到数据就打印 } }四个都打开,哪个收到数据,哪个就是 RS422。
但要注意:ttyAMA0很可能被内核控制台占用,打开会失败或读到内核日志,先从ttyAMA1~3试。