与 BLDC 控制器通信时,串行读取 () 以错误号 11 终止
Serial read() terminates with errno 11 while communicating with BLDC Controller
所以我正在使用 ARM9 处理器 (i.mx28) 运行 一个 Linux 内核通过 RS232(高级协议基于 CANOpen 协议)与 BLDC 控制器通信。发送到控制器的消息通常后跟一个响应。
控制器侧连接器具有 RX、TX 和 GND,因此没有流量控制线等。控制器手册指出,必须不使用软件流量控制来建立连接。因此,我将端口配置为忽略 DCD(使用 CLOCAL)以及 RTS/CTS(使用 ~CRTSCTS)和软件流控制(使用 ~IXON、~IXOFF 和 ~IXANY)。
我的问题是:串口设置好后,写入正常,软复位等命令立即执行。然而,尝试阅读提到的回复会导致 "Errno 11: Resource momentarily unavailable"。当我尝试在我的计算机 运行 终端上监视消息时,read() returns 0 而不是 -1,因此未设置 errno。反之亦然,我尝试将 BLDC 控制器连接到所述终端并手动重新启动它以查看启动时发送的启动消息。这也很好用。
过去 3 天我一直在滥用 Google 寻找类似的问题,但无济于事。在几种不同的非阻塞 IO 设置组合中,我还尝试了几种阻塞行为的方法,但是这导致永远阻塞并且仍然根本不读取任何内容。
我怀疑串行端口仍然需要 handshake/flow 控制,因为将系统连接到提供 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, '[=11=]', 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;
}
}
我真的希望这篇 post 读起来不会太长,如有任何想法,我们将不胜感激!
Trying to read mentioned responses, however, results in "Errno 11: Resource momentarily unavailable".
根据您的配置,errno 11 完全符合预期。您已尽一切可能将串行终端配置为非阻塞模式。
首先使用 O_NDELAY 选项打开串行终端。
然后修改一个(未初始化的)文件描述符以设置 O_NONBLOCK 选项。
最后,即使在非阻塞模式下这不起作用,termios 参数 VMIN 和 VTIME 都配置为零(即轮询模式)。
errno所指的"unavailable resource"就是简单的数据。用户缓冲区中没有可用于系统调用 return 的数据。
I also tried several ways of blocking behavior, this however resulted in blocking forever and still not reading anything at all.
没有实际代码,就没有什么可调试的。
您需要决定您的程序是使用阻塞模式还是非阻塞模式。
The main program needs to simultaneously read potential input from given controller and an 8 bit value from an eeprom connected via i2c.
您不需要非阻塞模式来确保 "simultaneous" 数据捕获。
读取设备的实际输入是由与您的应用程序完全异步执行的内核驱动程序执行的。
您的应用程序代码基本上无法控制这些输入操作实际发生的时间。
只要 UART 不报告接收器溢出错误,您就可以确信所有串行数据都已接收并最初存储在驱动程序的接收缓冲区中。
该数据最终将被复制到串行终端的缓冲区,其大小通常为 4096 字节。
从 I2C 设备读取也应该涉及系统缓冲区,但我没有看到从 EEPROM 读取数据的任何时间组件。
那是一个响应写入和读取命令的非易失性存储设备。
您的应用程序代码只是试图从内核缓冲区中获取数据。
EEPROM可以在程序方便的时候随时读取。
需要以足以防止系统缓冲区溢出的速率读取串行终端。
由于您的程序与 BLDC 控制器之间的通信结构为 request/response 对话框,因此请求串行终端输入,即程序仅在发送请求后才期待输入。
非阻塞模式非常适合这种情况。
改变
fcntl(fdSerial, F_SETFL, O_NONBLOCK);
到
fcntl(fileDescr, F_SETFL, 0);
会将刚刚打开的文件描述符置于阻塞模式。
正在替换
PortParams.c_cc[VMIN] = 0;
PortParams.c_cc[VTIME] = 0;
类似:
PortParams.c_cc[VMIN] = MIN(255, (sizeof(inBufSerial) - 1));
PortParams.c_cc[VTIME] = 1;
将尝试在每个系统调用中检索尽可能多的响应字节。
如果您更喜欢或需要更少的延迟,请研究
应删除写入后的 usleep(60000);
语句,或替换为对 tcdrain().
的调用
一般来说,任何固定的 delay/sleeping 系统调用都是有问题的。
所以我正在使用 ARM9 处理器 (i.mx28) 运行 一个 Linux 内核通过 RS232(高级协议基于 CANOpen 协议)与 BLDC 控制器通信。发送到控制器的消息通常后跟一个响应。
控制器侧连接器具有 RX、TX 和 GND,因此没有流量控制线等。控制器手册指出,必须不使用软件流量控制来建立连接。因此,我将端口配置为忽略 DCD(使用 CLOCAL)以及 RTS/CTS(使用 ~CRTSCTS)和软件流控制(使用 ~IXON、~IXOFF 和 ~IXANY)。
我的问题是:串口设置好后,写入正常,软复位等命令立即执行。然而,尝试阅读提到的回复会导致 "Errno 11: Resource momentarily unavailable"。当我尝试在我的计算机 运行 终端上监视消息时,read() returns 0 而不是 -1,因此未设置 errno。反之亦然,我尝试将 BLDC 控制器连接到所述终端并手动重新启动它以查看启动时发送的启动消息。这也很好用。
过去 3 天我一直在滥用 Google 寻找类似的问题,但无济于事。在几种不同的非阻塞 IO 设置组合中,我还尝试了几种阻塞行为的方法,但是这导致永远阻塞并且仍然根本不读取任何内容。
我怀疑串行端口仍然需要 handshake/flow 控制,因为将系统连接到提供 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, '[=11=]', 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;
}
}
我真的希望这篇 post 读起来不会太长,如有任何想法,我们将不胜感激!
Trying to read mentioned responses, however, results in "Errno 11: Resource momentarily unavailable".
根据您的配置,errno 11 完全符合预期。您已尽一切可能将串行终端配置为非阻塞模式。
首先使用 O_NDELAY 选项打开串行终端。
然后修改一个(未初始化的)文件描述符以设置 O_NONBLOCK 选项。
最后,即使在非阻塞模式下这不起作用,termios 参数 VMIN 和 VTIME 都配置为零(即轮询模式)。
errno所指的"unavailable resource"就是简单的数据。用户缓冲区中没有可用于系统调用 return 的数据。
I also tried several ways of blocking behavior, this however resulted in blocking forever and still not reading anything at all.
没有实际代码,就没有什么可调试的。
您需要决定您的程序是使用阻塞模式还是非阻塞模式。
The main program needs to simultaneously read potential input from given controller and an 8 bit value from an eeprom connected via i2c.
您不需要非阻塞模式来确保 "simultaneous" 数据捕获。
读取设备的实际输入是由与您的应用程序完全异步执行的内核驱动程序执行的。
您的应用程序代码基本上无法控制这些输入操作实际发生的时间。
只要 UART 不报告接收器溢出错误,您就可以确信所有串行数据都已接收并最初存储在驱动程序的接收缓冲区中。
该数据最终将被复制到串行终端的缓冲区,其大小通常为 4096 字节。
从 I2C 设备读取也应该涉及系统缓冲区,但我没有看到从 EEPROM 读取数据的任何时间组件。
那是一个响应写入和读取命令的非易失性存储设备。
您的应用程序代码只是试图从内核缓冲区中获取数据。
EEPROM可以在程序方便的时候随时读取。
需要以足以防止系统缓冲区溢出的速率读取串行终端。
由于您的程序与 BLDC 控制器之间的通信结构为 request/response 对话框,因此请求串行终端输入,即程序仅在发送请求后才期待输入。
非阻塞模式非常适合这种情况。
改变
fcntl(fdSerial, F_SETFL, O_NONBLOCK);
到
fcntl(fileDescr, F_SETFL, 0);
会将刚刚打开的文件描述符置于阻塞模式。
正在替换
PortParams.c_cc[VMIN] = 0;
PortParams.c_cc[VTIME] = 0;
类似:
PortParams.c_cc[VMIN] = MIN(255, (sizeof(inBufSerial) - 1));
PortParams.c_cc[VTIME] = 1;
将尝试在每个系统调用中检索尽可能多的响应字节。
如果您更喜欢或需要更少的延迟,请研究
应删除写入后的 usleep(60000);
语句,或替换为对 tcdrain().
的调用
一般来说,任何固定的 delay/sleeping 系统调用都是有问题的。