在kernel中增加rs485的读写控制


一些rs485的IC通过gpio来控制读写方向,本文基于8250的驱动,讲讲在kernel中如何通过控制gpio来控制rs485的读写方向。

首先是增加定义:

#include <linux/gpio.h>
#include <linux/timer.h>

#define RS485_NR        CONFIG_SERIAL_8250_NR_UARTS
#define RS485_GPIO_DEF  GPIO0_A3

typedef struct {
	ktime_t ktime;
	struct hrtimer timer;
	struct uart_port *port;
} rs485_ctl_t;

rs485_ctl_t rs485_ctl[RS485_NR];

然后在serial8250_init_port中初始化:

int i = port->line;

// 这里用到了struct uart_port中的空闲变量unused[0]来保存gpio
port->unused[0] = RS485_GPIO_DEF;
port->rs485_config = serial8250_rs485_config;

hrtimer_init(&rs485_ctl[i].timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);

rs485_ctl[i].timer.function = serial8250_rs485_stop_tx_timeout;
rs485_ctl[i].port = port;

接着实现在init中所涉及的函数:

// 这里通过struct serial_rs485的padding[0]在应用层传递gpio,如果没有,就用默认的gpio
int serial8250_rs485_config(struct uart_port *port, struct serial_rs485 *rs485)
{
	unsigned gpio;

	memcpy(&port->rs485, rs485, sizeof(struct serial_rs485));

	gpio = (unsigned)port->rs485.padding[0];

	if (gpio > 0)
		port->unused[0] = gpio;
	else
		gpio = (unsigned)port->unused[0];

	if (port->rs485.flags & SER_RS485_ENABLED)
		return serial8250_rs485_gpio_init(gpio);

	return -EINVAL;
}

static int serial8250_rs485_gpio_init(unsigned gpio)
{
	int ret;

	if (gpio_is_valid(gpio))
	{
		ret = gpio_request(gpio, "RS485 R/W Switch GPIO");
		if (ret)
			return ret;
		else
		{
			gpio_direction_output(gpio, 0);
			return 0;
		}
	}

	return -EBUSY;
}

static enum hrtimer_restart serial8250_rs485_stop_tx_timeout(struct hrtimer *t)
{
	rs485_ctl_t *rs485_ctl =
		container_of(t, rs485_ctl_t, timer);
	struct uart_port *port = rs485_ctl->port;
	unsigned gpio = (unsigned)port->unused[0];

	if (serial8250_tx_empty(port))
	{
		gpio_set_value(gpio, 0);
		return HRTIMER_NORESTART;
	}
	else
	{
		hrtimer_forward_now(t, rs485_ctl->ktime);
		return HRTIMER_RESTART;
	}
}

跟着在serial8250_start_tx中控制gpio来控制读写方向

// 发送数据前拉高gpio
if (port->rs485.flags & SER_RS485_ENABLED)
	serial8250_rs485_start_tx((unsigned)port->unused[0]);

// 发送完成后拉低gpio
if (port->rs485.flags & SER_RS485_ENABLED)
{
	int i = port->line;

	rs485_ctl[i].port = port;
	rs485_ctl[i].ktime = ktime_set(0, HZ * 1000);

	hrtimer_start(&rs485_ctl[i].timer,
	                rs485_ctl[i].ktime, HRTIMER_MODE_REL);
}

在关闭应用程序时,释放gpio

if (port->rs485.flags & SER_RS485_ENABLED)
	serial8250_rs485_gpio_free((unsigned)port->unused[0]);

实现相关的函数

static inline void serial8250_rs485_start_tx(unsigned gpio)
{
	gpio_set_value(gpio, 1);
}

static inline void serial8250_rs485_stop_tx(unsigned gpio)
{
	gpio_set_value(gpio, 0);
}

static inline void serial8250_rs485_gpio_free(unsigned gpio)
{
	if (gpio_is_valid(gpio))
	{
		gpio_set_value(gpio, 0);
		gpio_free(gpio);
	}

}

最后在serial8250_register_8250_port注释默认的rs485_config

// uart->port.rs485_config	= up->port.rs485_config;
· Written by Link