如何处理 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 中做 任何 类解析).因此,我推荐以下两个任务的“布局”,一个中断和两个任务之间的一个共享信号量:
- “高级”任务(我们称之为 ApplicationTask)可以执行以下操作:
- 构造整个帧并请求它们通过 UART 发送(将它们添加到某种队列中)。这种“构建整个框架”并将它们发送给其他任务通常会被包装到一些函数中。
- 将阻塞等待响应
- 将接收已解析的数据(全帧或 object/structure 保存已解析的数据)
- “字节级”任务(我们称之为 ByteTask)可以执行以下操作:
- 有传输数据队列(帧队列或原始字节队列)
- 有接收数据的队列
- 将“待传输数据”队列中的数据“推送”到 UART
- 解析出现在“已接收数据”队列中的 UART 数据并给出信号量以解锁 ApplicationTask
- UART 中断:
- 只传输被ByteTask
告知要传输的数据
- 将接收到的数据推送到ByteTask接收队列
- ApplicationTask 和 ByteTask 之间的共享信号量:
- 每当 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缓冲区的任务。
能否在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 中做 任何 类解析).因此,我推荐以下两个任务的“布局”,一个中断和两个任务之间的一个共享信号量:
- “高级”任务(我们称之为 ApplicationTask)可以执行以下操作:
- 构造整个帧并请求它们通过 UART 发送(将它们添加到某种队列中)。这种“构建整个框架”并将它们发送给其他任务通常会被包装到一些函数中。
- 将阻塞等待响应
- 将接收已解析的数据(全帧或 object/structure 保存已解析的数据)
- “字节级”任务(我们称之为 ByteTask)可以执行以下操作:
- 有传输数据队列(帧队列或原始字节队列)
- 有接收数据的队列
- 将“待传输数据”队列中的数据“推送”到 UART
- 解析出现在“已接收数据”队列中的 UART 数据并给出信号量以解锁 ApplicationTask
- UART 中断:
- 只传输被ByteTask 告知要传输的数据
- 将接收到的数据推送到ByteTask接收队列
- ApplicationTask 和 ByteTask 之间的共享信号量:
- 每当 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缓冲区的任务。