在与BLDC控制器通信时,串行read()以errno 11终止。

0 人关注

我正在使用一个运行Linux内核的ARM9处理器(i.mx28),通过RS232与一个BLDC控制器进行通信(高层协议是基于CANOpen协议)。发送给控制器的信息一般都会有一个回应。

控制器侧的连接器有RX、TX以及GND,所以没有流控线或其他。控制器手册指出,必须使用无软件流量控制来设置连接。因此,我将端口配置为忽略DCD(使用CLOCAL)以及RTS/CTS(使用~CRTSCTS)和软件流控制(使用~IXON、~IXOFF和~IXANY)。

我的问题如下:在设置了串口之后,写入工作很好,例如软复位的命令可以立即执行。然而,试图读取提到的响应,结果是 "Errno 11: Resource momentarily unavailable"。当我试图在运行终端的计算机上监视这些信息时,read()返回0而不是-1,因此errno没有被设置。反过来说,我试着把BLDC控制器连接到上述终端,并手动重启它,以看到启动时发送的启动信息。这也很好。

在过去的3天里,我一直在滥用谷歌寻找类似的问题,但无济于事。在非阻塞性IO的几种不同的设置组合中,我也尝试了几种阻塞行为的方式,然而这导致了永远的阻塞,而且还是根本读不到任何东西。

我怀疑,虽然termios的配置中没有说明,但串口仍在期待握手/流量控制,因为将系统连接到提供RTS/CTS线的计算机上,工作正常。

int configureSerialPort() {
//Open Serial Port--------------------------------------------
  int fileDescr = open("/dev/ttyS3", O_RDWR | O_NDELAY);
  if (fileDescr < 0){
    std::cout << "Unable to open Serial Port." << std::endl;
    exit(1);
  else std::cout << "Serial Port opened." << std::endl;
  fcntl(fdSerial, F_SETFL, O_NONBLOCK);
//Configure Serial Port for B115200, 8N1---------
  struct termios PortParams;
  if (tcgetattr(fileDescr, &PortParams) < 0) {
    std::cout << "Could not get Attributes." << std::endl;
    exit(1);
  else std::cout << "Got Attributes." << std::endl;
  cfsetispeed(&PortParams, 115200);
  cfsetospeed(&PortParams, 115200);
  PortParams.c_cflag |= (CLOCAL | CREAD);
  PortParams.c_cflag &= ~PARENB;
  PortParams.c_cflag &= ~CSTOPB;
  PortParams.c_cflag &= ~CSIZE;
  PortParams.c_cflag |= CS8;
  PortParams.c_cflag &= ~CRTSCTS;
  PortParams.c_lflag &= ~(ICANON | ECHO | ECHONL | IEXTEN | ISIG);
  PortParams.c_iflag &= ~(INPCK | ISTRIP | IGNPAR | PARMRK | IXON | IXOFF | IXANY);
  PortParams.c_oflag &= ~OPOST;
  PortParams.c_cc[VMIN] = 0;
  PortParams.c_cc[VTIME] = 0;
  if (tcsetattr(fileDescr, TCSANOW, &PortParams) != 0) {
    std::cout << "Could not set Attributes." << std::endl;
    exit(1);
  else std::cout << "Port configured." << std::endl;
  usleep(1000000);
  tcflush(fdSerial, TCIFLUSH);
  return fileDescr;

这是我到现在为止的配置,我的理解应该是没有问题的。

写入和读取是由它们各自的系统调用完成的。

wrLenSerial = write(fdSerial, &resetNode, sizeof(resetNode));
  if (wrLenSerial < sizeof(resetNode)) {
    std::cout << "Could not send Write Command." << std::endl;
  else std::cout << "Reset Command sent." << std::endl;
  usleep(60000);
  memset(inBufSerial, '\0', sizeof(inBufSerial));
  rdlenSerial = read(fdSerial, inBufSerial, (sizeof(inBufSerial) - 1));
  if (rdlenSerial < 0) {
    std::cout << "Read Error. " << errno << std::strerror(errno) << std::endl;
  else if (rdlenSerial == 0) {
    std::cout << "Nothing read." << std::endl;
  else {
    std::cout << "Node Response: ";
    for (int i = 0; i < rdlenSerial; i++) {
      int r = inBufSerial[i];
      std::cout << std::hex << r << std::dec << " " << std::endl;

我真的希望这个帖子不要读得太长,任何想法都值得赞赏!

5 个评论
你似乎混淆了非常不同的事情。你确定你的ARM电脑是在RS232上运行它们的串口,而不是在TTL电平上?你可以非常容易地验证你的问题是否与流量控制有关,只需断开工作的计算机上的RTS和CTS线。
The errno 鉴于你的配置,11是完全可以预期的。 你已经做了一切可能的工作,将串行终端配置成非阻塞模式。 研究 stackoverflow.com/questions/25996171/... . 确定你是想要阻塞模式还是非阻塞模式。 IMO 串行终端的非阻塞模式被过度使用,很少被证明/需要,而且往往实施得很差。
ARM被安装在一个具有Exar SP3222EEA-L作为RS232驱动器的载板上,所以没有任何问题(也请注意,传输工作正常)。但是,关于禁用RTS/CTS的想法很好,甚至没有想到这一点
@sawdust:在过去的几天里,我偶然发现了你的帖子,并多次尝试实现你的想法,然而似乎没有什么能解决问题。似乎我的代码不能识别传入的数据,尽管它显然是存在的。我还试着在我的ARM板上直接 cat 串口,这也是有效的。(是的,我绝对是做过头了,这就是过去几天的结果,哈哈)
主程序需要同时从给定的控制器中读取潜在的输入,并从通过i2c连接的eeprom中读取8位数值。由于我没有那么多经验,我的方法是交替读取这两个来源,以便做出相应的反应。所以在我的理解中,非阻塞式的读取是有必要的,但我相信有更好的解决方案。
c++
linux
serial-port
BaconPing
BaconPing
发布于 2019-08-13
1 个回答
sawdust
sawdust
发布于 2019-08-14
已采纳
0 人赞同

然而,试图阅读提到的回应,结果是 "Errno 11: Resource momentarily unavailable"。

The errno 鉴于你的配置,11是完全可以预期的。你已经做了一切可能的工作,将串行终端配置成非阻塞模式。
首先,用O_NDELAY选项打开串行终端。
然后,一个(未初始化的)文件描述符被修改以设置O_NONBLOCK选项。
最后,尽管在非阻塞模式下配置时是无效的,但termios参数VMIN和VTIME都被配置为零(即轮询模式)。

The "不可用的资源" ǞǞǞ errno 指的是简单的数据。 在用户缓冲区中没有可供系统调用返回的数据。

我还尝试了几种阻断行为的方法,然而这导致永远阻断,还是根本读不到东西。

没有实际的代码,就没有什么可以调试的。
你需要决定你的程序是要使用阻塞模式还是非阻塞模式。

You do not need nonblocking mode to ensure "同时" 数据采集。
读取设备的实际输入是由内核驱动执行的,与你的应用程序完全不同步。
你的应用程序代码对这些输入操作的实际发生时间基本上没有控制。
只要UART没有报告接收器超限错误,你就可以确信所有的串行数据都被接收了,并且最初存储在驱动器的接收缓冲区中。
这些数据最终将被复制到串行终端的缓冲区,其大小通常为4096字节。

从I2C设备中读取也应该涉及到系统缓冲区,但我没有看到从EEPROM中读取数据的任何时间成分。
那是一种非易失性存储设备,对写和读命令有反应。

你的应用程序代码只是试图从内核缓冲区获取数据。
EEPROM可以在对程序方便的任何时候被读取。
串行终端需要以足够的速度读取,以防止系统缓冲区的超限。

由于您的程序和BLDC控制器之间的通信结构为请求/响应对话,所以串行终端的输入是征求性的,也就是说,只有在发送请求之后,程序才会期待输入。
非阻塞模式完全适用于这种情况。

  fcntl(fdSerial, F_SETFL, O_NONBLOCK);
  fcntl(fileDescr, F_SETFL, 0);

will put the just-opened file descriptor in blocking mode.

  PortParams.c_cc[VMIN] = 0;
  PortParams.c_cc[VTIME] = 0;

with something like:

  PortParams.c_cc[VMIN] = MIN(255, (sizeof(inBufSerial) - 1));
  PortParams.c_cc[VTIME] = 1;