Linux C++ 串口编程 详解+实例

Linux C++ 串口编程 详解+实例

大致步骤

Linux下的串口编程其实与Windows下无本质差别,说白了就是类似读写文件一样,对串口进行读写操作,用到的函数无非就是open,close,read,write函数等。具体的读写操作可分为下面四个步骤

  1. 打开串口
  2. 配置串口
  3. 读写串口
  4. 关闭串口

打开串口

打开串口使用的是open函数,后缀第一个表示读写,第二个表示不允许进程干涉串口的读写操作(不太明白,实测发现终端输入会导致读写出错),第三个表示非阻塞。打开串口默认都是阻塞形式,使用标志位O_NDELAY可以设置为非阻塞模式,或者使用系统函数fcntl进行设置。
fd = open(“/dev/ttyS*”, O_RDWR | O_NOCTTY | O_NDELAY);
open可以接受的标志位如下:
O_RDONLY只读模式
O_WRONLY只写模式
O_RDWR读写模式
上述三个标志位必选其一
………………………………………………………………………………………………………………………………………………….
O_APPEND每次写操作都写入文件的末尾
O_CREAT如果指定文件不存在,则创建这个文件
O_EXCL如果要创建的文件已存在,则返回 -1,并且修改 errno 的值
O_TRUNC如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
O_NOCTTY如果路径名指向终端设备,不要把这个设备用作控制终端。
O_NONBLOCK如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)
下面三个常量同样是选用的,他们用于同步输入输出
O_DSYNC等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。
O_RSYNC读(read)等待所有写入同一区域的写操作完成后再进行
O_SYNC等待物理 I/O 结束后再 write,包括更新文件属性的 I/O
对于串口的打开操作,必须使用O_NOCTTY参数,它表示打开的是一个终端设备,程序不会成为该端口的控制终端。如果不使用此标志,任务的一个输入(比如键盘终止信号等)都会影响进程。
O_NDELAY表示不关心DCD信号所处的状态(端口的另一端是否激活或者停止)
打开串口后的句柄 “fd” 即代表了此串口,后续操作都是对句柄进行。

配置串口

串口配置主要包括波特率的配置,校验位,停止位的设置等等。具体如下:

 struct termios Opt;       //创建设置结构体,配置串口即改变结构体内元素的内容
 tcgetattr(fd, &Opt);      //保存原有的配置,保存在Opt中
 tcflush(fd, TCIOFLUSH);   //刷清输入输出队列
 cfsetispeed(&Opt, B9600); //设置输入波特率
 cfsetospeed(&Opt, B9600); //设置输出波特率

波特率可以设置115200,9600等等,值得注意的是在设置波特率的时候要在前面加B。接下来设置停止位,校验位等等,依旧使用上述创建的结构体:

Opt.c_cflag &= ~CSIZE;   //屏蔽数据位
Opt.c_cflag  |= CS8; // 数据位为 8CS7 for 7 
Opt.c_cflag &= ~CSTOPB;  // 一位停止位, 两位停止为 |= CSTOPB
Opt.c_cflag &= ~PARENB;  // 无校验
Opt.c_cflag |= PARENB;   //有校验
Opt.c_cflag &= ~PARODD;   // 偶校验
Opt.c_cflag |=  PARODD;   // 奇校验

接下来设置一些时间参数:

Opt.c_cc[VTIME] = 1;  //设置等待时间
Opt.c_cc[VMIN] = 100;     //设置最小字节数

时间参数只有在设置为阻塞模式下才能使用,VMIN代表读取的最小字节数,当VTIME参数设置为0时,只有当串口内的数据达到VMIN长度时,read函数才会进行读取操作;
当时间参数VTIME不为0时,若串口内到达的数据未到达VMIN,read函数将等待VTIME时间之后对数据进行读取;

另一种监测串口是否有数据到达的函数是select,使用此参数可以在确定串口内有数据时才对串口数据进行读写。

FD_ZERO(&rfds);
FD_SET(fd,&rfds);
timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
retval =select(fd+1,&rfds,NULL,NULL,NULL);

上述代码通过设置句柄rfds来对fd进行监控,并根据fd的状态,select函数将返回不同的值。其中timeout为超时等待时间,若不希望函数阻塞可以写为:

retval =select(fd+1,&rfds,NULL,NULL,&timeout);

读写串口

读函数:

res = read(fd,buf,len);

写函数:

n = write(fd, "str", len);

注意,当读写出错是函数将返回值-1。

关闭串口

关闭串口简单粗暴:

close(fd);

程序实例

bool connectDevice(){
  fd = open( SerialPort, O_RDWR|/*O_NOCTTY|*/O_NDELAY);
  if (-1 == fd)
  {
     ROS_WARN("Redsky UART:Can't Open Serial Port");
     return(-1);
  }
  // flushing is to be done after opening. This prevents first read and write to be spam'ish.
  tcflush(fd, TCIOFLUSH);
  int n = fcntl(fd, F_GETFL, 0);
  //恢复为阻塞模式
  fcntl(fd, F_SETFL, n & ~O_NDELAY);
  portSet(fd,9600,8,"None","1",false,false);
  connected = true;
  return 1;
}
int portSet(int m_fd, int baudrate, int databits, const std::string & parity, const std::string & stop, bool softwareHandshake, bool hardwareHandshake)
{
   struct termios newtio;
//  memset(&newtio, 0, sizeof(newtio));
   if (tcgetattr(m_fd, &newtio)!=0)
   {
      //std::cerr<<"tcgetattr() 3 failed"<<std::endl;
      return -1;
   }
   //printf("tcgetattr() 3 success!\n");

   speed_t _baud=0;
   switch (baudrate)
   {
  #ifdef B0
     case      0: _baud=B0;     break;
  #endif

  #ifdef B50
     case     50: _baud=B50;    break;
  #endif
  #ifdef B75
     case     75: _baud=B75;    break;
  #endif
  #ifdef B110
     case    110: _baud=B110;   break;
  #endif
  #ifdef B134
     case    134: _baud=B134;   break;
  #endif
  #ifdef B150
     case    150: _baud=B150;   break;
  #endif
  #ifdef B200
     case    200: _baud=B200;   break;
  #endif
  #ifdef B300
     case    300: _baud=B300;   break;
  #endif
  #ifdef B600
     case    600: _baud=B600;   break;
  #endif
  #ifdef B1200
     case   1200: _baud=B1200;  break;
  #endif
  #ifdef B1800
     case   1800: _baud=B1800;  break;
  #endif
  #ifdef B2400
     case   2400: _baud=B2400;  break;
  #endif
  #ifdef B4800
     case   4800: _baud=B4800;  break;
  #endif
  #ifdef B7200
     case   7200: _baud=B7200;  break;
  #endif
  #ifdef B9600
     case   9600: _baud=B9600;  break;
  #endif
  #ifdef B14400
     case  14400: _baud=B14400; break;
  #endif
  #ifdef B19200
     case  19200: _baud=B19200; break;
  #endif
  #ifdef B28800
     case  28800: _baud=B28800; break;
  #endif
  #ifdef B38400
     case  38400: _baud=B38400; break;
  #endif
  #ifdef B57600
     case  57600: _baud=B57600; break;
  #endif
  #ifdef B76800
     case  76800: _baud=B76800; break;
  #endif
  #ifdef B115200
     case 115200: _baud=B115200; break;
  #endif
  #ifdef B128000
     case 128000: _baud=B128000; break;
  #endif
  #ifdef B230400
     case 230400: _baud=B230400; break;
  #endif
  #ifdef B460800
     case 460800: _baud=B460800; break;
  #endif
  #ifdef B576000
     case 576000: _baud=B576000; break;
  #endif
  #ifdef B921600
     case 921600: _baud=B921600; break;
  #endif
   default:
//   case 256000:
//      _baud=B256000;
      break;
   }
   cfsetospeed(&newtio, (speed_t)_baud);
   cfsetispeed(&newtio, (speed_t)_baud);

   /* We generate mark and space parity ourself. */
   if (databits == 7 && (parity=="Mark" || parity == "Space"))
   {
      databits = 8;
   }
   switch (databits)
   {
   case 5:
      newtio.c_cflag = (newtio.c_cflag & ~CSIZE) | CS5;
      break;
   case 6:
      newtio.c_cflag = (newtio.c_cflag & ~CSIZE) | CS6;
      break;
   case 7:
      newtio.c_cflag = (newtio.c_cflag & ~CSIZE) | CS7;
      break;
   case 8:
   default:
      newtio.c_cflag = (newtio.c_cflag & ~CSIZE) | CS8;
      break;
   }
   newtio.c_cflag |= CLOCAL | CREAD;

   //parity
   newtio.c_cflag &= ~(PARENB | PARODD);
   if (parity == "Even")
   {
      newtio.c_cflag |= PARENB;
   }
   else if (parity== "Odd")
   {
      newtio.c_cflag |= (PARENB | PARODD);
   }

   //hardware handshake
  /*   if (hardwareHandshake)
      newtio.c_cflag |= CRTSCTS;
   else
      newtio.c_cflag &= ~CRTSCTS;*/
   newtio.c_cflag &= ~CRTSCTS;

   //stopbits
   if (stop=="2")
   {
      newtio.c_cflag |= CSTOPB;
   }
   else
   {
      newtio.c_cflag &= ~CSTOPB;
   }

   //   newtio.c_iflag=IGNPAR | IGNBRK;
   newtio.c_iflag=IGNBRK;
   //   newtio.c_iflag=IGNPAR;

   //software handshake
   if (softwareHandshake)
   {
      newtio.c_iflag |= IXON | IXOFF;
   }
   else
   {
      newtio.c_iflag &= ~(IXON|IXOFF|IXANY);
   }

   newtio.c_lflag=0;
   newtio.c_oflag=0;

   newtio.c_cc[VTIME]=1;
   newtio.c_cc[VMIN]=60;

//   tcflush(m_fd, TCIFLUSH);
   if (tcsetattr(m_fd, TCSANOW, &newtio)!=0)
   {
      std::cerr<<"tcsetattr() 1 failed"<<std::endl;
   }

   int mcs=0;
   ioctl(m_fd, TIOCMGET, &mcs);
   mcs |= TIOCM_RTS;
   ioctl(m_fd, TIOCMSET, &mcs);

   if (tcgetattr(m_fd, &newtio)!=0)
   {
      //std::cerr<<"tcgetattr() 4 failed"<<std::endl;
   }

   //hardware handshake
   if (hardwareHandshake)
   {
      newtio.c_cflag |= CRTSCTS;
   }
   else
   {
      newtio.c_cflag &= ~CRTSCTS;
   }
/*  if (on)
     newtio.c_cflag |= CRTSCTS;
  else
     newtio.c_cflag &= ~CRTSCTS;*/
   if (tcsetattr(m_fd, TCSANOW, &newtio)!=0)
   {
      //std::cerr<<"tcsetattr() 2 failed"<<std::endl;
   }
   //printf("tcsetattr() 2 success!\n");
   return 1;
}
int readRev(char* revBuf){
  //使用select设置阻塞时间
  fd_set rfds;
  FD_ZERO(&rfds);
  FD_SET(fd,&rfds);
  timeval timeout;
  timeout.tv_sec = 1;  //秒
  timeout.tv_usec = 0;  //微秒
  //int retval = select(fd+1, &rfds, NULL, NULL, &timeout);
  int retval = select(fd+1, &rfds, NULL, NULL, &timeout);

  if(retval <= 0){
    ROS_WARN("No data receive from port, select error");
    return -1;//"noData";
  }
  else{
    int len = read(fd, revBuf, MAXBUF);
  }
}

猜你喜欢

转载自blog.csdn.net/qq_34561506/article/details/74080678