配置串口奇偶校验位

1 概述

串口校验位(Parity)用于检测数据传输中的错误。常见的校验方式有奇校验(odd)、偶校验(even)和无校验(none)。本文介绍如何配置串口校验位,并提供了相应的应用指导。

1.1背景

部分客户在使用CM0产品串口功能时,需要配置串口校验位以实现数据传输的可靠性。本文将介绍如何配置串口校验位,并提供相应的应用指导。

1.2 适用范围

本应用适用于所有的CM0设备。

2 应用指导

下文仅以ED-IPC1100设备的RS485为例,介绍安装和配置串口校验位的步骤。

2.1 串口映射(须先确认)

在进行安装前,请先确认设备的串口映射与蓝牙 overlay 设置,确保 /dev/serial0 指向预期的硬件设备 /dev/ttyAMA0。

2.1.1 切换 overlay

sudo nano /boot/firmware/config.txt

2.1.2 禁用蓝牙(优先串口稳定性)

  • disable-bt:/dev/serial0 -> ttyAMA0,禁用蓝牙,PL011(硬件 UART)用于串口(推荐用于稳定通信)。
  • 如下图添加 dtoverlay=disable-bt,保存并重启设备。
step-1

2.1.3 保留蓝牙(使用 miniuart)

  • miniuart-bt:/dev/serial0 -> ttyAMA0,保留蓝牙但使用 mini UART,波特率可能受 CPU 频率影响(可能不稳定)。
  • 如下图添加 dtoverlay=miniuart-bt,保存并重启设备。
step-1

2.1.4 验证映射

readlink -f /dev/serial0

2.2 配置串口校验位设置服务

2.2.1 创建安装文件

sudo nano serial-service-installer.sh

将以下脚本内容复制到文件中:


#!/bin/bash

set -e

SCRIPT_NAME="serial-service-installer.sh"
SERVICE_NAME="serial-parity.service"
INSTALL_PATH="/usr/local/bin/serial-parity-daemon"
SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME"
CONFIG_FILE="/etc/serial-parity.conf"

# 默认配置
SERIAL_PORT="/dev/ttyAMA0"
BAUD_RATE="9600"
PARITY_MODE="even"

log() {
    echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

warn() {
    echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

error() {
    echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

manage_config() {
    local action="$1"
    local file_path="$2"
    
    case "$action" in
        "ensure")
            if [ ! -f "$file_path" ]; then
                mkdir -p "$(dirname "$file_path")" 2>/dev/null || true
                cat > "$file_path" << 'EOF'
# 串口奇偶校验配置
#偶校验even|奇校验odd|无校验none
PARITY_MODE=even
#接口配置
SERIAL_PORT=/dev/ttyAMA0
#波特率配置
BAUD_RATE=9600
EOF
                chmod 644 "$file_path" 2>/dev/null || true
                log "创建配置文件: $file_path"
            fi
            ;;
        "load")
            if [ -f "$file_path" ]; then
                source "$file_path"
            fi
            ;;
    esac
}

check_root() {
    if [[ $EUID -ne 0 ]]; then
        error "此操作需要root权限,请使用 sudo 执行"
        exit 1
    fi
}

check_serial_device() {
    local device="$1"
    if [ ! -e "$device" ]; then
        warn "串口设备 $device 不存在,但继续安装"
        return 1
    fi
    
    if [ ! -r "$device" ] || [ ! -w "$device" ]; then
        warn "对串口设备 $device 权限不足"
        return 1
    fi
    
    log "串口设备 $device 检查通过"
    return 0
}

create_daemon_script() {
    local daemon_path="$1"
    
    log "创建主服务程序..."
    
    cat > "$daemon_path" << 'DAEMON_EOF'
#!/bin/bash
# 串口奇偶校验守护进程

CONFIG_FILE="/etc/serial-parity.conf"

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1"
}

apply_serial_settings() {
    (
        case "$PARITY_MODE" in
            "even")
                stty -F "$SERIAL_PORT" speed "$BAUD_RATE" cs7 parenb -parodd -cstopb
                ;;
            "odd")
                stty -F "$SERIAL_PORT" speed "$BAUD_RATE" cs7 parenb parodd -cstopb
                ;;
            *)
                stty -F "$SERIAL_PORT" speed "$BAUD_RATE" cs8 -parenb -cstopb
                ;;
        esac
    ) >/dev/null 2>&1
    
    if [ $? -eq 0 ]; then
        log "串口设置成功: 模式=$PARITY_MODE, 波特率=$BAUD_RATE"
        return 0
    else
        log "串口设置失败"
        return 1
    fi
}

set_config() {
    local new_value="$1"
    
    [ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE"
    
    case "$new_value" in
        "even"|"odd"|"none")
            sed -i "s/^PARITY_MODE=.*/PARITY_MODE=$new_value/" "$CONFIG_FILE"
            PARITY_MODE="$new_value"
            log "设置校验模式: $PARITY_MODE"
            ;;
        *)
            if [[ "$new_value" =~ ^[0-9]+$ ]] && [ "$new_value" -ge 300 ] && [ "$new_value" -le 4000000 ]; then
                sed -i "s/^BAUD_RATE=.*/BAUD_RATE=$new_value/" "$CONFIG_FILE"
                BAUD_RATE="$new_value"
                log "设置波特率: $BAUD_RATE"
            else
                echo "错误: 无效参数 '$new_value'"
                echo "用法: $0 set {even|odd|none|波特率}"
                return 1
            fi
            ;;
    esac
    
    apply_serial_settings
}

show_help() {
    cat <<'EOF'
串口奇偶校验服务
用法: /usr/local/bin/serial-parity-daemon set {none|odd|even|波特率}

设置示例:
  sudo serial-parity-daemon set odd     # 奇校验  
  sudo serial-parity-daemon set 115200  # 115200波特率

服务管理:
  sudo systemctl {start|stop|status} serial-parity.service
EOF
}

main_service() {
    [ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE"
    
    log "启动串口服务: $SERIAL_PORT, $BAUD_RATE, $PARITY_MODE"
    
    [ ! -e "$SERIAL_PORT" ] && {
        log "错误: 串口设备不存在"
        exit 1
    }
    
    apply_serial_settings || exit 1
    
    log "服务运行中"
    
    while true; do
        sleep 3600
    done
}

case "${1:-}" in
    "start") main_service ;;
    "set") 
        [ -n "$2" ] && set_config "$2" || {
            echo "用法: $0 set {even|odd|none|波特率}"
            exit 1
        }
        ;;
    "help"|"-h"|*) show_help ;;
esac
DAEMON_EOF

    chmod +x "$daemon_path"
    log "主服务程序创建完成: $daemon_path"
}

create_systemd_service() {
    log "创建systemd服务文件..."
    
    cat > "$SERVICE_FILE" << SERVICE_EOF
[Unit]
Description=Serial Port Parity Configuration Service
After=multi-user.target

[Service]
Type=simple
ExecStart=$INSTALL_PATH start
Restart=always
RestartSec=5
User=root
Group=dialout

[Install]
WantedBy=multi-user.target
SERVICE_EOF

    log "systemd服务文件创建完成: $SERVICE_FILE"
}

install_service() {
    check_root
    log "开始安装串口服务..."
    
    check_serial_device "$SERIAL_PORT"
    
    mkdir -p "$(dirname "$INSTALL_PATH")"
    [ -f "$INSTALL_PATH" ] && {
        backup="$INSTALL_PATH.backup.$(date +%Y%m%d_%H%M%S)"
        cp "$INSTALL_PATH" "$backup"
        log "已备份现有文件: $backup"
    }
    
    create_daemon_script "$INSTALL_PATH"
    create_systemd_service
    manage_config "ensure" "$CONFIG_FILE"
    
    systemctl daemon-reload
    systemctl enable "$SERVICE_NAME"
    log "服务安装完成"
}

start_service() {
    check_root
    systemctl start "$SERVICE_NAME" && log "服务启动成功" || {
        error "服务启动失败"
        exit 1
    }
}

self_cleanup() {
    log "安装完成,清理安装脚本..."
    cat > /tmp/cleanup-$$.sh << 'CLEANUP_EOF'
#!/bin/bash
sleep 3
[ -f "$1" ] && rm -f "$1"
rm -f "$0"
CLEANUP_EOF

    chmod +x /tmp/cleanup-$$.sh
    nohup /tmp/cleanup-$$.sh "$0" >/dev/null 2>&1 &
}

show_help() {
    cat <<'EOF'
串口服务安装脚本
用法: sudo ./serial-service-installer.sh install
EOF
}

main() {
    case "${1:-}" in
        "install")
            install_service
            start_service
            self_cleanup
            ;;
        *) show_help ;;
    esac
}

[[ "${BASH_SOURCE[0]}" == "${0}" ]] && main "$@"

2.2.2 安装脚本并生成unit

sudo chmod +x serial-service-installer.sh
sudo ./serial-service-installer.sh install

2.2.3 查看服务状态

sudo systemctl status serial-parity.service

2.3 更改串口配置

2.3.1 切换奇偶校验和波特率(默认偶校验、9600)

sudo nano /etc/serial-parity.conf
step-1

2.3.3 修改校验位或波特率后重启服务生效

sudo systemctl stop serial-parity.service
sudo systemctl start serial-parity.service

说明:

  • even/odd 常配 7 数据位(cs7);none 使用 8 数据位(cs8)。

2.4 常用操作

  • 启动:sudo systemctl start serial-parity.service
  • 停止:sudo systemctl stop serial-parity.service
  • 开机自启:sudo systemctl enable serial-parity.service
  • 取消自启:sudo systemctl disable serial-parity.service
  • 查看状态:sudo systemctl status serial-parity.service