RS485是在串口基础上利用电平转换芯片,将TTL电平转换成485的差分信号,电路图如下:
RO: 接收器输出----接RX
RE: 接收器输出使能(低电平-接收使能)
DE: 驱动器输出使能(高电平-发送使能)
DI: 驱动器输入----接TX
在传输数据时候需要将RS485 RE置高,发送使能,接收禁止;发送完数据以后需要将RS485 RE置低,接收使能,发送禁止。
清楚原理,修改驱动:
1.dts添加re使能口:
&uart6 {pinctrl-0 = <&uart6m1_xfer>;status = "okay";rs485-gpios = <&gpio4 RK_PB5 GPIO_ACTIVE_HIGH>;};
2.驱动修改:
diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h index 93eb3c496ff1..0828068cca44 100644 --- a/include/uapi/linux/serial.h +++ b/include/uapi/linux/serial.h @@ -130,6 +130,7 @@ struct serial_rs485 {__u32 delay_rts_after_send; /* Delay after send (milliseconds) */__u32 padding[5]; /* Memory is cheap, new structsare a royal PITA .. */ + __u32 rs485_tx_en_gpio;};
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 82a4f6dab59a..f0a3da1b2930 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -26,6 +26,8 @@#include <linux/clk.h>#include <linux/reset.h>#include <linux/pm_runtime.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h>#include <asm/byteorder.h>@@ -502,6 +504,7 @@ static int dw8250_probe(struct platform_device *pdev)int irq;int err;u32 val; + int ret = 0;if (!regs) {dev_err(dev, "no registers defined\n"); @@ -544,6 +547,24 @@ static int dw8250_probe(struct platform_device *pdev)data->uart_16550_compatible = device_property_read_bool(dev,"snps,uart-16550-compatible");+ //wmc add + ret = of_get_named_gpio(p->dev->of_node, "rs485-gpios", 0);//GPIO4_B5 + uart.port.rs485.rs485_tx_en_gpio = ret; + if(141 == uart.port.rs485.rs485_tx_en_gpio) + { + err = gpio_request(141, "rs485-ctl"); + if(err<0){ + printk("wmc....rs485-ctl...failed..\n"); + return err; + } + + + gpio_direction_output(uart.port.rs485.rs485_tx_en_gpio, 0); + gpio_set_value(uart.port.rs485.rs485_tx_en_gpio, 0); // defalut low-level + + } + //end +err = device_property_read_u32(dev, "reg-shift", &val);
实现逻辑是,发送数据拉高re引脚,发送完数据再置低:
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 9e681b375bad..4d4fe01b328d 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -31,6 +31,7 @@#include <linux/uaccess.h>#include <linux/pm_runtime.h>#include <linux/ktime.h> +#include <linux/of_gpio.h>#include <asm/io.h>#include <asm/irq.h> @@ -1797,6 +1798,8 @@ 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; // 用于获取状态 + int i; // 用于循环计数if (port->x_char) {uart_xchar_out(port, UART_TX); @@ -1811,6 +1814,11 @@ void serial8250_tx_chars(struct uart_8250_port *up)return;}+ if(141 == up->port.rs485.rs485_tx_en_gpio) + { + gpio_set_value(up->port.rs485.rs485_tx_en_gpio, 1); + } +count = up->tx_loadsz;do {serial_out(up, UART_TX, xmit->buf[xmit->tail]); @@ -1848,7 +1856,25 @@ void serial8250_tx_chars(struct uart_8250_port *up)* the interrupt and RPM in __stop_tx()*/if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)) + {__stop_tx(up); + lsr = serial_in(up, UART_LSR); + if(141 == up->port.rs485.rs485_tx_en_gpio) + { + for(i = 0; i < 200; i++) + { + mdelay(3); + lsr = serial_in(up, UART_LSR); + if(UART_LSR_TEMT == (lsr & UART_LSR_TEMT)) + { + // printk("[%d] wait finished: %d, lsr: %d\n", i, (lsr & UART_LSR_TEMT) == UART_LSR_TEMT, lsr); + break; + } + } + gpio_set_value(up->port.rs485.rs485_tx_en_gpio, 0); + } + } + }EXPORT_SYMBOL_GPL(serial8250_tx_chars);
参考:https://blog.csdn.net/weixin_46461874/article/details/132718936
https://blog.csdn.net/guowuoo2008/article/details/131771333
https://blog.csdn.net/bing328924/article/details/134503353