Linux下串口配置与使用详解?Linux串口怎么配置?如何配置Linux串口?

06-11 4491阅读

串口通信基础与原理

串口(Serial Port)作为经典的异步通信接口,凭借其简单可靠的特性,在嵌入式开发、工业控制、物联网设备等领域持续发挥着重要作用,与并行通信相比,串行通信虽然传输速率较低,但具有布线简单、成本低廉、传输距离远等优势,特别适合设备间的长距离数据交换。

在Linux系统中,串口设备以设备文件的形式呈现于/dev目录,主要包括以下类型:

  • 物理串口/dev/ttyS*系列(如COM1对应/dev/ttyS0),对应主板的UART控制器
  • USB转串口/dev/ttyUSB*系列(需FTDI/CH340等芯片驱动),用于连接USB转串口适配器
  • 虚拟终端/dev/pts/*(通过伪终端模拟),常用于SSH会话和终端仿真

技术演进:Linux 4.0+内核开始使用/dev/ttyS*统一命名传统串口,取代了早期/dev/ttyS*/dev/ttyAMA*的混用情况,这一改进显著提高了设备命名的规范性。

Linux下串口配置与使用详解?Linux串口怎么配置?如何配置Linux串口?

设备识别与硬件检测

硬件识别技术

# 查看内核识别的串口设备(包含历史记录)
dmesg | grep -E "tty(S|USB)|serial"
# 枚举USB转串口设备详细信息
lsusb -v | grep -iE "Serial|FTDI|CH340|PL2303|CP210"
# 快速列出当前可用串口设备
ls /dev/tty{S,USB,ACM}* 2>/dev/null | sort

深度设备诊断

# 获取设备拓扑和属性信息(推荐方式)
udevadm info --query=all --name=/dev/ttyUSB0
# 查看串口芯片的详细参数(以CH340为例)
sudo cat /sys/bus/usb-serial/devices/ttyUSB0/../{manufacturer,product,serial}
# 检查串口硬件资源分配
sudo setserial -g /dev/ttyS[0123]

专业级串口参数配置

通信参数详解

参数类别 可选值 技术说明 典型应用场景
波特率 300-4000000 bps 决定数据传输速率 115200(常用平衡点)
数据位 5/6/7/8 bits 每个字节的数据位数 8(最通用)
停止位 1/1.5/2 bits 帧结束标志 1(标准配置)
校验方式 None/Even/Odd/Mark/Space 错误检测机制 None(常见)
流控机制 RTS/CTS/XON/XOFF/None 数据流控制 None(简单场景)

使用stty进行精确配置

# 设置115200波特率+8N1无流控(生产环境推荐配置)
sudo stty -F /dev/ttyUSB0 115200 cs8 -parenb -cstopb -crtscts ignbrk -brkint -icrnl -imaxbel
# 启用硬件流控(RTS/CTS)和本地回显
sudo stty -F /dev/ttyUSB0 crtscts echo
# 保存当前所有串口配置到文件(便于故障恢复)
for port in /dev/ttyS* /dev/ttyUSB*; do 
  [ -c $port ] && stty -F $port -g > ${port//\//_}.conf
done
# 交互式查看当前串口设置
stty -a -F /dev/ttyUSB0

终端工具高级应用

专业工具对比分析

工具 安装命令 核心优势 适用场景
screen 内置 极简主义,系统自带 快速测试
minicom sudo apt install minicom 支持脚本、日志记录和会话保持 工业级应用
picocom sudo apt install picocom 轻量级,完美支持嵌入式开发 资源受限环境
putty sudo apt install putty 图形界面,多协议支持 Windows迁移用户
gtkterm sudo apt install gtkterm 可视化配置,友好界面 初学者

minicom高级配置示例

# 生成默认配置文件(首次使用时)
minicom -s
# 启动时自动记录日志并添加时间戳
minicom -D /dev/ttyUSB0 -b 115200 -C "$(date +%Y%m%d).log" -t ansi
# 使用脚本自动化配置(~/.minirc.dfl)
pu port             /dev/ttyUSB0
pu baudrate         115200
pu bits             8
pu parity           N
pu stopbits         1
pu rtscts           Yes

权限管理与安全配置

临时权限方案

# 开放临时读写权限(重启后失效)
sudo chmod a+rw /dev/ttyUSB0
# 更安全的临时方案(使用ACL)
sudo setfacl -m u:$USER:rw- /dev/ttyUSB0

永久解决方案

# 将用户加入dialout组(推荐方式)
sudo usermod -aG dialout $USER && newgrp dialout
# 创建智能udev规则(/etc/udev/rules.d/99-serial.rules)
# FTDI芯片示例
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", \
    GROUP="dialout", MODE="0660", SYMLINK+="ftdi_%s{serial}"
# 重载udev规则并触发
sudo udevadm control --reload-rules && sudo udevadm trigger

企业级安全建议

  1. 避免直接使用chmod 777
  2. 为不同设备创建专用用户组
  3. 使用USB设备的serial number进行精确匹配
  4. 生产环境建议配合SELinux/AppArmor使用

高级调试与性能优化

专业调试技术

# 十六进制双向监控(需要两个终端)
# 终端1:发送端
sudo cat /dev/urandom | pv -L 1k > /dev/ttyUSB0
# 终端2:接收端
sudo stty -F /dev/ttyUSB0 raw && sudo od -vtx1 -w16 < /dev/ttyUSB0
# 使用socat进行协议分析
socat -d -d -lf socat.log /dev/ttyUSB0,raw,echo=0,b115200 SYSTEM:'tee input.log | socat - "PTY,link=/tmp/virtual,raw,echo=0" | tee output.log'
# 实时中断监控
watch -n1 'cat /proc/interrupts | grep -E "tty|uart"'

性能优化技巧

# 增大内核缓冲区(临时生效)
sudo sysctl -w kernel.serial.bufsize=131072
# 禁用串口控制台输出(释放资源)
sudo systemctl stop serial-getty@ttyS0.service
sudo systemctl disable serial-getty@ttyS0.service
# 优化CPU调度策略
chrt -f 99 minicom -D /dev/ttyUSB0

编程实战进阶

C语言工业级实现

#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
int serial_init(const char *device, int baud) {
    int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd < 0) {
        perror("open serial failed");
        return -1;
    }
    // 检查设备是否确实是串口
    if (!isatty(fd)) {
        fprintf(stderr, "%s is not a terminal device\n", device);
        close(fd);
        return -1;
    }
    struct termios options;
    tcgetattr(fd, &options);
    // 设置输入输出波特率
    cfsetispeed(&options, baud);
    cfsetospeed(&options, baud);
    // 8N1配置
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    // 启用接收和本地模式
    options.c_cflag |= (CLOCAL | CREAD);
    // 原始输入模式
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    // 关闭软件流控
    options.c_iflag &= ~(IXON | IXOFF | IXANY);
    // 原始输出模式
    options.c_oflag &= ~OPOST;
    // 设置超时:100ms内读取至少1个字符
    options.c_cc[VMIN]  = 1;
    options.c_cc[VTIME] = 1;
    if (tcsetattr(fd, TCSANOW, &options) != 0) {
        perror("tcsetattr failed");
        close(fd);
        return -1;
    }
    // 清空缓冲区
    tcflush(fd, TCIOFLUSH);
    return fd;
}

Python高级封装类

import serial
import serial.tools.list_ports
from threading import Lock
from time import monotonic
class IndustrialSerial:
    def __init__(self, port=None, vid_pid=None, **kwargs):
        self._lock = Lock()
        self._port = port or self._auto_detect(vid_pid)
        self._serial = None
        default = {
            'baudrate': 115200,
            'bytesize': serial.EIGHTBITS,
            'parity': serial.PARITY_NONE,
            'stopbits': serial.STOPBITS_ONE,
            'timeout': 1.0,
            'write_timeout': 1.0,
            'xonxoff': False,
            'rtscts': True,
            'dsrdtr': False,
            'inter_byte_timeout': 0.1
        }
        default.update(kwargs)
        self._params = default
    def _auto_detect(self, vid_pid):
        for port in serial.tools.list_ports.comports():
            if vid_pid and vid_pid in port.hwid:
                return port.device
        raise serial.SerialException("Target device not found")
    def __enter__(self):
        self.open()
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()
    def open(self):
        with self._lock:
            if not self._serial or not self._serial.is_open:
                self._serial = serial.Serial(self._port, **self._params)
    def close(self):
        with self._lock:
            if self._serial and self._serial.is_open:
                self._serial.close()
    def send(self, data, retry=3):
        with self._lock:
            while retry > 0:
                try:
                    return self._serial.write(data)
                except serial.SerialTimeoutException:
                    retry -= 1
                    if retry == 0:
                        raise
    def receive(self, size=1, timeout=None):
        with self._lock:
            old_timeout = self._serial.timeout
            if timeout is not None:
                self._serial.timeout = timeout
            try:
                start = monotonic()
                data = self._serial.read(size)
                elapsed = monotonic() - start
                if timeout and elapsed >= timeout and not data:
                    raise serial.SerialTimeoutException("Read timeout")
                return data
            finally:
                self._serial.timeout = old_timeout

企业级解决方案

高速通信优化

  1. 内核参数调优

    # 增大UART缓冲区
    echo "kernel.serial.bufsize=262144" | sudo tee /etc/sysctl.d/99-serial.conf
    # 提高中断处理优先级
    echo "IRQ_PRIORITY=high" | sudo tee /etc/default/irqbalance
  2. 硬件加速方案

    • 启用DMA传输(需硬件支持)
    • 使用带FIFO缓冲的USB转串口芯片(如FTDI FT232H)
    • 考虑使用PCIe串口卡替代USB转接

多线程安全模式

import threading
import queue
import logging
class SerialManager:
    def __init__(self, port, **kwargs):
        self._serial = serial.Serial(port, **kwargs)
        self._read_thread = threading.Thread(target=self._read_loop, daemon=True)
        self._write_queue = queue.Queue()
        self._callback = None
        self._running = False
        self._lock = threading.RLock()
    def set_callback(self, callback):
        with self._lock:
            self._callback = callback
    def start(self):
        with self._lock:
            if not self._running:
                self._running = True
                self._read_thread.start()
                threading.Thread(target=self._write_loop, daemon=True).start()
    def stop(self):
        with self._lock:
            self._running = False
            self._read_thread.join(timeout=1)
    def send(self, data):
        self._write_queue.put(data)
    def _read_loop(self):
        while self._running:
            try:
                data = self._serial.read_all()
                if data and self._callback:
                    try:
                        self._callback(data)
                    except Exception as e:
                        logging.error(f"Callback error: {e}")
            except serial.SerialException as e:
                logging.error(f"Read error: {e}")
                time.sleep(1)
    def _write_loop(self):
        while self._running:
            try:
                data = self._write_queue.get(timeout=0.1)
                with self._lock:
                    self._serial.write(data)
            except queue.Empty:
                continue
            except serial.SerialException as e:
                logging.error(f"Write error: {e}")

扩展知识与前沿技术

内核模块深度管理

# 查看串口驱动详细信息
modinfo usbserial
modinfo ftdi_sio
# 动态调整串口驱动参数(以pl2303为例)
echo "256" | sudo tee /sys/module/pl2303/parameters/minors
# 黑名单特定驱动(解决冲突)
echo "blacklist ch341" | sudo tee /etc/modprobe.d/blacklist-serial.conf

虚拟串口高级应用

# 创建带流量控制的虚拟串口对
socat -d -d -lf /var/log/virtual-serial.log \
    PTY,raw,echo=0,link=/tmp/virtual1,perm=0666 \
    PTY,raw,echo=0,link=/tmp/virtual2,perm=0666
# 模拟硬件流控行为
socat /tmp/virtual1,raw,crtscts=1,b115200 \
    SYSTEM:'tee input.log | socat - "PTY,link=/tmp/virtual3,raw" | tee output.log'

波特率智能检测算法

def detect_baudrate(port, test_string=b"AT\r\n", timeout=0.5):
    common_rates = [9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600]
    for rate in sorted(common_rates, reverse=True):
        try:
            with serial.Serial(port, rate, timeout=timeout) as ser:
                ser.write(test_string)
                response = ser.read(len(test_string))
                if response == test_string:
                    return rate
        except:
            continue
    return None

权威参考资料与延伸阅读

  1. Linux内核文档

    • Documentation/serial/tty.txt
    • Documentation/usb/usb-serial.txt
  2. 工业标准

    • RS-232/EIA-232-F标准
    • IEEE Std 1003.1(POSIX终端接口)
  3. 专业书籍

    • 《Linux设备驱动程序》(O'Reilly)
    • 《Serial Port Complete》(Lakeview Research)
  4. 芯片手册

    • FTDI FT232R/FT4232H技术参考手册
    • Silicon Labs CP210x编程指南
  5. 开源项目

    • pyserial项目(GitHub)
    • Linux Serial HOWTO

通过本指南的系统学习,开发者可以掌握从基础配置到高级优化的全套串口通信技能,满足工业自动化、物联网网关、机器人控制等复杂场景的需求,实际应用中建议:

  1. 根据具体硬件手册进行参数微调
  2. 关键任务系统实现双冗余串口
  3. 定期进行信号完整性测试
  4. 建立完善的错误处理和恢复机制

Linux下串口配置与使用详解?Linux串口怎么配置?如何配置Linux串口?

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码