如何使用 GPIO 引脚与 Qt 进行串行流控制?

How to use a GPIO pin, for serial flow control with Qt?

目标

在我的 Qt 应用程序中,我需要控制一个 GPIO 引脚,具体取决于通过串行总线发送的数据。因此,只要我传输数据,我就需要将其设置为高电平,并在传输结束后立即将其设置为低电平。将其视为串行通信流控制引脚,设置为 1 时启用传输,设置为 0 时启用数据接收。整个系统是半双工的,以主从方式通信。

问题

我设法接近解决方案,方法是在任何传输之前立即将其设置为 HIGH,根据波特率引入一些恒定延迟(我使用 QThread:usleep() ),然后再次将其设置为低,但是当我用示波器观察它时,我得到了随机的 "stretchings" 脉冲(保持高电平的时间比它应该的长)。

尝试过的解决方案

好吧,似乎发生了一些 "magic",这在我手动定义的延迟之上增加了一些额外的延迟。为了消除这种可能性,我使用了 bytesWritten() 信号,这样当我们完成将实际数据写入端口时,我可以触发我的 setPinLow() 插槽。所以我的代码现在看起来像这样:

classTTY::classTTY(/*someStuff*/) : port(/*some other stuff*/)
{
    s_port = new QSerialPort();
    connect(s_port, SIGNAL(bytesWritten(qint64)), this, SLOT(setPinLow()));

    if(GPIOPin->open(QFile::ReadWrite | QFile::Truncate | QFile::Text | QFile::Unbuffered)) {
        qDebug() << "GPIO pin ready to switch.";
    } else {
        qDebug() << "Failed to access GPIO pin";
    }

bool classTTY::sendData(data, replyLength)
{
    directionPinEnable(true);

    if(m_port->isOpen()) {
        s_expectedReplyLength = replyLength;
        s_receivedData.clear();

        s_port->flush();
        s_port->write(data);

        return true;
    }

return false;
}

void classTTY::setPinLow()
{
    gpioPinEnable(false);
}

void classTTY::gpioPinEnable(bool enable){

    if(enable == true){
        GPIOPin->write("1");
    } else if (enable == false) {
        GPIOPin->write("0");
    }
}

实施后,引脚开始提供非常短的脉冲,更像是 "spikes",这意味着(我认为)现在只要 Qt write() 进程持续,它就会保持高电平,而不是在数据的实际传播持续时。

问题

  1. 当我使用 naive 时添加的额外延迟是什么, QThread::usleep 方法,导致脉冲伸展?
  2. 为什么信号槽方法不起作用,因为它是 事件驱动?
  3. 一般来说,我怎样才能指示引脚仅在 传输数据,然后再次降为零,这样我就可以接收 奴隶的回复?
  1. What is that extra delay being added when I use the naive, QThread::usleep approach, that causes the stretch of the pulse?

Linux不是实时操作系统线程休眠挂起进程fo 不少于指定的时间。在睡眠期间,其他线程和进程可能 运行 并且可能在比您的睡眠时间更长的时间内不让出处理器,或者可能根本不让出并消耗其整个 OS 分配的时间片。除此之外,内核驱动程序中断处理程序将始终抢占用户级进程。 Linus 有一个用于实时调度的构建选项,但保证仍然不如真正的 RTOS 可靠,而且延迟通常更糟。

另请注意,不仅您的线程可以挂起比睡眠时间更长的时间,而且传输可能会延长超过波特率的位数 - 内核驱动程序可以被其他驱动程序抢占并且引入你无法控制的字符间间隙。

  1. Why the signal-slot approach is not working, since it is event-driven?

QSerialPort::waitForBytesWritten() 的文档指出:

This function blocks until at least one byte has been written to the serial port and the bytesWritten() signal has been emitted.

所以很明显,这里的语义是“一些数据已经写入”而不是“全部条数据已写入”。每当写入一个字节时它都会 return ,然后如果你再次调用它,如果字节继续被写入它可能会立即 return (因为 QSerialPort 被缓冲并且将独立写入数据你的申请)。

  1. In general, how can I instruct the pin to go active ONLY during the transmission of data and then drop again to zero, so I can receive the slave's reply?

不幸的是,Qt 不是答案;这种行为需要在串行端口内核驱动程序中实现,或者至少在 Qt 的较低级别上实现。 Qt QSerialPort 抽象并没有为您提供所需的控制级别或对实际事件 "on the wire" 的洞察力。它与硬件有些距离 - 有充分的理由。

但是有一个简单的解决方案 - 别费心了!这似乎完全没有必要。是主从通信,数据本身流控。奴隶只有在有人说话时才会说话,而主人在说话后必须期待并等待答复。为什么奴隶需要任何许可才能说话,而不是被暗示的说话?