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