ARM Linux UART驱动实现与优化指南 (arm linux uart驱动)

在现代计算机系统中,串口通信是非常常见的一种通信方式。在嵌入式系统中,UART(Universal Asynchronous Receiver/Tranitter)串口设备是必不可少的设备之一。Linux操作系统提供了一套完整的串口设备驱动程序,来支持Linux系统上的串口通信。本文将介绍ARM Linux系统中UART驱动的实现和优化指南。

一、ARM Linux系统中UART驱动的实现方案

1. 环境准备

需要准备好开发环境。本篇文章使用的是以ARM Cortex-A53为核心的嵌入式开发板,具体开发环境如下:

– 操作系统:Ubuntu 18.04.5 LTS

– 交叉编译工具链:Linaro 7.5.0 2023.12

– 调试工具:OpenOCD

2. 设备树中配置串口设备

在设备树中配置串口设备,可以使用如下代码:

chosen {

bootargs = “console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p2 rw rootwt”;

};

&uart1 {

pinctrl-names = “default”;

pinctrl-0 = ;

status = “okay”;

};

&uart2 {

pinctrl-names = “default”;

pinctrl-0 = ;

status = “okay”;

};

这里选择使用UART1和UART2两个串口设备。在bootargs中配置console=ttyS0,115200,意味着使用UART1作为控制台设备,并且波特率为115200。

3. 编写串口设备驱动

根据UART设备的特性,我们可以将UART驱动设计成如下结构:

– uart_driver: 代表整个串口驱动,包括所有的串口设备,可以用来注册和注销串口设备。

– uart_port: 代表单个串口设备中的一个端口,包括发送和接收缓存,可以用来控制实际的串口设备操作。

ARM Linux系统提供了一套完整的串口驱动程序接口,开发人员只需要按照这个接口来实现自己的驱动程序即可。下面是UART驱动程序的示例代码:

结构体定义

struct uart_port {

unsigned int uartclk; /* 波特率 */

unsigned int type; /* 串口类型 */

unsigned int irq; /* 中断号 */

unsigned long iobase; /* I/O地址 */

unsigned int iotype; /* I/O类型 */

unsigned int ioflags; /* I/O标志 */

unsigned int regshift; /* 寄存器位移量 */

unsigned int baudclk; /* 波特率时钟 */

unsigned int overrun; /* 接收缓存溢出标志 */

unsigned int txdelay; /* 发送延迟 */

unsigned int flags; /* 标志位 */

unsigned char fifosize; /* 缓存大小 */

unsigned char rx_trigger; /* 接收触发级别 */

unsigned char tx_empty; /* 发送空闲标志 */

unsigned short x_char; /* 特殊字符传输 */

unsigned char stop_bits; /* 停止位 */

unsigned char parity; /* 校验位 */

unsigned char uart_number; /* 串口编号 */

};

struct uart_driver {

const char *driver_name; /* 驱动名称 */

struct module *owner; /* 模块所有者 */

int (*probe)(struct uart_port *port); /* 用于设备初始化 */

void (*shutdown)(struct uart_port *port);/* 用于设备的清理 */

void (*start_tx)(struct uart_port *port);/* 开始发送数据 */

void (*stop_tx)(struct uart_port *port); /* 结束发送数据 */

void (*start_rx)(struct uart_port *port);/* 开始接收数据 */

void (*stop_rx)(struct uart_port *port); /* 结束接收数据 */

unsigned int (*tx_empty)(struct uart_port *port); /* 发送缓存空 */

void (*set_mctrl)(struct uart_port *port, unsigned int mctrl);/* 修改状态标志 */

unsigned int (*get_mctrl)(struct uart_port *port);/* 获取状态标志 */

void (*enable_ms)(struct uart_port *port); /* 允许中断 */

void (*disable_ms)(struct uart_port *port); /* 禁止中断 */

void (*handle_irq)(struct uart_port *port); /* 处理中断 */

void (*unthrottle)(struct uart_port *port); /* 发送缓存没有空间 */

void (*set_termios)(struct uart_port *port, struct ktermios *termios, struct ktermios *old); /* 设备波特率、数据位等 */

void (*pm)(struct uart_port *port, unsigned int state, unsigned int oldstate); /* 节能模式 */

const char *dev_name; /* 设备名称 */

struct uart_port *state; /* 串口状态 */

struct console *cons; /* 控制台 */

struct tty_driver *tty_driver; /* tty驱动程序 */

struct uart_ops const *ops; /* 操作设备的方法 */

};

驱动初始化

为了初始化UART驱动,我们需要编写一个probe函数,它将在加载驱动时被调用。

static int my_uart_probe(struct platform_device *pdev) {

struct device *dev = &pdev->dev;

struct uart_port *port;

int ret;

/* 为每个串口设备都分配一个port结构体 */

port = kzalloc(sizeof(struct uart_port), GFP_KERNEL);

if (!port)

return -ENOMEM;

/* 初始化UART设备端口 */

port->iobase = dev_get_drvdata(dev);

port->type = PORT_UART;

port->irq = pdev->irq;

port->uartclk = 24000000;

port->regshift = 2;

port->fifosize = 16;

port->ops = &my_uart_ops;

/* 注册串口设备 */

ret = uart_add_one_port(&my_uart_driver, port);

return ret;

}

操作函数

下面是UART驱动需要实现的操作函数:

static const struct uart_ops my_uart_ops = {

.tx_empty = my_uart_tx_empty,

.set_mctrl = my_uart_set_mctrl,

.get_mctrl = my_uart_get_mctrl,

.stop_tx = my_uart_stop_tx,

.start_tx = my_uart_start_tx,

.stop_rx = my_uart_stop_rx,

.start_rx = my_uart_start_rx,

.enable_ms = my_uart_enable_ms,

.disable_ms = my_uart_disable_ms,

.handle_irq = my_uart_handle_irq,

.unthrottle = my_uart_unthrottle,

};

其中,my_uart_tx_empty、my_uart_set_mctrl、my_uart_get_mctrl、my_uart_stop_tx、my_uart_start_tx、my_uart_stop_rx、my_uart_start_rx、my_uart_enable_ms、my_uart_disable_ms、my_uart_handle_irq、my_uart_unthrottle是实现UART操作的函数。

二、UART驱动优化指南

1. 调整波特率

通常情况下,串口设备的波特率是需要调整的。如果波特率设置不合适,将会导致数据传输出错。因此,在使用串口设备之前,调整合适的波特率是非常重要的。

可以使用stty命令调整串口设备的波特率:

stty -F /dev/ttyS0 115200

如果这个串口设备是指向控制台的,那么在设备树文件中的bootargs中,也需要设置相应的波特率,例如:

console=ttyS0,115200

2. 缓存读写

为了使UART驱动尽可能地高效,缓存读写是一个很重要的优化考虑。

在读取数据时,可以使用系统的FIFO缓存。FIFO缓存可以大大提高数据读取的效率和可靠性。

在写入数据时,也可以使用UAR的缓存来进行数据的存储和发送。这样可以大大提高数据传输的效率和可靠性。同时,在写入数据时,还需要考虑到发送延迟时间。在实际设备中,需要根据设备驱动的数据传输模式,合理地设置发送延迟时间。

3. 中断处理

为了提高数据传输的效率,UART设备通常会采用中断方式来处理接收数据和发送数据。中断处理是一个非常重要的优化点。

在中断处理时,需要考虑到以下几点:

– 中断事件的优先级

– 数据缓冲的处理方式

– 中断响应的效率

在注销中断时,需要注意一些细节。例如,避免在中断处理函数中进行阻塞操作,以及正确处理中断发生的时间。

4. 处理控制流

流控制机制是控制数据传输流量的一种方式。在串口通信中,流控制通常被使用。UART的硬件流控制和软件流控制是两种方法。

对于流控制的处理,需要注意以下几点:

– 使用精细的锁来避免竞争

– 控制流量,以便不会导致缓冲区溢出

– 处理流量,以便保持数据的可靠传输

需要确保流控制的配置是正确的,并避免可能导致数据传输故障的任何错误。


数据运维技术 » ARM Linux UART驱动实现与优化指南 (arm linux uart驱动)