• Linux应用基础——串口应用编程


    基于平台:IMX6ULL PRO开发板
    
    • 1

    串口介绍

    串口(UART)的全称是Universal Asynchronous Receiver and Transmitter,即异步发送和接收。USART无论是在单片机中、嵌入式中都起到了非常重要的作用。并且串口的结构简单、稳定。因此受到大部分人的喜爱。另外,学习过单片机、stm32的都知道串口的作用。即:可以用于调试打印信息、还可外接传感器模块。

    另外应用层设置串口的参数主要有:
    波特率、起始位、数据位、校验位、停止位、行规层(TTY体系内)。

    扩展—— TTY体系

    TTY简介

    Linux中,TTY也许是跟终端有关系的最为混乱的术语。TTY是TeleTYpe的一个老缩写。Teletypes,或者teletypewriters,原来指的是电传打字机,是通过串行线用打印机键盘通过阅读和发送信息的东西,和古老的电报机区别并不是很大。之后,当计算机只能以批处理方式运行时,电传打字机成为唯一能够被使用的“实时”输入/输出设备。最终,电传打字机被键盘和显示器终端所取代,但在终端或TTY接插的地方,操作系统仍然需要一个程序来监视串行端口。一个getty“Get TTY”的处理过程是:一个程序监视物理的TTY/终端接口。对一个虚拟网络控制台(VNC)来说,一个伪装的TTY(Pseudo-TTY,即假冒的TTY,也叫做“PTY”)是等价的终端。当你运行一个xterm(终端仿真程序)或GNOME终端程序时,PTY对虚拟的用户或者如xterm一样的伪终端来说,就像是一个TTY在运行。“Pseudo”的意思是“duplicating in a fake way”(用伪造的方法复制),它相比“virtual”或“emulated”更能真实的说明问题。而在的计算中,它却处于被放弃的阶段。
    tty也是一个Unix命令,用来给出当前终端设备的名称。
    终端是一种字符型设备,它有多种类型,通常使用tty来简称各种类型的终端设备。
    在Linux系统的设备特殊文件目录/dev/下。

    TTY系统框架

    在韦东山的视频教程中给出了这么一个系统框架图
    在这里面当中引用了行规层的概念。
    在这里插入图片描述

    代码部分

    我们都知道串口有波特率、起始位、数据位、校验位、停止位这几位。
    通过Linux应用串口的文章我们可以很好的吧串口编程逮捕编写出来。
    参考链接:https://www.cnblogs.com/feisky/archive/2010/05/21/1740893.html

    基本配置部分

    #include           /*标准输入输出定义*/
    #include          /*标准函数库定义*/
    #include          /*Unix 标准函数定义*/
    #include      
    #include       
    #include           /*文件控制定义*/
    #include         /*POSIX 终端控制定义*/
    #include           /*错误号定义*/
    /**********主要用来配置波特率、检验位等参数************/
    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; 
    	newtio.c_cflag &= ~CSIZE; 
    
    	newtio.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
    	newtio.c_oflag  &= ~OPOST;   /*Output*/
    
    	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);
    	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;
    	default:
    		cfsetispeed(&newtio, B9600);
    		cfsetospeed(&newtio, B9600);
    	break;
    	}
    	
    	if( nStop == 1 )
    		newtio.c_cflag &= ~CSTOPB;
    	else if ( nStop == 2 )
    		newtio.c_cflag |= CSTOPB;
    	
    	newtio.c_cc[VMIN]  = 1;  /* 读数据时的最小字节数: 没读到这些数据我就不返回! */
    	newtio.c_cc[VTIME] = 0; /* 等待第1个数据的时间: 
    	                         * 比如VMIN设为10表示至少读到10个数据才返回,
    	                         * 但是没有数据总不能一直等吧? 可以设置VTIME(单位是10秒)
    	                         * 假设VTIME=1,表示: 
    	                         *    10秒内一个数据都没有的话就返回
    	                         *    如果10秒内至少读到了1个字节,那就继续等待,完全读到VMIN个数据再返回
    	                         */
    
    	tcflush(fd,TCIFLUSH);
    	
    	if((tcsetattr(fd,TCSANOW,&newtio))!=0)
    	{
    		perror("com set error");
    		return -1;
    	}
    	//printf("set done!\n");
    	return 0;
    }
    /**********打开设备节点*************/
    int open_port(char *com)
    {
    	int fd;
    	//fd = open(com, O_RDWR|O_NOCTTY|O_NDELAY);
    	fd = open(com, O_RDWR|O_NOCTTY);
        if (-1 == fd){
    		return(-1);
        }
    	
    	  if(fcntl(fd, F_SETFL, 0)<0) /* 设置串口为阻塞状态*/
    	  {
    			printf("fcntl failed!\n");
    			return -1;
    	  }
      
    	  return fd;
    }
    int main(int argc, char **argv)
    {
    	int fd;
    	int iRet;
        char c;
    	/* 1. open */
    
    	/* 2. setup 
    	 * 115200,8N1
    	 * RAW mode
    	 * return data immediately
    	 */
    
    	/* 3. write and read */
    	
    	if (argc != 2)
    	{
    		printf("Usage: \n");
    		printf("%s \n", argv[0]);
    		return -1;
    	}
    
    	fd = open_port(argv[1]);
    	if (fd < 0)
    	{
    		printf("open %s err!\n", argv[1]);
    		return -1;
    	}
    
    	iRet = set_opt(fd, 115200, 8, 'N', 1);
    	if (iRet)
    	{
    		printf("set port err!\n");
    		return -1;
    	}
           printf("Enter a char: ");
    	while (1)
    	{
    	scanf("%c", &c);
    	/*********每次读写一位数据*************/
    		iRet = write(fd, &c, 1);
    		iRet = read(fd, &c, 1);
    		if (iRet == 0)   //读取到数据的时候
    		{
    		  printf("get: %02x %c\n", c, c);
            }
    		
    	}
    
    	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
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171

    GPS模块的应用

    查阅资料我们可以知道GPS模块也是可以通过串口通讯的。
    因此我们这在理简单的运用串口运用GPS模块进行通讯。
    在这里插入图片描述
    这里我们知道对于GPS的串口设置标准。

    GPS模块数据格式

    GPS使用多种标准数据格式,目前最通用的GNSS格式是NMEA0183格式。NMEA0183是最终定位格式,即将二进制定位格式转为统一标准定位格式,与卫星类型无关。这是一套定义接收机输出的标准信息,有几种不同的格式,每种都是独立相关的ASCII格式,逗点隔开数据流,数据流长度从30-100字符不等,通常以每秒间隔持续输出。
    在这里插入图片描述
    其标准格式为:

     $GPGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,M,<10>,M,<11>,<12>*hh
    
    • 1

    在其$上有多重格式,不同的格式代表的其意义不同:

    GPGGA:单GPS
    BDGGA:单北斗
    GLGGA:单GLONASS
    GNGGA:多星联合定位
    
    • 1
    • 2
    • 3
    • 4

    另外其<1>、<2>、<3> … 代表着不同的意思。
    在这里插入图片描述
    本例程使用GPGGA:单GPS模式,显示前五位的数据。即:时间、纬度、南北半球、经度、东西半球。

    GPS代码

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    /* set_opt(fd,115200,8,'N',1) */
    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; 
    	newtio.c_cflag &= ~CSIZE; 
    
    	newtio.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
    	newtio.c_oflag  &= ~OPOST;   /*Output*/
    
    	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);
    	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;
    	default:
    		cfsetispeed(&newtio, B9600);
    		cfsetospeed(&newtio, B9600);
    	break;
    	}
    	
    	if( nStop == 1 )
    		newtio.c_cflag &= ~CSTOPB;
    	else if ( nStop == 2 )
    		newtio.c_cflag |= CSTOPB;
    	
    	newtio.c_cc[VMIN]  = 1;  /* 读数据时的最小字节数: 没读到这些数据我就不返回! */
    	newtio.c_cc[VTIME] = 0; /* 等待第1个数据的时间: 
    	                         * 比如VMIN设为10表示至少读到10个数据才返回,
    	                         * 但是没有数据总不能一直等吧? 可以设置VTIME(单位是10秒)
    	                         * 假设VTIME=1,表示: 
    	                         *    10秒内一个数据都没有的话就返回
    	                         *    如果10秒内至少读到了1个字节,那就继续等待,完全读到VMIN个数据再返回
    	                         */
    
    	tcflush(fd,TCIFLUSH);
    	
    	if((tcsetattr(fd,TCSANOW,&newtio))!=0)
    	{
    		perror("com set error");
    		return -1;
    	}
    	//printf("set done!\n");
    	return 0;
    }
    
    int open_port(char *com)
    {
    	int fd;
    	//fd = open(com, O_RDWR|O_NOCTTY|O_NDELAY);
    	fd = open(com, O_RDWR|O_NOCTTY);
        if (-1 == fd){
    		return(-1);
        }
    	
    	  if(fcntl(fd, F_SETFL, 0)<0) /* 设置串口为阻塞状态*/
    	  {
    			printf("fcntl failed!\n");
    			return -1;
    	  }
      
    	  return fd;
    }
    
    
    int read_gps_raw_data(int fd, char *buf)
    {
    	int i = 0;
    	int iRet;
    	char c;
    	int start = 0;
    	
    	while (1)
    	{
    		iRet = read(fd, &c, 1);
    		if (iRet == 1)
    		{
    			if (c == '$')
    				start = 1;
    			if (start)
    			{
    				buf[i++] = c;
    			}
    			if (c == '\n' || c == '\r')
    				return 0;
    		}
    		else
    		{
    			return -1;
    		}
    	}
    }
    
    /* eg. $GPGGA,082559.00,4005.22599,N,11632.58234,E,1,04,3.08,14.6,M,-5.6,M,,*76" */
    int parse_gps_raw_data(char *buf, char *time, char *lat, char *ns, char *lng, char *ew)
    {
    	char tmp[10];
    	
    	if (buf[0] != '$')
    		return -1;
    	else if (strncmp(buf+3, "GGA", 3) != 0)
    		return -1;
    	else if (strstr(buf, ",,,,,"))
    	{
    		printf("Place the GPS to open area\n");
    		return -1;
    	}
    	else {
    		//printf("raw data: %s\n", buf);
    		sscanf(buf, "%[^,],%[^,],%[^,],%[^,],%[^,],%[^,]", tmp, time, lat, ns, lng, ew);
    		return 0;
    	}
    }
    
    
    /*
     * ./serial_send_recv 
     */
    int main(int argc, char **argv)
    {
    	int fd;
    	int iRet;
    	char c;
    	char buf[1000];
    	char time[100];
    	char Lat[100]; 
    	char ns[100]; 
    	char Lng[100]; 
    	char ew[100];
    
    	float fLat, fLng;
    
    	/* 1. open */
    
    	/* 2. setup 
    	 * 115200,8N1
    	 * RAW mode
    	 * return data immediately
    	 */
    
    	/* 3. write and read */
    	
    	if (argc != 2)
    	{
    		printf("Usage: \n");
    		printf("%s \n", argv[0]);
    		return -1;
    	}
    
    	fd = open_port(argv[1]);
    	if (fd < 0)
    	{
    		printf("open %s err!\n", argv[1]);
    		return -1;
    	}
    
    	iRet = set_opt(fd, 9600, 8, 'N', 1);
    	if (iRet)
    	{
    		printf("set port err!\n");
    		return -1;
    	}
    
    	while (1)
    	{
    		/* eg. $GPGGA,082559.00,4005.22599,N,11632.58234,E,1,04,3.08,14.6,M,-5.6,M,,*76"*/
    		/* read line */
    		iRet = read_gps_raw_data(fd, buf);
    		
    		/* parse line */
    		if (iRet == 0)
    		{
    			iRet = parse_gps_raw_data(buf, time, Lat, ns, Lng, ew);
    		}
    		
    		/* printf */
    		if (iRet == 0)
    		{
    			printf("Time : %s\n", time);
    			printf("ns   : %s\n", ns);
    			printf("ew   : %s\n", ew);
    			printf("Lat  : %s\n", Lat);
    			printf("Lng  : %s\n", Lng);
    
    			/* 纬度格式: ddmm.mmmm */
    			sscanf(Lat+2, "%f", &fLat);
    			fLat = fLat / 60;
    			fLat += (Lat[0] - '0')*10 + (Lat[1] - '0');
    
    			/* 经度格式: dddmm.mmmm */
    			sscanf(Lng+3, "%f", &fLng);
    			fLng = fLng / 60;
    			fLng += (Lng[0] - '0')*100 + (Lng[1] - '0')*10 + (Lng[2] - '0');
    			printf("Lng,Lat: %.06f,%.06f\n", fLng, fLat);
    		}
    	}
    
    	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
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
  • 相关阅读:
    Zookeeper学习笔记(1)—— 基础知识
    Docker基础知识
    instanceOf原理及手动实现
    Android NfcManager 之NFC接入
    设备发现:通向全面网络可见性的途径
    【MySQL篇】授权:授权与回收
    Qt元对象系统 day5
    20哈希表-三数之和
    ubuntu | 安装nvidia驱动
    vue项目中使用vant轮播图组件(桌面端)
  • 原文地址:https://blog.csdn.net/weixin_51914919/article/details/126186116