目录
串口驱动层级结构
485配置流程
dts相关
配置注册
初始化
485收发切换
delay_after_send
目前linux 内核中已经支持了485的实现,但由于底层驱动的支持情况,导致我们采用不同芯片时需要对底层驱动进行修改,以满足内核485的各个回调接口,进而实现485功能。
485 主要在于收发控制切换的时间。本文以 8250为示例,描述485配置及切换的流程。
串口驱动层级结构
不讨论tty,tty以下分为三层
1)串口通用层
2)8250通用层
3)具体的8250芯片驱动层
8250_dw为具体芯片层。在解析到dts中有对应的配置时,则调用此去的probe接口。如果dts里面有三个串口,则调用三次probe接口。而后依次调用上一层的接口将串口信息上报。
485配置流程
dts相关
在 8250_dw向8250注册串口时,8250层回调用通用的485 dts解析函数 uart_get_rs485_mode,以获取该串口对485的支持情况,该函数的代码 如下。通过此代码,我们可以看到几个dts参数,其功能分别为:
dts | 功能 |
rs485-rts-delay | 485收发切换后的延时,单位ms |
rs485-term | 切换GPIO管脚定义 |
linux,rs485-enabled-at-boot-time | 是否默认使能485,后续也可以通过ioctl改配 |
/**
* uart_get_rs485_mode() - retrieve rs485 properties for given uart
* @port: uart device's target port
*
* This function implements the device tree binding described in
* Documentation/devicetree/bindings/serial/rs485.txt.
*/
int uart_get_rs485_mode(struct uart_port *port)
{
struct serial_rs485 *rs485conf = &port->rs485;
struct device *dev = port->dev;
u32 rs485_delay[2];
int ret;
ret = device_property_read_u32_array(dev, "rs485-rts-delay",
rs485_delay, 2);
if (!ret) {
rs485conf->delay_rts_before_send = rs485_delay[0];
rs485conf->delay_rts_after_send = rs485_delay[1];
} else {
rs485conf->delay_rts_before_send = 0;
rs485conf->delay_rts_after_send = 0;
}
/*
* Clear full-duplex and enabled flags, set RTS polarity to active high
* to get to a defined state with the following properties:
*/
rs485conf->flags &= ~(SER_RS485_RX_DURING_TX | SER_RS485_ENABLED |
SER_RS485_TERMINATE_BUS |
SER_RS485_RTS_AFTER_SEND);
rs485conf->flags |= SER_RS485_RTS_ON_SEND;
if (device_property_read_bool(dev, "rs485-rx-during-tx"))
rs485conf->flags |= SER_RS485_RX_DURING_TX;
if (device_property_read_bool(dev, "linux,rs485-enabled-at-boot-time"))
rs485conf->flags |= SER_RS485_ENABLED;
if (device_property_read_bool(dev, "rs485-rts-active-low")) {
rs485conf->flags &= ~SER_RS485_RTS_ON_SEND;
rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
}
/*
* Disabling termination by default is the safe choice: Else if many
* bus participants enable it, no communication is possible at all.
* Works fine for short cables and users may enable for longer cables.
*/
port->rs485_term_gpio = devm_gpiod_get_optional(dev, "rs485-term",
GPIOD_OUT_LOW);
if (IS_ERR(port->rs485_term_gpio)) {
ret = PTR_ERR(port->rs485_term_gpio);
port->rs485_term_gpio = NULL;
return dev_err_probe(dev, ret, "Cannot get rs485-term-gpios\n");
}
return 0;
}
配置注册
最底层芯片驱动需要注册:
1) serial层的回调接口:
int (*rs485_config)(struct uart_port *, struct serial_rs485 *rs485);
完成485相关的初始化
2)8250层的回调接口:
void (*rs485_start_tx)(struct uart_8250_port *);
void (*rs485_stop_tx)(struct uart_8250_port *);
用于控制 485的收发切换
初始化
uart_set_rs485_config--》驱动的rs485_config回调
serial8250_em485_config-,除了基本的结构成员初始化,这里主要起了两个高精度定时器的处理函数,用于处理延时后的切换。
static int serial8250_em485_init(struct uart_8250_port *p)
{
if (p->em485)
return 0;
p->em485 = kmalloc(sizeof(struct uart_8250_em485), GFP_ATOMIC);
if (!p->em485)
return -ENOMEM;
hrtimer_init(&p->em485->stop_tx_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
p->em485->stop_tx_timer.function = &serial8250_em485_handle_stop_tx;
p->em485->start_tx_timer.function = &serial8250_em485_handle_start_tx;
p->em485->port = p;
p->em485->active_timer = NULL;
p->em485->tx_stopped = true;
p->rs485_stop_tx(p);
return 0;
}
485收发切换
默认接收,切换发送通过 接口 start_tx_rs485 ,相对比较容易。
再切换回接收时,由于存在发送FIFO的情况,需要处理,内核代码中加了判断处理,代码如下:
static inline void __stop_tx(struct uart_8250_port *p)
{
struct uart_8250_em485 *em485 = p->em485;
if (em485) {
unsigned char lsr = serial_in(p, UART_LSR);
/*
* To provide required timeing and allow FIFO transfer,
* __stop_tx_rs485() must be called only when both FIFO and
* shift register are empty. It is for device driver to enable
* interrupt on TEMT.
*/
if ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
return;
__stop_tx_rs485(p);
}
__do_stop_tx(p);
}
这里主要判断了LSR寄存器,如果lsr寄存器没有实现 ,则485的切换一直不会进行。笔者就遇到了这种不支持的情况。
delay_after_send
由于没有lsr的支持,这时需要靠延时来处理何时切换。内核代码已经支持,即delay_after_send,单位为ms。
比如发640个字节,发送fifo大小为64,那我们可以将此delay设置为
10bit*1000ms*len/baud
=10*1000*64/115200= 5.56~~6ms
实际测试:配置发送FIFO 为空时触发,每次发送64个,则延时配置7ms
如果发送650个字节,则最后一次发送10个字节,
10*1000*10/115200=0~1ms ,则延时配置2ms
__stop_tx用户在用户缓存数据发送完毕后调用。
由此可见,delay和芯片的FIFO大小及触发门限强相关。