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 接收功能
    在这里插入图片描述
    在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐