FreeRTOS 在多个 events/objects 上阻塞

FreeRTOS blocking on multiple events/objects

在 UDP/IP 堆栈解决方案示例 here 中,提出了在单个事件队列上阻塞的解决方案。

在等待队列的任务处理之前保护指针指向的数据的解决方案是什么。

例如,队列由 ISR 填满。如果 ISR 没有被适当的任务处理,它不应该写入 *pvData。但是由于可以有多个事件源,队列可能应该比一个项目长。是否应该制作结构:

typedef struct IP_TASK_COMMANDS
{
    eIPEvent_t eEventType;
    SemaphoreHandle_t data_read;
    void *pvData;
} xIPStackEvent_t;

信号量在 ISR 中获取并在处理数据的任务中给出。

您不希望您的 ISR 阻塞并等待数据缓冲区可用。如果您的 ISR 在缓冲区不可用时跳过更新并继续前进是合适的,那么信号量可能是有意义的。但 ISR 不应阻塞信号量。

这里有一个可供考虑的备选方案。创建一个包含多个适当大小的数据缓冲区的内存池。 ISR 从池中分配下一个可用缓冲区,将数据写入其中并将指向它的指针放在队列中。该任务从队列中读取指针,使用数据,然后将缓冲区释放回池中。如果 ISR 在任务使用数据之前再次运行,ISR 将分配一个新缓冲区,因此它不会覆盖以前的数据。

这是另一个考虑因素。 FreeRTOS 队列通过副本传递项目。如果数据缓冲区相对较小,那么只传递数据结构而不是指向数据结构的指针也许是有意义的。如果您传递数据结构,则队列服务将制作副本以提供给任务,并且 ISR 可以自由更新其原始缓冲区。

现在想想,使用Queue服务复制特性可能比创建自己的独立内存池更简单。当您创建队列并指定队列长度和项目大小时,我相信队列服务会创建一个内存池供队列使用。

如果您以 UDP 为例 - 通常您会有一个缓冲区池(或动态分配一个缓冲区),从中可以获取一个缓冲区并将其提供给 DMA。当 DMA 用接收到的数据填充缓冲区时,指向缓冲区的指针将进入 UDP 堆栈 - 此时只有 UDP 堆栈知道缓冲区的位置并对其负责。在某些时候,缓冲区中的数据可能会从 UDP 堆栈传递到可以使用它的应用程序。然后,应用程序 returns 将缓冲区 returns 放入池中(或释放分配的缓冲区),以便 DMA 再次使用它。反之亦然 - 应用程序可以分配一个缓冲区,其中填充了要发送的数据,通过 UDP 堆栈,到实际放置在线路上的 Tx 函数 - 在这种情况下,Tx 结束中断 returns缓冲池。

所以,简而言之,一次只有一个东西引用了缓冲区,所以没有问题。

[注意上面说应用程序分配或释放缓冲区的地方,该缓冲区将位于 UDP/IP 堆栈 API 中,由应用程序调用而不是直接由应用程序调用 - 这实际上是部分至少是我们自己的 TCP/IP 堆栈是如何实现的。]