使用Boost Asio从串口读取数据

9

我想使用boost.asio来检查串口上的传入数据包。每个数据包都以一个字节长的头部开始,并指定发送的消息类型。每种不同类型的消息都有自己的长度。我想编写的函数应该不断监听新的传入消息,当它发现一条消息时,应该读取它,并调用另一个函数来解析它。我的当前代码如下:

void check_for_incoming_messages()
{
    boost::asio::streambuf response;
    boost::system::error_code error;
    std::string s1, s2;
    if (boost::asio::read(port, response, boost::asio::transfer_at_least(0), error)) {
        s1 = streambuf_to_string(response);
        int msg_code = s1[0];
        if (msg_code < 0 || msg_code >= NUM_MESSAGES) {
            // Handle error, invalid message header
        }
        if (boost::asio::read(port, response, boost::asio::transfer_at_least(message_lengths[msg_code]-s1.length()), error)) {
            s2 = streambuf_to_string(response);
            // Handle the content of s1 and s2
        }
        else if (error != boost::asio::error::eof) {
            throw boost::system::system_error(error);
        }
    }
    else if (error != boost::asio::error::eof) {
        throw boost::system::system_error(error);
    }
}
< p> 我应该使用boost :: asio :: streambuf 吗?我如何从中提取数据以便解析消息?我还想知道是否需要有一个单独的线程仅调用此函数,使其更频繁地被调用。由于高流量和串行端口的缓冲区耗尽,我应该担心在两次调用函数之间丢失数据吗?我正在使用Qt的GUI库,我不知道处理所有事件需要多长时间。

编辑:有什么方法可以检查串行端口是否有任何传入数据?如果没有传入数据,我不希望函数被阻塞...

2个回答

25

本文有助于理解如何在串口异步使用ASIO:

更新(2019-03):

我最初链接的文章已经不再可用,即使在Internet Archive中也很难找到(这里是一个快照)。现在可以通过搜索轻松找到更新的关于使用ASIO进行串行I/O的文章,但是这篇较旧的文章仍然非常有用。我将其放在一个公共的gist中,以防丢失:

这篇文章中描述的代码似乎已经被复制到这里:

这篇文章的作者似乎已经更新了C++11相关内容。我认为原始文章是由fede.tft编写的。


谢谢! :) 那也是我发现我必须做的。 - HelloGoodbye
我发现为了不干扰程序流程,我必须异步读取。之前,我使用的是read函数,而正确的函数应该是async_read。同样的道理也适用于写入,应该使用async_write来防止程序在读取时出现暂停而导致程序冻结。 - HelloGoodbye
1
@HelloGoodbye,你能和我分享一下fede网页的源代码吗?该网站现在正在移动到互联网档案馆,无法克隆。谢谢! - TSL_
3
@ShawnLe 我预计原始来源不久将在互联网档案馆中提供克隆版本。然而,原始文章中的源代码似乎也可在Github上获得:https://github.com/fedetft/serial-port - kaliatech

3

Jason,

如果适用于您的应用程序,我强烈建议实现基于回调的异步串行RX。 如何使用asio执行非阻塞读取?有一个很好的小例子,展示了如何实现带有超时的异步串行传输。正如您所认识到的那样,它将需要多线程实现以获得性能优势,因此您需要仔细考虑接收到的数据将被缓冲以确保您不会进行大量复制。

至于boost::streambuff部分,我个人更喜欢将一些内存块作为char数组阻塞 - char m_RXBuffer[m_RXBuffSize]并使用boost :: asio :: buffer(m_RXBuffer,m_RXBuffSize)将目标缓冲区传递给async_read_some。特别是对于RS232,我始终发现底层数据是字节流自然地映射到简单的char数组比任何更复杂的数据结构都要好。

祝你好运!


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