使用终端的规范串行读取失败?

Canonical serial reading using terminos fail?

我正在尝试使用串行读取来自我的 arduino 的数据行。

我的arduino代码是这样的:Serial3.print(Z, 2);Serial3.print(F(";"));Serial3.println(F("END\n"));

这是我读取 ubuntu 上的数据的代码:

void setup(){  
//set up serial
   tcgetattr(dueSerial, &port_options); // Get the current attributes of the Serial port
   dueSerial = open("/dev/ttyUSB0", O_RDWR | O_NONBLOCK | O_NOCTTY | O_NDELAY);
   if (dueSerial == -1) {
     reportFailure("Could not open Arduino");
   } else {
     port_options.c_cflag &= ~PARENB; // Disables the Parity Enable bit(PARENB),So No Parity
     port_options.c_cflag &= ~CSTOPB; // CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit
     port_options.c_cflag &= ~CSIZE; // Clears the mask for setting the data size
     port_options.c_cflag |= CS8; // Set the data bits = 8
     port_options.c_cflag &= ~CRTSCTS; // No Hardware flow Control
     port_options.c_cflag |= (CREAD | CLOCAL); // Enable receiver,Ignore Modem Control lines
     port_options.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable XON/XOFF flow control both input & output
     port_options.c_lflag &= ~(ECHO | ECHONL | IEXTEN | ISIG); // no echo 
     port_options.c_iflag |= ICANON; //Enable canonical
     port_options.c_iflag |=   ICRNL; //map CR to NL
     //port_options.c_oflag &= ~OPOST; // No Output Processing
     //port_options.c_lflag = 0; //  enable raw input instead of canonical,
   /* 
         initialize all control characters 
         default values can be found in /usr/include/termios.h, and are given
         in the comments, but we don't need them here
       */
        port_options.c_cc[VINTR]    = 0;     /* Ctrl-c */ 
        port_options.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
        port_options.c_cc[VERASE]   = 0;     /* del */
        port_options.c_cc[VKILL]    = 0;     /* @ */
        port_options.c_cc[VEOF]     = 4;     /* Ctrl-d */
        port_options.c_cc[VTIME]    = 0;     /* inter-character timer unused */
        port_options.c_cc[VMIN]     = 0;     /* blocking read until 1 character arrives */
        port_options.c_cc[VSWTC]    = 0;     /* '[=10=]' */
        port_options.c_cc[VSTART]   = 0;     /* Ctrl-q */ 
        port_options.c_cc[VSTOP]    = 0;     /* Ctrl-s */
        port_options.c_cc[VSUSP]    = 0;     /* Ctrl-z */
        port_options.c_cc[VEOL]     = 0;     /* '[=10=]' */
        port_options.c_cc[VREPRINT] = 0;     /* Ctrl-r */
        port_options.c_cc[VDISCARD] = 0;     /* Ctrl-u */
        port_options.c_cc[VWERASE]  = 0;     /* Ctrl-w */
        port_options.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
        port_options.c_cc[VEOL2]    = 0;     /* '[=10=]' */
    
     cfsetispeed( & port_options, BAUDRATE); // Set Read  Speed
     cfsetospeed( & port_options, BAUDRATE); // Set Write Speed
      tcflush(dueSerial, TCIFLUSH);
     tcflush(dueSerial, TCIOFLUSH);
     int att = tcsetattr(dueSerial, TCSANOW, & port_options);
     if (att != 0) {
       reportFailure("ERROR in Setting Arduino port attributes");
     } else {
       LOG_INFO("SERIAL DUE Port Good to Go");
     }

   }
 }

 void UART::tick() {
   //Arduino msg = "IMU;LAX;LAY;LAZ;AVX;AVY;AVZ;AY;AP;AR;END"
   //  rx_buffer[0] = '0';
     memset(&rx_buffer, '[=10=]', sizeof(rx_buffer));
  // tcflush(dueSerial, TCIOFLUSH);
     
     rx_length = read(dueSerial, &rx_buffer,255);
 if (rx_length < 0) {
     LOG_INFO("Error reading");
 }else{
   LOG_INFO("Read %i bytes. Received message: %s", rx_length, rx_buffer);
 }
}

但是当我尝试这段代码时,我一次得到很多行,所以我的输出看起来像这样:

2020-11-15 09:13:09.491 INFO  packages/skeleton_pose_estimation/apps/usb/UART.cpp@87: Read 0 bytes. Received message: 
2020-11-15 09:13:09.496 INFO  packages/skeleton_pose_estimation/apps/usb/UART.cpp@87: Read 255 bytes. Received message: 0.01;0.00;0.00;0.00;0.00;0.00;0.00;END

IMU;-0.00;0.02;0.03;0.00;0.00;0.00;0.00;0.00;0.00;END

IMU;-0.00;0.02;0.03;0.00;-0.00;0.00;0.00;0.00;0.00;END

IMU;-0.00;0.02;-0.02;0.00;-0.00;0.00;0.00;0.00;0.00;END

IMU;-0.00;0.02;-0.02;-0.00;0.00;0.00;0.00;0.00;
2020-11-15 09:13:09.501 INFO  packages/skeleton_pose_estimation/apps/usb/UART.cpp@87: Read 241 bytes. Received message: 0.00;END

IMU;-0.01;-0.02;-0.01;-0.00;0.00;0.00;0.00;0.00;0.00;END

IMU;-0.01;-0.02;-0.01;-0.00;-0.00;0.00;0.00;0.00;0.00;END

IMU;-0.01;-0.02;0.03;-0.00;-0.00;0.00;0.00;0.00;0.00;END

IMU;-0.01;-0.02;0.03;0.00;0.00;0.00;0.00;0.00;0.00;END


2020-11-15 09:13:09.506 INFO  packages/skeleton_pose_estimation/apps/usb/UART.cpp@87: Read 0 bytes. Received message: 
2020-11-15 09:13:09.511 INFO  packages/skeleton_pose_estimation/apps/usb/UART.cpp@87: Read 0 bytes. Received message: 

但我希望它在每次 read() 函数调用时只读取一行。 我相信我要么设置了一个错误的参数,使 conanical 模式未被使用,要么它忽略了我的 \n 和 \r 但不知道为什么....

请帮我找出原因。 非常感谢!

But I want it to read only one line per read() function call. I believe that I either set a wrong parameter making the conanical mode unused ...

您的程序没有按预期运行,因为规范模式从未真正设置过。
语句

 port_options.c_iflag |= ICANON; //Enable canonical

不正确。 ICANONc_lflag 成员中,而不在 c_iflag.


您的代码还有其他问题。

(1)变量dueSerial未初始化使用:

void setup(){  
//set up serial
   tcgetattr(dueSerial, &port_options); // Get the current attributes of the Serial port
   dueSerial = open(...);
   ...

需要先获取并验证文件描述符,然后才能在 tcgetattr() 调用中使用。
语句的正确顺序是:

void setup(){  
   //set up serial
   dueSerial = open(...);
   if (dueSerial == -1) {
     /* abort */
   }     
   tcgetattr(dueSerial, &port_options);
   ...

(2) 在您的 termios 初始化中未指定大量输入转换。
规范模式启用各种选项来转换某些输入字符,并且大多数这些选项需要被禁用才能被程序读取(相对于交互式终端)。
通常 INPCK(启用输入奇偶校验)、IUCLC(将大写字符映射为小写)和 IMAXBEL(输入队列已满时响铃)被禁用。
您需要检查是否还需要 IGNCR(保留或忽略回车符 return)、INLCR(将换行符转换为回车符 return)和 ICRNL(翻译carriage return to newline unless IGNCR is set) 也被禁用。

(3) 非阻塞模式的使用值得怀疑。
由于您希望 "每次 read() 函数调用只读取一行",因此阻塞模式是获得该结果的正确方法。
如果您坚持使用非阻塞模式,那么 read() 系统调用将始终 return “立即”并且可能 return 根本没有任何数据。