Linux下串口信息的读取

在Linux下串口信息的读取有了一点心得体会。

 

  1. 打开串口

 

  与其他的关于设备编程的方法一样,在Linux下,操作、控制串口也是通过操作起设备文件进行的。在Linux下,串口的设备文件是/dev/ttyS0或/dev/ttyS1等。因此要读写串口,我们首先要打开串口:

 

  char *dev = "/dev/ttyS0"; //串口1

 

  int fd = open( dev, O_RDWR );

 

  //| O_NOCTTY | O_NDELAY

 

  if (-1 == fd)

 

  {

 

  perror("Can't Open Serial Port");

 

  return -1;

 

  }

 

  else

 

  return fd;

 

  2. 设置串口速度

 

  打开串口成功后,我们就可以对其进行读写了。首先要设置串口的波特率:

 

  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) {

 

  tcflush(fd, TCIOFLUSH);

 

  cfsetispeed(&Opt, speed_arr);

 

  cfsetospeed(&Opt, speed_arr);

 

  status = tcsetattr(fd, TCSANOW, &Opt);

 

  if (status != 0) {

 

  perror("tcsetattr fd");

 

  return;

 

  }

 

  tcflush(fd,TCIOFLUSH);

 

  }

 

  }

 

  }

 

  3. 设置串口信息

 

  这主要包括:数据位、停止位、奇偶校验位这些主要的信息。

 

  /**

 

  *@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;

 

  options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/

 

  options.c_oflag &= ~OPOST; /*Output*/

 

  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;

 

  tcflush(fd,TCIFLUSH);

 

  options.c_cc[VTIME] = 0; /* 设置超时0 seconds*/

 

  options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/

 

  if (tcsetattr(fd,TCSANOW,&options) != 0)

 

  {

 

  perror("SetupSerial 3");

 

  return (FALSE);

 

  }

 

  return (TRUE);

 

  }

 

  在上述代码中,有两句话特别重要:

 

  options.c_cc[VTIME] = 0; /* 设置超时0 seconds*/

 

  options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/

 

  这两句话决定了对串口读取的函数read()的一些功能。我将着重介绍一下他们对read()函数的影响。

 

  对串口操作的结构体是

 

  Struct{

 

  tcflag_t c_iflag; /*输入模式标记*/

 

  tcflag_t c_oflag; /*输出模式标记*/

 

  tcflag_t c_cflag; /*控制模式标记*/

 

  tcflag_t c_lflag; /*本地模式标记*/

 

  cc_t c_line; /*线路规程*/

 

  cc_t c_cc[NCCS]; /*控制符号*/

 

  };

 

  其中cc_t c_line只有在一些特殊的系统程序(比如,设置通过tty设备来通信的网络协议)中才会用。在数组c_cc中有两个下标(VTIME和VMIN)对应的元素不是控制符,并且只是在原始模式下有效。只有在原始模式下,他们决定了read()函数在什么时候返回。在标准模式下,除非设置了O_NONBLOCK选项,否则只有当遇到文件结束符或各行的字符都已经编辑完毕后才返回。

 

  控制符VTIME和VMIN之间有着复杂的关系。VTIME定义要求等待的零到几百毫秒的时间量(通常是一个8位的unsigned char变量,取值不能大于cc_t)。VMIN定义了要求等待的最小字节数(不是要求读的字节数——read()的第三个参数才是指定要求读的最大字节数),这个字节数可能是0.

 

  l 如果VTIME取0,VMIN定义了要求等待读取的最小字节数。函数read()只有在读取了VMIN个字节的数据或者收到一个信号的时候才返回。

 

  l 如果VMIN取0,VTIME定义了即使没有数据可以读取,read()函数返回前也要等待几百毫秒的时间量。这时,read()函数不需要像其通常情况那样要遇到一个文件结束标志才返回0.

 

  l 如果VTIME和VMIN都不取0,VTIME定义的是当接收到第一个字节的数据后开始计算等待的时间量。如果当调用read函数时可以得到数据,计时器马上开始计时。如果当调用read函数时还没有任何数据可读,则等接收到第一个字节的数据后,计时器开始计时。函数read可能会在读取到VMIN个字节的数据后返回,也可能在计时完毕后返回,这主要取决于哪个条件首先实现。不过函数至少会读取到一个字节的数据,因为计时器是在读取到第一个数据时开始计时的。

 

  l 如果VTIME和VMIN都取0,即使读取不到任何数据,函数read也会立即返回。同时,返回值0表示read函数不需要等待文件结束标志就返回了。

 

  这就是这两个变量对read函数的影响。我使用的读卡器每次传送的数据是13个字节,一开始,我把它们设置成

 

  options.c_cc[VTIME] = 150

 

  options.c_cc[VMIN] = 0;

 

  结果,每次读取的信息只有8个字节,剩下的5个字节要等到下一次打卡时才能收到。就是由于这个原因造成的。根据上面规则的第一条,我把VTIME取0,VMIN=13,也就是正好等于一次需要接收的字节数。这样就实现了一次读取13个字节值。同时,得出这样的结论,如果读卡器送出的数据为n个字节,那么就把VMIN=n,这样一次读取的信息正好为读卡器送出的信息,并且读取的时候不需要进行循环读取。

 

  4. 读取数据

 

  有了上面的函数后,我设置了串口的基本信息,根据我们自己的实际情况,设置了相应的参数,就可以读取数据了。

 

  void getcardinfo(char *buff){

 

  int fd;

 

  int nread,count=0;

 

  char tempbuff[13];

 

  char *dev = "/dev/ttyS0"; //串口1

 

  fd = OpenDev(dev);

 

  set_speed(fd,9600);

 

  if (set_Parity(fd,8,1,'N') == FALSE) {

 

  printf("Set Parity Error\n");

 

  //return -1;

 

  }

 

  while (1) //循环读取数据

 

  {

 

  count=0;

 

  //sleep(5000);

 

  while(1)

 

  {

 

  if((nread = read(fd, tempbuff, 13))>0)

 

  {

 

  //printf("\nLen %d\n",nread);

 

  memcpy(&buff[count],tempbuff,nread);

 

  count+=nread;

 

  }

 

  if(count==13)

 

  {

 

  buff[count+1] = '\0';

 

  //printf( "\n%s", buff);

 

  break;

 

  }

 

  }

 

  //break;

 

  }

 

  //return buff;

 

  close(fd);

 

  pthread_exit(NULL);

 

  //close(fd);

 

  // exit (0);

 

  }

 

  这是我原来的程序,其实把VMIN设置以后,可以改成:

 

  void getcardinfo(char *buff){

 

  int fd;

 

  int nread,count=0;

 

  char tempbuff[13];

 

  char *dev = "/dev/ttyS0"; //串口1

 

  fd = OpenDev(dev);

 

  set_speed(fd,9600);

 

  if (set_Parity(fd,8,1,'N') == FALSE) {

 

  printf("Set Parity Error\n");

 

  //return -1;

 

  }

 

  nread = read(fd, buff, 13)

 

  close(fd);

 

  }

 

  5. 程序完整代码:

 

  #include /*标准输入输出定义*/

 

  #include /*标准函数库定义*/

 

  #include /*Unix 标准函数定义*/

 

  #include

 

  #include

 

  #include /*文件控制定义*/

 

  #include /*PPSIX 终端控制定义*/

 

  #include /*错误号定义*/

 

  #define FALSE -1

 

  #define TRUE 0

 

  /**

 

  *@brief 设置串口通信速率

 

  *@param fd 类型 int 打开串口的文件句柄

 

  *@param speed 类型 int 串口速度

 

  *@return void

 

  */

 

  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) {

 

  tcflush(fd, TCIOFLUSH);

 

  cfsetispeed(&Opt, speed_arr);

 

  cfsetospeed(&Opt, speed_arr);

 

  status = tcsetattr(fd, TCSANOW, &Opt);

 

  if (status != 0) {

 

  perror("tcsetattr fd");

 

  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;

 

  options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/

 

  options.c_oflag &= ~OPOST; /*Output*/

 

  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;

 

  tcflush(fd,TCIFLUSH);

 

  options.c_cc[VTIME] = 0; /* 设置超时15 seconds*/

 

  options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/

 

  if (tcsetattr(fd,TCSANOW,&options) != 0)

 

  {

 

  perror("SetupSerial 3");

 

  return (FALSE);

 

  }

 

  return (TRUE);

 

  }

 

  /**********************************************************************

 

  代码说明:使用串口一测试的,发送的数据是字符,

 

  但是没有发送字符串结束符号,所以接收到后,后面加上了结束符号

 

  **********************************************************************/

 

  /*********************************************************************/

 

  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;

 

  }

 

  void getcardinfo(char *buff){

 

  int fd;

 

  int nread,count=0;

 

  char tempbuff[13];

 

  char *dev = "/dev/ttyS0"; //串口1

 

  fd = OpenDev(dev);

 

  set_speed(fd,9600);

 

  if (set_Parity(fd,8,1,'N') == FALSE) {

 

  printf("Set Parity Error\n");

 

  //return -1;

 

  }

 

  while (1) //循环读取数据

 

  {

 

  count=0;

 

  //sleep(5000);

 

  while(1)

 

  {

 

  if((nread = read(fd, tempbuff, 13))>0)

 

  {

 

  //printf("\nLen %d\n",nread);

 

  memcpy(&buff[count],tempbuff,nread);

 

  count+=nread;

 

  }

 

  if(count==13)

 

  {

 

  buff[count+1] = '\0';

 

  //printf( "\n%s", buff);

 

  break;

 

  }

 

  }

 

  //break;

 

  }

 

  //return buff;

 

  close(fd);

 

  pthread_exit(NULL);

 

  //close(fd);

 

  // exit (0);

 

  } 本文作者:在Linux下串口信息的读取有了一点心得体会。

 

  1. 打开串口

 

  与其他的关于设备编程的方法一样,在Linux下,操作、控制串口也是通过操作起设备文件进行的。在Linux下,串口的设备文件是/dev/ttyS0或/dev/ttyS1等。因此要读写串口,我们首先要打开串口:

 

  char *dev = "/dev/ttyS0"; //串口1

 

  int fd = open( dev, O_RDWR );

 

  //| O_NOCTTY | O_NDELAY

 

  if (-1 == fd)

 

  {

 

  perror("Can't Open Serial Port");

 

  return -1;

 

  }

 

  else

 

  return fd;

 

  2. 设置串口速度

 

  打开串口成功后,我们就可以对其进行读写了。首先要设置串口的波特率:

 

  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) {

 

  tcflush(fd, TCIOFLUSH);

 

  cfsetispeed(&Opt, speed_arr);

 

  cfsetospeed(&Opt, speed_arr);

 

  status = tcsetattr(fd, TCSANOW, &Opt);

 

  if (status != 0) {

 

  perror("tcsetattr fd");

 

  return;

 

  }

 

  tcflush(fd,TCIOFLUSH);

 

  }

 

  }

 

  }

 

  3. 设置串口信息

 

  这主要包括:数据位、停止位、奇偶校验位这些主要的信息。

 

  /**

 

  *@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;

 

  options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/

 

  options.c_oflag &= ~OPOST; /*Output*/

 

  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;

 

  tcflush(fd,TCIFLUSH);

 

  options.c_cc[VTIME] = 0; /* 设置超时0 seconds*/

 

  options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/

 

  if (tcsetattr(fd,TCSANOW,&options) != 0)

 

  {

 

  perror("SetupSerial 3");

 

  return (FALSE);

 

  }

 

  return (TRUE);

 

  }

 

  在上述代码中,有两句话特别重要:

 

  options.c_cc[VTIME] = 0; /* 设置超时0 seconds*/

 

  options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/

 

  这两句话决定了对串口读取的函数read()的一些功能。我将着重介绍一下他们对read()函数的影响。

 

  对串口操作的结构体是

 

  Struct{

 

  tcflag_t c_iflag; /*输入模式标记*/

 

  tcflag_t c_oflag; /*输出模式标记*/

 

  tcflag_t c_cflag; /*控制模式标记*/

 

  tcflag_t c_lflag; /*本地模式标记*/

 

  cc_t c_line; /*线路规程*/

 

  cc_t c_cc[NCCS]; /*控制符号*/

 

  };

 

  其中cc_t c_line只有在一些特殊的系统程序(比如,设置通过tty设备来通信的网络协议)中才会用。在数组c_cc中有两个下标(VTIME和VMIN)对应的元素不是控制符,并且只是在原始模式下有效。只有在原始模式下,他们决定了read()函数在什么时候返回。在标准模式下,除非设置了O_NONBLOCK选项,否则只有当遇到文件结束符或各行的字符都已经编辑完毕后才返回。

 

  控制符VTIME和VMIN之间有着复杂的关系。VTIME定义要求等待的零到几百毫秒的时间量(通常是一个8位的unsigned char变量,取值不能大于cc_t)。VMIN定义了要求等待的最小字节数(不是要求读的字节数——read()的第三个参数才是指定要求读的最大字节数),这个字节数可能是0.

 

  l 如果VTIME取0,VMIN定义了要求等待读取的最小字节数。函数read()只有在读取了VMIN个字节的数据或者收到一个信号的时候才返回。

 

  l 如果VMIN取0,VTIME定义了即使没有数据可以读取,read()函数返回前也要等待几百毫秒的时间量。这时,read()函数不需要像其通常情况那样要遇到一个文件结束标志才返回0.

 

  l 如果VTIME和VMIN都不取0,VTIME定义的是当接收到第一个字节的数据后开始计算等待的时间量。如果当调用read函数时可以得到数据,计时器马上开始计时。如果当调用read函数时还没有任何数据可读,则等接收到第一个字节的数据后,计时器开始计时。函数read可能会在读取到VMIN个字节的数据后返回,也可能在计时完毕后返回,这主要取决于哪个条件首先实现。不过函数至少会读取到一个字节的数据,因为计时器是在读取到第一个数据时开始计时的。

 

  l 如果VTIME和VMIN都取0,即使读取不到任何数据,函数read也会立即返回。同时,返回值0表示read函数不需要等待文件结束标志就返回了。

 

  这就是这两个变量对read函数的影响。我使用的读卡器每次传送的数据是13个字节,一开始,我把它们设置成

 

  options.c_cc[VTIME] = 150

 

  options.c_cc[VMIN] = 0;

 

  结果,每次读取的信息只有8个字节,剩下的5个字节要等到下一次打卡时才能收到。就是由于这个原因造成的。根据上面规则的第一条,我把VTIME取0,VMIN=13,也就是正好等于一次需要接收的字节数。这样就实现了一次读取13个字节值。同时,得出这样的结论,如果读卡器送出的数据为n个字节,那么就把VMIN=n,这样一次读取的信息正好为读卡器送出的信息,并且读取的时候不需要进行循环读取。

 

  4. 读取数据

 

  有了上面的函数后,我设置了串口的基本信息,根据我们自己的实际情况,设置了相应的参数,就可以读取数据了。

 

  void getcardinfo(char *buff){

 

  int fd;

 

  int nread,count=0;

 

  char tempbuff[13];

 

  char *dev = "/dev/ttyS0"; //串口1

 

  fd = OpenDev(dev);

 

  set_speed(fd,9600);

 

  if (set_Parity(fd,8,1,'N') == FALSE) {

 

  printf("Set Parity Error\n");

 

  //return -1;

 

  }

 

  while (1) //循环读取数据

 

  {

 

  count=0;

 

  //sleep(5000);

 

  while(1)

 

  {

 

  if((nread = read(fd, tempbuff, 13))>0)

 

  {

 

  //printf("\nLen %d\n",nread);

 

  memcpy(&buff[count],tempbuff,nread);

 

  count+=nread;

 

  }

 

  if(count==13)

 

  {

 

  buff[count+1] = '\0';

 

  //printf( "\n%s", buff);

 

  break;

 

  }

 

  }

 

  //break;

 

  }

 

  //return buff;

 

  close(fd);

 

  pthread_exit(NULL);

 

  //close(fd);

 

  // exit (0);

 

  }

 

  这是我原来的程序,其实把VMIN设置以后,可以改成:

 

  void getcardinfo(char *buff){

 

  int fd;

 

  int nread,count=0;

 

  char tempbuff[13];

 

  char *dev = "/dev/ttyS0"; //串口1

 

  fd = OpenDev(dev);

 

  set_speed(fd,9600);

 

  if (set_Parity(fd,8,1,'N') == FALSE) {

 

  printf("Set Parity Error\n");

 

  //return -1;

 

  }

 

  nread = read(fd, buff, 13)

 

  close(fd);

 

  }

 

  5. 程序完整代码:

 

  #include /*标准输入输出定义*/

 

  #include /*标准函数库定义*/

 

  #include /*Unix 标准函数定义*/

 

  #include

 

  #include

 

  #include /*文件控制定义*/

 

  #include /*PPSIX 终端控制定义*/

 

  #include /*错误号定义*/

 

  #define FALSE -1

 

  #define TRUE 0

 

  /**

 

  *@brief 设置串口通信速率

 

  *@param fd 类型 int 打开串口的文件句柄

 

  *@param speed 类型 int 串口速度

 

  *@return void

 

  */

 

  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) {

 

  tcflush(fd, TCIOFLUSH);

 

  cfsetispeed(&Opt, speed_arr);

 

  cfsetospeed(&Opt, speed_arr);

 

  status = tcsetattr(fd, TCSANOW, &Opt);

 

  if (status != 0) {

 

  perror("tcsetattr fd");

 

  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;

 

  options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/

 

  options.c_oflag &= ~OPOST; /*Output*/

 

  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;

 

  tcflush(fd,TCIFLUSH);

 

  options.c_cc[VTIME] = 0; /* 设置超时15 seconds*/

 

  options.c_cc[VMIN] = 13; /* define the minimum bytes data to be readed*/

 

  if (tcsetattr(fd,TCSANOW,&options) != 0)

 

  {

 

  perror("SetupSerial 3");

 

  return (FALSE);

 

  }

 

  return (TRUE);

 

  }

 

  /**********************************************************************

 

  代码说明:使用串口一测试的,发送的数据是字符,

 

  但是没有发送字符串结束符号,所以接收到后,后面加上了结束符号

 

  **********************************************************************/

 

  /*********************************************************************/

 

  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;

 

  }

 

  void getcardinfo(char *buff){

 

  int fd;

 

  int nread,count=0;

 

  char tempbuff[13];

 

  char *dev = "/dev/ttyS0"; //串口1

 

  fd = OpenDev(dev);

 

  set_speed(fd,9600);

 

  if (set_Parity(fd,8,1,'N') == FALSE) {

 

  printf("Set Parity Error\n");

 

  //return -1;

 

  }

 

  while (1) //循环读取数据

 

  {

 

  count=0;

 

  //sleep(5000);

 

  while(1)

 

  {

 

  if((nread = read(fd, tempbuff, 13))>0)

 

  {

 

  //printf("\nLen %d\n",nread);

 

  memcpy(&buff[count],tempbuff,nread);

 

  count+=nread;

 

  }

 

  if(count==13)

 

  {

 

  buff[count+1] = '\0';

 

  //printf( "\n%s", buff);

 

  break;

 

  }

 

  }

 

  //break;

 

  }

 

  //return buff;

 

  close(fd);

 

  pthread_exit(NULL);

 

  //close(fd);

 

  // exit (0);

 

  }

//改为:

//SerialPort.h

#pragma once

#include <string>

#include <termios.h>

#include <functional>

#include <vector>

typedef std::function<void(std::vector<uint8_t>&)> OnReadSerialPortDataCallBack;

class SerialPort

{

public:

SerialPort();

~SerialPort();

bool OpenPort(const std::string &portName, const OnReadSerialPortDataCallBack &cb);

void ClosePort();

int WriteData(const char* buf, int len);

int set_port_attr(int fd,

int baudrate, // B1200 B2400 B4800 B9600 .. B115200

int databit, // 5, 6, 7, 8

const char *stopbit, // "1", "1.5", "2"

char parity, // N(o), O(dd), E(ven)

int vtime=20,

int vmin=2);

private:

void set_baudrate(struct termios *opt, unsigned int baudrate);

void set_data_bit(struct termios *opt, unsigned int databit);

void set_parity(struct termios *opt, char parity);

void set_stopbit(struct termios *opt, const char *stopbit);

void OnReadData();

private:

int mFd;

bool mbExit = false;

OnReadSerialPortDataCallBack mCallBack;

};

// SerialPort.cpp

#include "SerialPort.h"

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <fcntl.h>

#include <unistd.h>

#include <thread>

SerialPort::SerialPort()

{

std::thread t(&SerialPort::ReadData, this);

t.detach();

}

SerialPort::~SerialPort()

{

ClosePort();

mbExit = true;

}

bool SerialPort::OpenPort(const std::string &portName, const OnReadSerialPortDataCallBack &cb)

{

mCallBack = cb;

mFd = open(portName.c_str(), O_RDWR);

if (mFd < 0)

{

printf("open port fail, portName:%s\n", portName.c_str());

return false;

}

return true;

}

void SerialPort::ClosePort()

{

if (mFd >= 0)

{

close(mFd);

mFd = -1;

}

}

int SerialPort::set_port_attr(

int fd,

int baudrate, // B1200 B2400 B4800 B9600 .. B115200

int databit, // 5, 6, 7, 8

const char *stopbit, // "1", "1.5", "2"

char parity, // N(o), O(dd), E(ven)

int vtime, // // 等待数据时间(10秒的倍数),每个单位是0.1秒 若20就是2秒

int vmin) // // 最少可读数据,非规范模式读取时的最小字符数,设为0则为非阻塞,如果设为其它值则阻塞,

// 直到读到到对应的数据,就像一个阀值一样,比如设为8,如果只接收到3个数据,那么它是不会返回的,只有凑齐8个数据后一齐才READ返回,阻塞在那儿

{

struct termios opt; //配置串口的属性定义在结构体struct termios中

tcgetattr(mFd, &opt); //获取终端控制属性

//设置波特率

set_baudrate(&opt, baudrate);

opt.c_cflag |= CLOCAL | CREAD; /* | CRTSCTS */

//设置数据位

set_data_bit(&opt, databit);

//设置校验位

set_parity(&opt, parity);

//设置停止位

set_stopbit(&opt, stopbit);

//其它设置

opt.c_oflag = 0;

opt.c_lflag |= 0;

opt.c_oflag &= ~OPOST;

opt.c_cc[VTIME] = vtime;

opt.c_cc[VMIN] = vmin;

/* TCIFLUSH 刷清输入队列

TCOFLUSH 刷清输出队列

TCIOFLUSH 刷清输入、输出队列 */

tcflush(fd, TCIOFLUSH); // //刷串口清缓存

return (tcsetattr(mFd, TCSANOW, &opt)); // //设置终端控制属性,TCSANOW:不等数据传输完毕就立即改变属性

}

/**

* @brief set_baudrate 设置波特率

* @param opt

* @param baudrate

*/

void SerialPort::set_baudrate(struct termios *opt, unsigned int baudrate)

{

cfsetispeed(opt, baudrate);

cfsetospeed(opt, baudrate);

}

/**

* @brief set_data_bit 设置数据位

* @param opt

* @param databit

*/

void SerialPort::set_data_bit(struct termios *opt, unsigned int databit)

{

opt->c_cflag &= ~CSIZE;

switch (databit) {

case 8:

opt->c_cflag |= CS8;

break;

case 7:

opt->c_cflag |= CS7;

break;

case 6:

opt->c_cflag |= CS6;

break;

case 5:

opt->c_cflag |= CS5;

break;

default:

opt->c_cflag |= CS8;

break;

}

}

/**

* @brief set_parity 设置奇偶校验位

* @param opt

* @param parity

*/

void SerialPort::set_parity(struct termios *opt, char parity)

{

switch (parity) {

case 'N': /* no parity check */

opt->c_cflag &= ~PARENB;

break;

case 'E': /* even */

opt->c_cflag |= PARENB;

opt->c_cflag &= ~PARODD;

break;

case 'O': /* odd */

opt->c_cflag |= PARENB;

opt->c_cflag |= ~PARODD;

break;

default: /* no parity check */

opt->c_cflag &= ~PARENB;

break;

}

}

/**

* @brief set_stopbit 设置停止位

* @param opt

* @param stopbit

*/

void SerialPort::set_stopbit(struct termios *opt, const char *stopbit)

{

if (0 == strcmp(stopbit, "1")) {

opt->c_cflag &= ~CSTOPB; /* 1 stop bit */

}

else if (0 == strcmp(stopbit, "1")) {

opt->c_cflag &= ~CSTOPB; /* 1.5 stop bit */

}

else if (0 == strcmp(stopbit, "2")) {

opt->c_cflag |= CSTOPB; /* 2 stop bits */

}

else {

opt->c_cflag &= ~CSTOPB; /* 1 stop bit */

}

}

int SerialPort::WriteData(const char* buf, int len)

{

if (mFd < 0) return -1;

int totalLen = len;

int wtLen = 0;

while (totalLen > 0)

{

int n = write(mFd, buf+ wtLen, totalLen);

totalLen -= n;

wtLen += n;

}

//fflush(&mFd);

return wtLen;

}

void SerialPort::OnReadData()

{

uint8_t dataBuf[1024 * 500] = { 0 };

while (!mbExit)

{

int len = read(mFd, dataBuf, sizeof(dataBuf));

if (len > 0)

{

if (!mCallBack)

{

std::vector<uint8_t> vec(dataBuf, dataBuf + len);

mCallBack(vec);

}

}

}

}

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux系统下串口编程指的是在Linux操作系统中使用程序来控制串口设备,通常可以用于与串口设备进行通信。 要在Linux系统中进行串口编程,需要使用特定的API(应用程序编程接口)。常用的API包括POSIX的termios库和Linux专有的serial API。 使用termios库的步骤如下: 1. 使用open函数打开串口设备文件,例如"/dev/ttyS0"。 2. 使用tcgetattr函数获取串口的属性。 3. 使用修改串口属性的函数(如cfsetispeed和cfsetospeed)设置串口的输入/输出波特率。 4. 使用tcsetattr函数将修改后的属性设置回串口。 5. 使用read和write函数进行串口数据读写。 6. 使用close函数关闭串口。 使用serial API的步骤如下: 1. 使用open函数打开串口设备文件,例如"/dev/ttyS0"。 2. 使用ioctl函数设置串口的属性,包括波特率、数据位、停止位和校验方式等。 3. 使用read和write函数进行串口数据读写。 4. 使用close函数关闭串口。 在编写串口程序时,还需要注意其他问题,如确保串口设备文件有足够的权限访问、处理串口通信中 ### 回答2: Linux串口编程是通过使用系统调用函数来实现的。在Linux系统中,串口设备可以被看作是一种特殊的文件,通过打开和操作该文件,我们可以进行串口通信操作。 在Linux中,常用的串口编程函数是open、close、read、write和ioctl等。我们首先需要使用open函数来打开串口设备文件,例如/dev/ttyS0。打开成功后,我们可以使用read函数从串口读取数据,使用write函数向串口写入数据,使用close函数关闭串口设备。 除了基本的读写操作,我们还可以使用ioctl函数来设置和获取串口的属性。通过设置串口的波特率、数据位、校验位、停止位等参数,我们可以实现不同的串口通信需求。一般情况下,我们需要使用termios结构体来保存和配置串口属性,然后通过ioctl函数来设置。 另外,Linux下还提供了一种轮询和一种中断的方式来进行串口通信。轮询方式是通过不断地查询串口是否有数据到达,然后进行读取。中断方式是通过设置串口的中断响应函数,当有数据到达时,会触发中断并执行相应的处理函数。 总之,Linux串口编程是一种通过打开、读写和ioctl等系统调用函数来操作串口设备文件的方法。通过设置串口属性,我们可以实现不同的串口通信需求。 ### 回答3: Linux下的串口编程是指在Linux操作系统下对串行端口(串口)进行数据输入输出的一种编程技术。串口编程通常用于连接外部设备与计算机之间进行数据交互。 在Linux下,串口设备被视作特殊的文件,通过打开和读写这些特殊文件来对串口进行操作。常用的串口编程接口有标准C库的系统调用,如open()、read()、write()等,也可以使用Linux提供的串口库函数,如termios库函数。 串口编程的基本流程如下: 1. 打开串口设备文件,使用open()函数,得到串口文件描述符。 2. 配置串口参数,使用termios库函数,设置波特率、数据位、停止位、奇偶校验等参数。 3. 读取或写入串口数据,使用read()或write()函数进行数据交互。 4. 关闭串口设备,使用close()函数关闭串口文件描述符。 在进行串口编程时,需要注意以下几点: - 波特率、数据位、停止位、奇偶校验等参数需要与外设保持一致。 - 需要使用合适的访问权限打开串口设备文件。 - 需要适当处理读写时可能发生的错误和异常。 - 可以使用非阻塞模式进行读写,以提高程序的响应性。 通过Linux下的串口编程,可以实现与各种外设的通信,如传感器、控制器、无线模块等。它被广泛应用于嵌入式系统、物联网设备、机器人等领域,具有较高的灵活性和扩展性。同时,针对不同需求,还可以使用一些开源的串口通信库,如pyserial、libserial等,简化串口编程的过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值