C++ 串口读取问题:ioctl(FIONREAD) 是否设置了错误的值?
C++ Serial port reading issue: does ioctl(FIONREAD) set a wrong value?
我遇到了一个非常奇怪的问题,我无法解决。我想使用 c++ 在 Mac Os X 上读取(仅读取)微控制器通过 USB 作为串口(FTDI)收集和发送的数据。一个完整数据序列的大小始终正好是 10 个字节。但是我使用以下代码读取数据:
导入的库:
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
代码:
void init(){
serial = open(port.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); // 0_RDONLY ?
struct termios options;
//set opt to 115200-8n1
cfsetspeed(&options, B115200);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
tcsetattr(serial, TCSANOW, &options);
if (serial < 0){
//Error
}else{
//run loop
}
}
void serial_loop(){
long bytes_read;
int bytes_available;
unsigned char msg[10];
while(1){
do{
usleep(1000);
ioctl(serial, FIONREAD, &bytes_available);
}while(bytes_available < 10); //wait for the sequence to complete
bytes_read = read(serial, msg, 10);
//do some parsing here
}
}
此代码在几天前有效,但现在已无效。根据终端 -> 屏幕命令,数据可以完美地到达计算机。我检查了仍然正确的端口文件名,并且端口也已成功打开。
我将我的问题缩小到 ioctl-command FIONREAD,它没有将正确的数字写入 bytes_available-var(不再)。
它确实有效,而且我相信,我没有更改代码中的任何内容。
您是否发现任何可能导致此问题的问题?
我的代码中是否有任何危险的段落?
谢谢你的帮助,我真的被困在这里了...
编辑:
多亏了反馈,我才能够再次得到它运行。这是当前代码:
int serial;
void init(){
serial = open(port.c_str(), O_RDWR | O_NOCTTY); //removed 0_NONBLOCK
struct termios options;
//set opt to 115200-8n1
cfsetspeed(&options, B115200);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //Non-canonical
options.c_cc[VMIN] = 1; //block read until at least 1 byte was recieved
options.c_lflag = 0;
tcsetattr(serial, TCSANOW, &options);
if (serial < 0){
//Error
}else{
//run loop
}
}
void serial_loop(){
int datalength = 10;
long bytes_read = 0;
int bytes_in_msg = 0;
unsigned char buf[datalength];
unsigned char msg[datalength];
do{
bytes_read = read(serial, buf, datalength-bytes_in_msg);
usleep(1000);
if (bytes_read>0){
memcpy(&msg[bytes_in_msg], &buf, bytes_read);
}
bytes_in_msg += bytes_read;
}while(bytes_in_msg < datalength);
//do some parsing here
}
}
这是有效的,但还有什么可能有问题吗?
感谢您的支持!
您的代码已损坏。没有协议可以允许 reader 等待写入者和写入者等待 reader。如果这样做,可能会导致死锁。
您的 do/while 循环拒绝读取任何数据,直到写入器写入所有 10 个。但是,串行端口允许写入器拒绝写入更多数据,直到 reader 读取它已经拥有的数据书面。因此它也不能允许 reader 拒绝读取更多数据,直到作者写入更多数据。
您不能使用FIONREAD
等待写入更多字节,因为写入器可能也在等待您。相反,在字节可用时读取字节,从而确保您解除对编写器的阻塞。将它们累积在缓冲区中,当你有需要的数量时中断。
This code worked a few days ago but now it isn't anymore.
变化无常的程序行为通常表示初始化不当或不完整。
您的 termios 初始化仅配置波特率和字符帧,其他一切都留给机会。
参见 Setting Terminal Modes Properly
和 Serial Programming Guide for POSIX Operating Systems.
您修改后的代码仍未正确解决此问题。
代码从不通过调用 tcgetattr() 函数来初始化 termios 结构 options
。
有关示例代码,请参阅我对 How to open, read, and write from serial port in C?
的回答
options.c_lflag = 0;
这不是分配 termios 元素的正确方法。
options.c_cc[VMIN] = 1;
非规范模式需要同时定义 VMIN 和 VTIME 条目。
您的代码使用 VTIME 所在位置存在的任何垃圾值。
参见 Linux Blocking vs. non Blocking Serial Read。
FIONREAD which doesn't write the correct number to the bytes_available-var (anymore).
负面描述,即没有发生的事情,不如对发生的事情的描述有用或具体。
那么您要返回什么样的值?
为什么你认为它是“错误的”?
更重要的是,您为什么不检查每个系统调用的 return 值,尤其是您认为会给您带来问题的 ioctl()?
很可能是 ioctl() 失败,没有更新您的 bytes_available
变量,并且 return 编辑了一个错误代码。
您的代码无条件地使用 returned 参数,而不是首先检查一个好的 return。
另一个批评您的代码的答案具有误导性。可以禁用流量控制。您的代码被破坏是因为它没有进行正确的初始化并且没有检查错误 returns,而不是因为 comm link 是 “死锁”.
在您修改后的代码中:
bytes_read = read(serial, buf, datalength-bytes_in_msg);
usleep(1000);
阻塞读取之后 and/or 之前的休眠或延迟是多余的。
我遇到了一个非常奇怪的问题,我无法解决。我想使用 c++ 在 Mac Os X 上读取(仅读取)微控制器通过 USB 作为串口(FTDI)收集和发送的数据。一个完整数据序列的大小始终正好是 10 个字节。但是我使用以下代码读取数据:
导入的库:
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
代码:
void init(){
serial = open(port.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); // 0_RDONLY ?
struct termios options;
//set opt to 115200-8n1
cfsetspeed(&options, B115200);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
tcsetattr(serial, TCSANOW, &options);
if (serial < 0){
//Error
}else{
//run loop
}
}
void serial_loop(){
long bytes_read;
int bytes_available;
unsigned char msg[10];
while(1){
do{
usleep(1000);
ioctl(serial, FIONREAD, &bytes_available);
}while(bytes_available < 10); //wait for the sequence to complete
bytes_read = read(serial, msg, 10);
//do some parsing here
}
}
此代码在几天前有效,但现在已无效。根据终端 -> 屏幕命令,数据可以完美地到达计算机。我检查了仍然正确的端口文件名,并且端口也已成功打开。 我将我的问题缩小到 ioctl-command FIONREAD,它没有将正确的数字写入 bytes_available-var(不再)。 它确实有效,而且我相信,我没有更改代码中的任何内容。
您是否发现任何可能导致此问题的问题? 我的代码中是否有任何危险的段落? 谢谢你的帮助,我真的被困在这里了...
编辑: 多亏了反馈,我才能够再次得到它运行。这是当前代码:
int serial;
void init(){
serial = open(port.c_str(), O_RDWR | O_NOCTTY); //removed 0_NONBLOCK
struct termios options;
//set opt to 115200-8n1
cfsetspeed(&options, B115200);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //Non-canonical
options.c_cc[VMIN] = 1; //block read until at least 1 byte was recieved
options.c_lflag = 0;
tcsetattr(serial, TCSANOW, &options);
if (serial < 0){
//Error
}else{
//run loop
}
}
void serial_loop(){
int datalength = 10;
long bytes_read = 0;
int bytes_in_msg = 0;
unsigned char buf[datalength];
unsigned char msg[datalength];
do{
bytes_read = read(serial, buf, datalength-bytes_in_msg);
usleep(1000);
if (bytes_read>0){
memcpy(&msg[bytes_in_msg], &buf, bytes_read);
}
bytes_in_msg += bytes_read;
}while(bytes_in_msg < datalength);
//do some parsing here
}
}
这是有效的,但还有什么可能有问题吗?
感谢您的支持!
您的代码已损坏。没有协议可以允许 reader 等待写入者和写入者等待 reader。如果这样做,可能会导致死锁。
您的 do/while 循环拒绝读取任何数据,直到写入器写入所有 10 个。但是,串行端口允许写入器拒绝写入更多数据,直到 reader 读取它已经拥有的数据书面。因此它也不能允许 reader 拒绝读取更多数据,直到作者写入更多数据。
您不能使用FIONREAD
等待写入更多字节,因为写入器可能也在等待您。相反,在字节可用时读取字节,从而确保您解除对编写器的阻塞。将它们累积在缓冲区中,当你有需要的数量时中断。
This code worked a few days ago but now it isn't anymore.
变化无常的程序行为通常表示初始化不当或不完整。
您的 termios 初始化仅配置波特率和字符帧,其他一切都留给机会。
参见 Setting Terminal Modes Properly
和 Serial Programming Guide for POSIX Operating Systems.
您修改后的代码仍未正确解决此问题。
代码从不通过调用 tcgetattr() 函数来初始化 termios 结构 options
。
有关示例代码,请参阅我对 How to open, read, and write from serial port in C?
options.c_lflag = 0;
这不是分配 termios 元素的正确方法。
options.c_cc[VMIN] = 1;
非规范模式需要同时定义 VMIN 和 VTIME 条目。
您的代码使用 VTIME 所在位置存在的任何垃圾值。
参见 Linux Blocking vs. non Blocking Serial Read。
FIONREAD which doesn't write the correct number to the bytes_available-var (anymore).
负面描述,即没有发生的事情,不如对发生的事情的描述有用或具体。
那么您要返回什么样的值?
为什么你认为它是“错误的”?
更重要的是,您为什么不检查每个系统调用的 return 值,尤其是您认为会给您带来问题的 ioctl()?
很可能是 ioctl() 失败,没有更新您的 bytes_available
变量,并且 return 编辑了一个错误代码。
您的代码无条件地使用 returned 参数,而不是首先检查一个好的 return。
另一个批评您的代码的答案具有误导性。可以禁用流量控制。您的代码被破坏是因为它没有进行正确的初始化并且没有检查错误 returns,而不是因为 comm link 是 “死锁”.
在您修改后的代码中:
bytes_read = read(serial, buf, datalength-bytes_in_msg);
usleep(1000);
阻塞读取之后 and/or 之前的休眠或延迟是多余的。