Linux串口编程总结

来源:互联网 发布:dos启动windows 编辑:程序博客网 时间:2024/06/05 11:38
对串口的操作一般分为四步:打开串口、设置串口、读写串口和关闭串口。Linux下,可以通过设备文件访问串口只需要open()相应的设备文件即可。串口的设备文件名:1.直接连接的串口,串口的设备文件为/dev/ttyS0、/dev/ttyS1等等,S0对应串口1,以此类推。2.通过USB转串口连接,设备文件为/dev/ttyUSB0$dmesg|grep ttyS* 命令可以显示系统串口拔插信息。需要注意的是,普通用户不能打开设备文件。可以提升shell的权限$sudo -s,这样就使shell拥有了超级权限。$sudo su username 可以退回普通权限。
打开串口:{intfd = open( Dev, O_RDWR );         if (-1 == fd){ perror("Can't Open Serial Port");return -1;}elsereturn 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 或者8stopbits 类型  int 停止位   取值为 1 或者2parity  类型  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 0int 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){intfd = open( Dev, O_RDWR );         //| O_NOCTTY | O_NDELAYif (-1 == fd){ /*设置数据位数*/perror("Can't Open Serial Port");return -1;}elsereturn 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);}




参考文章:
  Linux串口编程详解
串口通信编程(linux)
Linux系统下串口接收数据,部分特殊字符丢失的解决方法
0 0
原创粉丝点击