竭盡所能提高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底層驅動外,對於寫失敗或關閉文件失敗的情況並沒有很好的應對方案。如果您有更好的建議,歡迎留言。

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