Linux驱动开发|UART驱动
UART驱动
UART驱动
串口是一个常用的外设,在 Linux 下通常通过串口和其他设备或传感器进行通信,根据电平的不同,串口分为 TTL 和 RS232。虽然接口电平不同,但其驱动程序都是一样的,通过外接 RS485芯片就可以将串口转换为 RS485 信号。本文介绍如何驱动 I.MX6U-ALPHA 开发板上的 UART3 串口
一、UART驱动框架
串口驱动没有主机端和设备端之分,就只有一个串口驱动,且驱动也已经由 NXP 官方已经编写好了,我们要做的就是在设备树中添加所要使用的串口节点信息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成/dev/ttymxcX(X=0….n)文件
1.1 uart_driver 注册与注销
虽然串口驱动不需要我们去写,但串口驱动框架还是需要了解的, uart_driver 结构体表示 UART 驱动, 其定义在 include/linux/serial_core.h 文件中
struct uart_driver {
struct module *owner; /* 模块所属者 */
const char *driver_name; /* 驱动名字 */
const char *dev_name; /* 设备名字 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
int nr; /* 设备数 */
struct console *cons; /* 控制台 */
/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};
每个串口驱动都需要定义一个 uart_driver,加载驱动时向系统注册这个 uart_driver,注销驱动时注销掉注册的 uart_driver
/************ 注册uart_driver *************/
int uart_register_driver(struct uart_driver *drv)
//drv:要注册的 uart_driver
//返回值:0,成功;负值,失败
/************ 注销uart_driver *************/
void uart_unregister_driver(struct uart_driver *drv)
//drv:要注销的 uart_driver
1.2 uart_port 的添加与移除
uart_port 表示一个具体的 port, 其定义在 include/linux/serial_core.h 文件中
struct uart_port {
spinlock_t lock; /* port lock */
unsigned long iobase; /* in/out[bwl] */
unsigned char __iomem *membase; /* read/write[bwl] */
......
const struct uart_ops *ops; /* ops包含了串口的具体驱动函数 */
unsigned int custom_divisor;
unsigned int line; /* port index */
unsigned int minor;
resource_size_t mapbase; /* for ioremap */
resource_size_t mapsize;
struct device *dev; /* parent device */
......
};
每个 UART 都有一个 uart_port,使用如下函数将 uart_port和 uart_driver 结合或者移除
/************ 将uart_port与相应的uart_driver结合 **************/
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
//drv:此port对应的 uart_driver
//uport:要添加到uart_driver中的port
//返回值:0,成功;负值,失败
/************ 将uart_port从相应的uart_driver中移除 *************/
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
//drv:要卸载的port所对应的uart_driver
//uport:要卸载的uart_port
//返回值:0,成功;负值,失败
1.3 uart_ops 实现
uart_port 中的成员变量 ops 包含了针对 UART 具体的驱动函数,Linux 系统收发数据最终调用的都是 ops 中的函数。ops 是 uart_ops类型的结构体指针变量,定义在 include/linux/serial_core.h 文件中
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
unsigned int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *);
void (*start_tx)(struct uart_port *);
void (*throttle)(struct uart_port *);
void (*unthrottle)(struct uart_port *);
void (*send_xchar)(struct uart_port *, char ch);
void (*stop_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
int (*startup)(struct uart_port *);
void (*shutdown)(struct uart_port *);
void (*flush_buffer)(struct uart_port *);
void (*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old);
void (*set_ldisc)(struct uart_port *, struct ktermios *);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
/* Return a string describing the type of the port */
const char *(*type)(struct uart_port *);
/* Release IO and memory resources used by the port.
* This includes iounmap if necessary. */
void (*release_port)(struct uart_port *);
/* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary. */
int (*request_port)(struct uart_port *);
void (*config_port)(struct uart_port *, int);
int (*verify_port)(struct uart_port *, struct serial_struct *);
int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct uart_port *);
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};
二、UART驱动实例
I.MX6U-ALPHA 开发板上 RS232、 RS485 和 GPS 这三个接口都连接到了 UART3 上。下面以RS232为例,介绍如何驱动开发板上的 UART3 接口。RS232的原理图如下图示:
2.1 RS232 驱动编写
IMX6U的 UART 驱动已经由 NXP 编写好了,因此要使用 UART3 接口,只需要在设备树中添加 UART3 对于的设备节点即可
- 修改或添加pinctrl节点:在iomuxc中创建UART3对应的pinctrl子节点
pinctrl_uart3: uart3grp {
fsl,pins = <
MX6UL_PAD_UART3_TX_DATA__UART3_DCE_TX 0X1b0b1
MX6UL_PAD_UART3_RX_DATA__UART3_DCE_RX 0X1b0b1
>;
};
- 添加子节点:添加UART3对应的子节点 uart3
&uart3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart3>;
status = "okay";
};
- 检查PIN是否冲突:检查pinctrl中设置以及设备节点中指定的引脚有没有被别的外设使用
保存修改后,在kernel主目录下使用“make dtbs”命令编译设备树,使用新的设备树文件启动Llinux系统
设备树修改成功的话,系统启动后会生成一个名为“/dev/ttymxc2”的设备文件,此文件就是 UART3 对应的设备文件,应用程序可以通过访问 ttymxc2 来实现对 UART3 的操作
2.2 RS232 驱动测试
参考开发板移植minicom一文将 minicom 移植到开发板中,这样就可以借助 minicom 对串口进行读写操作
-
RS232 连接:使用 USB转公头DB9数据线将开发板与电脑连接起来,电脑上串口助手波特率设为 115200
-
minicom 设置:开发板中使用“minicom -s”命令,打开配置界面,选中“Serial port setup”进行设置(如下图所设置)
-
设置完成后按回车键确认,退出配置界面后,minicom串口界面如下图示
-
依次按下"CTRL-A"和" Z " 进入帮助信息界面,按下"E"打开回显功能
-
发送测试:测试开发板通过 UART3 向电脑发送数据的功能
-
接收测试:测试开发板的 UART3 接收功能
更多推荐