平台:ARM9

编程语言C

要求 - 纯 C 语言,没有外部库,也没有 boost。

操作系统-REX RTOS

我有两个线程在嵌入式平台中运行 -

  1. 一个是在驱动程序级别处理与硬件的所有通信和数据传输。
  2. 第二个线程运行使用与硬件之间的数据的应用程序。

这个想法是将应用程序线程与驱动程序线程解耦,以便我们可以更改硬件驱动程序线程中的硬件和实现,但对应用程序线程的影响最小。

我的挑战是从硬件接收的数据可能是动态的,即我们不预先知道应用程序线程应该为每个发往/来自硬件的请求预留多少内存,因为这是在运行时确定的。

我认为驱动程序线程可以通知应用程序线程有太多数据需要读取。然后应用程序线程分配内存并请求驱动程序线程读取数据。然后由应用程序线程来相应地处理数据。这样,所有内存管理都在应用程序线程内进行。

有帮助吗?

解决方案

我想到了几个选择:

1)在驱动程序中malloc内存,在应用程序中释放它。但...我们倾向于避免在任何接近实时要求的情况下使用 malloc。如果您可以访问 malloc/free,并且不存在“实时”问题或内存碎片问题(即你的堆足够大),那么这是一个相当简单的方法。驱动程序只是通过消息队列将分配的指针发送到应用程序线程,完成后应用程序会释放内存。注意内存泄漏。

2) 环形或循环缓冲区。驱动程序完全管理固定大小的环形缓冲区,并在缓冲区准备就绪时简单地向应用程序发送消息。请参阅此处了解一些详细信息: 圆形缓冲器. 。然后,应用程序通过驱动程序 API 再次将数据标记为“可用”,这有助于向应用程序线程隐藏环形缓冲区详细信息。我们对我们的一个驱动程序使用这种方法,该驱动程序具有与您所描述的一组非常相似的要求。在这种情况下,您需要关心确定环形缓冲区的“最佳”大小、驱动程序中的溢出处理等。

祝你好运!

其他提示

您没有指定操作系统,但您以某种方式拥有“线程”。除了其中一个处于驱动程序级别(中断处理程序)而另一个听起来像应用程序(用户态/内核)。但这也不匹配,因为您的驱动程序和应用程序甚至在数据处理之前就已经进行了通信。

您的术语令人困惑且不鼓舞人心。这是否是自制(RT)操作系统?

如果您有一个真正的操作系统,那么就有一些既定的方法来编写驱动程序并将数据传递给用户空间。阅读文档或使用现有驱动程序之一作为参考。

如果这是一个自定义操作系统,您仍然可以参考其他开源驱动程序来获取想法,但显然您不会那么方便地进行设置。预先分配驱动程序代码中的所有内存,在数据到达时填充它,然后将其交给应用程序代码。内存量将取决于您的应用程序处理数据的速度、您计划接受的最大数据量以及支持您的应用程序需要多少内部数据队列。

这是 C,我最终不得不让应用程序向驱动程序注册回调。回调的目的是在驱动程序从设备读取数据后处理数据。驱动程序管理内存,即分配内存,调用回调,最后释放内存。此外,回调仅具有内存的读取权限。因此,理想情况下,应用程序应该将缓冲区的内容复制到自己的内存中,并立即退出回调。然后,它就可以按照自己的意愿随时随地处理数据。

我更新了文档,以明确应用程序回调的使用,即假定回调返回时,内存不应再被视为有效。如果以任何其他方式使用回调,则行为是未定义的。

我的第一个想法是使用循环缓冲区。这是一些示例代码。请随意调整它以适应您自己的用途。您可能不想要全局变量。你可能不想要#defines:

#define LENGTH (1024)
#define MASK (LENGTH-1)
uint8 circularBuffer[ LENGTH ];
int circularBuffer_add = 0;
int circularBuffer_rmv = 0;

void copyIn( uint8 * circularBuffer, uint8 * inputBuffer, int n ) {
    int i;
    for( i = 0; i < n; i++ ) {
        circularBuffer[ circularBuffer_add ] = inputBuffer[ i ];
        circularBuffer_add = ( circularBuffer_add + 1 ) & MASK;
    } 
}

void copyOut( uint8 * circularBuffer, uint8 * outputBuffer, int n ) {
    int i;
    for( i = 0; i < n; i++ ) {
        outputBuffer[ i ] = circularBuffer[ circularBuffer_rmv ];
        circularBuffer_rmv = ( circularBuffer_rmv + 1 ) & MASK;
    } 
}

另外,上面的代码假设您的数据单位是数据类型“uint8”。您可以更改它,以便它使用其他数据类型。或者您甚至可以使其通用并使用 memcpy() 复制到循环缓冲区中。

这段代码的主要特点是它如何处理 add 和 rmv ptr。


一旦你使用上面的代码就可以工作了。 我建议在某个时候切换所有从硬件读取的内容以使用平台的 直接内存访问 应用程序编程接口.

这是 切换到直接内存访问很重要,因为上面的代码使用了与 DMA 相关的大量周期 它使用几乎零周期。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top