当前位置:   article > 正文

【LINUX】i.MX6学习笔记(3) 驱动多个串口_正点原子多串口驱动

正点原子多串口驱动

1. 引言

  • 硬件环境:正点原子 IMX6 阿尔法开发板。

最近在做的项目里,需要在应用层驱动多个串口。排了一下引脚,准备把uart1、2、3、4、5、6、7、8这8个都驱动起来.
串口这个东西,按理说应该是最简单最基础的了,但是毕竟是刚开始学习,为了实现这个还是理了半天,在此记录一下流程,踩过的坑和操作的步骤,便于大家借鉴,也是自己的一个记录。

这个实验代码量不是很大,主要要注意uart4的一个官方坑,还有一堆引脚的名称(原谅我真的要吐槽,Imx6 引脚名字真的绕,为什么不直接用A1、B2这样命名呢……每次找引脚太麻烦了),强烈建议使用NXP官方的i.MX Pins Tool 工具,图形化的配置,相对来说直观一些,有点类似ST的cubeMX。
在这里插入图片描述

2. 实验步骤

2.1 驱动

串口的驱动相对来说还是比较方便的,无他,就是驱动NXP已经写完了,我们只需要在设备树中添加对应的设备即可,这样就会在/dev下生成对应的设备结点,进而可以在应用层进行进行调用。
原子在开发板上和出厂代码上,已经做了2个串口,Uart1和Uart3,对应生成了了/dev/ttymxc0,/dev/ttymxc2。
我们只需要按格式在设备树中把对应的设备添加,就可以直接使用了。

2.1.1 引脚分配

因为在这个项目中用不到LCD,我就把部分LCD的信号脚给分出来当串口使用了。实测的时候,需要用转接板把排线引出来验证。
在这里插入图片描述

基本分配下来引脚如下:
i.MX Pins软件中勾选引脚功能
这些引脚在阿尔法开发板上(I.MX6U ALPHA V2.2)基本都有引出,可以进行测试。

  • UART1:左侧USB转串口
  • UART2/UART3/UART5:中间插针
  • UART6:摄像头插口,CSI_MCLK,CSI_PIXCLK
  • UART4/UART7/UART7:LCD转接板。LCD_DATA16、LCD_DATA17、LCD_DATA20、LCD_DATA21、LCD_DE、LCD_PCLK

在这里还是要推一下这个i.MX Pins Tool工具,勾选完之后,一键生成设备树文件,虽然不能直接使用,但是好歹每个引脚的名称是给出来了,如下:
i.MX Pins生成的设备树代码
不然自己找引脚名字往设备树里填也很麻烦。

另外备注一下,引脚名称都是在

  • \arch\arm\boot\dts\imx6ul-pinfunc.h
  • \arch\arm\boot\dts\imx6ull-pinfunc.h
  • \arch\arm\boot\dts\imx6ull-pinfunc-snvs.h

几个文件中定义的。

2.1.2 设备树添加

我是基于原子的出厂代码来做的修改,修改了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的设备结点了。
在这里插入图片描述

2.2 应用层

在原子的实验中,应用层是通过移植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;  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155

2.3 驱动进一步封装

NXP的串口驱动在 linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek\drivers\tty\serial\imx.c (直接搜索ttymxc设备结点名就能找到)。
如果选择自行进行串口的封装,比如把一些固定设备的串口协议也写进去,封装成另外一个驱动,就不能直接使用NXP的串口驱动了,应用层也不能直接去调用/dev/ttymxc的相关及结点。
这个之后再写。

3. 遇到的坑

3.1 Uart5 只能发送不能接收

遇到一个坑,好在之前有人踩过。
就是Uart5在驱动的时候无法接收,只能发送。因为NXP的引脚名比较绕,之前是以为接收引脚被占用了,查了半天设备树,最后在网上搜一下一下,发现是个官方的坑,还好有人踩过,不然估计得很花精力。
在这里插入图片描述
具体可以见附录2链接(imx6ull 下 UART5问题)。

附录

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/944674
推荐阅读
相关标签
  

闽ICP备14008679号