如何处理 FreeRTOS 中的超时 - 在 vTaskDelay 到期之前从中断中唤醒任务?

How to handle timeout in FreeRTOS - wake up task from interrupt before vTaskDelay expires?

能否在vTaskDelay到期前唤醒任务?

我有这样的代码:

在任务中(hnd_uart_task)代码:

transmit_frame();
vTaskDelay(100);  // task should wait 100 ticks or be woken up by uart ISR
parse_response();

UART 中断:

// if byte was received
BaseType_t xYieldRequired = xTaskResumeFromISR(hnd_uart_task);
portYIELD_FROM_ISR(xYieldRequired);

不,因为这不是 vTaskDelay 的用途。

最接近您的解决方案是创建一个信号量,您尝试以 100 毫秒的延迟进入任务并从 ISR 提供​​该信号量。这样,任务将阻塞最多 100 毫秒以等待给出信号量,之后它将解除阻塞并恢复执行。如果早点给出,它会早点解锁,这就是我想你想要的。

但是,根据您所写的内容,我假设您想要实现如下目标:

  • 通过 UART 发送一些数据
  • 等待回复
  • 收到响应后,立即对响应进行处理

在这种情况下,在同一个任务中同时进行阻塞和解析会很困难(你真的不想在你的 ISR 中做 任何 类解析).因此,我推荐以下两个任务的“布局”,一个中断和两个任务之间的一个共享信号量:

  1. “高级”任务(我们称之为 ApplicationTask)可以执行以下操作:
  • 构造整个帧并请求它们通过 UART 发送(将它们添加到某种队列中)。这种“构建整个框架”并将它们发送给其他任务通常会被包装到一些函数中。
  • 将阻塞等待响应
  • 将接收已解析的数据(全帧或 object/structure 保存已解析的数据)
  1. “字节级”任务(我们称之为 ByteTask)可以执行以下操作:
  • 有传输数据队列(帧队列或原始字节队列)
  • 有接收数据的队列
  • 将“待传输数据”队列中的数据“推送”到 UART
  • 解析出现在“已接收数据”队列中的 UART 数据并给出信号量以解锁 ApplicationTask
  1. UART 中断:
  • 只传输被ByteTask
  • 告知要传输的数据
  • 将接收到的数据推送到ByteTask接收队列
  1. ApplicationTaskByteTask 之间的共享信号量:
  • 每当 ApplicationTask 想要“等待接收响应”时,它都会尝试获取此信号量。最大阻塞时间可以作为“响应接收超时”。
  • 每当 ByteTask 接收并解析足够的数据以确定“是的,这是一个完整的响应”时,它就会给出这个信号量。

以上是一个超级简单的示例,在您开发应用程序时应该很容易扩展到更多任务。你可以得到比这更花哨的东西(例如 ByteTask 同时处理多个 UART,有一个信号量池用于阻塞多个任务,做一些更复杂的消息调度等。 ),但上面的例子应该能让你更好地了解如何处理这样的事情。

您可以使用带超时的任务通知,而不是使用 vTaskDelay()

USART 中断:

// if byte was received
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(hnd_uart_task, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

任务代码:

transmit_frame();
ulTaskNotifyTake(pdTRUE, 100);
parse_response();

ulTaskNotifyTake(pdTRUE, 100) returns 当从 ISR 收到任务通知时,或当 100 tick 超时时间过去时。

但正如@Jacek Şlimok 指出的那样,byte-by-byte 解析可能不是个好主意。确切的方法取决于所使用的协议。但一般来说,您设置 DMA 或中断以用传入字节填充接收缓冲区。例如,在解析Modbus帧时,可以使用空闲线检测硬件中断并通知解析RX缓冲区的任务。