单片机学习小组
直播中

王霞

7年用户 1280经验值
私信 关注

如何提高FATFS SD性能?

受到警告
提示: 作者被禁止或删除 内容自动屏蔽

回帖(1)

黄远飞

2022-2-11 10:54:19
最近开发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底层驱动外,对于写失败或关闭文件失败的情况并没有很好的应对方案。如果您有更好的建议,欢迎留言。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分