通过无竞争条件的缓冲区将数据写入SD卡

问题描述 投票:2回答:1

我正在为数据记录设备编写固件。它以20 Hz的频率从传感器读取数据,并将数据写入SD卡。但是,将数据写入SD卡的时间不一致(大约200-300毫秒)。因此,一种解决方案是以一致的速率(使用计时器中断)将数据写入缓冲区,并使用第二个线程在缓冲区已满时将数据写入SD卡。

这是我的幼稚实现:

#define N 64
char buffer[N];
int count;

ISR() {
    if (count < N) {
        char a = analogRead(A0);
        buffer[count] = a;
        count = count + 1;
    }
}

void loop() {
    if (count == N) {
        myFile.open("data.csv", FILE_WRITE);

        int i = 0;
        for (i = 0; i < N; i++) {
            myFile.print(buffer[i]);
        }
        myFile.close();
        count = 0;
    }
}

该代码具有以下问题:

  1. 当缓冲区已满时,正在将数据写入SD卡阻止读取操作
  2. 它可能有比赛条件。

解决此问题的最佳方法是什么?使用循环缓冲区还是双缓冲?我如何确保不出现比赛条件?

io arduino embedded system deadlock
1个回答
3
投票

您宁愿回答自己的问题;您应该使用双缓冲或循环缓冲。双缓冲可能更易于实现,并且适用于诸如SD卡之类的块写入通常更有效的设备。

缓冲区长度选择可能需要一些考虑;通常,您将使缓冲区与SD扇区缓冲区大小相同(通常为512字节),但这可能不切实际,并且采样率低至20 sps,优化SD写入性能可能不是问题。

另一个考虑因素是,您需要通过选择适当的缓冲区大小来将采样率与文件系统延迟相匹配。在这种情况下,64个样本缓冲区的缓冲区将填满三秒多一点,但是块写入仅需花费300毫秒-因此,如果需要,您可以使用更小的缓冲区-8个样本就足够了-尽管要小心,您可能会观察到300毫秒的延迟,但是当物理闪存中的特定边界越过时,延迟可能会更大-我已经看到某些卡在1 MB边界处存在显着的延迟-而且,卡的性能在大小和制造商之间也存在很大差异。] >

下面是您的实现采用双缓冲的改编版。我已经将缓冲区长度减少到32个样本,但是使用双缓冲时,总数不更改为64,但是写延迟减少到了1.6秒。

// Double buffer and its management data/constants
static volatile char buffer[2][32];
static const int BUFFLEN = sizeof(buffer[0]);
static const unsigned char EMPTY = 0xff;
static volatile unsigned char inbuffer = 0;
static volatile unsigned char outbuffer = EMPTY;

ISR()
{
    static int count = 0;

    // Write to current buffer
    char a = analogRead(A0);
    buffer[inbuffer][count] = a;
    count++ ;

    // If buffer full...
    if( count >= BUFFLEN )
    {
        // Signal to loop() that data available (not EMPTY)
        outbuffer = inbuffer;

        // Toggle input buffer
        inbuffer = inbuffer == 0 ? 1 : 0;
        count = 0;
    }
}

void loop()
{
    // If buffer available...
    if( outbuffer != EMPTY )
    {
        // Write buffer
        myFile.open("data.csv", FILE_WRITE);
        for( int i = 0; i < BUFFLEN; i++)
        {
            myFile.print(buffer[outbuffer][i]);
        }
        myFile.close();

        // Set the buffer to empty
        outbuffer = EMPTY;
    }
}

注意共享数据]的使用volatile和[C0

。重要的是,在并发执行上下文之间共享的数据必须被显式访问,并且;在基于8位AVR的Arduino上访问unsigned char需要多条机器指令,并且中断可能会在int的读/写过程中部分发生,并导致读取不正确的值。
© www.soinside.com 2019 - 2024. All rights reserved.