1

平台:ARM9

编程语言 C

要求 - 纯 C,没有外部库,也没有提升。

操作系统 - REX 实时操作系统

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

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

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

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

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

4

4 回答 4

3

想到几个选项:

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

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

祝你好运!

于 2009-03-27T12:20:49.313 回答
0

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

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

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

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

于 2009-03-27T13:14:24.130 回答
0

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

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

于 2009-03-30T13:13:53.280 回答
0

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


一旦你使用上面的代码工作。我建议在某个时候切换所有从硬件读取的内容,以使用平台的直接内存访问API

切换到直接内存访问很重要,因为上述代码相对于使用几乎零周期的 DMA 使用了很多周期。

于 2009-03-30T15:10:39.893 回答