openwrt使用485接口進行通信

用戶態使用:

485從本質上來講也是一種ttl串口,只是在ttl串口上外掛了一個485芯片。由於485是半雙工通信接口,所以有時候需要一根gpio來控制芯片處在接收模式還是發送模式(現在有些可以通過硬件電路自動配置數據接收和發送模式,這種軟件就不需要感知了,但顯然我們的硬件沒有這麼做-_-|||)。

機智的小夥伴可能會想,我在用戶態中發送報文時將485切換成發送模式,發送完之後立即切換成接收模式就可以了。但是,實際上這是行不通的,因為ttl驅動是異步發送數據的,所以必須在驅動中進行接收和發送的切換。

寫ttl驅動的人應該也考慮到了485的兼容,所以他提供了一個ioctl用於將串口切換到485模式,但是由於管理收發的gpio是我們硬件自己確定的,所以沒有人能想到這個,我們必須自己來修改ttl驅動。

openwrt使用485接口進行通信

我們本次使用的串口用的是自帶的ttl驅動,所以該驅動代碼在drivers/tty/8250_core.c文件中,本文以該驅動為例子進行修改。

初始化時將控制485的gpio設置成接收狀態

如下圖可以知道,我們用來控制485模式的gpio是37。

openwrt使用485接口進行通信
openwrt使用485接口進行通信

發包開始時設置成發送模式:

openwrt使用485接口進行通信

發包結束後切換成收報模式:

調試後確定報文發送完畢後會調用__stop_tx,而不是一開始註冊的serial8250_stop_tx,這邊保持統一性,兩個函數內都添加了切換函數:實際測試發現使用serial8250_tx_empty來判斷報文是否發送完畢比用延時效果好;

openwrt使用485接口進行通信
openwrt使用485接口進行通信

附內核補丁文件:

Index: linux-3.18.109/drivers/tty/serial/8250/8250_core.c

===================================================================

--- linux-3.18.109.orig/drivers/tty/serial/8250/8250_core.c2019-04-02 17:34:55.657050544 +0800

+++ linux-3.18.109/drivers/tty/serial/8250/8250_core.c2019-04-02 17:43:52.413035144 +0800

@@ -47,7 +47,7 @@

#include

#include "8250.h"

-

+#include <linux>

/*

* Configuration:

* share_irqs - whether we pass IRQF_SHARED to request_irq(). This option

@@ -65,7 +65,7 @@

}

static unsigned int skip_txen_test; /* force skip of txen test at init time */

-

+static unsigned int serial8250_tx_empty(struct uart_port *port);

/*

* Debugging.

*/

@@ -330,6 +330,25 @@

.flags= UART_CAP_FIFO | UART_CAP_AFE,

},

};

+#define RS485_GPIO 37

+static int serial8250_rs485_config

+(

+ struct uart_8250_port *up,

+ struct serial_rs485 *rs485

+)

+{

+ if (rs485->flags & SER_RS485_ENABLED) {

+ gpiod_direction_output(gpio_to_desc(RS485_GPIO), 0);

+ gpiod_set_value(gpio_to_desc(RS485_GPIO), 0);

+ }

+ else {

+ //printk(KERN_INFO "uart %d set 485 off\\n", up->port.line);

+ }

+

+ memcpy(&up->rs485, rs485, sizeof(*rs485));

+

+ return 0;

+}

/* Uart divisor latch read */

static int default_serial_dl_read(struct uart_8250_port *up)

@@ -1319,6 +1338,14 @@

p->ier &= ~UART_IER_THRI;

serial_out(p, UART_IER, p->ier);

serial8250_rpm_put_tx(p);

+ if (p->rs485.flags & SER_RS485_ENABLED) {

+ //printk("__stop_tx end send\\r\\n");

+ while(!serial8250_tx_empty(&(p->port)))

+ {

+ ;

+ }

+ gpiod_set_value(gpio_to_desc(RS485_GPIO), 0);

+ }

}

}

@@ -1337,12 +1364,23 @@

serial_icr_write(up, UART_ACR, up->acr);

}

serial8250_rpm_put(up);

+ if (up->rs485.flags & SER_RS485_ENABLED) {

+ //printk("serial8250_stop_tx edn send\\r\\n");

+ while(!serial8250_tx_empty(port))

+ {

+ ;

+ }

+ gpiod_set_value(gpio_to_desc(RS485_GPIO), 0);

+ }

}

static void serial8250_start_tx(struct uart_port *port)

{

struct uart_8250_port *up = up_to_u8250p(port);

-

+ if (up->rs485.flags & SER_RS485_ENABLED) {

+ //printk("serial8250_start_tx start send\\r\\n");

+ gpiod_set_value(gpio_to_desc(RS485_GPIO), 1);

+ }

serial8250_rpm_get_tx(up);

if (up->dma && !serial8250_tx_dma(up)) {

return;

@@ -3578,7 +3616,7 @@

uart->port.fifosize= up->port.fifosize;

uart->tx_loadsz= up->tx_loadsz;

uart->capabilities= up->capabilities;

-uart->rs485_config= up->rs485_config;

+uart->rs485_config= serial8250_rs485_config;

uart->rs485= up->rs485;

uart->port.throttle= up->port.throttle;

uart->port.unthrottle= up->port.unthrottle;


分享到:


相關文章: