在UDP / IP协议栈解决方案示例here中,提出了一种阻止单个事件队列的解决方案。
在等待队列的任务处理指针之前,保护指针指向的数据的解决方案是什么。
例如说,队列是从ISR填充的。如果相关任务尚未处理,则ISR不应写入*pvData
。但由于可能有多个事件源,因此队列应该比一个项长。应该制作结构:
typedef struct IP_TASK_COMMANDS
{
eIPEvent_t eEventType;
SemaphoreHandle_t data_read;
void *pvData;
} xIPStackEvent_t;
使用ISR中的信号量,并在完成数据处理的任务中给出。
答案 0 :(得分:2)
如果您采用UDP示例 - 通常您将拥有一个缓冲池(或动态分配缓冲区),从中获取缓冲区并将其提供给DMA。当DMA用接收到的数据填充缓冲区时,指向缓冲区的指针进入UDP堆栈 - 此时只有UDP堆栈知道缓冲区的位置并负责它。在某些时候,缓冲区中的数据可能会从UDP堆栈传递到可以使用它的应用程序。然后,应用程序将缓冲区返回到池中(或释放分配的缓冲区),以便DMA再次可用。反过来也是如此 - 应用程序可以分配一个缓冲区,该缓冲区填充了要通过UDP堆栈发送的数据到Tx函数,在Tx函数中它实际放置在线路上 - 在这种情况下,它是返回的Tx结束中断池中的缓冲区。
因此,简而言之,一次只有一个引用缓冲区的东西,所以没有问题。
[注意上面它说应用程序分配或释放缓冲区,它将在应用程序而不是应用程序直接调用的UDP / IP堆栈API中 - 这实际上至少部分是我们自己的{{ 3}}堆栈已实现。]
答案 1 :(得分:1)
您不希望ISR阻止并等待数据缓冲区变为可用。如果适合您的ISR只是跳过更新并在缓冲区不可用时继续,那么信号量可能是有意义的。但ISR不应该阻止信号量。
这是另一种考虑因素。创建包含多个适当大小的数据缓冲区的内存池。 ISR从池中分配下一个可用缓冲区,将数据写入它并将指针放在队列中。该任务从队列中读取指针,使用数据,然后将缓冲区释放回池中。如果ISR在任务使用数据之前再次运行,则ISR将分配一个新的缓冲区,因此它不会覆盖以前的数据。
这是另一个考虑因素。 FreeRTOS队列按副本传递项目。如果数据缓冲区相对较小,那么传递数据结构而不是指向数据结构的指针可能是有意义的。如果您传递数据结构,那么队列服务将复制以提供给任务,ISR可以自由更新它的原始缓冲区。
现在我考虑一下,使用队列服务复制功能可能比创建自己的单独内存池更简单。当您创建队列并指定队列长度和项目大小时,我相信队列服务会创建一个队列使用的内存池。