Linux串口编程总结

对串口的操作一般分为四步:打开串口、设置串口、读写串口和关闭串口。Linux下,可以通过设备文件访问串口只需要open()相应的设备文件即可。
串口的设备文件名:
1.直接连接的串口,串口的设备文件为/dev/ttyS0、/dev/ttyS1等等,S0对应串口1,以此类推。
2.通过USB转串口连接,设备文件为/dev/ttyUSB0
$dmesg|grep ttyS* 命令可以显示系统串口拔插信息。
需要注意的是,普通用户不能打开设备文件。可以提升shell的权限$sudo -s,这样就使shell拥有了超级权限。$sudo su username 可以退回普通权限。
打开串口:
{
int	fd = open( Dev, O_RDWR );         
	if (-1 == fd)
		{ 
			perror("Can't Open Serial Port");
			return -1;
		}
	else
	return fd;

}
设置串口:
设置串口的关键在于termios结构体的设置。termios的详细文档在这个链接
termios函数族提供了一个常规的终端接口,用于控制非同步通信端口。这个结构体包含了至少下列成员: 
tcflag_t c_iflag;      /* 输入模式 */
tcflag_t c_oflag;      /* 输出模式 */ 
tcflag_t c_cflag;      /* 控制模式 */ 
tcflag_t c_lflag;      /* 本地模式 */ 
cc_t c_cc[NCCS];       /* 控制字符 */
设置串口通信速率:
/*  设置串口通信速率
fd     类型 int  打开串口的文件句柄
speed  类型 int  串口速度
*/

int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
	    B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300,
	    38400,  19200,  9600, 4800, 2400, 1200,  300, };
void set_speed(int fd, int speed)
{
  int   i;
  int   status;
  struct termios   Opt;
  tcgetattr(fd, &Opt);
  for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
   {
   	if  (speed == name_arr[i])
   	{
   	    tcflush(fd, TCIOFLUSH);
    	cfsetispeed(&Opt, speed_arr[i]);
    	cfsetospeed(&Opt, speed_arr[i]);
    	status = tcsetattr(fd, TCSANOW, &Opt);
    	if  (status != 0)
            perror("tcsetattr fd1");
     	return;
     	}
   tcflush(fd,TCIOFLUSH);
   }
}
设置串口停止位、数据位、校验位:
/*
设置串口数据位,停止位和效验位
fd     类型  int  打开的串口文件句柄
databits 类型  int 数据位   取值 为 7 或者8
stopbits 类型  int 停止位   取值为 1 或者2
parity  类型  int  效验类型 取值为N,E,O,,S
*/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
	struct termios options;
 if  ( tcgetattr( fd,&options)  !=  0)
  {
  	perror("SetupSerial 1");
  	return(FALSE);
  }
  options.c_cflag &= ~CSIZE;
  switch (databits) /*设置数据位数*/
  {
  	case 7:
  		options.c_cflag |= CS7;
  		break;
  	case 8:
		options.c_cflag |= CS8;
		break;
	default:
		fprintf(stderr,"Unsupported data size\n");
		return (FALSE);
	}
  switch (parity)
  	{
  	case 'n':
	case 'N':
		options.c_cflag &= ~PARENB;   /* Clear parity enable */
		options.c_iflag &= ~INPCK;     /* Enable parity checking */
		break;
	case 'o':
	case 'O':
		options.c_cflag |= (PARODD | PARENB);  /* 设置为奇效验*/ 
		options.c_iflag |= INPCK;             /* Disnable parity checking */
		break;
	case 'e':
	case 'E':
		options.c_cflag |= PARENB;     /* Enable parity */
		options.c_cflag &= ~PARODD;   /* 转换为偶效验*/  
		options.c_iflag |= INPCK;       /* Disnable parity checking */
		break;
	case 'S':
	case 's':  /*as no parity*/
		options.c_cflag &= ~PARENB;
		options.c_cflag &= ~CSTOPB;
		break;
	default:
		fprintf(stderr,"Unsupported parity\n");
		return (FALSE);
		}
  /* 设置停止位*/   
  switch (stopbits)
  	{
  	case 1:
  		options.c_cflag &= ~CSTOPB;
		break;
	case 2:
		options.c_cflag |= CSTOPB;
		break;
	default:
		fprintf(stderr,"Unsupported stop bits\n");
		return (FALSE);
	}
  /* Set input parity option */
  if (parity != 'n')
  		options.c_iflag |= INPCK;
    options.c_cc[VTIME] = 150; // 15 seconds
    options.c_cc[VMIN] = 0;

  tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */
  if (tcsetattr(fd,TCSANOW,&options) != 0)
  	{
  		perror("SetupSerial 3");
		return (FALSE);
	}
  return (TRUE);
 }
读写串口:
 while(1)
  	{
   		while((nread = read(fd,buff,512))>0)
   		{
		<span style="white-space:pre">	</span>for(i=0;i<nread;i++)
			{
				printf("%3x",buff[i]);
			}
      	         <span style="white-space:pre">	</span>printf("\n");
  		 <span style="white-space:pre">	</span>write(fd,buff,nread);
   	 	}
  	}
关闭串口:
close(fd)
在编程过程中出现的问题:
1.多串口同时读写操作。
多串口读写需要在打开设备文件后设置为非阻塞型读写。
fd = OpenDev(dev);
fcntl(fd,F_SETFL,FNDELAY);      //非阻塞
2.发送0x00~0xff全部十六进制数。
(1)收到05 06 07 08 09 0a
修改终端I/O的输入处理模式为非规范输入方式。规范方式输入处理中,终端输入以行为单位进行处理,0x0a即'/n',相当于按下了enter键,本行才输出。
Opt.c_lflag &= ~(ICANON);          //非规范方式输入
(2)收到05 06 07 08 09 0a 0b 0c 0a 0e ...
0x05之前的数据都相当于特殊的控制字符,所以要去除。
Opt.c_lflag &= ~(ICANON|ISIG);          //非规范方式输入、去除ISIG
(3)收到01 02 03 ... 0a 0b 0c 0a 0e 0f...
window下的回车编码为“0x0d 0x0a”,而Linux下的回车编码为“0x0a“。所以要取消回车与新行符的映射。
 Opt.c_iflag &= ~(INLCR|ICRNL|IGNCR);//取消映射 关闭软件流控制
 Opt.c_oflag &= ~(ONLCR|OCRNL);          //取消映射
(4)0x11、0x13收不到
0x11和0x13是软件流控制符。
 Opt.c_iflag &= ~(INLCR|ICRNL|IGNCR|IXON|IXOFF);//取消映射 关闭软件流控制
(5)从0x80开始往后,数据printf为ffffff80 。定义字符是应定义为无符号型。
//	char buff[512];
        unsigned char buff[512];
(6)完整接收0x00到0xff。
3.串口read一次最多读8个字节。
与串口的缓存有关,一般一次最多缓存8个字节。

最后附上完整的双串口读写程序:
#include     <stdio.h>      /*标准输入输出定义*/
#include     <stdlib.h>     /*标准函数库定义*/
#include     <unistd.h>     /*Unix标准函数定义*/
#include     <sys/types.h>  /**/
#include     <sys/stat.h>   /**/
#include     <fcntl.h>      /*文件控制定义*/
#include     <termios.h>    /*PPSIX终端控制定义*/
#include     <errno.h>      /*错误号定义*/

/***@brief  设置串口通信速率
*@param  fd     类型 int  打开串口的文件句柄
*@param  speed  类型 int  串口速度
*@return  void*/

#define FALSE -1
#define TRUE 0

int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
	    B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300,
	    38400,  19200,  9600, 4800, 2400, 1200,  300, };
void set_speed(int fd, int speed)
{
  int   i;
  int   status;
  struct termios   Opt;
  tcgetattr(fd, &Opt);

 Opt.c_lflag &= ~(ICANON|ISIG);          //非规范方式输入
 Opt.c_iflag &= ~(INLCR|ICRNL|IGNCR);
 Opt.c_oflag &= ~(ONLCR|OCRNL);          //取消映射



  for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
   {
   	if  (speed == name_arr[i])
   	{
   	    tcflush(fd, TCIOFLUSH);
    	cfsetispeed(&Opt, speed_arr[i]);
    	cfsetospeed(&Opt, speed_arr[i]);
    	status = tcsetattr(fd, TCSANOW, &Opt);
    	if  (status != 0)
            perror("tcsetattr fd1");
     	return;
     	}
   tcflush(fd,TCIOFLUSH);
   }
}
/**
*@brief   设置串口数据位,停止位和效验位
*@param  fd     类型  int  打开的串口文件句柄*
*@param  databits 类型  int 数据位   取值 为 7 或者8*
*@param  stopbits 类型  int 停止位   取值为 1 或者2*
*@param  parity  类型  int  效验类型 取值为N,E,O,,S
*/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
	struct termios options;

 if  ( tcgetattr( fd,&options)  !=  0)
  {
  	perror("SetupSerial 1");
  	return(FALSE);
  }
  options.c_cflag &= ~CSIZE;
  switch (databits) /*设置数据位数*/
  {
  	case 7:
  		options.c_cflag |= CS7;
  		break;
  	case 8:
		options.c_cflag |= CS8;
		break;
	default:
		fprintf(stderr,"Unsupported data size\n");
		return (FALSE);
	}
  switch (parity)
  	{
  	case 'n':
	case 'N':
		options.c_cflag &= ~PARENB;   /* Clear parity enable */
		options.c_iflag &= ~INPCK;     /* Enable parity checking */
		break;
	case 'o':
	case 'O':
		options.c_cflag |= (PARODD | PARENB);  /* 设置为奇效验*/ 
		options.c_iflag |= INPCK;             /* Disnable parity checking */
		break;
	case 'e':
	case 'E':
		options.c_cflag |= PARENB;     /* Enable parity */
		options.c_cflag &= ~PARODD;   /* 转换为偶效验*/  
		options.c_iflag |= INPCK;       /* Disnable parity checking */
		break;
	case 'S':
	case 's':  /*as no parity*/
		options.c_cflag &= ~PARENB;
		options.c_cflag &= ~CSTOPB;
		break;
	default:
		fprintf(stderr,"Unsupported parity\n");
		return (FALSE);
		}
  /* 设置停止位*/   
  switch (stopbits)
  	{
  	case 1:
  		options.c_cflag &= ~CSTOPB;
		break;
	case 2:
		options.c_cflag |= CSTOPB;
		break;
	default:
		fprintf(stderr,"Unsupported stop bits\n");
		return (FALSE);
	}
  /* Set input parity option */
  if (parity != 'n')
  		options.c_iflag |= INPCK;
    options.c_cc[VTIME] = 150; // 15 seconds
    options.c_cc[VMIN] = 0;

  tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */
  if (tcsetattr(fd,TCSANOW,&options) != 0)
  	{
  		perror("SetupSerial 3");
		return (FALSE);
	}



/*	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
        options.c_iflag &= ~(INLCR | ICRNL | IGNCR);
        options.c_oflag &= ~(ONLCR | OCRNL);

*/



  return (TRUE);
 }
/**
*@breif 打开串口
*/
int OpenDev(char *Dev)
{
int	fd = open( Dev, O_RDWR );         //| O_NOCTTY | O_NDELAY
	if (-1 == fd)
		{ /*设置数据位数*/
			perror("Can't Open Serial Port");
			return -1;
		}
	else
	return fd;

}
/**
*@breif 	main()
*/
int main(int argc, char **argv)
{
	int fd_1,fd_2;
	int i;
	int nread_1,nread_2;
	char buff_1[512],buff_2[512];
	char *dev_1="/dev/ttyS0";
	char *dev_2="/dev/ttyUSB0";
	fd_1 = OpenDev(dev_1);
	fd_2 = OpenDev(dev_2);

	fcntl(fd_1,F_SETFL,FNDELAY);
	fcntl(fd_2,F_SETFL,FNDELAY);   //设置为非阻塞型	


	if (fd_1>0)
	set_speed(fd_1,19200);
	else
	{
		printf("Can't Open Serial Port 1!\n");
		exit(0);
	}
	if (set_Parity(fd_1,8,1,'N')== FALSE)
 	 {
   		 printf("Set Parity 1 Error\n");
   	 	exit(1);
 	 }

	if (fd_2>0)
        set_speed(fd_2,19200);
        else
        {
                printf("Can't Open Serial Port 2!\n");
                exit(0);
        }
        if (set_Parity(fd_2,8,1,'N')== FALSE)
         {
                 printf("Set Parity 2 Error\n");
                exit(1);
         }

 	 while(1)
  	{
		while((nread_1 = read(fd_1,buff_1,512))>0)
   		{
      		/*printf("\nLen %d\n",nread);
      		buff[nread+1]='\0';
      		printf("\n%s",buff);*/

		
		printf("Serial_Port_1:%d\n",nread_1);
		for(i=0;i<nread_1;i++)
		{
	//		printf("%d\n",nread);
	//		printf("%d\n",i);
			printf("%3x",buff_1[i]);
		}
		printf("\n");
		write(fd_1,buff_1,nread_1);
   	 	}

		while((nread_2 = read(fd_2,buff_2,512))>0)
		{
		printf("Serial_Port_2:%d\n",nread_2);
		for(i=0;i<nread_2;i++)
		{
			printf("%3x",buff_2[i]);
		}
		printf("\n");
		write(fd_2,buff_2,nread_2);
		}
  	}
    //close(fd);
    //exit(0);
}




参考文章:
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页