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的硬件流控制和软件流控制是两种方法。
对于流控制的处理,需要注意以下几点:
– 使用精细的锁来避免竞争
– 控制流量,以便不会导致缓冲区溢出
– 处理流量,以便保持数据的可靠传输
需要确保流控制的配置是正确的,并避免可能导致数据传输故障的任何错误。