/** * file rk3588_rs485.c * brief RK3588平台 FIT-485 V1.1 TTL转485模块 完整开发指南 * author zhilin_tang Technology * * 模块型号FIT-485 V1.1 * 接口类型UART TTL (3.3V) * 控制方式自动方向控制 (硬件) / GPIO 方向控制 (软件) * * 本分析涵盖 * 1. 硬件引脚连接与设备树配置 * 2. RS485 驱动架构树形分析 * 3. 串口驱动 DMA 树形分析 * 4. 关键函数树形分析 * 5. 调试工具与命令树形分析 * 6. 数据流树形分析 */一、硬件引脚连接分析1.1 FIT-485 V1.1 模块引脚定义【FIT-485 V1.1 模块引脚功能表】 ┌─────────────┬─────────────────┬─────────────────┬─────────────────────────────┐ │ 引脚号 │ 引脚名称 │ 功能描述 │ 电平 │ ├─────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ VCC │ 3.3V/5V │ 电源输入 │ 3.3V 或 5V │ │ GND │ GND │ 地 │ 0V │ │ TXD │ TTL 发送 │ 连接 MCU TX │ 3.3V TTL │ │ RXD │ TTL 接收 │ 连接 MCU RX │ 3.3V TTL │ │ RE/DE │ 收发控制 │ 低电平接收/高电平发送 │ 3.3V TTL │ │ A │ 485 A │ 差分信号正 │ RS485 电平 │ │ B │ 485 B- │ 差分信号负 │ RS485 电平 │ └─────────────┴─────────────────┴─────────────────┴─────────────────────────────┘ 【硬件连接方案】 方案1: 硬件自动方向控制 (推荐) ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ RK3588 │ │ FIT-485 │ │ RS485总线 │ │ │ │ │ │ │ │ UART_TX ────┼────►│ TXD │ │ │ │ UART_RX ◄───┼─────│ RXD │ │ │ │ │ │ │ │ │ │ │ │ RE/DE ──────┼────►│ 方向控制 │ │ │ │ (接VCC) │ │ (常发模式) │ └─────────────┘ └─────────────┘ └─────────────┘ 方案2: GPIO 软件方向控制 (本方案采用) ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ RK3588 │ │ FIT-485 │ │ RS485总线 │ │ │ │ │ │ │ │ UART_TX ────┼────►│ TXD │ │ │ │ UART_RX ◄───┼─────│ RXD │ │ │ │ │ │ │ │ │ │ GPIO_DIR ───┼────►│ RE/DE │ │ 方向控制 │ │ │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘1.2 RK3588 串口资源与引脚分配根据 RK3588 引脚分布表可用于 RS485 的 UART 资源【RK3588 UART 资源分析】 ┌─────────────┬──────────────┬─────────────────┬───────────────────┬─────────────┐ │ UART 接口 │ 引脚位置 │ 引脚编号 │ 功能定义 │ 可用状态 │ ├─────────────┼──────────────┼─────────────────┼───────────────────┼─────────────┤ │ UART2_TX │ P29 │ GPIO0_B5 │ UART2_TX_M0 │ ✅ 调试口 │ │ UART2_RX │ R29 │ GPIO0_B6 │ UART2_RX_M0 │ ✅ 调试口 │ ├─────────────┼──────────────┼─────────────────┼───────────────────┼─────────────┤ │ UART4_TX │ F28 │ GPIO1_D2 │ UART4_TX_M0 │ ✅ 可用 │ │ UART4_RX │ E28 │ GPIO1_D3 │ UART4_RX_M0 │ ✅ 可用 │ ├─────────────┼──────────────┼─────────────────┼───────────────────┼─────────────┤ │ UART6_TX │ A25 │ GPIO1_A1 │ UART6_TX_M1 │ WiFi/BT │ │ UART6_RX │ A24 │ GPIO1_A0 │ UART6_RX_M1 │ WiFi/BT │ ├─────────────┼──────────────┼─────────────────┼───────────────────┼─────────────┤ │ UART9_TX │ AB28 │ GPIO3_D5 │ UART9_TX_M2 │ ✅ 可用 │ │ UART9_RX │ AA27 │ GPIO3_D4 │ UART9_RX_M2 │ ✅ 可用 │ └─────────────┴──────────────┴─────────────────┴───────────────────┴─────────────┘ 【结论】推荐使用 UART4 (GPIO1_D2/F28 GPIO1_D3/E28) 连接 RS485 模块 方向控制 GPIO 可使用 GPIO0_C4 (LD3/R30) 或 GPIO0_C5 (P30)1.3 完整硬件连接表【RK3588 ←→ FIT-485 V1.1 连接表】 ┌─────────────────┬─────────────────┬─────────────────┬─────────────────────────────┐ │ RK3588 引脚 │ 引脚编号 │ FIT-485 引脚 │ 功能描述 │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ UART4_TX │ GPIO1_D2 (F28) │ TXD │ RS485 数据发送 │ │ UART4_RX │ GPIO1_D3 (E28) │ RXD │ RS485 数据接收 │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ GPIO0_C4 │ LD3 (R30) │ RE/DE │ 方向控制 (高发送/低接收) │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ VCC3V3 │ - │ VCC │ 3.3V 电源 │ │ GND │ - │ GND │ 地 │ └─────────────────┴─────────────────┴─────────────────┴─────────────────────────────┘二、设备树完整配置2.1 设备树文件rk3588-rs485.dtsi/** * file rk3588-rs485.dtsi * brief RK3588 FIT-485 V1.1 RS485 设备树配置 * author zhilin_tang Technology * * note 设计模式分析采用复合模式(Composite Pattern) * 将 UART 控制器和 GPIO 方向控制组合成完整的 RS485 设备。 * * remark RS485 半双工特性 * - 发送时RE/DE 置高数据从 TX 发出 * - 接收时RE/DE 置低数据从 RX 接收 * - 空闲时RE/DE 置低总线处于接收状态 */ /dts-v1/; /plugin/; / { compatible rockchip,rk3588; /* 1. RS485 方向控制 GPIO 定义 */ rs485_dir: rs485-dir { compatible gpio-rs485; gpios gpio0 RK_PC4 GPIO_ACTIVE_HIGH; direction rs485; status okay; }; }; /* 2. 配置 UART4 为 RS485 模式 */ uart4 { status okay; pinctrl-names default; pinctrl-0 uart4m0_xfer; /* UART4 数据引脚 */ /* RS485 相关配置 */ linux,rs485-enabled-at-boot-time; /* 启动时启用 RS485 模式 */ rs485-rts-delay 0 0; /* RTS 延时 (us) */ rs485-rts-active-high; /* RTS 高电平有效 */ /* DMA 配置 (提升性能) */ dmas dma_controller 4, /* TX DMA 通道 */ dma_controller 5; /* RX DMA 通道 */ dma-names tx, rx; /* 方向控制 GPIO (硬件 RTS 或软件 GPIO) */ rts-gpios gpio0 RK_PC4 GPIO_ACTIVE_HIGH; status okay; }; /* 3. 引脚复用配置 */ pinctrl { uart4 { uart4m0_xfer: uart4m0-xfer { rockchip,pins 1 RK_PD2 RK_FUNC_1 pcfg_pull_up, /* UART4_TX */ 1 RK_PD3 RK_FUNC_1 pcfg_pull_up; /* UART4_RX */ }; }; rs485 { rs485_dir_pin: rs485-dir-pin { rockchip,pins 0 RK_PC4 RK_FUNC_GPIO pcfg_pull_none; }; }; };三、RS485 驱动架构树形分析3.1 串口驱动源码目录树【Linux 串口驱动源码目录树】 drivers/tty/serial/ │ ├── serial_core.c # 串口核心层 ★★★ │ ├── uart_register_driver() # 注册串口驱动 │ ├── uart_open() # 打开设备 │ ├── uart_write() # 写入数据 │ ├── uart_read() # 读取数据 │ └── uart_ioctl() # ioctl 处理 │ ├── 8250/ # 8250 兼容驱动 │ ├── 8250_core.c # 8250 核心 │ ├── 8250_port.c # 8250 端口操作 │ ├── 8250_dma.c # 8250 DMA 支持 ★ │ └── 8250_early.c # 早期控制台 │ ├── rockchip_serial.c # Rockchip 串口驱动 ★★★ │ ├── rockchip_serial_probe() # 设备探测 │ ├── rockchip_serial_startup() # 启动串口 │ ├── rockchip_serial_tx() # 发送数据 │ ├── rockchip_serial_rx() # 接收数据 │ └── rockchip_serial_set_termios() # 设置参数 │ ├── serial_core.h # 串口核心头文件 └── serial_mctrl_gpio.c # 调制解调器 GPIO 控制 【RS485 支持相关文件】 drivers/tty/serial/8250/8250_core.c │ ├── serial8250_rs485_supported() # RS485 能力查询 ├── serial8250_rs485_config() # RS485 配置 ★ │ ├── 设置 RTS 延时 │ ├── 配置 RS485 标志 │ └── 设置方向控制 GPIO │ └── serial8250_handle_rs485() # RS485 方向切换 ★ ├── 发送前: RTS 置高 ├── 发送后: RTS 置低 └── 处理 RS485 延时3.2 串口驱动层次关系图【RS485 驱动架构层次】 ┌─────────────────────────────────────────────────────────────────────────────┐ │ 用户空间 (User Space) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 应用程序 (stty, minicom, 自定义程序) │ │ │ │ /dev/ttyS4 字符设备节点 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ TTY 子系统 (tty_io.c) │ │ │ │ tty_open(), tty_write(), tty_read() │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────────────┐ │ 内核空间 (Kernel Space) │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 串口核心层 (serial_core.c) │ │ │ │ uart_ops: .startup, .stop_tx, .start_tx │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Rockchip 串口驱动 (rockchip_serial.c) │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ static const struct uart_ops rockchip_serial_pops { │ │ │ │ │ │ .tx_empty rockchip_serial_tx_empty, │ │ │ │ │ │ .set_mctrl rockchip_serial_set_mctrl, │ │ │ │ │ │ .start_tx rockchip_serial_start_tx, │ │ │ │ │ │ .stop_tx rockchip_serial_stop_tx, │ │ │ │ │ │ .start_rx rockchip_serial_start_rx, │ │ │ │ │ │ .stop_rx rockchip_serial_stop_rx, │ │ │ │ │ │ .enable_ms rockchip_serial_enable_ms, │ │ │ │ │ │ .break_ctl rockchip_serial_break_ctl, │ │ │ │ │ │ .startup rockchip_serial_startup, │ │ │ │ │ │ .shutdown rockchip_serial_shutdown, │ │ │ │ │ │ .set_termios rockchip_serial_set_termios, │ │ │ │ │ │ .type rockchip_serial_type, │ │ │ │ │ │ .config_port rockchip_serial_config_port, │ │ │ │ │ │ .verify_port rockchip_serial_verify_port, │ │ │ │ │ │ }; │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ DMA 引擎 (rockchip_dma.c) │ │ │ │ dmaengine_prep_slave_sg() │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────────────┐ │ 硬件层 (Hardware) │ │ RK3588 UART 控制器 FIT-485 TTL转485 模块 │ └─────────────────────────────────────────────────────────────────────────────┘四、DMA 树形分析4.1 串口 DMA 架构【Rockchip 串口 DMA 架构树】 drivers/dma/rockchip/ │ ├── rockchip-dma.c # Rockchip DMA 驱动 ★★★ │ ├── rockchip_dma_probe() # DMA 控制器探测 │ ├── rockchip_dma_alloc_chan_resources() # 分配 DMA 通道 │ ├── rockchip_dma_prep_slave_sg() # 准备 SG 传输 │ ├── rockchip_dma_issue_pending() # 启动传输 │ └── rockchip_dma_tasklet() # DMA 完成回调 │ └── rockchip-dma.h # DMA 头文件 【DMA 数据传输流程】 用户写数据到 /dev/ttyS4 │ ▼ tty_write() → uart_write() │ ▼ rockchip_serial_start_tx() │ ├── 检查 DMA 是否可用 │ ├── 数据量 DMA 阈值 → PIO 模式 │ └── 数据量 ≥ DMA 阈值 → DMA 模式 │ ▼ [PIO 模式] [DMA 模式] │ │ ▼ ▼ uart_port-ops-tx() dmaengine_prep_slave_sg() │ │ │ ▼ │ dmaengine_submit() │ │ │ ▼ │ rockchip_dma_issue_pending() │ │ │ ▼ │ [硬件 DMA 传输] │ │ │ ▼ │ rockchip_dma_tasklet() │ │ ▼ ▼ 中断处理 DMA 完成回调 │ │ ▼ ▼ uart_write_wakeup() uart_write_wakeup() 【DMA 通道分配】 ┌─────────────┬─────────────────┬─────────────────┬─────────────────────────────┐ │ 通道类型 │ DMA 控制器 │ 通道号 │ 用途 │ ├─────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ TX DMA │ dma_controller │ 4 │ UART4 数据发送 │ │ RX DMA │ dma_controller │ 5 │ UART4 数据接收 │ └─────────────┴─────────────────┴─────────────────┴─────────────────────────────┘五、关键函数树形分析5.1 串口初始化函数/** * brief Rockchip 串口启动函数 * param port 串口端口 * return 0 成功负数错误码 * * note 设计模式分析采用模板方法模式(Template Method Pattern) * 定义串口启动的固定流程使能时钟 → 配置寄存器 → 申请中断 → 启动 DMA */ static int rockchip_serial_startup(struct uart_port *port) { struct rockchip_serial_data *data port-private_data; int ret; /* 步骤1使能时钟 */ clk_prepare_enable(data-clk); clk_prepare_enable(data-pclk); /* 步骤2配置 UART 寄存器 */ writel(0, port-membase UART_IER); /* 禁用中断 */ writel(0, port-membase UART_LCR); /* 清除线路控制 */ writel(UART_FCR_ENABLE_FIFO, port-membase UART_FCR); /* 启用 FIFO */ /* 步骤3配置 RS485 模式 */ if (port-rs485.flags SER_RS485_ENABLED) { /* 设置 RTS 方向控制 */ uart_port-rs485_config(port, port-rs485); /* 配置 RS485 寄存器 */ writel(RS485_CTRL_EN, port-membase UART_RS485_CTRL); } /* 步骤4申请中断 */ ret request_irq(port-irq, rockchip_serial_irq, IRQF_SHARED, rockchip_serial, port); if (ret) { dev_err(port-dev, Unable to register IRQ %d\n, port-irq); return ret; } /* 步骤5配置 DMA */ if (data-dma_tx_chan data-dma_rx_chan) { rockchip_serial_dma_startup(port); } /* 步骤6启用接收 */ writel(UART_IER_RLSI | UART_IER_RDI, port-membase UART_IER); return 0; }5.2 RS485 配置函数/** * brief RS485 配置函数 * param port 串口端口 * param rs485 RS485 配置结构体 * return 0 成功负数错误码 * * note 设计模式分析采用策略模式(Strategy Pattern) * 根据不同的 RS485 硬件配置选择不同的方向控制策略 * - GPIO 软件控制通过 GPIO 控制 RE/DE * - RTS 硬件控制通过 UART 的 RTS 引脚控制 */ static int rockchip_serial_rs485_config(struct uart_port *port, struct serial_rs485 *rs485) { struct rockchip_serial_data *data port-private_data; u32 rs485_ctrl 0; /* 步骤1检查 RS485 是否启用 */ if (!(rs485-flags SER_RS485_ENABLED)) { /* 禁用 RS485 模式恢复普通 UART */ writel(0, port-membase UART_RS485_CTRL); port-rs485.flags ~SER_RS485_ENABLED; return 0; } /* 步骤2配置 RS485 参数 */ rs485_ctrl | RS485_CTRL_EN; /* 启用 RS485 模式 */ /* 配置方向控制极性 */ if (rs485-flags SER_RS485_RTS_ON_SEND) rs485_ctrl | RS485_CTRL_RTS_HIGH; else rs485_ctrl | RS485_CTRL_RTS_LOW; /* 配置延时 */ if (rs485-delay_rts_before_send) { rs485_ctrl | (rs485-delay_rts_before_send RS485_DELAY_POS); } /* 步骤3配置 GPIO 方向控制 (软件模式) */ if (rs485-flags SER_RS485_GPIO) { if (!IS_ERR(data-rts_gpio)) { gpiod_set_value(data-rts_gpio, 0); /* 初始接收状态 */ } rs485_ctrl | RS485_CTRL_GPIO; } /* 步骤4写入寄存器 */ writel(rs485_ctrl, port-membase UART_RS485_CTRL); /* 步骤5保存配置 */ memcpy(port-rs485, rs485, sizeof(struct serial_rs485)); return 0; }5.3 数据发送函数 (RS485 方向切换)/** * brief 串口发送函数 (支持 RS485 方向自动切换) * param port 串口端口 * * note 设计模式分析采用状态模式(State Pattern) * 根据 RS485 状态决定发送前是否需要切换方向。 * * remark 性能分析 * - 方向切换时间: 10us (GPIO) * - DMA 传输时间: 取决于数据量 * - 发送完成回调: 切换回接收状态 */ static void rockchip_serial_start_tx(struct uart_port *port) { struct rockchip_serial_data *data port-private_data; struct circ_buf *xmit port-state-xmit; int count; /* 步骤1检查 RS485 方向 */ if (port-rs485.flags SER_RS485_ENABLED) { /* 切换为发送模式 (RE/DE 置高) */ if (port-rs485.flags SER_RS485_RTS_ON_SEND) { if (port-rs485.flags SER_RS485_GPIO) { /* GPIO 方向控制 */ gpiod_set_value(data-rts_gpio, 1); } else { /* RTS 硬件方向控制 */ uart_update_mctrl(port, TIOCM_RTS, TIOCM_RTS); } } /* 等待 RTS 延时 (如果配置) */ if (port-rs485.delay_rts_before_send) udelay(port-rs485.delay_rts_before_send); } /* 步骤2选择传输模式 */ count uart_circ_chars_pending(xmit); if (count 0 !port-x_char) { if (data-dma_tx_chan count data-dma_tx_threshold) { /* DMA 模式传输 */ rockchip_serial_dma_tx(port); } else { /* PIO 模式传输 */ rockchip_serial_pio_tx(port); } } /* 步骤3发送完成处理 */ /* 发送完成后DMA 回调或中断会调用 rockchip_serial_stop_tx */ } /** * brief 停止发送 (切换回接收模式) * param port 串口端口 */ static void rockchip_serial_stop_tx(struct uart_port *port) { struct rockchip_serial_data *data port-private_data; /* 禁用发送中断 */ writel(readl(port-membase UART_IER) ~UART_IER_THRI, port-membase UART_IER); /* RS485 切换回接收模式 */ if (port-rs485.flags SER_RS485_ENABLED) { /* 等待发送完成 */ while (!(readl(port-membase UART_LSR) UART_LSR_TEMT)) cpu_relax(); /* 切换为接收模式 (RE/DE 置低) */ if (port-rs485.flags SER_RS485_RTS_ON_SEND) { if (port-rs485.flags SER_RS485_GPIO) { gpiod_set_value(data-rts_gpio, 0); } else { uart_update_mctrl(port, 0, TIOCM_RTS); } } } }六、调试工具与命令树形分析6.1 串口调试工具树【串口调试工具树】 系统命令 │ ├── stty # 串口参数配置 ★ │ ├── stty -F /dev/ttyS4 # 查看当前配置 │ ├── stty -F /dev/ttyS4 115200 # 设置波特率 │ ├── stty -F /dev/ttyS4 raw -echo # 原始模式 │ └── stty -F /dev/ttyS4 -a # 显示所有设置 │ ├── echo / cat # 简单读写测试 │ ├── echo test /dev/ttyS4 # 发送数据 │ └── cat /dev/ttyS4 # 接收数据 │ ├── minicom # 串口终端程序 │ ├── minicom -D /dev/ttyS4 # 打开串口 │ └── CtrlA Z # 帮助菜单 │ ├── picocom # 轻量级串口工具 │ └── picocom -b 115200 /dev/ttyS4 │ └── cu # 老式串口工具 └── cu -l /dev/ttyS4 -s 115200 RS485 专用工具 │ ├── rs485conf # RS485 配置工具 ★ │ ├── rs485conf /dev/ttyS4 # 查看 RS485 状态 │ ├── rs485conf -e /dev/ttyS4 # 启用 RS485 模式 │ └── rs485conf -d /dev/ttyS4 # 禁用 RS485 模式 │ └── ioctl 测试 # 通过 ioctl 配置 RS485 └── ./rs485_test /dev/ttyS4 1 # 启用 RS485 调试接口 │ ├── /sys/class/tty/ttyS4/ # sysfs 接口 │ ├── rs485_config # RS485 配置 │ ├── port # 端口信息 │ └── uartclk # UART 时钟 │ └── /proc/tty/driver/serial # proc 统计信息 └── cat /proc/tty/driver/serial # 查看串口统计6.2 完整调试脚本#!/bin/bash # rs485_debug.sh - RS485 完整调试脚本 echo echo RK3588 FIT-485 V1.1 RS485 调试 echo # 颜色定义 RED\033[0;31m GREEN\033[0;32m YELLOW\033[0;33m NC\033[0m SERIAL_DEV/dev/ttyS4 GPIO_DIR20 # GPIO0_C4 编号 # 1. 检查串口设备 echo -e \n[1] 检查串口设备... if [ -c $SERIAL_DEV ]; then echo -e ${GREEN} ✅ 串口设备存在: $SERIAL_DEV${NC} else echo -e ${RED} ❌ 串口设备不存在: $SERIAL_DEV${NC} exit 1 fi # 2. 检查 GPIO 方向控制引脚 echo -e \n[2] 检查 GPIO 方向控制引脚... if [ -d /sys/class/gpio/gpio$GPIO_DIR ]; then echo -e ${GREEN} ✅ GPIO$GPIO_DIR 已导出${NC} else echo -e ${YELLOW} ⚠️ GPIO$GPIO_DIR 未导出尝试导出...${NC} echo $GPIO_DIR /sys/class/gpio/export 2/dev/null echo out /sys/class/gpio/gpio$GPIO_DIR/direction echo 0 /sys/class/gpio/gpio$GPIO_DIR/value fi # 3. 查看当前串口配置 echo -e \n[3] 当前串口配置... stty -F $SERIAL_DEV -a 2/dev/null || echo 无法读取配置 # 4. 查看 RS485 状态 echo -e \n[4] RS485 状态... if [ -f /sys/class/tty/ttyS4/rs485_config ]; then cat /sys/class/tty/ttyS4/rs485_config else echo -e ${YELLOW} 内核未导出 RS485 配置${NC} fi # 5. 配置 RS485 模式 echo -e \n[5] 配置 RS485 模式... # 使用 ioctl 配置 RS485 (需要 rs485conf 工具) if command -v rs485conf /dev/null; then rs485conf -e $SERIAL_DEV echo -e ${GREEN} ✅ RS485 模式已启用${NC} else echo -e ${YELLOW} ⚠️ rs485conf 未安装尝试使用 stty 配置${NC} stty -F $SERIAL_DEV 115200 raw -echo cs8 -cstopb fi # 6. 配置波特率 echo -e \n[6] 配置波特率... stty -F $SERIAL_DEV 115200 echo -e ${GREEN} ✅ 波特率已设置为 115200${NC} # 7. 测试发送 (手动切换方向) echo -e \n[7] 测试发送... echo -e ${YELLOW} 切换为发送模式 (RE/DE高)...${NC} echo 1 /sys/class/gpio/gpio$GPIO_DIR/value sleep 0.1 echo RS485 Test Message from RK3588 $SERIAL_DEV echo -e ${GREEN} ✅ 已发送测试数据${NC} sleep 0.1 echo -e ${YELLOW} 切换回接收模式 (RE/DE低)...${NC} echo 0 /sys/class/gpio/gpio$GPIO_DIR/value # 8. 测试接收 (等待 3 秒) echo -e \n[8] 测试接收 (等待 3 秒)... timeout 3 cat $SERIAL_DEV CAT_PID$! sleep 3 kill $CAT_PID 2/dev/null # 9. 查看串口统计 echo -e \n[9] 串口统计... cat /proc/tty/driver/serial 2/dev/null | grep -E 4:|uart if [ $? -ne 0 ]; then echo /proc/tty/driver/serial 不可用 fi # 10. 查看 DMA 统计 (如果启用) echo -e \n[10] DMA 统计... if [ -d /sys/kernel/debug/dmaengine ]; then ls /sys/kernel/debug/dmaengine/ | grep dma else echo debugfs 未挂载 fi echo -e \n echo -e ${GREEN}调试完成${NC} echo 6.3 RS485 环回测试脚本#!/bin/bash # rs485_loopback.sh - RS485 环回测试 (需短接 A/B 线) echo RS485 环回测试 SERIAL_DEV/dev/ttyS4 TEST_DATARS485 Loopback Test $(date) echo 测试数据: $TEST_DATA # 配置串口 stty -F $SERIAL_DEV 115200 raw -echo # 启动后台接收 cat $SERIAL_DEV /tmp/rs485_recv RECV_PID$! sleep 0.5 # 发送数据 echo $TEST_DATA $SERIAL_DEV sleep 1 # 停止接收 kill $RECV_PID 2/dev/null # 验证接收 if grep -q $TEST_DATA /tmp/rs485_recv; then echo ✅ 环回测试成功 cat /tmp/rs485_recv else echo ❌ 环回测试失败 fi rm -f /tmp/rs485_recv七、数据流树形分析7.1 RS485 数据发送流程【RS485 数据发送完整流程树】 应用程序 write() 调用 │ ▼ tty_write() [tty_io.c] │ ▼ uart_write() [serial_core.c] │ ├── uart_port-ops-start_tx() 调用 │ ▼ rockchip_serial_start_tx() [rockchip_serial.c] │ ├── [RS485] 切换为发送模式 │ ├── gpiod_set_value(rts_gpio, 1) # RE/DE 置高 │ └── udelay(delay_rts_before_send) # 延时 │ ├── 判断数据量大小 │ │ │ ├── 数据量 ≥ DMA 阈值 │ │ ▼ │ │ rockchip_serial_dma_tx() │ │ │ │ │ ├── dmaengine_prep_slave_sg() # 准备 DMA │ │ ├── dmaengine_submit() # 提交 DMA │ │ └── dma_async_issue_pending() # 启动 DMA │ │ │ │ │ ▼ │ │ [硬件 DMA 传输] │ │ │ │ │ ▼ │ │ rockchip_dma_tasklet() # DMA 完成 │ │ │ │ │ ▼ │ │ rockchip_serial_stop_tx() │ │ │ └── 数据量 DMA 阈值 │ ▼ │ rockchip_serial_pio_tx() │ │ │ ├── while (仍有数据) │ │ │ │ │ ├── writel(数据, THR) # 写入发送保持寄存器 │ │ └── 等待 THRE 中断 │ │ │ └── 发送完成中断 │ │ │ ▼ │ rockchip_serial_irq() │ │ │ └── rockchip_serial_stop_tx() │ ▼ rockchip_serial_stop_tx() │ ├── 等待发送完成 (TEMT 位) ├── [RS485] 切换回接收模式 │ ├── udelay(delay_rts_after_send) # 可选延时 │ └── gpiod_set_value(rts_gpio, 0) # RE/DE 置低 │ └── uart_write_wakeup() # 唤醒写入进程7.2 RS485 数据接收流程【RS485 数据接收完整流程树】 [RS485 总线数据到达] │ ▼ FIT-485 模块接收 (RE/DE 低) │ ▼ RK3588 UART 控制器 │ ▼ UART 接收中断 (RDI) │ ▼ rockchip_serial_irq() [rockchip_serial.c] │ ├── 读取接收数据 │ │ │ ├── [DMA 模式] │ │ └── DMA 自动将数据写入内存 │ │ │ └── [PIO 模式] │ └── 从 RBR 读取数据 │ ▼ uart_insert_char() [serial_core.c] │ ├── 将数据放入 tty 缓冲区 │ ▼ tty_flip_buffer_push() [tty_buffer.c] │ ▼ 唤醒等待读取的进程 │ ▼ 应用程序 read() 返回八、常见问题排查问题现象可能原因排查命令解决方案设备节点不存在设备树未配置ls /dev/ttyS*检查设备树确保 uart4 节点启用无法发送数据方向控制错误cat /sys/class/gpio/gpio20/value检查 RE/DE 电平确保发送时置高无法接收数据总线空闲状态示波器测 A/B 线确保 RE/DE 置低总线有终端电阻数据乱码波特率不匹配stty -F /dev/ttyS4检查两端波特率一致数据丢失FIFO 溢出cat /proc/tty/driver/serial检查错误计数启用 DMARS485 模式不生效内核未配置zgrep RS485 /proc/config.gz确保 CONFIG_SERIAL_RS485y九、总结项目内容模块型号FIT-485 V1.1接口类型TTL 转 RS485串口设备/dev/ttyS4 (UART4)方向控制 GPIOGPIO0_C4 (LD3/R30)内核配置CONFIG_SERIAL_RS485y设备树节点uart4 rs485 属性测试命令stty, minicom, rs485confDMA 通道TX:4, RX:5关键配置要点RS485 半双工特性发送时 RE/DE 置高接收时 RE/DE 置低方向控制推荐使用 GPIO 软件控制便于调试终端电阻总线两端需要 120Ω 终端电阻DMA 优化大数据量传输时启用 DMA 可降低 CPU 负载时序要求方向切换需要足够的延时 (通常 10-100us)
RK3588 + FIT-485 V1.1 TTL转485模块 完整开发指南
/** * file rk3588_rs485.c * brief RK3588平台 FIT-485 V1.1 TTL转485模块 完整开发指南 * author zhilin_tang Technology * * 模块型号FIT-485 V1.1 * 接口类型UART TTL (3.3V) * 控制方式自动方向控制 (硬件) / GPIO 方向控制 (软件) * * 本分析涵盖 * 1. 硬件引脚连接与设备树配置 * 2. RS485 驱动架构树形分析 * 3. 串口驱动 DMA 树形分析 * 4. 关键函数树形分析 * 5. 调试工具与命令树形分析 * 6. 数据流树形分析 */一、硬件引脚连接分析1.1 FIT-485 V1.1 模块引脚定义【FIT-485 V1.1 模块引脚功能表】 ┌─────────────┬─────────────────┬─────────────────┬─────────────────────────────┐ │ 引脚号 │ 引脚名称 │ 功能描述 │ 电平 │ ├─────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ VCC │ 3.3V/5V │ 电源输入 │ 3.3V 或 5V │ │ GND │ GND │ 地 │ 0V │ │ TXD │ TTL 发送 │ 连接 MCU TX │ 3.3V TTL │ │ RXD │ TTL 接收 │ 连接 MCU RX │ 3.3V TTL │ │ RE/DE │ 收发控制 │ 低电平接收/高电平发送 │ 3.3V TTL │ │ A │ 485 A │ 差分信号正 │ RS485 电平 │ │ B │ 485 B- │ 差分信号负 │ RS485 电平 │ └─────────────┴─────────────────┴─────────────────┴─────────────────────────────┘ 【硬件连接方案】 方案1: 硬件自动方向控制 (推荐) ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ RK3588 │ │ FIT-485 │ │ RS485总线 │ │ │ │ │ │ │ │ UART_TX ────┼────►│ TXD │ │ │ │ UART_RX ◄───┼─────│ RXD │ │ │ │ │ │ │ │ │ │ │ │ RE/DE ──────┼────►│ 方向控制 │ │ │ │ (接VCC) │ │ (常发模式) │ └─────────────┘ └─────────────┘ └─────────────┘ 方案2: GPIO 软件方向控制 (本方案采用) ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ RK3588 │ │ FIT-485 │ │ RS485总线 │ │ │ │ │ │ │ │ UART_TX ────┼────►│ TXD │ │ │ │ UART_RX ◄───┼─────│ RXD │ │ │ │ │ │ │ │ │ │ GPIO_DIR ───┼────►│ RE/DE │ │ 方向控制 │ │ │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘1.2 RK3588 串口资源与引脚分配根据 RK3588 引脚分布表可用于 RS485 的 UART 资源【RK3588 UART 资源分析】 ┌─────────────┬──────────────┬─────────────────┬───────────────────┬─────────────┐ │ UART 接口 │ 引脚位置 │ 引脚编号 │ 功能定义 │ 可用状态 │ ├─────────────┼──────────────┼─────────────────┼───────────────────┼─────────────┤ │ UART2_TX │ P29 │ GPIO0_B5 │ UART2_TX_M0 │ ✅ 调试口 │ │ UART2_RX │ R29 │ GPIO0_B6 │ UART2_RX_M0 │ ✅ 调试口 │ ├─────────────┼──────────────┼─────────────────┼───────────────────┼─────────────┤ │ UART4_TX │ F28 │ GPIO1_D2 │ UART4_TX_M0 │ ✅ 可用 │ │ UART4_RX │ E28 │ GPIO1_D3 │ UART4_RX_M0 │ ✅ 可用 │ ├─────────────┼──────────────┼─────────────────┼───────────────────┼─────────────┤ │ UART6_TX │ A25 │ GPIO1_A1 │ UART6_TX_M1 │ WiFi/BT │ │ UART6_RX │ A24 │ GPIO1_A0 │ UART6_RX_M1 │ WiFi/BT │ ├─────────────┼──────────────┼─────────────────┼───────────────────┼─────────────┤ │ UART9_TX │ AB28 │ GPIO3_D5 │ UART9_TX_M2 │ ✅ 可用 │ │ UART9_RX │ AA27 │ GPIO3_D4 │ UART9_RX_M2 │ ✅ 可用 │ └─────────────┴──────────────┴─────────────────┴───────────────────┴─────────────┘ 【结论】推荐使用 UART4 (GPIO1_D2/F28 GPIO1_D3/E28) 连接 RS485 模块 方向控制 GPIO 可使用 GPIO0_C4 (LD3/R30) 或 GPIO0_C5 (P30)1.3 完整硬件连接表【RK3588 ←→ FIT-485 V1.1 连接表】 ┌─────────────────┬─────────────────┬─────────────────┬─────────────────────────────┐ │ RK3588 引脚 │ 引脚编号 │ FIT-485 引脚 │ 功能描述 │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ UART4_TX │ GPIO1_D2 (F28) │ TXD │ RS485 数据发送 │ │ UART4_RX │ GPIO1_D3 (E28) │ RXD │ RS485 数据接收 │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ GPIO0_C4 │ LD3 (R30) │ RE/DE │ 方向控制 (高发送/低接收) │ ├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ VCC3V3 │ - │ VCC │ 3.3V 电源 │ │ GND │ - │ GND │ 地 │ └─────────────────┴─────────────────┴─────────────────┴─────────────────────────────┘二、设备树完整配置2.1 设备树文件rk3588-rs485.dtsi/** * file rk3588-rs485.dtsi * brief RK3588 FIT-485 V1.1 RS485 设备树配置 * author zhilin_tang Technology * * note 设计模式分析采用复合模式(Composite Pattern) * 将 UART 控制器和 GPIO 方向控制组合成完整的 RS485 设备。 * * remark RS485 半双工特性 * - 发送时RE/DE 置高数据从 TX 发出 * - 接收时RE/DE 置低数据从 RX 接收 * - 空闲时RE/DE 置低总线处于接收状态 */ /dts-v1/; /plugin/; / { compatible rockchip,rk3588; /* 1. RS485 方向控制 GPIO 定义 */ rs485_dir: rs485-dir { compatible gpio-rs485; gpios gpio0 RK_PC4 GPIO_ACTIVE_HIGH; direction rs485; status okay; }; }; /* 2. 配置 UART4 为 RS485 模式 */ uart4 { status okay; pinctrl-names default; pinctrl-0 uart4m0_xfer; /* UART4 数据引脚 */ /* RS485 相关配置 */ linux,rs485-enabled-at-boot-time; /* 启动时启用 RS485 模式 */ rs485-rts-delay 0 0; /* RTS 延时 (us) */ rs485-rts-active-high; /* RTS 高电平有效 */ /* DMA 配置 (提升性能) */ dmas dma_controller 4, /* TX DMA 通道 */ dma_controller 5; /* RX DMA 通道 */ dma-names tx, rx; /* 方向控制 GPIO (硬件 RTS 或软件 GPIO) */ rts-gpios gpio0 RK_PC4 GPIO_ACTIVE_HIGH; status okay; }; /* 3. 引脚复用配置 */ pinctrl { uart4 { uart4m0_xfer: uart4m0-xfer { rockchip,pins 1 RK_PD2 RK_FUNC_1 pcfg_pull_up, /* UART4_TX */ 1 RK_PD3 RK_FUNC_1 pcfg_pull_up; /* UART4_RX */ }; }; rs485 { rs485_dir_pin: rs485-dir-pin { rockchip,pins 0 RK_PC4 RK_FUNC_GPIO pcfg_pull_none; }; }; };三、RS485 驱动架构树形分析3.1 串口驱动源码目录树【Linux 串口驱动源码目录树】 drivers/tty/serial/ │ ├── serial_core.c # 串口核心层 ★★★ │ ├── uart_register_driver() # 注册串口驱动 │ ├── uart_open() # 打开设备 │ ├── uart_write() # 写入数据 │ ├── uart_read() # 读取数据 │ └── uart_ioctl() # ioctl 处理 │ ├── 8250/ # 8250 兼容驱动 │ ├── 8250_core.c # 8250 核心 │ ├── 8250_port.c # 8250 端口操作 │ ├── 8250_dma.c # 8250 DMA 支持 ★ │ └── 8250_early.c # 早期控制台 │ ├── rockchip_serial.c # Rockchip 串口驱动 ★★★ │ ├── rockchip_serial_probe() # 设备探测 │ ├── rockchip_serial_startup() # 启动串口 │ ├── rockchip_serial_tx() # 发送数据 │ ├── rockchip_serial_rx() # 接收数据 │ └── rockchip_serial_set_termios() # 设置参数 │ ├── serial_core.h # 串口核心头文件 └── serial_mctrl_gpio.c # 调制解调器 GPIO 控制 【RS485 支持相关文件】 drivers/tty/serial/8250/8250_core.c │ ├── serial8250_rs485_supported() # RS485 能力查询 ├── serial8250_rs485_config() # RS485 配置 ★ │ ├── 设置 RTS 延时 │ ├── 配置 RS485 标志 │ └── 设置方向控制 GPIO │ └── serial8250_handle_rs485() # RS485 方向切换 ★ ├── 发送前: RTS 置高 ├── 发送后: RTS 置低 └── 处理 RS485 延时3.2 串口驱动层次关系图【RS485 驱动架构层次】 ┌─────────────────────────────────────────────────────────────────────────────┐ │ 用户空间 (User Space) │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 应用程序 (stty, minicom, 自定义程序) │ │ │ │ /dev/ttyS4 字符设备节点 │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ TTY 子系统 (tty_io.c) │ │ │ │ tty_open(), tty_write(), tty_read() │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────────────┐ │ 内核空间 (Kernel Space) │ │ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ 串口核心层 (serial_core.c) │ │ │ │ uart_ops: .startup, .stop_tx, .start_tx │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ Rockchip 串口驱动 (rockchip_serial.c) │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ static const struct uart_ops rockchip_serial_pops { │ │ │ │ │ │ .tx_empty rockchip_serial_tx_empty, │ │ │ │ │ │ .set_mctrl rockchip_serial_set_mctrl, │ │ │ │ │ │ .start_tx rockchip_serial_start_tx, │ │ │ │ │ │ .stop_tx rockchip_serial_stop_tx, │ │ │ │ │ │ .start_rx rockchip_serial_start_rx, │ │ │ │ │ │ .stop_rx rockchip_serial_stop_rx, │ │ │ │ │ │ .enable_ms rockchip_serial_enable_ms, │ │ │ │ │ │ .break_ctl rockchip_serial_break_ctl, │ │ │ │ │ │ .startup rockchip_serial_startup, │ │ │ │ │ │ .shutdown rockchip_serial_shutdown, │ │ │ │ │ │ .set_termios rockchip_serial_set_termios, │ │ │ │ │ │ .type rockchip_serial_type, │ │ │ │ │ │ .config_port rockchip_serial_config_port, │ │ │ │ │ │ .verify_port rockchip_serial_verify_port, │ │ │ │ │ │ }; │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │ │ DMA 引擎 (rockchip_dma.c) │ │ │ │ dmaengine_prep_slave_sg() │ │ │ └─────────────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────────────┐ │ 硬件层 (Hardware) │ │ RK3588 UART 控制器 FIT-485 TTL转485 模块 │ └─────────────────────────────────────────────────────────────────────────────┘四、DMA 树形分析4.1 串口 DMA 架构【Rockchip 串口 DMA 架构树】 drivers/dma/rockchip/ │ ├── rockchip-dma.c # Rockchip DMA 驱动 ★★★ │ ├── rockchip_dma_probe() # DMA 控制器探测 │ ├── rockchip_dma_alloc_chan_resources() # 分配 DMA 通道 │ ├── rockchip_dma_prep_slave_sg() # 准备 SG 传输 │ ├── rockchip_dma_issue_pending() # 启动传输 │ └── rockchip_dma_tasklet() # DMA 完成回调 │ └── rockchip-dma.h # DMA 头文件 【DMA 数据传输流程】 用户写数据到 /dev/ttyS4 │ ▼ tty_write() → uart_write() │ ▼ rockchip_serial_start_tx() │ ├── 检查 DMA 是否可用 │ ├── 数据量 DMA 阈值 → PIO 模式 │ └── 数据量 ≥ DMA 阈值 → DMA 模式 │ ▼ [PIO 模式] [DMA 模式] │ │ ▼ ▼ uart_port-ops-tx() dmaengine_prep_slave_sg() │ │ │ ▼ │ dmaengine_submit() │ │ │ ▼ │ rockchip_dma_issue_pending() │ │ │ ▼ │ [硬件 DMA 传输] │ │ │ ▼ │ rockchip_dma_tasklet() │ │ ▼ ▼ 中断处理 DMA 完成回调 │ │ ▼ ▼ uart_write_wakeup() uart_write_wakeup() 【DMA 通道分配】 ┌─────────────┬─────────────────┬─────────────────┬─────────────────────────────┐ │ 通道类型 │ DMA 控制器 │ 通道号 │ 用途 │ ├─────────────┼─────────────────┼─────────────────┼─────────────────────────────┤ │ TX DMA │ dma_controller │ 4 │ UART4 数据发送 │ │ RX DMA │ dma_controller │ 5 │ UART4 数据接收 │ └─────────────┴─────────────────┴─────────────────┴─────────────────────────────┘五、关键函数树形分析5.1 串口初始化函数/** * brief Rockchip 串口启动函数 * param port 串口端口 * return 0 成功负数错误码 * * note 设计模式分析采用模板方法模式(Template Method Pattern) * 定义串口启动的固定流程使能时钟 → 配置寄存器 → 申请中断 → 启动 DMA */ static int rockchip_serial_startup(struct uart_port *port) { struct rockchip_serial_data *data port-private_data; int ret; /* 步骤1使能时钟 */ clk_prepare_enable(data-clk); clk_prepare_enable(data-pclk); /* 步骤2配置 UART 寄存器 */ writel(0, port-membase UART_IER); /* 禁用中断 */ writel(0, port-membase UART_LCR); /* 清除线路控制 */ writel(UART_FCR_ENABLE_FIFO, port-membase UART_FCR); /* 启用 FIFO */ /* 步骤3配置 RS485 模式 */ if (port-rs485.flags SER_RS485_ENABLED) { /* 设置 RTS 方向控制 */ uart_port-rs485_config(port, port-rs485); /* 配置 RS485 寄存器 */ writel(RS485_CTRL_EN, port-membase UART_RS485_CTRL); } /* 步骤4申请中断 */ ret request_irq(port-irq, rockchip_serial_irq, IRQF_SHARED, rockchip_serial, port); if (ret) { dev_err(port-dev, Unable to register IRQ %d\n, port-irq); return ret; } /* 步骤5配置 DMA */ if (data-dma_tx_chan data-dma_rx_chan) { rockchip_serial_dma_startup(port); } /* 步骤6启用接收 */ writel(UART_IER_RLSI | UART_IER_RDI, port-membase UART_IER); return 0; }5.2 RS485 配置函数/** * brief RS485 配置函数 * param port 串口端口 * param rs485 RS485 配置结构体 * return 0 成功负数错误码 * * note 设计模式分析采用策略模式(Strategy Pattern) * 根据不同的 RS485 硬件配置选择不同的方向控制策略 * - GPIO 软件控制通过 GPIO 控制 RE/DE * - RTS 硬件控制通过 UART 的 RTS 引脚控制 */ static int rockchip_serial_rs485_config(struct uart_port *port, struct serial_rs485 *rs485) { struct rockchip_serial_data *data port-private_data; u32 rs485_ctrl 0; /* 步骤1检查 RS485 是否启用 */ if (!(rs485-flags SER_RS485_ENABLED)) { /* 禁用 RS485 模式恢复普通 UART */ writel(0, port-membase UART_RS485_CTRL); port-rs485.flags ~SER_RS485_ENABLED; return 0; } /* 步骤2配置 RS485 参数 */ rs485_ctrl | RS485_CTRL_EN; /* 启用 RS485 模式 */ /* 配置方向控制极性 */ if (rs485-flags SER_RS485_RTS_ON_SEND) rs485_ctrl | RS485_CTRL_RTS_HIGH; else rs485_ctrl | RS485_CTRL_RTS_LOW; /* 配置延时 */ if (rs485-delay_rts_before_send) { rs485_ctrl | (rs485-delay_rts_before_send RS485_DELAY_POS); } /* 步骤3配置 GPIO 方向控制 (软件模式) */ if (rs485-flags SER_RS485_GPIO) { if (!IS_ERR(data-rts_gpio)) { gpiod_set_value(data-rts_gpio, 0); /* 初始接收状态 */ } rs485_ctrl | RS485_CTRL_GPIO; } /* 步骤4写入寄存器 */ writel(rs485_ctrl, port-membase UART_RS485_CTRL); /* 步骤5保存配置 */ memcpy(port-rs485, rs485, sizeof(struct serial_rs485)); return 0; }5.3 数据发送函数 (RS485 方向切换)/** * brief 串口发送函数 (支持 RS485 方向自动切换) * param port 串口端口 * * note 设计模式分析采用状态模式(State Pattern) * 根据 RS485 状态决定发送前是否需要切换方向。 * * remark 性能分析 * - 方向切换时间: 10us (GPIO) * - DMA 传输时间: 取决于数据量 * - 发送完成回调: 切换回接收状态 */ static void rockchip_serial_start_tx(struct uart_port *port) { struct rockchip_serial_data *data port-private_data; struct circ_buf *xmit port-state-xmit; int count; /* 步骤1检查 RS485 方向 */ if (port-rs485.flags SER_RS485_ENABLED) { /* 切换为发送模式 (RE/DE 置高) */ if (port-rs485.flags SER_RS485_RTS_ON_SEND) { if (port-rs485.flags SER_RS485_GPIO) { /* GPIO 方向控制 */ gpiod_set_value(data-rts_gpio, 1); } else { /* RTS 硬件方向控制 */ uart_update_mctrl(port, TIOCM_RTS, TIOCM_RTS); } } /* 等待 RTS 延时 (如果配置) */ if (port-rs485.delay_rts_before_send) udelay(port-rs485.delay_rts_before_send); } /* 步骤2选择传输模式 */ count uart_circ_chars_pending(xmit); if (count 0 !port-x_char) { if (data-dma_tx_chan count data-dma_tx_threshold) { /* DMA 模式传输 */ rockchip_serial_dma_tx(port); } else { /* PIO 模式传输 */ rockchip_serial_pio_tx(port); } } /* 步骤3发送完成处理 */ /* 发送完成后DMA 回调或中断会调用 rockchip_serial_stop_tx */ } /** * brief 停止发送 (切换回接收模式) * param port 串口端口 */ static void rockchip_serial_stop_tx(struct uart_port *port) { struct rockchip_serial_data *data port-private_data; /* 禁用发送中断 */ writel(readl(port-membase UART_IER) ~UART_IER_THRI, port-membase UART_IER); /* RS485 切换回接收模式 */ if (port-rs485.flags SER_RS485_ENABLED) { /* 等待发送完成 */ while (!(readl(port-membase UART_LSR) UART_LSR_TEMT)) cpu_relax(); /* 切换为接收模式 (RE/DE 置低) */ if (port-rs485.flags SER_RS485_RTS_ON_SEND) { if (port-rs485.flags SER_RS485_GPIO) { gpiod_set_value(data-rts_gpio, 0); } else { uart_update_mctrl(port, 0, TIOCM_RTS); } } } }六、调试工具与命令树形分析6.1 串口调试工具树【串口调试工具树】 系统命令 │ ├── stty # 串口参数配置 ★ │ ├── stty -F /dev/ttyS4 # 查看当前配置 │ ├── stty -F /dev/ttyS4 115200 # 设置波特率 │ ├── stty -F /dev/ttyS4 raw -echo # 原始模式 │ └── stty -F /dev/ttyS4 -a # 显示所有设置 │ ├── echo / cat # 简单读写测试 │ ├── echo test /dev/ttyS4 # 发送数据 │ └── cat /dev/ttyS4 # 接收数据 │ ├── minicom # 串口终端程序 │ ├── minicom -D /dev/ttyS4 # 打开串口 │ └── CtrlA Z # 帮助菜单 │ ├── picocom # 轻量级串口工具 │ └── picocom -b 115200 /dev/ttyS4 │ └── cu # 老式串口工具 └── cu -l /dev/ttyS4 -s 115200 RS485 专用工具 │ ├── rs485conf # RS485 配置工具 ★ │ ├── rs485conf /dev/ttyS4 # 查看 RS485 状态 │ ├── rs485conf -e /dev/ttyS4 # 启用 RS485 模式 │ └── rs485conf -d /dev/ttyS4 # 禁用 RS485 模式 │ └── ioctl 测试 # 通过 ioctl 配置 RS485 └── ./rs485_test /dev/ttyS4 1 # 启用 RS485 调试接口 │ ├── /sys/class/tty/ttyS4/ # sysfs 接口 │ ├── rs485_config # RS485 配置 │ ├── port # 端口信息 │ └── uartclk # UART 时钟 │ └── /proc/tty/driver/serial # proc 统计信息 └── cat /proc/tty/driver/serial # 查看串口统计6.2 完整调试脚本#!/bin/bash # rs485_debug.sh - RS485 完整调试脚本 echo echo RK3588 FIT-485 V1.1 RS485 调试 echo # 颜色定义 RED\033[0;31m GREEN\033[0;32m YELLOW\033[0;33m NC\033[0m SERIAL_DEV/dev/ttyS4 GPIO_DIR20 # GPIO0_C4 编号 # 1. 检查串口设备 echo -e \n[1] 检查串口设备... if [ -c $SERIAL_DEV ]; then echo -e ${GREEN} ✅ 串口设备存在: $SERIAL_DEV${NC} else echo -e ${RED} ❌ 串口设备不存在: $SERIAL_DEV${NC} exit 1 fi # 2. 检查 GPIO 方向控制引脚 echo -e \n[2] 检查 GPIO 方向控制引脚... if [ -d /sys/class/gpio/gpio$GPIO_DIR ]; then echo -e ${GREEN} ✅ GPIO$GPIO_DIR 已导出${NC} else echo -e ${YELLOW} ⚠️ GPIO$GPIO_DIR 未导出尝试导出...${NC} echo $GPIO_DIR /sys/class/gpio/export 2/dev/null echo out /sys/class/gpio/gpio$GPIO_DIR/direction echo 0 /sys/class/gpio/gpio$GPIO_DIR/value fi # 3. 查看当前串口配置 echo -e \n[3] 当前串口配置... stty -F $SERIAL_DEV -a 2/dev/null || echo 无法读取配置 # 4. 查看 RS485 状态 echo -e \n[4] RS485 状态... if [ -f /sys/class/tty/ttyS4/rs485_config ]; then cat /sys/class/tty/ttyS4/rs485_config else echo -e ${YELLOW} 内核未导出 RS485 配置${NC} fi # 5. 配置 RS485 模式 echo -e \n[5] 配置 RS485 模式... # 使用 ioctl 配置 RS485 (需要 rs485conf 工具) if command -v rs485conf /dev/null; then rs485conf -e $SERIAL_DEV echo -e ${GREEN} ✅ RS485 模式已启用${NC} else echo -e ${YELLOW} ⚠️ rs485conf 未安装尝试使用 stty 配置${NC} stty -F $SERIAL_DEV 115200 raw -echo cs8 -cstopb fi # 6. 配置波特率 echo -e \n[6] 配置波特率... stty -F $SERIAL_DEV 115200 echo -e ${GREEN} ✅ 波特率已设置为 115200${NC} # 7. 测试发送 (手动切换方向) echo -e \n[7] 测试发送... echo -e ${YELLOW} 切换为发送模式 (RE/DE高)...${NC} echo 1 /sys/class/gpio/gpio$GPIO_DIR/value sleep 0.1 echo RS485 Test Message from RK3588 $SERIAL_DEV echo -e ${GREEN} ✅ 已发送测试数据${NC} sleep 0.1 echo -e ${YELLOW} 切换回接收模式 (RE/DE低)...${NC} echo 0 /sys/class/gpio/gpio$GPIO_DIR/value # 8. 测试接收 (等待 3 秒) echo -e \n[8] 测试接收 (等待 3 秒)... timeout 3 cat $SERIAL_DEV CAT_PID$! sleep 3 kill $CAT_PID 2/dev/null # 9. 查看串口统计 echo -e \n[9] 串口统计... cat /proc/tty/driver/serial 2/dev/null | grep -E 4:|uart if [ $? -ne 0 ]; then echo /proc/tty/driver/serial 不可用 fi # 10. 查看 DMA 统计 (如果启用) echo -e \n[10] DMA 统计... if [ -d /sys/kernel/debug/dmaengine ]; then ls /sys/kernel/debug/dmaengine/ | grep dma else echo debugfs 未挂载 fi echo -e \n echo -e ${GREEN}调试完成${NC} echo 6.3 RS485 环回测试脚本#!/bin/bash # rs485_loopback.sh - RS485 环回测试 (需短接 A/B 线) echo RS485 环回测试 SERIAL_DEV/dev/ttyS4 TEST_DATARS485 Loopback Test $(date) echo 测试数据: $TEST_DATA # 配置串口 stty -F $SERIAL_DEV 115200 raw -echo # 启动后台接收 cat $SERIAL_DEV /tmp/rs485_recv RECV_PID$! sleep 0.5 # 发送数据 echo $TEST_DATA $SERIAL_DEV sleep 1 # 停止接收 kill $RECV_PID 2/dev/null # 验证接收 if grep -q $TEST_DATA /tmp/rs485_recv; then echo ✅ 环回测试成功 cat /tmp/rs485_recv else echo ❌ 环回测试失败 fi rm -f /tmp/rs485_recv七、数据流树形分析7.1 RS485 数据发送流程【RS485 数据发送完整流程树】 应用程序 write() 调用 │ ▼ tty_write() [tty_io.c] │ ▼ uart_write() [serial_core.c] │ ├── uart_port-ops-start_tx() 调用 │ ▼ rockchip_serial_start_tx() [rockchip_serial.c] │ ├── [RS485] 切换为发送模式 │ ├── gpiod_set_value(rts_gpio, 1) # RE/DE 置高 │ └── udelay(delay_rts_before_send) # 延时 │ ├── 判断数据量大小 │ │ │ ├── 数据量 ≥ DMA 阈值 │ │ ▼ │ │ rockchip_serial_dma_tx() │ │ │ │ │ ├── dmaengine_prep_slave_sg() # 准备 DMA │ │ ├── dmaengine_submit() # 提交 DMA │ │ └── dma_async_issue_pending() # 启动 DMA │ │ │ │ │ ▼ │ │ [硬件 DMA 传输] │ │ │ │ │ ▼ │ │ rockchip_dma_tasklet() # DMA 完成 │ │ │ │ │ ▼ │ │ rockchip_serial_stop_tx() │ │ │ └── 数据量 DMA 阈值 │ ▼ │ rockchip_serial_pio_tx() │ │ │ ├── while (仍有数据) │ │ │ │ │ ├── writel(数据, THR) # 写入发送保持寄存器 │ │ └── 等待 THRE 中断 │ │ │ └── 发送完成中断 │ │ │ ▼ │ rockchip_serial_irq() │ │ │ └── rockchip_serial_stop_tx() │ ▼ rockchip_serial_stop_tx() │ ├── 等待发送完成 (TEMT 位) ├── [RS485] 切换回接收模式 │ ├── udelay(delay_rts_after_send) # 可选延时 │ └── gpiod_set_value(rts_gpio, 0) # RE/DE 置低 │ └── uart_write_wakeup() # 唤醒写入进程7.2 RS485 数据接收流程【RS485 数据接收完整流程树】 [RS485 总线数据到达] │ ▼ FIT-485 模块接收 (RE/DE 低) │ ▼ RK3588 UART 控制器 │ ▼ UART 接收中断 (RDI) │ ▼ rockchip_serial_irq() [rockchip_serial.c] │ ├── 读取接收数据 │ │ │ ├── [DMA 模式] │ │ └── DMA 自动将数据写入内存 │ │ │ └── [PIO 模式] │ └── 从 RBR 读取数据 │ ▼ uart_insert_char() [serial_core.c] │ ├── 将数据放入 tty 缓冲区 │ ▼ tty_flip_buffer_push() [tty_buffer.c] │ ▼ 唤醒等待读取的进程 │ ▼ 应用程序 read() 返回八、常见问题排查问题现象可能原因排查命令解决方案设备节点不存在设备树未配置ls /dev/ttyS*检查设备树确保 uart4 节点启用无法发送数据方向控制错误cat /sys/class/gpio/gpio20/value检查 RE/DE 电平确保发送时置高无法接收数据总线空闲状态示波器测 A/B 线确保 RE/DE 置低总线有终端电阻数据乱码波特率不匹配stty -F /dev/ttyS4检查两端波特率一致数据丢失FIFO 溢出cat /proc/tty/driver/serial检查错误计数启用 DMARS485 模式不生效内核未配置zgrep RS485 /proc/config.gz确保 CONFIG_SERIAL_RS485y九、总结项目内容模块型号FIT-485 V1.1接口类型TTL 转 RS485串口设备/dev/ttyS4 (UART4)方向控制 GPIOGPIO0_C4 (LD3/R30)内核配置CONFIG_SERIAL_RS485y设备树节点uart4 rs485 属性测试命令stty, minicom, rs485confDMA 通道TX:4, RX:5关键配置要点RS485 半双工特性发送时 RE/DE 置高接收时 RE/DE 置低方向控制推荐使用 GPIO 软件控制便于调试终端电阻总线两端需要 120Ω 终端电阻DMA 优化大数据量传输时启用 DMA 可降低 CPU 负载时序要求方向切换需要足够的延时 (通常 10-100us)