C++ 无法写入串行设备,但可以从中读取

C++ 无法写入串行设备,但可以从中读取,c++,c,linux,serial-port,C++,C,Linux,Serial Port,我使用的是非阻塞IO,我只是尝试写入和读取串行端口。串行端口的读取按预期工作,而写入则不按预期工作 这就是我设置串行线路的方式。如您所见,它被设置为非阻塞和规范。可能有些标志是多余的,但它们主要来自以下示例: 这就是我试图写作的方式。我正在使用select等待IO资源变为可用,因为尝试直接写入会导致错误资源暂时不可用。但即使等待20秒,该设备仍然不可用 void tSerial::writeSerial(std::string message) { int n; fd_set r

我使用的是非阻塞IO,我只是尝试写入和读取串行端口。串行端口的读取按预期工作,而写入则不按预期工作

这就是我设置串行线路的方式。如您所见,它被设置为非阻塞和规范。可能有些标志是多余的,但它们主要来自以下示例:

这就是我试图写作的方式。我正在使用
select
等待IO资源变为可用,因为尝试直接写入会导致错误
资源暂时不可用
。但即使等待20秒,该设备仍然不可用

void tSerial::writeSerial(std::string message)
{
    int n;
    fd_set rfds;
    FD_ZERO(&rfds);
    FD_SET(m_serialPort, &rfds);
    spdlog::debug("[tSerial] writing command to serial {}", message);
    struct timeval tv;
    tv.tv_sec = 20;
    tv.tv_usec = 0;
    int retval = select(1, NULL, &rfds, NULL, &tv);
    if (retval == -1)
    {
        spdlog::error("[tSerial] select error");
    }
    else if (retval)
    {

        n = write(m_serialPort, message.c_str(), message.length());
        tcflush(m_serialPort, TCIOFLUSH);
        if (n < 0)
        {
            spdlog::error("[tSerial] error writing: {}", strerror(errno));
        }
    }
    else
    {

        spdlog::error("[tSerial] Resource unavailable after 20 seconds wait");
    }
}
void tSerial::writeSerial(std::string消息)
{
int n;
fd_集rfds;
FD_ZERO(和RFD);
FD_集(m_串行端口和RFD);
调试(“[tSerial]将命令写入串行{}”,消息);
结构时间值电视;
tv.tv_sec=20;
tv.tv_usec=0;
int retval=select(1,NULL,&rfds,NULL,&tv);
如果(retval==-1)
{
spdlog::error(“[tSerial]select error”);
}
否则如果(返回)
{
n=write(m_serialPort,message.c_str(),message.length());
tcflush(m_串行端口,TCIOFLUSH);
if(n<0)
{
错误(“[tSerial]写入错误:{}”,strerror(errno));
}
}
其他的
{
spdlog::错误(“[tSerial]资源在等待20秒后不可用”);
}
}
我正在使用
select
等待IO资源可用。。。但即使等待20秒,该设备仍然不可用

void tSerial::writeSerial(std::string message)
{
    int n;
    fd_set rfds;
    FD_ZERO(&rfds);
    FD_SET(m_serialPort, &rfds);
    spdlog::debug("[tSerial] writing command to serial {}", message);
    struct timeval tv;
    tv.tv_sec = 20;
    tv.tv_usec = 0;
    int retval = select(1, NULL, &rfds, NULL, &tv);
    if (retval == -1)
    {
        spdlog::error("[tSerial] select error");
    }
    else if (retval)
    {

        n = write(m_serialPort, message.c_str(), message.length());
        tcflush(m_serialPort, TCIOFLUSH);
        if (n < 0)
        {
            spdlog::error("[tSerial] error writing: {}", strerror(errno));
        }
    }
    else
    {

        spdlog::error("[tSerial] Resource unavailable after 20 seconds wait");
    }
}
由于未正确编程select()调用,您的程序运行不正常:

int retval = select(1, NULL, &rfds, NULL, &tv);
根据man页面,第一个参数“应设置为三组中编号最高的文件描述符,加上1。”
由于您正在传递值
1
select()可以检查的唯一文件描述符是
stdin
(它是只读的,永远不会准备输出),并且永远不会(打开的串行终端的文件描述符)。
相反,系统调用需要

select(m_serialPort + 1, ...);

您的代码还有其他问题

(1) 使用非阻塞模式有问题。
似乎您更喜欢向程序中添加额外的代码以等待,而不是让操作系统为您执行。如果您的程序没有有效地利用其时间片,那么最好让操作系统管理系统资源

(2) 在write()之后使用tcflush()是荒谬和错误的
您可能打算使用tcdrain()
研究页面

(3) 复制和修改代码时,您至少引入了一个错误。
ICANON
位于
c\u-lflag
成员中,而不是
c\u-cflag

(4) 好的编码实践是使用有意义的变量名。
将用于读取参数的变量名重新用作写入参数是草率和混乱的:

fd_set rfds;  
...
int retval = select(..., NULL, &rfds, NULL, &tv);
您的程序应该对第三个参数使用类似于
wfds
的内容,而不是
rfds


。。。尝试直接写入会导致错误
资源暂时不可用

使用select()时的问题很容易解释。

对于上述问题,您需要提供一个最小的、可复制的示例,即完整的程序。

那么调用
write
返回-1?在这种情况下,
errno
是什么?根本不需要选择,没有时间设备“不可用”。直接从cmd行写入工作?不使用
选择
写入
返回您所说的-1
errno
为11,
EAGAIN
。因此,建议在第二个答案中使用
select
<代码>回显
对设备的回显按预期工作。microcom也能用。嗯,我看不出代码有什么问题。您确定设备没有被其他程序占用吗?如果将其设置为原始模式(有关标志,请参阅termios手册页),会发生什么情况?为什么不直接在
打开中将其设置为无块
tcsetattr
成功如果至少设置了一个设置,请重新阅读以查看是否确实应用了所有设置。尝试创建一个虚拟串行设备,并尝试它是否适用于该设备。使用
stty
检查真实设备。谢谢,我将逐一解决问题。(1) 用例是写入可以阻塞一段时间,而读取不应该阻塞,因为有更多的“入口点”可以导致程序唤醒。我写这篇文章的方式很简单,我知道它不会占用太多的资源。也许我可以用selecteverywhere来代替,但当我开始编写这个函数时,我不知道该如何使用它来编写解耦代码。(2)删除它。在另一篇堆栈溢出文章中看到了这一点,我已经习惯了在其他语言中被称为flush,但这两种方法都不一定要尝试flush。(3) 很好的捕获,不确定我实际上设置了什么作为标志,但这可能是“破坏”串行端口的问题的一部分。(4) 好的一点,wfds更为合理。事实证明,串行端口昨天根本没有工作。我今天尝试了这些改变,效果很好。不确定是由于某个标志导致串行端口处于错误状态,还是由于其他原因。