如何在C++中读取串口设备数据

11

我正在尝试弄清楚如何在Linux C++中使用串行通信读写我的Arduino。目前,我正在尝试从我的 Arduino 读取响应,我是通过以下方式“触发”的:

echo "g" > /dev/ttyACM0"

我尝试使用以下命令在终端中查看我的Arduino响应:

tail -f /dev/ttyACM0

这个功能运作良好。现在我想在C++应用程序中做同样的事情。因此,我进行了以下测试。

void testSerialComm()
{

    std::string port = "/dev/ttyACM0";
    int device = open(port.c_str(), O_RDWR | O_NOCTTY | O_SYNC);

    std::string response;
    char buffer[64];

    do
    {
        int n = read(device, buffer, sizeof buffer);

        if (n > 0) {
            response += std::string(buffer);
            std::cout << buffer;
        }

    } while (buffer[0] != 'X'); // 'X' means end of transmission

    std::cout << "Response is: " << std::endl;
    std::cout << response << std::endl;

    close(device);

}

发生几次“消息”传输后,传输会有些混乱。我的测试应用程序将响应字符按随机顺序写入,出现了一些问题。我尝试通过以下命令配置/dev/ttyACM0设备:

stty -F /dev/ttyUSB0 cs8 115200 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts

不行。有人能帮我理解如何在C++中与我的Arduino通信吗?


3
ftell() 返回什么结果? - πάντα ῥεῖ
1
我过去使用过boost serial。快速简便。 - Trevor Hickey
1
所以你应该对这个错误返回做出反应,检查 errno 看看到底出了什么问题。 - πάντα ῥεῖ
4
你不能在tty上进行搜索,这是你问题的主要原因。你无法事先知道数据的大小,必须不断地读取直到读取完全部数据为止。 - Alan Stokes
1
那么,你打算如何在字符设备中进行查找呢?这几乎是不可能的。 - Lol4t0
显示剩余9条评论
1个回答

14
所示代码打开/dev/ttyACM0,尝试寻找这个“文件”的末尾,并根据结果文件位置分配一个老式的、C风格的内存缓冲区。
这种方法的问题在于,您只能浏览常规的普通文件。 /dev/ttyACM0不是普通文件,而是设备。虽然有些设备是可寻址的,但这个设备不是。 根据注释,您已经独立发现这一点。
串口设备是可读写的,但不可寻址。串口上没有“寻址”的概念。这毫无意义。
要从串口读取数据,只需读取即可。 操作系统会维护某个大小的内部缓冲区,因此,如果已经通过串口接收到一些字符,则最初的读取将返回所有这些字符(前提是read()缓冲区大小足够大)。例如,如果传递了一个1024个字符的缓冲区,并且已经从串口读取了五个字符,则read()将返回5,以相应地指示。
如果尚未读取任何字符,并且您将串口打开为阻止设备,则read()将至少阻塞,直到从串口读取了一个字符,然后返回。
因此,为了从串口读取数据,您只需从其读取,直到您决定已经从其中读取了所有内容。如何决定?这取决于您。您可以决定仅在读取换行符时进行读取。或者您可能决定仅在读取了固定数量的字符#n后才进行读取。这完全取决于你。

当然,如果硬件得到适当的安排,并且您与串口设备进行必要的串口控制引脚协议安排,并且根据您的配置,DCD和/或DSR引脚被指示以表示串口设备不再可用,那么您的read()将立即返回0,表示在串口设备上出现了伪文件结束条件,这也是您需要实现必要逻辑来处理的内容。

附注:由于它们自己的内部缓冲算法,既不是C样式的stdio,也不是C++样式的iostream,无法很好地使用字符设备。 在使用串口时,最好使用open(2)read(2)write(2)close(2)。 但以上所有内容仍然适用。


1
非常感谢!我自动地认为我必须像读取文件一样读取设备。从你的回答中,我明白了我不应该使用fwrite来写入设备?你的意思是说这个帖子中的方法(https://dev59.com/pnjZa4cB1Zd3GeqPgaR3)是一个糟糕的解决方案? - birgersp
1
使用缓冲类,例如C++ [io]fstream,在您确切知道所需输入和输出的情况下是可以的。但是,在必须准备处理不同可能性的情况下,当您必须弄清楚您最终读取的内容时,正如我所说,您应该使用open()、read()和write()这些实际的系统调用,因为fstream类引入的缓冲通常会妨碍操作。 - Sam Varshavchik
现在正在尝试在设备上使用“open”和“read”。我之前使用“tail -f /dev/ttyACM0”来查看Arduino返回的内容。然后,我的应用程序能够读取Arduino响应的前两个字符(仅此而已)。如果我关闭“tail”程序,我的应用程序将无法读取任何字符?我会更新我的问题,说明我目前的情况。 - birgersp
通常情况下,同一时间只有一个进程可以从串口读取数据。当多个进程都打开了该串口时,每个从串口接收到的字符都会被其中一个进程随机地读取。另外,由于每个进程通常会尽可能多地读取缓冲区中的数据,因此在进程终止之前已经从串口读取了额外的输入数据,这些数据可能仍然被成功读取。 - Sam Varshavchik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接