0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

RK3588串口RS485自动收发控制:内核驱动层改造实战

眺望电子 2026-03-20 08:30 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

【前言】

在工业通信场景中,RS485因其远距离传输、抗干扰能力强、支持多节点组网等特性,成为工控领域的首选通信方式。然而,与RS232的全双工通信不同,RS485采用半双工模式——同一时刻只能发送或接收。这就要求我们必须精确控制收发状态切换:

发送数据前:将控制脚置为高电平,使能发送器

数据发送完成后:将控制脚置为低电平,切换为接收模式

瑞芯微RK系列芯片(以眺望电子RK3588核心板为例)的UART控制器并未内置RS485自动控制功能,本文介绍一种内核级驱动改造方案,通过GPIO实现可靠的收发控制。

一、硬件连接原理

RS485收发器通常有以下引脚:

引脚功能控制方式
RO接收输出连接UART RX
DI发送输入连接UART TX
RE接收使能低电平有效
DE发送使能高电平有效
A/B差分信号线总线接口

如下图所示,将RE和DE引脚短接,通过单个GPIO统一控制,实现收发模式切换:

f54d2e52-23f3-11f1-96ea-92fbcf53809c.png

GPIO输出高电平 → DE=1, RE=1 → 发送模式

GPIO输出低电平 → DE=0, RE=0 → 接收模式

瑞芯微的内核源码没有⾃带的配置接⼝,需要我们修改如下驱动代码。


二、设备树配置

在设备树中为UART节点添加 rts_gpio 属性:

--- a/arch/arm64/boot/dts/rockchip/talowe-rk3588-common.dtsi+++ b/arch/arm64/boot/dts/rockchip/talowe-rk3588-common.dtsi@@ -1057,13 +1057,14 @@&uart6 {&uart9 {pinctrl-names = "default";pinctrl-0 = <&uart9m2_xfer>;- // rts-gpio = <&gpio3 RK_PC4 GPIO_ACTIVE_HIGH>;+ rts_gpio = <&gpio3 RK_PC4 GPIO_ACTIVE_LOW>;status = "okay";};&uart0 {pinctrl-names = "default";pinctrl-0 = <&uart0m2_xfer>;+ rts_gpio = <&gpio3 RK_PC5 GPIO_ACTIVE_LOW>;status = "okay";};


重要提示:属性名必须是 rts_gpio(单数形式),不能写成 rts-gpios(复数形式)。若使用复数形式,串口驱动会触发 mctrl_gpio 机制,导致本文方案失效。

f55e38f0-23f3-11f1-96ea-92fbcf53809c.png


三、驱动层改造详解

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.cindex 2f7553cea..3cb98ae0d 100644--- a/drivers/tty/serial/8250/8250_core.c+++ b/drivers/tty/serial/8250/8250_core.c@@ -1024,6 +1024,8 @@int serial8250_register_8250_port(const struct uart_8250_port *up)#ifdefCONFIG_ARCH_ROCKCHIPuart->port.line = up->port.line;#endif+ if (up->rts_gpios > 0)+ uart->rts_gpios = up->rts_gpios;/* Take tx_loadsz from fifosize if it wasn't set separately */if (uart->port.fifosize && !uart->tx_loadsz)uart->tx_loadsz = uart->port.fifosize;diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.cindex 816c0122c..d37909cd3 100644--- a/drivers/tty/serial/8250/8250_dw.c+++ b/drivers/tty/serial/8250/8250_dw.c@@ -35,7 +35,8 @@#else#include"8250_dwlib.h"#endif-+#include+#include/* Offsets for the DesignWare specific registers */#defineDW_UART_USR 0x1f /* UART Status Register */#defineDW_UART_DMASA 0xa8 /* DMA Software Ack */@@ -573,7 +574,7 @@static int dw8250_probe(struct platform_device *pdev)int irq;int err;u32 val;+ int rts_gpios;regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!regs)return dev_err_probe(dev, -EINVAL, "no registers defined\n");@@ -610,7 +611,16 @@static int dw8250_probe(struct platform_device *pdev)#ifdefCONFIG_ARCH_ROCKCHIPdata->irq = irq;#endif-+ rts_gpios = of_get_named_gpio(dev->of_node,"rts_gpio", 0);+ up->rts_gpios = rts_gpios;+ if (up->rts_gpios > 0)+ {+ printk("rts_gpios=%d\n", up->rts_gpios);+ gpio_direction_output(up->rts_gpios, 0);+ gpio_set_value(up->rts_gpios, 0);+ }++data->uart_16550_compatible = device_property_read_bool(dev,"snps,uart-16550-compatible");diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.cindex b7a3634c6..e7c49272a 100644--- a/drivers/tty/serial/8250/8250_port.c+++ b/drivers/tty/serial/8250/8250_port.c@@ -38,6 +38,8 @@#include"8250.h"+#include/* Nuvoton NPCM timeout register */#defineUART_NPCM_TOR 7#defineUART_NPCM_TOIE BIT(7) /* Timeout Interrupt Enable */@@ -1825,7 +1827,7 @@void serial8250_tx_chars(struct uart_8250_port *up)struct uart_port *port = &up->port;struct circ_buf *xmit = &port->state->xmit;int count;-+ int lsr,cnt;if (port->x_char) {uart_xchar_out(port, UART_TX);return;@@ -1838,7 +1840,11 @@void serial8250_tx_chars(struct uart_8250_port *up)__stop_tx(up);return;}+ if (up->rts_gpios > 0 )+ {+ gpio_set_value(up->rts_gpios, 1);+ }count = up->tx_loadsz;do {serial_out(up, UART_TX, xmit->buf[xmit->tail]);@@ -1878,7 +1884,19 @@void serial8250_tx_chars(struct uart_8250_port *up)if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))+ {__stop_tx(up);+ if (up->rts_gpios > 0 )+ {+ for (cnt = 0; cnt < 200; cnt++)+ {+ mdelay(3);+ lsr = serial_in(up, UART_LSR);+ if(UART_LSR_TEMT == (lsr & UART_LSR_TEMT))+ {+ break;+ }+ }+ gpio_set_value(up->rts_gpios, 0);+ }+ }}EXPORT_SYMBOL_GPL(serial8250_tx_chars);diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.hindex 46d76b035..e088fe6ba 100644--- a/include/linux/serial_8250.h+++ b/include/linux/serial_8250.h@@ -141,6 +141,7 @@struct uart_8250_port {/* Serial port overrun backoff */struct delayed_work overrun_backoff;u32 overrun_backoff_time_ms;+ int rts_gpios;};static inline struct uart_8250_port *up_to_u8250p(struct uart_port *up)diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.hindex cea06924b..9f3435e98 100644--- a/include/uapi/linux/serial.h+++ b/include/uapi/linux/serial.h@@ -134,7 +134,7 @@struct serial_rs485 {__u32 delay_rts_before_send; /* Delay before send (milliseconds) */__u32 delay_rts_after_send; /* Delay after send (milliseconds) */-+/* The fields below are defined by flags */union {__u32 padding[5]; /* Memory is cheap, new structs are a pain */

3.1核心数据结构扩展

修改 include/linux/serial_8250.h,在 uart_8250_port 结构体中新增GPIO字段:

structuart_8250_port {// ... 原有字段/* Serial port overrun backoff */structdelayed_work overrun_backoff;u32 overrun_backoff_time_ms;intrts_gpios; /* 新增:RS485方向控制GPIO */};


3.2平台驱动初始化

修改 drivers/tty/serial/8250/8250_dw.c,在probe函数中解析设备树GPIO:

#include#includestaticintdw8250_probe(structplatform_device *pdev){// ... 原有代码intrts_gpios;// 从设备树获取GPIOrts_gpios =of_get_named_gpio(dev->of_node,"rts_gpio",0);up->rts_gpios = rts_gpios;if(up->rts_gpios >0) {printk("rts_gpios=%d\n", up->rts_gpios);gpio_direction_output(up->rts_gpios,0);gpio_set_value(up->rts_gpios,0); // 默认接收模式}// ... 后续代码}


3.3注册时传递GPIO信息

修改 drivers/tty/serial/8250/8250_core.c:

intserial8250_register_8250_port(conststructuart_8250_port *up){// ... 原有代码#ifdefCONFIG_ARCH_ROCKCHIPuart->port.line = up->port.line;#endif// 传递GPIO信息到新实例if(up->rts_gpios >0)uart->rts_gpios = up->rts_gpios;// ... 后续代码}


3.4发送时序控制(核心逻辑)

修改 drivers/tty/serial/8250/8250_port.c 中的 serial8250_tx_chars 函数:

#includevoidserial8250_tx_chars(structuart_8250_port *up){structuart_port*port = &up->port;structcirc_buf*xmit = &port->state->xmit;intcount;intlsr, cnt;// ... 原有检查代码/* ===== 发送前:置高GPIO,进入发送模式 ===== */if(up->rts_gpios >0) {gpio_set_value(up->rts_gpios,1);}count = up->tx_loadsz;do{serial_out(up, UART_TX, xmit->buf[xmit->tail]);xmit->tail = (xmit->tail +1) & (UART_XMIT_SIZE -1);port->icount.tx++;if(uart_circ_empty(xmit))break;}while(--count >0);// ... 原有发送逻辑/* ===== 发送完成后:等待FIFO清空,再置低GPIO ===== */if(uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)) {__stop_tx(up);if(up->rts_gpios >0) {// 轮询等待发送完成(最多600ms)for(cnt =0; cnt < 200; cnt++) {mdelay(3);lsr = serial_in(up, UART_LSR);if (UART_LSR_TEMT == (lsr & UART_LSR_TEMT))break;}gpio_set_value(up->rts_gpios,0); // 切换回接收模式}}}


四、关键设计要点

4.1为什么要在8250_core.c中传递GPIO?

因为 rts_gpios 定义在 struct uart_8250_port 结构体中,而发送函数 serial8250_tx_chars 操作的是该结构体的实例。如果不通过 serial8250_register_8250_port 函数传递,在 8250_port.c 中引用的 up->rts_gpios 将为0,导致GPIO控制失效。


4.2发送完成检测策略

方案采用轮询UART_LSR寄存器的方式检测发送完成:

// UART_LSR_TEMT位为1表示发送FIFO和移位寄存器均为空if(UART_LSR_TEMT == (lsr & UART_LSR_TEMT))break;

每次轮询间隔3ms

最大轮询200次(总计600ms超时)

适用于最高波特率115200bps下的典型帧传输



4.3多串口支持

本方案支持对多个UART同时进行RS485改造,只需在设备树中为各串口配置不同的GPIO即可。每个串口的GPIO状态独立维护,互不干扰。



4.4注意事项

设备树属性名:使用 rts_gpio(单数),勿用 rts-gpios(复数)

GPIO有效电平:根据RS485收发器规格选择 GPIO_ACTIVE_LOW 或 GPIO_ACTIVE_HIGH

超时设置:若波特率较低或数据量较大,需适当延长轮询超时时间


五、总结

本文介绍的驱动层改造方案,通过设备树配置+内核驱动修改,实现了RK3588 RS485的自动控制。相比用户空间轮询方案,具有以下优势:

时序精确:在数据发送的最开始置高GPIO,在所有数据彻底发送完成后置低,无用户态切换延迟

应用透明:应用层无需任何修改,直接按普通串口使用

资源节省:无需外置RS485协议芯片,降低BOM成本

内核标准:基于8250串口驱动框架,兼容性好,易于维护


相关代码补丁可在眺望电子提供的SDK上应用,适用于眺望电子RK3568、RK3588、RK3576等RK全系列芯片平台。

广州眺望电子科技有限公司专注于嵌入式处理器模组的研发与应用,提供从硬件设计到驱动开发,系统解决方案的全流程技术支持。欢迎关注我们的公众号,获取更多嵌入式项目开发实战经验。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • RS485
    +关注

    关注

    40

    文章

    1351

    浏览量

    86226
  • 串口
    +关注

    关注

    15

    文章

    1626

    浏览量

    83307
  • 无线收发控制

    关注

    0

    文章

    2

    浏览量

    747
  • RK3588
    +关注

    关注

    8

    文章

    586

    浏览量

    7542
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    RK3588 PCB推荐叠及阻抗设计

    近期华秋电子联合瑞芯微、凡亿重磅发布了:《RK3588 PCB设计指导白皮书》,帮助开发者更好地规范利用RK3588开发产品,提高所设计的PCB质量,在实战中巩固及提高PCB设计水平。本文
    发表于 08-10 09:32 2020次阅读
    <b class='flag-5'>RK3588</b> PCB推荐叠<b class='flag-5'>层</b>及阻抗设计

    HaaS100通过RS485串口控制380V电机

    开发笔记:阿里云物联网硬件HaaS100通过RS485串口控制380V电机:涉及到modbus的驱动函数使用,以及跟厂家的协议匹配,数值处理等方面。
    发表于 09-13 07:43

    RS485串口是如何完成数据收发的呢

    RS232和RS485串口有区别吗?RS485串口是如何完成数据收发的呢?
    发表于 12-09 06:11

    RS485 232串口通信数据解析

    文章目录(RS485 232串口通信数据解析实用干货(1)生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX
    发表于 02-22 07:14

    RS232-RS485串口通信详解

    RS232-RS485串口通信详解,很不错的东东
    发表于 08-09 15:08 30次下载

    RS232/RS485 串口检测软件分享

    RS232/RS485串口检测软件,感兴趣的小伙伴们可以瞧一瞧。
    发表于 11-16 11:22 9次下载

    RS485串口转mqtt协议网关

    RS485串口转mqtt协议网关
    发表于 11-21 10:12 2851次阅读
    <b class='flag-5'>RS485</b><b class='flag-5'>串口</b>转mqtt协议网关

    RS485/RS232串口转Modbus网关

    RS485/RS232串口转Modbus网关
    发表于 12-03 16:59 3179次阅读

    RS485或者rs232串口设备如何上传mqtt平台

    RS485或者rs232串口设备如何上传mqtt平台
    发表于 12-03 17:03 2308次阅读

    modbus转MQTT协议网关RS485串口接入华为云

    modbus转MQTT协议网关RS485串口一键快速接入华为云金鸽BL101E
    发表于 12-06 15:01 1877次阅读
    modbus转MQTT协议网关<b class='flag-5'>RS485</b><b class='flag-5'>串口</b>接入华为云

    RS485串口转以太网接入MODBUS TCP第三方云平台

    RS485串口转以太网接入MODBUS TCP第三方云平台
    发表于 12-06 14:57 2906次阅读

    (RS485 232串口通信数据解析实用干货(1)

    文章目录(RS485 232串口通信数据解析实用干货(1)生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX
    发表于 12-28 19:35 17次下载
    (<b class='flag-5'>RS485</b> 232<b class='flag-5'>串口</b>通信数据解析实用干货(1)

    如何实现RS485串口通讯采集模拟量

    如何实现RS485串口通讯采集模拟量流程分享
    的头像 发表于 08-23 11:30 6018次阅读
    如何实现<b class='flag-5'>RS485</b><b class='flag-5'>串口</b>通讯采集模拟量

    如何实现开关量输入与继电器输出联动功能RS485串口通讯

    如何实现开关量输入与继电器输出联动功能RS485串口通讯流程分享
    的头像 发表于 08-23 11:33 3131次阅读
    如何实现开关量输入与继电器输出联动功能<b class='flag-5'>RS485</b><b class='flag-5'>串口</b>通讯

    工业RS485串口网关实现485接口数据采集

    其成为首选的串行接口。但是RS485通信的最远的短离也只有1.2KM,无法实现远程控制,对于分布式的设备来说,环境不适合布线或者布线成本高,数据采集远程监控存在问题。那么如何解决这个问题呢? 利用物通博联工业RS485
    的头像 发表于 02-03 16:56 2843次阅读
    工业<b class='flag-5'>RS485</b><b class='flag-5'>串口</b>网关实现<b class='flag-5'>485</b>接口数据采集