天天看点

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

目录

1.Linux下UART驱动框架

    1)uart_driver注册与注销

    2)uart_port的添加与移除

    3)uart_ops实现

2.RS232驱动编写

    1)UART3 IO节点创建

    2)添加uart3节点

3.移植minicom

    1)移植ncurses

    2)移植minicom

4.RS232驱动测试

    1)RS232连接设置

    2)minicom设置

    3)RS232收发测试

      串口是很常用的一个外设,在Linux下通常通过串口和其他设备或传感器进行通信,根据电平的

      不同,串口分为TTL和RS232。不管是什么样的接口电平,其驱动程序都是一样的,通过外接

      RS485这样的芯片就可以将串口转换为RS485信号,正点原子的I.MX6U-ALPHA开发板就是这

      么做的。对于正点原子的I.MX6U-ALPHA开发板而言,RS232、RS485以及GPS模块接口通通

      连接到了I.MX6U的UART3接口上,因此这些外设最终都归结为UART3的串口驱动。本章我们

      就来学习一下如何驱动I.MX6U-ALPHA开发板上的UART3串口,进而实现RS232、RS485以及

      GSP驱动。

1.Linux下UART驱动框架

    1)uart_driver注册与注销

              同I2C、SPI一样,Linux也提供了串口驱动框架,我们只需要按照相应的串口框架编写驱

              动程序即可。串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,而且这个驱

              动也已经由NXP官方已经编写好了,我们真正要做的就是在设备树中添加所要使用的串口

              节点信息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生

              成/dev/ttymxcX(X=0….n)文件。

              虽然串口驱动不需要我们去写,但是串口驱动框架我们还是需要了解的,uart_driver结构

              体表示UART驱动,uart_driver定义在include/linux/serial_core.h文件中,内容如下:

295 struct uart_driver {
296 	struct module *owner; /* 模块所属者 */
297 	const char *driver_name; /* 驱动名字 */
298 	const char *dev_name; /* 设备名字 */
299 	int major; /* 主设备号 */
300 	int minor; /* 次设备号 */
301 	int nr; /* 设备数 */
302 	struct console *cons; /* 控制台 */
303
304 	/*
305 	* these are private; the low level driver should not
306 	* touch these; they should be initialised to NULL
307 	*/
308 	struct uart_state *state;
309 	struct tty_driver *tty_driver;
310 };
           

              每个串口驱动都需要定义一个uart_driver,加载驱动的时候通过uart_register_driver函数

              向系统注册这个uart_driver,此函数原型如下:

int uart_register_driver(struct uart_driver *drv)
           

              函数参数和返回值含义如下:

              drv:要注册的uart_driver。

              返回值:0,成功;负值,失败。

              注销驱动的时候也需要注销掉前面注册的uart_driver,需要用到uart_unregister_driver

              函数,函数原型如下:

void uart_unregister_driver(struct uart_driver *drv)
           

              函数参数和返回值含义如下:

              drv:要注销的uart_driver。

               返回值:无。

    2)uart_port的添加与移除

              uart_port表示一个具体的port,uart_port定义在include/linux/serial_core.h文件,内容如下

              (有省略):

117 struct uart_port {
118 	spinlock_t lock; /* port lock */
119 	unsigned long iobase; /* in/out[bwl] */
120 	unsigned char __iomem *membase; /* read/write[bwl] */
......
235 	const struct uart_ops *ops;
236 	unsigned int custom_divisor;
237 	unsigned int line; /* port index */
238 	unsigned int minor;
239 	resource_size_t mapbase; /* for ioremap */
240 	resource_size_t mapsize;
241 	struct device *dev; /* parent device */
......
250 };
           

              uart_port中最主要的就是第235行的ops,ops包含了串口的具体驱动函数,这个我们稍后

              再看。每个UART都有一个uart_port,那么uart_port是怎么和uart_driver结合起来的呢?

              这里要用到uart_add_one_port函数,函数原型如下:

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
           

              函数参数和返回值含义如下:

              drv:此port对应的uart_driver。

              uport:要添加到uart_driver中的port。

              返回值:0,成功;负值,失败。

              卸载UART驱动的时候也需要将uart_port从相应的uart_driver中移除,需要用到

              uart_remove_one_port函数,函数原型如下:

int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
           

              函数参数和返回值含义如下:

              drv:要卸载的port所对应的uart_driver。

              uport:要卸载的uart_port。

              返回值:0,成功;负值,失败。

    3)uart_ops实现

             在上面讲解uart_port的时候说过,uart_port中的ops成员变量很重要,因为ops包含了针对

             UART具体的驱动函数,Linux系统收发数据最终调用的都是ops中的函数。ops是uart_ops

             类型的结构体指针变量,uart_ops定义在include/linux/serial_core.h文件中,内容如下:

49 struct uart_ops {
50 	unsigned int (*tx_empty)(struct uart_port *);
51 	void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
52 	unsigned int (*get_mctrl)(struct uart_port *);
53 	void (*stop_tx)(struct uart_port *);
54 	void (*start_tx)(struct uart_port *);
55 	void (*throttle)(struct uart_port *);
56 	void (*unthrottle)(struct uart_port *);
57 	void (*send_xchar)(struct uart_port *, char ch);
58 	void (*stop_rx)(struct uart_port *);
59 	void (*enable_ms)(struct uart_port *);
60 	void (*break_ctl)(struct uart_port *, int ctl);
61 	int (*startup)(struct uart_port *);
62 	void (*shutdown)(struct uart_port *);
63 	void (*flush_buffer)(struct uart_port *);
64 	void (*set_termios)(struct uart_port *, struct ktermios *new,
65 						struct ktermios *old);
66 	void (*set_ldisc)(struct uart_port *, struct ktermios *);
67 	void (*pm)(struct uart_port *, unsigned int state,
68 				unsigned int oldstate);
69
70 	/*
71 	* Return a string describing the type of the port
72 	*/
73 	const char *(*type)(struct uart_port *);
74
75 	/*
76 	* Release IO and memory resources used by the port.
77 	* This includes iounmap if necessary.
78 	*/
79 	void (*release_port)(struct uart_port *);
80
81 	/*
82 	* Request IO and memory resources used by the port.
83 	* This includes iomapping the port if necessary.
84 	*/
85 	int (*request_port)(struct uart_port *);
86 	void (*config_port)(struct uart_port *, int);
87 	int (*verify_port)(struct uart_port *, struct serial_struct *);
88 	int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
89 	#ifdef CONFIG_CONSOLE_POLL
90 	int (*poll_init)(struct uart_port *);
91 	void (*poll_put_char)(struct uart_port *, unsigned char);
92 	int (*poll_get_char)(struct uart_port *);
93 	#endif
94 };
           

              UART驱动编写人员需要实现uart_ops,因为uart_ops是最底层的UART驱动接口,是实实

              在在的和UART寄存器打交道的。关于uart_ops结构体中的这些函数的具体含义请参考

              Documentation/serial/driver这个文档。UART驱动框架大概就是这些,接下来我们理论联

              系实际,看一下NXP官方的UART驱动文件是如何编写的。

2.RS232驱动编写

      前面我们已经说过了,I.MX6U的UART驱动NXP已经编写好了,所以不需要我们编写。我们要

      做的就是在设备树中添加UART3对应的设备节点即可。打开imx6ull-alientek-emmc.dts文件,

      在此文件中只有UART1对应的uart1节点,并没有UART3对应的节点,因此我们可以参考uart1

      节点创建uart3节点。

    1)UART3 IO节点创建

              UART3用到了UART3_TXD和UART3_RXD这两个IO,因此要先在iomuxc中创建UART3

              对应的pinctrl子节点,在iomuxc中添加如下内容:

1 pinctrl_uart3: uart3grp {
2     fsl,pins = <
3         MX6UL_PAD_UART3_TX_DATA__UART3_DCE_TX 0X1b0b1
4         MX6UL_PAD_UART3_RX_DATA__UART3_DCE_RX 0X1b0b1
5     >;
6 };
           

               最后检查一下UART3_TX和UART3_RX这两个引脚有没有被用作其他功能,如果有的话

               要将其屏蔽掉,保证这两个IO只用作UART3,切记!!!

    2)添加uart3节点

              默认情况下imx6ull-alientek-emmc.dts中只有uart1和uart2这两个节点,如图1所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图1 uart1和 uart2节点 

              uart1是UART1的,在正点原子的I.MX6U-ALPHA开发板上没有用到UART2,而且UART2

              默认用到了UART3的IO,因此需要将uart2这个节点删除掉,然后加上UART3对应的

              uart3,uart3节点内容如下: 

1 &uart3 {
2 	pinctrl-names = "default";
3 	pinctrl-0 = <&pinctrl_uart3>;
4 	status = "okay";
5 };
           

              完成以后重新编译设备树并使用新的设备树启动Linux,如果设备树修改成功的话,系统

              启动以后就会生成一个名为“/dev/ttymxc2”的设备文件,ttymxc2就是UART3对应的设备

              文件,应用程序可以通过访问ttymxc2来实现对UART3的操作。

3.移植minicom

      minicom类似我们常用的串口调试助手,是Linux下很常用的一个串口工具,将minicom移植到

      我们的开发板中,这样我们就可以借助minicom对串口进行读写操作。

    1)移植ncurses

              minicom需要用到ncurses,依次需要先移植ncurses,如果前面已经移植好了ncurses,那

              么这里就不需要再次移植了,只需要在编译minicom的时候指定ncurses库和头文件目录即

              可。

              首先在ubuntu中创建一个目录来存放我们要移植的文件,比如我

              在/home/zuozhongkai/linux/IMX6ULL目录下创建了一个名为“tool”的目录来存放所有的移

              植文件。然后下载ncurses源码,将ncurses-6.0.tar.gz拷贝到Ubuntu中创建的tool目录

              下,然后进行解压,解压命令如下:

tar -vxzf ncurses-6.0.tar.gz
           

              解压完成以后就会生成一个名为“ncurses-6.0”的文件夹,此文件夹就是ncurese的源码文

              件夹。在tool目录下新建名为“ncurses”目录,用于保存ncurses编译结果,一切准备就绪

              以后就可以编译ncureses库了。进入到ncureses源码目录下,也就是刚刚解压出来的

              ncurses-6.0目录中,首先是配置ncureses,输入如下命令:

./configure --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/ncurses --host=arm-linux-

gnueabihf --target=arm-linux-gnueabihf --with-shared --without-profile --disable-stripping --without-progs --with-manpages --without-tests

              configure就是配置脚本,--prefix用于指定编译结果的保存目录,这里肯定将编译结果保存

              到我们前面创建的“ncurses”目录中。--host用于指定编译器前缀,这里设置为“arm-linux-

              gnueabihf”,--target用于指定目标,这里也设置为“arm-linux-gnueabihf”。配置命令写好

              以后点击回车键,等待配置完成,配置成功以后如图2所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图2 配置成功

              配置成功以后输入“make”命令开始编译,编译成功以后如图3所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

 图3 编译成功

              编译成功以后输入“makeinstall”命令安装,安装的意思就是将编译出来的结果拷贝到--

              pfefix指定的目录里面去。安装成功以后如图4所示: 

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图4 安装成功

              安装成功以后查看一下前面创建的“ncurses”文件夹,会发现里面多了一些东西,如图5所

              示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图5 编译出来的结果

              我们需要将图5中include、lib和share这三个目录中存放的文件分别拷贝到开发板根文件系

              统中的/usr/include、/usr/lib和/usr/share这三个目录中,如果哪个目录不存在的话请自行

              创建!!拷贝命令如下:

sudo cp lib/* /home/zuozhongkai/linux/nfs/rootfs/usr/lib/ -rfa

sudo cp share/* /home/zuozhongkai/linux/nfs/rootfs/usr/share/ -rfa

sudo cp include/* /home/zuozhongkai/linux/nfs/rootfs/usr/include/ -rfa

              然后在开发板根目录的/etc/profile(没有的话自己创建一个)文件中添加如下所示内容:

1 #!/bin/sh
2 LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
3 export LD_LIBRARY_PATH
4
5 export TERM=vt100
6 export TERMINFO=/usr/share/terminfo
           

    2)移植minicom

              继续移植minicom,获取minicom源码,将minicom-2.7.1.tar.gz拷贝到ubuntu中

              的/home/zuozhongkai/linux/IMX6ULL/tool目录下,然后在tool目录下新建一个名为

              “minicom”的子目录,用于存放minicom编译结果。一切准备好以后就可以编译minicom

              了,先解压minicom,命令如下:

tar -vxzf minicom-2.7.1.tar.gz
           

              解压完成以后会生成一个叫做minicom-2.7.1的文件夹,这个就是minicom的源码,进入到

              此目录中,然后配置minicom,配置命令如下:

cd minicom-2.7.1/ //进入 minicom 源码目录

./configure CC=arm-linux-gnueabihf-gcc --prefix=/home/zuozhongkai/linux/IMX6ULL/tool/

minicom --host=arm-linux-gnueabihf CPPFLAGS=-I/home/zuozhongkai/linux/IMX6ULL/tool/

ncurses/include LDFLAGS=-L/home/zuozhongkai/linux/IMX6ULL/tool/ncurses/lib -enable-cfg-dir=/etc/minicom //配置

              CC表示要使用的gcc交叉编译器,--prefix指定编译出来的文件存放目录,肯定要存放到我

              们前面创建的minicom目录中。--host指定交叉编译器前缀,CPPFLAGS指定ncurses的头

              文件路径,LDFLAGS指定ncurses的库路径。配置成功的话如图6所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图6 配置成功

              配置成功以后执行如下命令编译并安装:

make
make install
           

              编译安装完成以后,前面创建的minicom目录内容如图7所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

 图7 minicom安装编译结果

              将minicom目录中bin子目录下的所有文件拷贝到开发板根目录中的/usr/bin目录下,命令

              如下:

sudo cp bin/* /home/zuozhongkai/linux/nfs/rootfs/usr/bin/
           

              完成以后在开发板中输入“minicom -v”来查看minicom工作是否正常,结果如图8所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图8 minicom版本号

              从图8可以看出,此时minicom版本号为2.7.1,minicom版本号查看正常。输入如下命令打

              开minicom配置界面:

minicom -s
           

              结果是打不开minicom配置界面,提示如图9所示信息:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

 图9 minicom打开失败

              从图9可以看出,minicom异常嚣张,竟然让我们“Go away”,这能容忍?!必须要治一

              下。解决方法很简单,新建/etc/passwd文件,然后在passwd文件里面输入如下所示内

               容: 

root:x:0:0:root:/root:/bin/sh
           

              完成以后重启开发板!

              开发板重启以后再执行“minicom -s”命令,此时minicom配置界面就可以打开了,如图10

              所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图10 mincom 配置界面

              如果能出现图10所示界面,那么就说明mincom工作正常了。

4.RS232驱动测试

    1)RS232连接设置

              在测试之前要先将I.MX6U-ALPHA开发板的RS232与电脑连接起来,首先设置JP1跳线

              帽,如图11所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

 图11 UART3 跳线帽设置

              跳线帽设置好以后使用RS232线将开发板与电脑连接起来,这里建议使用USB转

              DB9(RS232)数据线,比如正点原子售卖的CH340方案的USB转公头DB9数据线,如图12

              所示: 

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图12 USB 转 DB9数据线

              图12中所示的数据线是带有CH340芯片的,因此当连接到电脑以后就会出现一个COM

              口,这个COM口就是我们要使用的COM口。比如在我的电脑上就是COM9,在

              SecureCRT上新建一个连接,串口为COM9,波特率为115200。

    2)minicom设置

              在开发板中输入“minicom -s”,打开minicom配置界面,然后选中“Serial port setup”,如图

              13所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图13 选中串口设置项

             选中“Serial port setup”以后点击回车,进入设置菜单,如图14所示: 

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图14 串口设置项

              图14中有7个设置项目,分别对应A、B……G,比如第一个是选中串口,UART3的串口文

              件为/dev/ttymxc2,因此串口设置要设置为/dev/ttymxc2。设置方法就是按下键盘上

              的‘A’,然后输入“/dev/ttymxc2”即可,如图14所示: 

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图14 串口设备文件设置

              设置完以后按下回车键确认,确认完以后就可以设置其他的配置项。比如E设置波特率、

              数据位和停止位的、F设置硬件流控的,设置方法都一样,设置完以后如图15所示: 

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

 图15 UART3设置

              都设置完成以后按下回车键确认并退出,这时候会退回到如图13所示的界面,按下ESC键

              退出图13所示的配置界面,退出以后如图16所示: 

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图16 minicom串口界面

              图14就是我们的串口调试界面,可以看出当前的串口文件为/dev/ttymxc2,按下CTRL-

              A,然后再按下Z就可以打开minicom帮助信息界面,如图17所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

 图17  minicom帮助信息界面

              从图17可以看出,minicom有很多快捷键,本实验我们打开minicom的回显功能,回显功

               能配置项为“localEchoon/off..E”,因此按下E即可打开/关闭回显功能。

    3)RS232收发测试

              1、发送测试

                   首先测试开发板通过UART3向电脑发送数据的功能,需要打开minicom的回显功能(不

                   打开也可以,但是在minicom中看不到自己输入的内容),回显功能打开以后输入

                    “AAAA”,如图18所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图18 通过UART3向电脑发送“AAAA”

                   图18中的“AAAA”相当于开发板通过UART3向电脑发送“AAAA”,那么COM9就会接收

                   到“AAAA”,SecureCRT中COM9收到的数据如图19所示: 

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

 图19 电脑接收到开发板发送过来的数据

                   可以看出,开发板通过UART3向电脑发送数据正常,那么接下来就测试开发板数据接

                   收功能。

             2、接收测试

                   接下来测试开发板的UART3接收功能,同样的,要先打开SecureCRT上COM9的本地

                   回显,否则的话你在COM9上输出的内容会看不到,但是实际上是已经发送给了开发

                   板。选中SecureCRT的Options->SessionOptions->Adavnced,打开回话配置界面,

                   然后选中“Localecho”,如图20所示: 

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图20 电脑向开发板发送“BBBB”

                   SecureCRT设置好以后向开发板发送一个“BBBB”,在SecureCRT的COM9上输入

                   “BBBB”,如图21所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

图21 电脑向开发板发送“BBBB”  

                   此时开发板的minicom就会接收到发送过来的“BBBB”,如图22所示:

Linux驱动开发篇-RS232/485/GPS驱动实验1.Linux下UART驱动框架2.RS232驱动编写3.移植minicom4.RS232驱动测试

 图22 开发板接收到发送过来的数据

                   UART3收发测试都没有问题,说明我们的UART3驱动工作正常。如果要退出minicom

                   的话,在minicom通信界面按下CRTL+A,然后按下X来关闭minicom。关于minicom的

                   使用我们这里讲的很简单,大家可以在网上查找更加详细的minicom使用教程。

继续阅读