竭尽所能提高FATFS SD性能

最近开发log存储器,移植FATFS文件系统,四路串口通道接收数据后存储在SD卡内。在调试中设置串口波特率为256000bps,每10ms发送250个字节,四路通道同时工作,MCU主频为120MHz,使用一张CLASS4 8G卡和一张CLASS10 16G卡进行测试。在测试中使用8G卡速度上不来导致数据丢失,为了提高SD卡兼容性与稳定性,竭力挖掘SD性能,提高写入速度。

开发环境

主控MCU : STM32F205RE
SD硬件接口 : SDIO
SD卡 : 金士顿16G(CLASS10)、金士顿8G(CLASS4)
FATFS版本 : R0.11
软件开发库 : HAL库

SD底层驱动

1、SD驱动配置为4线数据总线可提高速度,在SD卡初始化后调用库函数HAL_SD_ConfigWideBusOperation()可配置4线数据总线。
2、配置SD读写为DMA中断方式,关于SD中断与DMA中断哪个优先级高的问题,网上众说纷纭。但发现在HAL库中stm32f2xx_hal_sd.c 原文注释:Configure the SDIO and DMA interrupt priorities using functions HAL_NVIC_SetPriority();DMA priority is superior to SDIO priority.(不加翻译,免得暴露英文水平)

提高FATFS利用效率

1、对于高频操作文件,可以上电直接打开文件f_open,之后除非读写失败之外不再关闭文件f_close,使用f_sync代替f_close写入SD卡。同时为了防止断电丢失大量数据的情况,可以在f_write之后加f_sync。

f_open();
while (1) {
	if (get_data()) {
    	f_write();
    	f_sync();
    }
}
f_close;

2、加入队列配合FATFS文件系统写数据,底层串口接收到的数据放入队列中,应用层去队列中拿数据写入SD卡。对于数据吞吐量比较大的可以设置大一些队列,我设置了队列数据节点为1k字节,开了100个队列。队列数据节点越大,写入的块数据就越大,写SD卡速度就越快。

测试数据块大小对写入速度的影响

数据块对应写入速度表
测试方法:

#define DATA_SIZE	512 //数据块大小 
#define DATA_CYCLE (8192 * 2)  
uint8_t buffer[DATA_SIZE];

void test_sd_rate(void)
{
	int i;
	
	f_open();
	//调用计时
	for (i = 0; i < DATA_CYCLE; i++) {
		f_write(, buffer, DATA_SIZE, );
	}
	f_sync();
	//统计计时
}

核心代码

#define FILE_CLOSE	0x00
#define FILE_OPEN	0x01
#define SD_FULL_MEM	0x01
#define SD_NOT_FULL_MEM 0x00
#define SD_LIMIT_MEMORY_MB	10

static const char *files[4] = {
  "CHANNEL2.TXT",
  "CHANNEL3.TXT",
  "CHANNEL4.TXT",
  "CHANNEL6.TXT"
};
static uint8_t files_status[4] = {
  FILE_CLOSE,
  FILE_CLOSE,
  FILE_CLOSE,
  FILE_CLOSE
};
uint32_t byteswriten;
FATFS SDFatFS;
char SDPath[4];
FIL fileobj[4];
QNODE data; /* 队列数据节点 */

FRESULT open_append(FIL* fp, const char* path)
{
	FRESULT fr;
	fr = f_open(fp, path, FA_WRITE | FA_OPEN_ALWAYS);
	if (fr == FR_OK) {
		fr = f_lseek(fp, f_size(fp));
		if (fr != FR_OK) {
			f_close(fp);
		}
	}
	return fr;
}
/* 查询SD卡内存容量 */
static uint8_t query_sd_memory(void)
{
	int ret;
	DWORD file_nclst;
	uint32_t free_mbs;
	FATFS *fs = &SDFatfs;
	
	ret = f_getfree(SDPath, &file_nclst, &fs);
	if (ret != FR_OK) {
		printf("f_getfree = %d \n", ret);
    }
	free_mbs = file_nclst * (f->csize) / 2 / 1024;
	if (free_mbs <= SD_LIMIT_MEMORY_MB)
		return SD_FULL_MEM;
	return SD_NOT_FULL_MEM;
}

void fatfs_link_driver_init(void)
{
	if (FATFS_LinkDriver(&SD_Driver, SDPath) == 0) {
		if (f_mount(&SDFatfs, SDPath, 0) != FR_OK) {
			printf("SD卡挂载文件系统失败!\n");
		}
	}
}
void log_task(void)
{
	int ret;
	static uint8_t sd_memory_status = SD_NOT_FULL_MEM	;
	
	if (false == queue_remove(&g_queue, &data)) return;
	if (BSP_SD_IsDetected() == SD_NOT_PRESENT || g_system_restart) return;
	if (sd_memory_status == SD_FULL_MEM	) return;
	if (files_status[data.channel] == FILE_CLOSE) {
		open_append(&fileobj[data.channel], files[data.channel]);
		files_status[data.channel] = FILE_OPEN;
	}
	sd_memory_status = query_sd_memory();
	ret = f_write(&fileobj[data.channel], data.data, data.len, &byteswritten);
	if (ret != FR_OK || byteswritten != data.len) {
		f_close(&fileobj[data.channel]);
		files_status[data.channel] = FILE_CLOSE;
	}
	ret = f_sync(&fileobj[data.channel]);
	if (ret != FR_OK) {
		f_close(&fileobj[data.channel]);
		files_status[data.channel] = FILE_CLOSE;
	}
}

总结

经过以上一些处理,可以提高SD卡读写速度,除了优化SD底层驱动外,对于写失败或关闭文件失败的情况并没有很好的应对方案。如果您有更好的建议,欢迎留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章