赞
踩
最近在做的项目里,需要在应用层驱动多个串口。排了一下引脚,准备把uart1、2、3、4、5、6、7、8这8个都驱动起来.
串口这个东西,按理说应该是最简单最基础的了,但是毕竟是刚开始学习,为了实现这个还是理了半天,在此记录一下流程,踩过的坑和操作的步骤,便于大家借鉴,也是自己的一个记录。
这个实验代码量不是很大,主要要注意uart4的一个官方坑,还有一堆引脚的名称(原谅我真的要吐槽,Imx6 引脚名字真的绕,为什么不直接用A1、B2这样命名呢……每次找引脚太麻烦了),强烈建议使用NXP官方的i.MX Pins Tool 工具,图形化的配置,相对来说直观一些,有点类似ST的cubeMX。
串口的驱动相对来说还是比较方便的,无他,就是驱动NXP已经写完了,我们只需要在设备树中添加对应的设备即可,这样就会在/dev下生成对应的设备结点,进而可以在应用层进行进行调用。
原子在开发板上和出厂代码上,已经做了2个串口,Uart1和Uart3,对应生成了了/dev/ttymxc0,/dev/ttymxc2。
我们只需要按格式在设备树中把对应的设备添加,就可以直接使用了。
因为在这个项目中用不到LCD,我就把部分LCD的信号脚给分出来当串口使用了。实测的时候,需要用转接板把排线引出来验证。
基本分配下来引脚如下:
这些引脚在阿尔法开发板上(I.MX6U ALPHA V2.2)基本都有引出,可以进行测试。
在这里还是要推一下这个i.MX Pins Tool工具,勾选完之后,一键生成设备树文件,虽然不能直接使用,但是好歹每个引脚的名称是给出来了,如下:
不然自己找引脚名字往设备树里填也很麻烦。
另外备注一下,引脚名称都是在
几个文件中定义的。
我是基于原子的出厂代码来做的修改,修改了linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek\arch\arm\boot\dts\imx6ull-alientek-emmc.dts设备树。
在设备中添加对应的引脚
然后添加对应的控制器
注意还要把不用的冲突引脚disabled掉。
然后重新编译设备树,拷贝到/run/media/mmcblk1p1,注意要和原设备树文件同名,重启,完成更新。
这时候在看,在/dev下就有了Uart1~8的设备结点了。
在原子的实验中,应用层是通过移植minicom来驱动的。
应用层我直接从网上找了一段代码简单改了一下。
也没啥要多做说明的,交叉编一下,直接调用即可。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <termios.h> int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop) { struct termios newtio,oldtio; if ( tcgetattr( fd,&oldtio) != 0) { perror("SetupSerial 1"); return -1; } bzero( &newtio, sizeof( newtio ) ); newtio.c_cflag |= CLOCAL | CREAD; //CLOCAL:忽略modem控制线 CREAD:打开接受者 newtio.c_cflag &= ~CSIZE; //字符长度掩码。取值为:CS5,CS6,CS7或CS8 switch( nBits ) { case 7: newtio.c_cflag |= CS7; break; case 8: newtio.c_cflag |= CS8; break; } switch( nEvent ) { case 'O': newtio.c_cflag |= PARENB; //允许输出产生奇偶信息以及输入到奇偶校验 newtio.c_cflag |= PARODD; //输入和输出是奇及校验 newtio.c_iflag |= (INPCK | ISTRIP); // INPACK:启用输入奇偶检测;ISTRIP:去掉第八位 break; case 'E': newtio.c_iflag |= (INPCK | ISTRIP); newtio.c_cflag |= PARENB; newtio.c_cflag &= ~PARODD; break; case 'N': newtio.c_cflag &= ~PARENB; break; } switch( nSpeed ) { case 2400: cfsetispeed(&newtio, B2400); cfsetospeed(&newtio, B2400); break; case 4800: cfsetispeed(&newtio, B4800); cfsetospeed(&newtio, B4800); break; case 9600: cfsetispeed(&newtio, B9600); cfsetospeed(&newtio, B9600); break; case 115200: cfsetispeed(&newtio, B115200); cfsetospeed(&newtio, B115200); break; case 460800: cfsetispeed(&newtio, B460800); cfsetospeed(&newtio, B460800); break; default: cfsetispeed(&newtio, B9600); cfsetospeed(&newtio, B9600); break; } if( nStop == 1 ) newtio.c_cflag &= ~CSTOPB; //CSTOPB:设置两个停止位,而不是一个 else if ( nStop == 2 ) newtio.c_cflag |= CSTOPB; newtio.c_cc[VTIME] = 0; //VTIME:非cannoical模式读时的延时,以十分之一秒位单位 newtio.c_cc[VMIN] = 0; //VMIN:非canonical模式读到最小字符数 tcflush(fd,TCIFLUSH); // 改变在所有写入 fd 引用的对象的输出都被传输后生效,所有已接受但未读入的输入都在改变发生前丢弃。 if((tcsetattr(fd,TCSANOW,&newtio))!=0) //TCSANOW:改变立即发生 { perror("com set error"); return -1; } printf("set done!\n\r"); return 0; } int main(int argc , char ** argv) { int fd1,nset,nread,ret; char buf[100]={"test com data!...........\n"}; char buf1[100]; char *filename; //Step1 参数校验 if(argc <2 ||argc >3) { printf("Input ERR! input argc %d\r\ tn",argc); int i; for(i=0;i++;i<argc) printf("argv[%d] %s\r\n",argv[i]); printf("\r\n[usage]\r\n"); printf("\t e.g. ./app_uart /dev/ttymxc0 \r\n"); printf("\t ./app_uart /dev/ttymxc2 tt \r\n\r\n"); return -1; } filename = argv[1]; if(argc==3) { memset(buf,0,sizeof(buf)); strcpy(buf,argv[2]); strcat(buf,"\r\n"); printf("Send %s",buf); } fd1 = open( filename, O_RDWR); if (fd1 == -1) { printf("open %s fail\r\n",filename); exit(1); } printf("open %s success!!\n",filename); nset = set_opt(fd1, 115200, 8, 'N', 1); if (nset == -1) exit(1); printf("SET S1 success!!\n"); printf("enter the loop!!\n"); while (1) { memset(buf1, 0, sizeof(buf1)); ret = write(fd1, buf, 100); if( ret > 0){ printf("write success! wait data receive\n"); } nread = read(fd1, buf1, 100); if(nread > 0){ printf("redatad: nread = %s\n", buf1); printf("circle test success\n"); } sleep(1); } close(fd1); return 0; }
NXP的串口驱动在 linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek\drivers\tty\serial\imx.c (直接搜索ttymxc设备结点名就能找到)。
如果选择自行进行串口的封装,比如把一些固定设备的串口协议也写进去,封装成另外一个驱动,就不能直接使用NXP的串口驱动了,应用层也不能直接去调用/dev/ttymxc的相关及结点。
这个之后再写。
遇到一个坑,好在之前有人踩过。
就是Uart5在驱动的时候无法接收,只能发送。因为NXP的引脚名比较绕,之前是以为接收引脚被占用了,查了半天设备树,最后在网上搜一下一下,发现是个官方的坑,还好有人踩过,不然估计得很花精力。
具体可以见附录2链接(imx6ull 下 UART5问题)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。