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