用戶態使用:
485從本質上來講也是一種ttl串口,只是在ttl串口上外掛了一個485芯片。由於485是半雙工通信接口,所以有時候需要一根gpio來控制芯片處在接收模式還是發送模式(現在有些可以通過硬件電路自動配置數據接收和發送模式,這種軟件就不需要感知了,但顯然我們的硬件沒有這麼做-_-|||)。
機智的小夥伴可能會想,我在用戶態中發送報文時將485切換成發送模式,發送完之後立即切換成接收模式就可以了。但是,實際上這是行不通的,因為ttl驅動是異步發送數據的,所以必須在驅動中進行接收和發送的切換。
寫ttl驅動的人應該也考慮到了485的兼容,所以他提供了一個ioctl用於將串口切換到485模式,但是由於管理收發的gpio是我們硬件自己確定的,所以沒有人能想到這個,我們必須自己來修改ttl驅動。
我們本次使用的串口用的是自帶的ttl驅動,所以該驅動代碼在drivers/tty/8250_core.c文件中,本文以該驅動為例子進行修改。
初始化時將控制485的gpio設置成接收狀態
如下圖可以知道,我們用來控制485模式的gpio是37。
發包開始時設置成發送模式:
發包結束後切換成收報模式:
調試後確定報文發送完畢後會調用__stop_tx,而不是一開始註冊的serial8250_stop_tx,這邊保持統一性,兩個函數內都添加了切換函數:實際測試發現使用serial8250_tx_empty來判斷報文是否發送完畢比用延時效果好;
附內核補丁文件:
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;