使用select()和fgets()从串行端口访问信息

Using select() and fgets() to access information from a serial port

本文关键字:串行端口 访问 信息 select 使用 fgets      更新时间:2023-10-16

这是这个问题的后续问题:如何在程序中间等待串口的输入

我正在编写一个程序来控制一个铱调制解调器,该调制解调器需要在程序中间等待串行端口的响应,以验证是否给出了正确的响应。为了完成这个任务,一个用户建议我使用select()命令来等待这个输入。

然而,我在使用这种方法时遇到了一些困难。最初,select()每次都会返回响应超时的值(即使调制解调器发送回正确的响应,我用同时运行的另一个程序验证了这一点)。现在,程序在一次迭代后停止,即使从调制解调器发回了正确的响应。

 //setting the file descriptor to the port
int fd = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
  /*
   * Could not open the port.
   */
  perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
 fcntl(fd, F_SETFL, 0);
FILE *out = fopen(portName.c_str(), "w");//sets the serial port
FILE *in = fopen(portName.c_str(), "r");

fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
struct timeval timeout = { 10, 0 }; /* 10 seconds */
//int ret = select(fd+1, &fds, NULL, NULL, &timeout);
/* ret == 0 means timeout, ret == 1 means descriptor is ready for reading,
 ret == -1 means error (check errno) */
char buf[100];
int i =0; 
while(i<(sizeof(messageArray)/sizeof(messageArray[0])))
{
  //creates a string with the AT command that writes to the module
  std::string line1("AT+SBDWT=");
  line1+=convertInt( messageArray[i].numChar);
  line1+=" ";
  line1+=convertInt(messageArray[i].packetNumber);
  line1+=" ";
  line1+=messageArray[i].data;
  line1+=std::string("rn");
  //creates a string with the AT command that initiates the SBD session
  std::string line2("AT+SBDI");
  line2+=std::string("rn");
  fputs(line1.c_str(), out); //sends to serial port
  //usleep(7000000);     
  int ret =select(fd+1, &fds, NULL, NULL, &timeout);
  /* ret == 0 means timeout, ret == 1 means descriptor is ready for reading,
 ret == -1 means error (check errno) */
  if (ret ==1){
fgets (buf ,sizeof(buf), in);
//add code to check if response is correct
  }
  else if(ret == 0) {
perror("timeout error ");
  }
  else if (ret ==-1) {
perror("some other error");
  }
  fputs(line2.c_str(), out); //sends to serial port
  //usleep(7000000); //Pauses  between the addition of each packet.
  int ret2 = select(fd+1, &fds, NULL, NULL, &timeout);
  /* ret == 0 means timeout, ret == 1 means descriptor is ready for reading,
 ret == -1 means error (check errno) */
  if(ret2 == 0) {
perror("timeout error ");
  }
  else if (ret2 ==-1) {
perror("some other error");
  }
  i++;
}

读/写/选择没有使用相同的文件句柄,这有点奇怪。

你没有重置你的fd_sets,这是由select修改的,并且会在超时的情况下取消所有的fds,默认情况下使下一次调用超时(因为你要求没有fds)。

您还使用了缓冲IO,这在这种情况下必然会造成麻烦。如。fgets等待EOF(它不会发生),或者一个换行符,一直读取。它将阻塞,直到它得到它的换行符,所以可能会让你无限期挂起,如果它从来没有发生。它也可能读取比它需要的更多的数据到缓冲区,扰乱你的select读信号(你有数据在缓冲区中,但选择将超时,因为没有什么可读的文件句柄)。

底线是:

  • 在循环中使用FD_SET来设置/重置你的fd设置,也重置你的超时,因为select可能会修改它。
  • 使用单个句柄进行读/写/选择,而不是多个句柄。打开fopen(..., "w+")open(..., O_RDWR)
  • 文件
  • 如果仍然使用fopen,尝试使用setvbuf_IONBF缓冲选项禁用缓冲。
  • 否则,使用open/read/write代替fopen等。

我注意到在对你上一个问题的回答中提到了这一点。

您可能应该在输出文件流上使用fflush()。