Io 在没有竞争条件的情况下,通过缓冲区将数据写入SD卡
我正在为数据记录设备编写固件。它以20 Hz的频率从传感器读取数据,并将数据写入SD卡。但是,将数据写入SD卡的时间不一致(约200-300ms)。因此,一种解决方案是以一致的速率(使用计时器中断)将数据写入缓冲区,并具有第二个线程,在缓冲区满时将数据写入SD卡 以下是我的天真实现:Io 在没有竞争条件的情况下,通过缓冲区将数据写入SD卡,io,arduino,embedded,system,deadlock,Io,Arduino,Embedded,System,Deadlock,我正在为数据记录设备编写固件。它以20 Hz的频率从传感器读取数据,并将数据写入SD卡。但是,将数据写入SD卡的时间不一致(约200-300ms)。因此,一种解决方案是以一致的速率(使用计时器中断)将数据写入缓冲区,并具有第二个线程,在缓冲区满时将数据写入SD卡 以下是我的天真实现: #define N 64 char buffer[N]; int count; ISR() { if (count < N) { char a = analogRead(A0);
#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;
}
}
定义N 64
字符缓冲区[N];
整数计数;
ISR(){
如果(计数解决这个问题的最好办法是什么?使用循环缓冲还是双缓冲?我如何确保比赛条件不会发生?你回答了自己的问题;您应该使用双缓冲或循环缓冲。双缓冲可能更容易实现,并且适用于诸如SD卡之类的设备,对于这些设备,块写入通常更有效 缓冲区长度的选择可能需要一些考虑;通常,您会使缓冲区的大小与SD扇区缓冲区的大小相同(通常为512字节),但这可能不实际,而且采样率低至20 SP,优化SD写入性能可能不是问题 另一个需要考虑的问题是,您需要通过选择适当的缓冲区大小使采样率与文件系统延迟相匹配。在这种情况下,64个样本缓冲区将在3秒钟多一点的时间内填满,但块写入只需要300毫秒-因此,如果需要,您可以使用更小的缓冲区-8个样本就足够了-尽管要小心,您可能已经观察到300毫秒的延迟,但当物理闪存中的特定边界被跨越时,它可能会更大—我已经看到一些卡在1M字节边界上有明显的延迟—而且卡的性能在不同的大小和制造商之间有很大的差异 下面是对双缓冲实现的修改。我已将缓冲区长度减少到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;
}
}
//双缓冲区及其管理数据/常量
静态易失性字符缓冲区[2][32];
静态常量int BUFFLEN=sizeof(缓冲区[0]);
静态常量unsigned char EMPTY=0xff;
静态易失性无符号字符inbuffer=0;
静态易失性无符号字符溢出=空;
ISR()
{
静态整数计数=0;
//写入当前缓冲区
字符a=模拟读数(A0);
缓冲区[inbuffer][count]=a;
计数++;
//如果缓冲区已满。。。
如果(计数>=BUFFLEN)
{
//向循环()发送信号,表明数据可用(非空)
突出=缓冲;
//切换输入缓冲区
inbuffer=inbuffer==0?1:0;
计数=0;
}
}
void循环()
{
//如果缓冲区可用。。。
如果(突发!=空)
{
//写缓冲区
myFile.open(“data.csv”,FILE\u WRITE);
对于(int i=0;i
请注意,共享数据使用了
volatile
和unsigned char
。重要的是,并发执行上下文之间共享的数据是以显式和原子方式访问的;访问基于8位AVR的Arduino上的int
需要多条机器指令,中断可能部分通过读取/写入loop()
发生,并导致读取不正确的值。太好了!出于好奇,如何在Arduino中实现互斥或信号量?信号量和互斥仅在先发制人的多任务操作系统中才相关。虽然完全可以在Arduino硬件上实现或部署RTOS,但Arduino也是一个应用程序框架和库,不基于RTOS,因此它不再是Arduino了。此外,您不能在ISR中使用互斥或等待信号量;您可能会有一个ADC线程和一个文件系统访问线程,并使用RTOS队列或管道作为它们之间的缓冲区,而不是滚动您自己的线程。。。例如:。注意“不做任何事情”的循环()函数;RTO通过使用setup()
进行初始化和存根loop()
在Arduino草图开发环境中工作。我担心的是,如果使用Arduino库,则没有关于线程安全或其他方面的文档。我想补充一点小评论。在Flash中写入数据时要考虑的另一件事是,根据闪存驱动器和闪存的类型,有时写入闪存不希望被任何ISR中断。需要记住的是。@Alex:在这种情况下,闪存是SD卡上的,并且有一个独立的控制器,而不是片上的NOR闪存,所以这个问题不适用。