☰
银河麒麟系统RS232串口通信实战:从设备节点到数据解析
2026/10/23 3:28:17 网站建设 项目流程

前阵子在某项目的调试现场,我需要在银河麒麟系统上接一台老式工业仪表。仪表后背只有一个九针RS232串口,每分钟往外面吐一条状态数据。工控机装的是银河麒麟操作系统,USB口插了一个RS232转USB的线。原本以为这就是“插根线、装个工具、收个数据”十分钟搞定的事,结果从设备节点找不到开始,一直折腾到权限、流控、电平转换,前前后后花了两个下午才把数据稳定收下来。回头复盘,问题都不算深,但每一步都卡在“原来这个系统里串口是这么玩”的经验缺失上。

如果你也准备在银河麒麟(或者类似的Linux发行版)上做RS232串口通信调试,不管是给工业设备收数据、接传感器,还是自制单片机开发板做联调,这篇实例复盘应该能帮你少走不少弯路。我会把整个项目的操作链路、工具选择、关键参数和踩坑点全部拆开讲。

1. 把设备接上之后第一件事:确认银河麒麟真的看到了这个串口

1.1 从COM口思维切换到tty设备节点思维

在Windows上,串口叫COM1、COM2,插上USB转串口线之后,驱动装好就能在设备管理器里看到新的COM口号。但在银河麒麟这样的Linux系统里,串口不叫COM口,而是以一个设备文件的形式出现在/dev目录下。这算是从Windows转过来的人最容易懵的第一关。

主板自带的原生串口通常对应/dev/ttyS0、/dev/ttyS1这样的节点,USB转串口线通常对应/dev/ttyUSB0、/dev/ttyUSB1,某些ARM板上的串口则可能是/dev/ttyAMA0。这里的数字编号不一定是固定的,每次插拔USB转串口线,设备节点可能从ttyUSB0变成ttyUSB1,这个后面会专门说。

所以拿到一台银河麒麟机器,第一步不是急着开串口工具,而是先搞清楚系统认不认这个串口、它挂在了哪个设备节点上。

1.2 确认驱动与设备节点的三板斧

我习惯用三个命令快速摸底。第一个是dmesg:

dmesg | grep -i tty

插上USB转串口线之后执行这条命令,如果驱动正常加载,会看到类似“usb 1-1: FT232 USB Serial Device converter now attached to ttyUSB0”之类的输出。如果什么输出都没有,说明内核根本没识别到这个设备,这时候得先解决驱动问题,而不是去折腾串口工具。

第二个命令是直接看设备节点:

ls -l /dev/ttyS* /dev/ttyUSB* 2>/dev/null

能看到节点存在,说明设备已经被系统接管,后面才有操作空间。

第三个命令是用stty查看当前串口参数:

stty -F /dev/ttyUSB0 -a

这条命令会显示当前波特率、数据位、停止位、校验位等配置。很多朋友一上来就开串口工具,然后发现数据不对,其实用stty先看一眼系统侧默认参数,很多问题当场就能定位。

需要注意,操作/dev/ttyUSB0之前,得确认设备节点没有被其他进程占用,这个用lsof就能查:

lsof /dev/ttyUSB0

有输出说明有其他程序占着这个串口,得先把它关掉。

1.3 普通用户访问串口的权限配置

摸清设备节点之后,紧接着就会撞上第二个大坑:权限。默认情况下,串口设备节点属于root用户和dialout组,当前用户如果不是root、也不在dialout组里,打开串口时会直接报Permission denied。

解决办法很简单,把当前用户加入dialout组:

sudo usermod -aG dialout $USER

改完组之后需要重新登录一次让权限生效。这一步做完,再打开串口工具基本就不会遇到权限拒绝了。这个坑非常典型,我们项目里两个同事都在这上面卡了十几分钟,因为他们习惯性用sudo去开工具,虽然也能用,但后面写Python脚本时就没这么好运了,脚本一跑就报权限错误。

还有一个细节:USB转串口的设备节点编号可能在重启之后变化,比如这次是ttyUSB0,下次变成ttyUSB1。如果脚本里硬编码了设备节点,重启后就会找不到设备。稳妥的做法是通过/dev/serial/by-id/这个稳定路径找到设备。我们项目里就用了这个符号链接,重启之后再也不用手动改脚本了。

2. 串口工具选型:minicom、picocom、CuteCom在国产系统上的实际取舍

2.1 三款主流串口工具的定位差异

串口工具的选择直接决定了调试效率,我在这类系统上前后试过不少,最后常用的就三个:minicom、picocom、CuteCom。

工具类型特点适合场景
minicom命令行功能全面,支持脚本、日志、流控配置,历史最悠久长期交互式调试,需要保存日志
picocom命令行轻量,配置参数直接写在启动命令里,几乎没有交互配置快速验证、脚本化调用
CuteCom图形界面可视化,参数一目了然,支持十六进制显示新手友好、偶尔看一眼数据

minicom就像全功能瑞士军刀,什么都有但配置起来偏繁琐;picocom就是一个极简单的直筒,参数对了直接开干;CuteCom则是图形化的,适合不习惯命令行的同事上手。

我在实际项目里的建议是:日常临时验证用picocom最快,涉及长时间盯着设备输出、需要记录日志的场景用minicom,给组内不熟悉命令行的同事演示时用CuteCom。

2.2 安装与基本配置实测

银河麒麟基于Debian系生态,所以安装方式和平常的Ubuntu差不多,直接apt就能装。我实测下来三个工具都能直接装,没有遇到源里缺包的情况。

sudo apt update sudo apt install -y minicom picocom cutecom

装完之后先用picocom做一次快速连通测试,这是效率最高的方式:

picocom -b 115200 /dev/ttyUSB0

-b指定波特率,后面跟设备节点。命令敲下去之后如果屏幕出现“Terminal ready”,说明串口已经打开成功,这时候任何输入都会直接发到串口对端,设备返回的数据也会直接显示在终端上。退出picocom的快捷键是Ctrl+A然后按Ctrl+X。

minicom的配置稍微绕一点,第一次使用需要先进入配置界面:

sudo minicom -s

在配置菜单里选择“Serial port setup”,依次修改串口设备为/dev/ttyUSB0、波特率为115200,还要把Hardware Flow Control设为No。这个流控选项是最容易被忽略的,很多设备不支持硬件流控,默认开着会导致发出去的数据没反应、收不到数据或者数据发不出去。配置好之后,minicom的正常打开方式就是:

minicom -D /dev/ttyUSB0 -b 115200

2.3 容易被忽略的快捷键与流控选项

用命令行串口工具,快捷键是一定要记的。minicom里Ctrl+A是功能键引导,之后再按Z查看帮助菜单、按X退出、按L打开日志记录、按W切换换行。这些快捷键不熟悉的话,最大的问题就是“不知道怎么退出”,只能Ctrl+C硬杀进程,退出后串口资源可能还被占着,导致下次打开报Resource busy。

picocom的快捷键相对少,Ctrl+A然后Ctrl+X退出,同样是引导式设计。两家工具都选择Ctrl+A作为引导键,就是为了避免和终端本身的快捷键冲突。

关于流控,这里多啰嗦一句。串口通信里的流控分为硬件流控(RTS/CTS)和软件流控(XON/XOFF),大多数RS232设备默认不启用流控。如果工具里默认开了硬件流控而设备那边没接对应的流控引脚,轻则通信卡住,重则完全无法收发。我在minicom里遇到过一次“能打开串口但发什么都不回”的情况,最后排查下来就是硬件流控这个选项在作怪。所以拿到一个新的串口设备,参数不确定的时候,先把流控全部关掉再试。

如果你只想快速看一眼设备发的到底是文本还是二进制,可以用这条命令把串口输出导成hex格式:

stty -F /dev/ttyUSB0 115200 cat /dev/ttyUSB0 | hexdump -C

cat的波特率默认读的是系统stty里的配置,所以先用stty把波特率设置好,否则cat收到的同样是一堆错的字节。这个命令在排查时需要频繁使用。

3. RS232实例拆解:回环自测、电平转换与真实设备联调

3.1 RS232电平与TTL电平:接错线的后果

在实例联调之前,必须先搞清楚一个基本概念:RS232是一种电平标准,不是简单的“串口”代名词。RS232标准的逻辑1用负电压表示(-3V到-15V),逻辑0用正电压表示(+3V到+15V),这是上世纪工业设备留下的老标准。而我们日常接触的很多单片机开发板、传感器模块,串口用的是TTL电平,逻辑1是3.3V或5V,逻辑0是0V。

这两个标准不能直接对接。把TTL电平的设备直接插到RS232接口上,轻则通信失败,重则烧毁IO口。所以做RS232通信时要注意:

  • 电脑主板原生九针串口,输出的是RS232电平;
  • USB转RS232线(带DB9公头那种),输出的是RS232电平;
  • USB转TTL模块,输出的是TTL电平;
  • 单片机的串口引脚,输出的是TTL电平。

我们项目里那个仪表是真正的RS232电平输出,所以用的是RS232转USB线。选择上推荐那些内核自带驱动的芯片方案,在银河麒麟下插上就能用。

芯片方案设备节点驱动集成情况实际稳定性
FT232/dev/ttyUSB0内核自带很稳
CP2102/dev/ttyUSB0内核自带稳定
CH340/dev/ttyUSB0内核自带可用,极个别场景有乱码

这三种方案的内核驱动,Linux主线都已经集成了,实测都不用额外装驱动。

3.2 回环测试:先证明串口通路是干净的

接好设备之前,我强烈建议先做一次回环测试。所谓回环测试,就是把串口的TX(发送)引脚和RX(接收)引脚短接,让数据发出去之后立刻从接收端回来。

操作上非常简单:拿一根杜邦线或者短导线,把DB9接头的2脚(RX)和3脚(TX)短接,如果是USB转TTL模块,就直接把板子上的TX和RX短接。DB9的2脚是RX、3脚是TX、5脚是GND,这个线序一定要记牢。然后打开picocom或者minicom,在终端里随便敲几个字符。如果串口通路正常,屏幕上会看到自己敲进去的字符“绕了一圈又回来了”。

如果回环测试能收到自己发的数据,说明串口工具、设备节点、驱动、参数这几环都是通的,后面连真实设备时可以把问题锁定在设备本身或者接线方式上。如果回环测试都收不到,那就别急着连设备了,先把上面这几环逐一查一遍。

回环测试这个习惯帮我省了无数次无意义的联调时间。很多时候设备怎么都不回数据,大家围着设备端折腾参数,最后发现就是USB线接触不良。先做回环,至少能确定“我这边的通路是干净的”。

3.3 与真实设备的参数协商与联调过程

回环测试通过之后,就可以把真实设备接上来了。这一步最关键的是参数协商:串口通信的双方必须使用完全一致的波特率、数据位、停止位和校验位,否则收下来的就是一屏乱码。

我们项目里的工业仪表用的是115200波特率、8个数据位、1个停止位、无校验,通行的缩写就是115200 8N1。但实际上设备的默认参数常常不是这个,很多老设备出厂是9600 8N1甚至4800 8N1。所以接到不熟悉的设备时,不要想当然,最好翻一下设备的说明书或者问设备厂家。如果在现场实在找不到参数,可以用“扫描法”:把波特率从2400、4800、9600、19200、38400、57600、115200逐个试一遍,开到某个档位时数据不再乱码,基本就是它了。

参数确认之后,打开工具开始观察数据。我当时用picocom连上仪表之后,终端里就开始按秒刷出仪表的状态帧,每一帧是十几个字节的十六进制数据,肉眼能看出开头是固定的帧头。这个阶段我建议打开工具的日志记录功能,把原始数据落盘,为后面写解析脚本留素材。minicom里是Ctrl+A然后L,picocom可以用--logfile参数:

picocom -b 115200 --logfile /tmp/serial_capture.log /dev/ttyUSB0

4. 用Python接管串口:pyserial读写与数据帧解析实例

4.1 为什么最后选了pyserial而不是继续用命令行工具

命令行工具适合人工观察和验证,但设备数据要是每分钟几十条甚至每秒上百条,人盯着终端看根本盯不过来。我们最后要做的事情是:把仪表上报的数据持续采集下来,做简单的解析和落盘。这显然得用脚本。

在银河麒麟下做串口编程,最顺手的方案是Python加pyserial库。pyserial是Python生态里事实上的串口标准库,跨平台、API稳定、文档齐全,而且银河麒麟的软件源里直接有打包好的python3-serial包,安装没有障碍。

4.2 读写串口的核心代码与参数说明

安装很简单:

sudo apt install -y python3-serial

或者用pip:

pip install pyserial

打开串口的基础代码是这样的:

import serial ser = serial.Serial() ser.port = '/dev/ttyUSB0' ser.baudrate = 115200 ser.bytesize = serial.EIGHTBITS ser.parity = serial.PARITY_NONE ser.stopbits = serial.STOPBITS_ONE ser.timeout = 1 ser.open() if ser.is_open: print(f'串口 {ser.name} 已打开,参数: 115200 8N1')

这里每个参数都有明确含义:

  • baudrate是波特率,必须和设备一致;
  • bytesize是数据位,常用8;
  • parity是校验位,NONE表示无校验;
  • stopbits是停止位,常用1;
  • timeout是读超时时间,单位秒,这个参数很重要,设置成0表示非阻塞读,可能返回空数据,设置成None表示一直等到有数据为止。

写数据和读数据是串口编程的基础操作。一次简单的“发指令、收响应”的请求-响应流程是这样的:

# 发送一组查询指令(十六进制) cmd = bytes.fromhex('01 03 00 00 00 02 C4 0B') ser.write(cmd) # 读取返回 resp = ser.read(32) print('原始返回:', resp.hex().upper())

如果是持续被动接收设备上报的数据,可以写一个循环读取的脚本:

import serial import datetime ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1) with open('/tmp/serial_data.log', 'a') as f: while True: data = ser.read(64) if data: line = f"{datetime.datetime.now().isoformat()} {data.hex().upper()}" print(line) f.write(line + '\n') f.flush()

这里把每条数据加上了时间戳并写入文件,方便后续回溯。用hex()把字节流转成可读的十六进制字符串再落盘,避免直接写原始字节导致日志乱码。

4.3 数据帧解析与异常处理

原始数据收到之后,真正的重头戏是解析。工业设备的报文一般不直接是纯文本,而是带有帧头、长度、数据、校验的二进制帧。我们那台仪表的帧格式大概是:帧头0xAA,功能码,数据长度,数据体,CRC低字节,CRC高字节。解析的思路大同小异:先按帧头对齐,再取长度,再校验CRC,最后取数据体。

下面给一个简化但可以跑通的解析框架:

def calc_crc16(data): crc = 0xFFFF for byte in data: crc ^= byte for _ in range(8): if crc & 0x0001: crc = (crc >> 1) ^ 0xA001 else: crc = crc >> 1 return crc.to_bytes(2, byteorder='little') def parse_serial_frame(buf): start = buf.find(bytes([0xAA])) if start == -1: return None if start + 5 > len(buf): return None func = buf[start + 1] length = buf[start + 2] if start + 3 + length + 2 > len(buf): return None payload = buf[start + 3: start + 3 + length] crc_received = buf[start + 3 + length: start + 3 + length + 2] crc_calculated = calc_crc16(bytes([func, length]) + payload) if crc_received != crc_calculated: print('CRC校验失败') return None return payload

这段代码针对的是典型的Modbus RTU类报文结构,具体的帧格式要按你设备的协议来调整,但整个思路是通用的:找帧头、取长度、校验、切数据。解析逻辑写完之后,配合上面的循环读取脚本,就可以把数据从“一堆十六进制字节”变成“能看懂的字段”。

异常处理这块,读串口最常见的异常是serial.SerialException,比如设备被拔掉、设备节点被其他程序占用。循环里最好用try-except包住,并保证程序挂掉后能重新打开串口,否则服务跑着跑着就静默退出了。

import time while True: try: data = ser.read(64) if data: payload = parse_serial_frame(data) if payload: print('解析到的数据:', payload.hex().upper()) except serial.SerialException as e: print('串口异常,3秒后重试:', e) time.sleep(3) continue

4.4 让采集脚本开机自动运行

脚本在终端里跑没问题之后,下一步就是让它服务化,开机自动跑、意外退出自动重启。在银河麒麟上这个用systemd就能搞定。写一个服务文件:

[Unit] Description=Serial Data Collector Service After=network.target [Service] ExecStart=/usr/bin/python3 /opt/serial_collector/collector.py WorkingDirectory=/opt/serial_collector Restart=always RestartSec=5 [Install] WantedBy=multi-user.target

把上面的内容存成/etc/systemd/system/serial-collector.service,然后:

sudo systemctl daemon-reload sudo systemctl enable serial-collector sudo systemctl start serial-collector

注意ExecStart里的Python解释器路径,直接用which python3查一下实际路径再填,不要照抄/usr/bin/python3,万一你的系统里Python是装在别的路径,服务就会启动失败。查看服务运行状态用systemctl status serial-collector,查看日志用journalctl -u serial-collector -f。

5. 实测中遇到的七个典型故障与完整排查链路

5.1 故障一:Permission denied(权限拒绝)

现象:用picocom、minicom或者Python脚本打开串口时,直接报Permission denied。原因就是当前用户不在dialout组里,也没有root权限。解决办法在前面已经写了,usermod -aG dialout加组,重新登录。

这里补充一个细节:如果改完组还是不行,检查一下当前会话是否完全退出重新登录过。只开一个新终端是没用的,因为用户的组信息在登录时就已经固定了。

5.2 故障二:打开串口但收不到任何数据

现象:工具能正常打开串口,设备也在发数据,但终端里空空荡荡。这时候按照这个顺序排查:

  1. 确认设备节点对不对。用ls -l /dev/ttyUSB*看节点是否存在,如果存在多个节点,可能打开错了,比如设备实际在ttyUSB1,你开了ttyUSB0;
  2. 确认接线。RS232是交叉接线,设备端的TX接电脑端的RX,设备端的RX接电脑端的TX,GND接GND。很多人习惯性直连,TX接TX、RX接RX,结果什么都收不到。如果实在不确定线序,用万用表量一下引脚电平,RS232空闲时TX脚应该是一个负电压(约-7V到-12V);
  3. 关闭流控。minicom默认的硬件流控改成No再试;
  4. 确认设备是否真的有数据往外发。有些设备需要先发指令才会响应,不是主动上报。用回环测试排除自身通路问题后,剩余怀疑点就在设备和接线上。

5.3 故障三:收到满屏乱码

现象:终端里内容哗哗地冒,但全是乱码。最典型的原因就是波特率不匹配。设备是9600,工具设成115200,接收端采样频率和发送端对不上,解析出来的自然是一堆乱码。逐个试波特率档位,找到不乱码的那一档。

乱码还有一个容易被忽略的来源:地线没接好。RS232通信要求两端共地,如果设备供电和电脑供电不是同一套电源且没有接GND,电平参考点不一致会导致数据看起来时好时坏、偶发乱码。

5.4 故障四:Resource busy(串口被占用)

现象:打开串口时报Resource busy或者could not open port。原因基本上就是有另一个进程占用了串口,常见于minicom或picocom没有正常退出。用fuser查占用进程:

fuser -v /dev/ttyUSB0

找到PID后直接kill掉,或者用fuser -k /dev/ttyUSB0强制释放。避免这个问题的方法是每次退出工具都用正常快捷键退出,不要硬杀终端。

5.5 故障五:设备上报的数据时有时无

现象:数据不是稳定的,有时候连续来好几条,有时候一分钟没动静。这种“间歇性抽风”问题排查起来最费时间。

我们当时遇到的情况最后定位到两个原因:一个是USB转串口线接触不良,插头松了;另一个是设备端的RS232线缆过长,信号衰减严重。RS232标准在9600波特率下理论可以支持到15米左右,但实际现场环境里,线缆质量不好、靠近动力电缆走线,都会让通信稳定性大打折扣。检查线缆、换一根屏蔽线,问题就解决了。

5.6 故障六:工具能连上但一退出就掉线

现象:用minicom调试一切正常,退出来之后再用Python脚本去开串口,发现设备不响应了。这往往是因为minicom退出时对串口做了重置,把DTR/RTS引脚状态改了,有些设备把这当成一个复位信号。

解决办法是在Python脚本里显式设置DTR和RTS的状态:

ser.dtr = False ser.rts = False

具体值要看设备对引脚电平的定义,但思路就是主动控制这两个引脚,而不是依赖系统默认状态。

5.7 故障七:终端显示乱码但十六进制模式正常

现象:设备的应答数据在串口工具里看全是乱码,但切换到hex模式后发现内容其实是对的。这是因为设备返回的是二进制数据,而不是ASCII文本。终端把每个字节按ASCII字符集解释,大于0x7F的字节就显示出乱码方块。

解决办法是看数据时直接用hex模式,picocom没有内置hex模式,可以通过管道方式用xxd:

picocom -b 115200 /dev/ttyUSB0 | xxd

这个“乱码”其实不算故障,但现场经常被当成波特率不匹配处理,折腾半天才发现设备压根儿没在发文本。

5.8 一条可复用的排查链路清单

最后把我们项目里沉淀下来的排查顺序整理成一份清单,按这个顺序走一遍,绝大多数串口通信问题都能定位到具体环节:

步骤检查内容验证方法
1设备是否被系统识别dmesg | grep -i tty、ls -l /dev/ttyUSB*
2是否有权限访问普通用户打开串口,无Permission denied
3串口是否被占用lsof /dev/ttyUSB0 无输出
4自身通路是否干净TX-RX短接做回环测试,能收到自己发的数据
5接线是否正确RS232交叉接线,TX接RX、RX接TX、GND共地
6参数是否一致波特率、数据位、停止位、校验位、流控逐项核对
7数据是否稳定上报长时观察,确认无间歇性断流、乱码

按照这条链路走一遍,从“完全没头绪”到“定位到具体环节”通常不超过十分钟。很多问题的本质不是某一个坑有多深,而是没有一套固定的排查顺序,东一下西一下,反而越调越乱。

项目收尾那天,我把整套环境整理成了一份操作手册发给组里的同事,里面最关键的一句话是:不管多着急,先做回环测试再连设备。这个习惯后来帮我们省了非常多的时间,因为回环测试能在一分钟内把“电脑这一端”和“设备那一端”的问题切分清楚,剩下的就只是检查设备和线缆了。

另外说一句实在话,银河麒麟系统在串口这块和主流Linux发行版没有本质区别,网上搜到的关于Ubuntu的串口资料基本都能照搬。但它毕竟不是个“满教程满天飞”的系统,所以自己项目里踩过的坑、用顺手的脚本,值得花半小时沉淀成文档。下次接手类似任务,你会发现那半小时省回来的时间是十倍以上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询