#include "board.h" #include "soft_sdcard.h" #include "ff.h" #include "hard_sdio_sd.h" #include "my_math.h" #include "soft_delay.h" #include "soft_time.h" #include #include #include #include "test.h" // 掉hpm的sd fatfs库 /* //==========================连续写入测试============================= WriteBuffer字节 写入速度 写固定字节数 写大数据块 512 475kb 写512字节用时1052us 写2*1024*1024字节用时4310us 1024 563kb 写1024字节用时1775us 写2*1024*1024字节用时3637us 2048 1280kb 写2048字节用时1562us 写2*1024*1024字节用时1600us 4096 2528KB 写4096字节用时1582us 写2*1024*1024字节用时810us /////////////=============实际测试==非连续写入==写512字节用时平均4500us========问题出在哪里???? 实际测试 正常记录过程中拔掉TF卡不会造成死机。f_write之前会检查系统,如果CARD_ERR会直接返回。且不耗时间,直接返回。 不连续记录时,每次写512字节用时4000us。写5120字节用时5000us。。。所以猜测写操作的时间主要浪费在了数据发送完成后的写入环节。。 */ /** ****************************************************************************** * 定义变量 ****************************************************************************** */ FATFS fs; /* FatFs文件系统对象 */ FIL fnew_data, fnew_log; /* 文件对象 */ FRESULT res_sd; /* 文件操作结果 */ UINT frnum; /* 文件成功读数量 */ UINT fwnum; /* 文件成功写数量 */ // BYTE ReadBuffer[_MAX_SS] = {0}; /* 读缓冲区 */ // BYTE WriteBuffer[_MAX_SS] = {0}; /* 写缓冲区*/ BYTE work[FF_MAX_SS] = {0}; /* 工作区 (larger is better for processing time) */ DIR logdir; DIR posdir; FILINFO fno; int file_num = 0; const TCHAR driver_num_buf[3] = { DEV_SD + '0', ':', '/' }; // FATFS文件名不区分大小写。默认大写。 const char *logpath = "LOG"; const char *pospath = "POS"; static uint8_t _sd_init_ok = 0; const char *show_error_string(FRESULT fresult); static FRESULT sd_mount_fs(void); static FRESULT sd_mkfs(void); uint8_t sdcard_initok(void) { return _sd_init_ok; } static FRESULT sd_mount_fs(void) { FRESULT fresult = f_mount(&fs, driver_num_buf, 1); if (fresult == FR_OK) { printf("SD card has been mounted successfully\n"); } else { printf("Failed to mount SD card, cause: %s\n", show_error_string(fresult)); } fresult = f_chdrive(driver_num_buf); return fresult; } static FRESULT sd_mkfs(void) { printf("Formatting the SD card, depending on the SD card capacity, the formatting process may take a long time\n"); FRESULT fresult = f_mkfs(driver_num_buf, NULL, work, sizeof(work)); if (fresult != FR_OK) { printf("Making File system failed, cause: %s\n", show_error_string(fresult)); } else { printf("Making file system is successful\n"); } return fresult; } const char *show_error_string(FRESULT fresult) { const char *result_str; switch (fresult) { case FR_OK: result_str = "succeeded"; break; case FR_DISK_ERR: result_str = "A hard error occurred in the low level disk I/O level"; break; case FR_INT_ERR: result_str = "Assertion failed"; break; case FR_NOT_READY: result_str = "The physical drive cannot work"; break; case FR_NO_FILE: result_str = "Could not find the file"; break; case FR_NO_PATH: result_str = "Could not find the path"; break; case FR_INVALID_NAME: result_str = "Tha path name format is invalid"; break; case FR_DENIED: result_str = "Access denied due to prohibited access or directory full"; break; case FR_EXIST: result_str = "Access denied due to prohibited access"; break; case FR_INVALID_OBJECT: result_str = "The file/directory object is invalid"; break; case FR_WRITE_PROTECTED: result_str = "The physical drive is write protected"; break; case FR_INVALID_DRIVE: result_str = "The logical driver number is invalid"; break; case FR_NOT_ENABLED: result_str = "The volume has no work area"; break; case FR_NO_FILESYSTEM: result_str = "There is no valid FAT volume"; break; case FR_MKFS_ABORTED: result_str = "THe f_mkfs() aborted due to any problem"; break; case FR_TIMEOUT: result_str = "Could not get a grant to access the volume within defined period"; break; case FR_LOCKED: result_str = "The operation is rejected according to the file sharing policy"; break; case FR_NOT_ENOUGH_CORE: result_str = "LFN working buffer could not be allocated"; break; case FR_TOO_MANY_OPEN_FILES: result_str = "Number of open files > FF_FS_LOCK"; break; case FR_INVALID_PARAMETER: result_str = "Given parameter is invalid"; break; default: result_str = "Unknown error"; break; } return result_str; } /** * @brief 初始化 SD 卡 * @param init_type * @arg 0 初始化 SD 卡, 1 格式化 SD 卡 */ void sdcard_inital(uint8_t init_type) { bool need_init_filesystem = true; DSTATUS dstatus = disk_status(DEV_SD); printf("sd state is %d\r\n", dstatus); // 找到SD卡 if (dstatus == STA_NODISK) { printf("No disk in the SD slot, please insert an SD card...\n"); do { dstatus = disk_status(DEV_SD); } while (dstatus == STA_NODISK); board_delay_ms(100); printf("Detected SD card, re-initialize the filesystem...\n"); need_init_filesystem = true; } dstatus = disk_initialize(DEV_SD); if (dstatus != RES_OK) { printf("Failed to initialize SD disk\n"); } if (need_init_filesystem) { res_sd = sd_mount_fs(); if (res_sd == FR_NO_FILESYSTEM || init_type == 1) { printf("There is no File system available, making file system...\n"); res_sd = sd_mkfs(); if (res_sd != FR_OK) { printf("Failed to make filesystem, cause:%s\n", show_error_string(res_sd)); } else { need_init_filesystem = false; } } } // 创建文件夹 res_sd = f_mkdir(logpath); if (res_sd != FR_OK && res_sd != FR_EXIST) { printf("Failed to create LOG directory, cause: %d\n", res_sd); } res_sd = f_mkdir(pospath); if (res_sd != FR_OK && res_sd != FR_EXIST) { printf("Failed to create POS directory, cause: %d\n", res_sd); } // 检查是否存在LOG文件 res_sd = f_open(&fnew_data, "LOG/LOG_FLY.DAT", FA_CREATE_NEW); if (res_sd == FR_OK) { f_close(&fnew_data); printf("LOG_FLY.DAT file created\n"); } else if (res_sd == FR_EXIST) { printf("LOG_FLY.DAT file already exists\n"); } else { printf("Failed to create LOG_FLY.DAT, cause: %d\n", res_sd); } DWORD nclst; unsigned long free_size = 0; FATFS *fs_p = &fs; // 检查剩余容量 res_sd = f_getfree(driver_num_buf, &nclst, &fs_p); if (res_sd == FR_OK) { free_size = nclst * fs_p->csize / 2; /* 单位:KB */ printf("Free space: %lu KB\n", free_size); } else { printf("Failed to get free space, cause: %d\n", res_sd); } // 打开log文件夹, 读下一个的文件 res_sd = f_opendir(&logdir, logpath); if (res_sd == FR_OK) { res_sd = f_readdir(&logdir, &fno); // 如果剩余容量小于96MB, 删除部分数据文件, 防止写满 while (free_size < 96 * 1024 && res_sd == FR_OK && fno.fname[0] != 0) { // 跳过目录和LOG_FLY.DAT文件 if (fno.fname[0] != 'L' && !(fno.fattrib & AM_DIR)) { char datafile_path[50] = {0}; // 拼接文件路径 strcat(datafile_path, logpath); strcat(datafile_path, "/"); strcat(datafile_path, fno.fname); // 删除一个文件 printf("Deleting old file: %s\n", datafile_path); f_unlink(datafile_path); // 再读一下剩余的容量是否够用 res_sd = f_getfree("", &nclst, &fs_p); if (res_sd == FR_OK) { free_size = nclst * fs_p->csize / 2; printf("Free space now: %lu KB\n", free_size); } } // 读下一个文件 res_sd = f_readdir(&logdir, &fno); } // 关闭文件夹 res_sd = f_closedir(&logdir); } else { printf("Failed to open LOG directory, cause: %d\n", res_sd); } _sd_init_ok = 1; printf("SD card initialization completed!\n"); } #ifdef SOFT_SD_TEST // 测试代码 - 注意:这里不再重复包含头文件,因为上面已经包含了 // 测试配置 #define TEST_FILE_NAME "test_speed.bin" #define TEST_BUFFER_SIZE 4096 // 测试缓冲区大小 #define TEST_DATA_SIZE (2 * 1024 * 1024) // 测试数据大小 2MB #define TEST_ITERATIONS 10 // 测试次数 // 测试结果结构体 typedef struct { uint32_t buffer_size; // 缓冲区大小 uint32_t total_bytes; // 总写入字节数 uint32_t total_time_us; // 总耗时(微秒) uint32_t avg_time_per_write_us; // 平均每次写入耗时 float speed_kb_per_sec; // 写入速度(KB/s) } test_result_t; // 函数声明 void sd_speed_test(void); void sd_continuous_write_test(void); void sd_random_write_test(void); void sd_large_block_test(void); void sd_integrity_test(void); void sd_run_tests(void); int sd_write_data(const char *filename, const uint8_t *data, uint32_t size); void main_test_demo(void); /** * @brief 测试SD卡写入速度 */ void sd_speed_test(void) { if (!sdcard_initok()) { printf("SD card not initialized!\n"); return; } printf("\n========== SD Card Speed Test ==========\n"); // 测试1: 连续写入测试 sd_continuous_write_test(); // 测试2: 随机写入测试(模拟实际记录场景) sd_random_write_test(); // 测试3: 不同缓冲区大小测试 sd_large_block_test(); } /** * @brief 连续写入测试 */ void sd_continuous_write_test(void) { test_result_t result; FIL test_file; UINT bytes_written; uint32_t start_time, end_time; uint8_t *buffer; printf("\n--- Continuous Write Test ---\n"); // 分配测试缓冲区 buffer = (uint8_t *)malloc(TEST_BUFFER_SIZE); if (buffer == NULL) { printf("Failed to allocate buffer!\n"); return; } // 填充测试数据 for (uint32_t i = 0; i < TEST_BUFFER_SIZE; i++) { buffer[i] = i & 0xFF; } // 创建测试文件 FRESULT res = f_open(&test_file, TEST_FILE_NAME, FA_WRITE | FA_CREATE_ALWAYS); if (res != FR_OK) { printf("Failed to create test file! Error: %d\n", res); free(buffer); return; } // 开始测试 start_time = micros(); uint32_t remaining = TEST_DATA_SIZE; uint32_t write_count = 0; while (remaining > 0) { uint32_t write_size = (remaining > TEST_BUFFER_SIZE) ? TEST_BUFFER_SIZE : remaining; res = f_write(&test_file, buffer, write_size, &bytes_written); if (res != FR_OK || bytes_written != write_size) { printf("Write failed! Error: %d, Written: %d\n", res, bytes_written); break; } remaining -= bytes_written; write_count++; } end_time = micros(); // 关闭文件 f_close(&test_file); // 计算结果 result.buffer_size = TEST_BUFFER_SIZE; result.total_bytes = TEST_DATA_SIZE; result.total_time_us = end_time - start_time; result.avg_time_per_write_us = result.total_time_us / write_count; result.speed_kb_per_sec = (float)(TEST_DATA_SIZE) / 1024.0f / (result.total_time_us / 1000000.0f); printf("Continuous Write Test Results:\n"); printf(" Buffer size: %d bytes\n", result.buffer_size); printf(" Total data: %d bytes (%.2f MB)\n", result.total_bytes, result.total_bytes / (1024.0f * 1024.0f)); printf(" Write count: %d\n", write_count); printf(" Total time: %d us (%.2f ms)\n", result.total_time_us, result.total_time_us / 1000.0f); printf(" Avg time per write: %d us\n", result.avg_time_per_write_us); printf(" Speed: %.2f KB/s (%.2f MB/s)\n", result.speed_kb_per_sec, result.speed_kb_per_sec / 1024.0f); free(buffer); } /** * @brief 随机写入测试(模拟实际记录场景) */ void sd_random_write_test(void) { test_result_t result; FIL test_file; UINT bytes_written; uint32_t start_time, end_time; uint8_t *buffer; printf("\n--- Random Write Test (Simulating Real Recording) ---\n"); // 分配测试缓冲区 buffer = (uint8_t *)malloc(512); // 模拟实际记录时的512字节数据包 if (buffer == NULL) { printf("Failed to allocate buffer!\n"); return; } // 填充测试数据 for (uint32_t i = 0; i < 512; i++) { buffer[i] = i & 0xFF; } // 创建测试文件 FRESULT res = f_open(&test_file, "random_test.bin", FA_WRITE | FA_CREATE_ALWAYS); if (res != FR_OK) { printf("Failed to create test file! Error: %d\n", res); free(buffer); return; } // 测试不同写入大小的性能 uint32_t test_sizes[] = {512, 1024, 2048, 4096}; int test_count = sizeof(test_sizes) / sizeof(test_sizes[0]); for (int t = 0; t < test_count; t++) { uint32_t write_size = test_sizes[t]; uint32_t total_data = 2 * 1024 * 1024; // 2MB uint32_t write_count = total_data / write_size; printf("\n Testing write size: %d bytes\n", write_size); // 重新定位文件开头 f_lseek(&test_file, 0); start_time = micros(); for (uint32_t i = 0; i < write_count; i++) { res = f_write(&test_file, buffer, write_size, &bytes_written); if (res != FR_OK || bytes_written != write_size) { printf(" Write failed at %d! Error: %d\n", i, res); break; } } end_time = micros(); result.buffer_size = write_size; result.total_bytes = total_data; result.total_time_us = end_time - start_time; result.avg_time_per_write_us = result.total_time_us / write_count; result.speed_kb_per_sec = (float)(total_data) / 1024.0f / (result.total_time_us / 1000000.0f); printf(" Total time: %d us (%.2f ms)\n", result.total_time_us, result.total_time_us / 1000.0f); printf(" Avg time per write: %d us\n", result.avg_time_per_write_us); printf(" Speed: %.2f KB/s (%.2f MB/s)\n", result.speed_kb_per_sec, result.speed_kb_per_sec / 1024.0f); } // 关闭文件 f_close(&test_file); free(buffer); } /** * @brief 大数据块写入测试 */ void sd_large_block_test(void) { FIL test_file; UINT bytes_written; uint32_t start_time, end_time; uint8_t *buffer; printf("\n--- Large Block Write Test ---\n"); // 测试不同缓冲区大小 uint32_t buffer_sizes[] = {512, 1024, 2048, 4096, 8192}; int test_count = sizeof(buffer_sizes) / sizeof(buffer_sizes[0]); for (int t = 0; t < test_count; t++) { uint32_t buffer_size = buffer_sizes[t]; // 分配测试缓冲区 buffer = (uint8_t *)malloc(buffer_size); if (buffer == NULL) { printf("Failed to allocate buffer size %d!\n", buffer_size); continue; } // 填充测试数据 for (uint32_t i = 0; i < buffer_size; i++) { buffer[i] = i & 0xFF; } // 创建测试文件 FRESULT res = f_open(&test_file, "large_block_test.bin", FA_WRITE | FA_CREATE_ALWAYS); if (res != FR_OK) { printf("Failed to create test file! Error: %d\n", res); free(buffer); continue; } uint32_t total_data = 2 * 1024 * 1024; // 2MB uint32_t write_count = total_data / buffer_size; start_time = micros(); for (uint32_t i = 0; i < write_count; i++) { res = f_write(&test_file, buffer, buffer_size, &bytes_written); if (res != FR_OK || bytes_written != buffer_size) { printf(" Write failed at %d! Error: %d\n", i, res); break; } } end_time = micros(); // 关闭文件 f_close(&test_file); uint32_t total_time_us = end_time - start_time; float speed_kb_per_sec = (float)(total_data) / 1024.0f / (total_time_us / 1000000.0f); printf(" Buffer size: %5d bytes -> Total time: %6d us (%.2f ms) -> Speed: %.2f KB/s (%.2f MB/s)\n", buffer_size, total_time_us, total_time_us / 1000.0f, speed_kb_per_sec, speed_kb_per_sec / 1024.0f); free(buffer); // 删除测试文件 f_unlink("large_block_test.bin"); } } /** * @brief 测试文件写入并验证数据完整性 */ void sd_integrity_test(void) { FIL test_file; UINT bytes_written, bytes_read; uint32_t start_time, end_time; uint8_t *write_buffer, *read_buffer; uint32_t test_size = 1024 ; // 1MB测试数据 printf("\n--- Data Integrity Test ---\n"); if (!sdcard_initok()) { printf("SD card not initialized!\n"); return; } // 分配缓冲区 write_buffer = (uint8_t *)malloc(test_size); read_buffer = (uint8_t *)malloc(test_size); if (write_buffer == NULL || read_buffer == NULL) { printf("Failed to allocate buffers!\n"); if (write_buffer) free(write_buffer); if (read_buffer) free(read_buffer); return; } // 填充测试数据(使用特定的模式) for (uint32_t i = 0; i < test_size; i++) { write_buffer[i] = i & 0xFF; } // 写入测试 FRESULT res = f_open(&test_file, "integrity_test.bin", FA_WRITE | FA_CREATE_ALWAYS); if (res != FR_OK) { printf("Failed to create test file! Error: %d\n", res); goto cleanup; } start_time = micros(); res = f_write(&test_file, write_buffer, test_size, &bytes_written); end_time = micros(); if (res != FR_OK || bytes_written != test_size) { printf("Write failed! Error: %d, Written: %d\n", res, bytes_written); f_close(&test_file); goto cleanup; } printf("Write completed: %d bytes in %d us (%.2f KB/s)\n", bytes_written, end_time - start_time, (float)bytes_written / 1024.0f / ((end_time - start_time) / 1000000.0f)); f_close(&test_file); // 读取测试 res = f_open(&test_file, "integrity_test.bin", FA_READ); if (res != FR_OK) { printf("Failed to open test file for reading! Error: %d\n", res); goto cleanup; } start_time = micros(); res = f_read(&test_file, read_buffer, test_size, &bytes_read); end_time = micros(); if (res != FR_OK || bytes_read != test_size) { printf("Read failed! Error: %d, Read: %d\n", res, bytes_read); f_close(&test_file); goto cleanup; } printf("Read completed: %d bytes in %d us (%.2f KB/s)\n", bytes_read, end_time - start_time, (float)bytes_read / 1024.0f / ((end_time - start_time) / 1000000.0f)); f_close(&test_file); // 验证数据完整性 printf("Verifying data integrity...\n"); int error_count = 0; for (uint32_t i = 0; i < test_size; i++) { if (write_buffer[i] != read_buffer[i]) { if (error_count < 10) { // 只显示前10个错误 printf("Data mismatch at offset %d: expected 0x%02X, got 0x%02X\n", i, write_buffer[i], read_buffer[i]); } error_count++; } } if (error_count == 0) { printf("Data integrity test PASSED! All %d bytes verified.\n", test_size); } else { printf("Data integrity test FAILED! %d errors found.\n", error_count); } // 删除测试文件 f_unlink("integrity_test.bin"); cleanup: free(write_buffer); free(read_buffer); } /** * @brief 主测试函数,可以在主循环中调用 */ void sd_run_tests(void) { printf("\n"); printf("****************************************\n"); printf("* SD Card Performance Test *\n"); printf("****************************************\n"); // 运行各种测试 sd_speed_test(); sd_integrity_test(); printf("\n========== All Tests Completed ==========\n"); } /** * @brief 简单的文件写入测试(用于实际应用) * @param filename 文件名 * @param data 数据指针 * @param size 数据大小 * @return 0:成功, -1:失败 */ int sd_write_data(const char *filename, const uint8_t *data, uint32_t size) { FIL file; UINT bytes_written; uint32_t start_time, end_time; if (!sdcard_initok()) { printf("SD card not ready!\n"); return -1; } // 打开或创建文件 FRESULT res = f_open(&file, filename, FA_WRITE | FA_OPEN_ALWAYS); if (res != FR_OK) { printf("Failed to open file %s! Error: %d\n", filename, res); return -1; } // 移动到文件末尾 f_lseek(&file, f_size(&file)); // 写入数据 start_time = micros(); res = f_write(&file, data, size, &bytes_written); end_time = micros(); if (res != FR_OK || bytes_written != size) { printf("Write failed! Error: %d, Written: %d\n", res, bytes_written); f_close(&file); return -1; } printf("Written %d bytes to %s in %d us (%.2f KB/s)\n", bytes_written, filename, end_time - start_time, (float)bytes_written / 1024.0f / ((end_time - start_time) / 1000000.0f)); f_close(&file); return 0; } #include "hard_system_delay.h" /** * @brief 测试主函数 */ void sd_test_demo(void) { // 先初始化SD卡 sdcard_inital(0); system_time_initial(); if (sdcard_initok()) { // 运行性能测试 sd_run_tests(); // 示例:写入实际数据 uint8_t test_data[512]; for (int i = 0; i < 512; i++) { test_data[i] = i & 0xFF; } // 写入到LOG目录下的测试文件 sd_write_data("LOG/test_data.dat", test_data, sizeof(test_data)); printf("\nAll tests completed. You can now use the SD card for your application.\n"); } else { printf("SD card initialization failed!\n"); } } #endif /* SOFT_SD_TEST */