/** * @file FluxSD.c * @brief SD卡源文件 * * 用于存储测试日志 * * @author wang xiang en * @date 2025-04-18 * @version 版本号 * @copyright 版权声明((C)2025, YUWELL MEDTECH Co.ltd) */ #include "FluxSD.h" /* 用于信息存储的SD卡结构体 */ struct SD_Data sdData; /* SD卡配置 */ const static char *SD_TAG = "SD_TEST"; const char* names[] = {"CLK", "CMD", "D0", "D1", "D2", "D3"}; const int pins[] = {CONFIG_EXAMPLE_PIN_CLK, CONFIG_EXAMPLE_PIN_CMD, CONFIG_EXAMPLE_PIN_D0, CONFIG_EXAMPLE_PIN_D1, CONFIG_EXAMPLE_PIN_D2, CONFIG_EXAMPLE_PIN_D3 }; const int pin_count = sizeof(pins)/sizeof(pins[0]); pin_configuration_t config = { .names = names, .pins = pins, }; /** * @brief 等待SD引脚状态改变 * * SD卡引脚检测子函数 * * @param[in] i 序号值 * @param[in] level 检测的电平 * @param[in] timeout 超时时间 * @return 返回值说明 */ static uint32_t get_cycles_until_pin_level(int i, int level, int timeout) { uint32_t start = esp_cpu_get_cycle_count(); while(gpio_get_level(i) == !level && esp_cpu_get_cycle_count() - start < timeout) { ; } uint32_t end = esp_cpu_get_cycle_count(); return end - start; } /** * @brief SD卡引脚检测 * * 测试SD卡引脚驱动能力及配置情况 * * @param[in] config 配置情况 * @param[in] pin_count 引脚数目 */ static void check_sd_card_pins(pin_configuration_t *config, const int pin_count) { ESP_LOGI(SD_TAG, "Testing SD pin connections and pullup strength"); gpio_config_t io_conf = {}; for (int i = 0; i < pin_count; ++i) { io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL(config->pins[i]); io_conf.pull_down_en = 0; io_conf.pull_up_en = 0; gpio_config(&io_conf); } printf("\n**** PIN recovery time ****\n\n"); for (int i = 0; i < pin_count; ++i) { gpio_set_direction(config->pins[i], GPIO_MODE_INPUT_OUTPUT_OD); gpio_set_level(config->pins[i], 0); usleep(100); gpio_set_level(config->pins[i], 1); uint32_t cycles = get_cycles_until_pin_level(config->pins[i], 1, 10000); printf("PIN %2d %3s %"PRIu32" cycles\n", config->pins[i], config->names[i], cycles); } printf("\n**** PIN recovery time with weak pullup ****\n\n"); for (int i = 0; i < pin_count; ++i) { gpio_set_direction(config->pins[i], GPIO_MODE_INPUT_OUTPUT_OD); gpio_pullup_en(config->pins[i]); gpio_set_level(config->pins[i], 0); usleep(100); gpio_set_level(config->pins[i], 1); uint32_t cycles = get_cycles_until_pin_level(config->pins[i], 1, 10000); printf("PIN %2d %3s %"PRIu32" cycles\n", config->pins[i], config->names[i], cycles); gpio_pullup_dis(config->pins[i]); } } /** * @brief SD卡写测试 * * 向特定路径写入一段字符串 * * @param[in] path 需要写入的路径 * @param[in] data 要写入的数据 */ static esp_err_t s_example_write_file(const char *path, char *data) { ESP_LOGI(SD_TAG, "Opening file %s", path); FILE *f = fopen(path, "a"); if (f == NULL) { ESP_LOGE(SD_TAG, "Failed to open file for writing"); return ESP_FAIL; } fprintf(f, data); fclose(f); ESP_LOGI(SD_TAG, "File written"); return ESP_OK; } /** * @brief 向SD卡中写入日志 * * 向已定路径写入一段字符串 * * @param[in] data 要写入的数据 * @param[in] file_name 文件名 * @param[in] functionName 函数名 * @param[in] taskName 任务名 * @param[in] line_num 行号 * @param[in] freeStack 空闲栈大小 * @return 返回值说明 0:成功,其他:失败 */ esp_err_t example_write_log(char *data,char *file_name,char *functionName,char *taskName,uint16_t line_num,int32_t freeStack) { FILE *f = fopen(sdData.log_file_dir, "a+"); if (f == NULL) { ESP_LOGE(SD_TAG, "Failed to open file for writing"); return ESP_FAIL; } fprintf(f, "%s,%s,%s,%s,%d,%ld,%s\n",\ sdData.strftime_buf,\ file_name,\ functionName,\ taskName,\ line_num,\ freeStack,\ data ); fclose(f); //ESP_LOGI(SD_TAG, "File written to log file %s",sdData.log_file_dir); return ESP_OK; } /** * @brief SD卡读测试 * * 读取一段字符串 * * @param[in] path 读取的文件所在的路径 */ static esp_err_t s_example_read_file(const char *path) { ESP_LOGI(SD_TAG, "Reading file %s", path); /* 创建文件控制句柄 */ FILE *f = fopen(path, "r"); if (f == NULL) { ESP_LOGE(SD_TAG, "Failed to open file for reading"); return ESP_FAIL; } char line[EXAMPLE_MAX_CHAR_SIZE]; fgets(line, sizeof(line), f);//整行读取 //fread(line, sizeof(line), f); fclose(f); // strip newline char *pos = strchr(line, '\n'); if (pos) { *pos = '\0'; } ESP_LOGI(SD_TAG, "Read from file: '%s'", line); return ESP_OK; } /** * @brief SD卡初始化 * * 初始化SD读写总线,将文件系统挂载到固定挂载点。 * */ void flux_sd_init(void) { esp_err_t ret; esp_vfs_fat_sdmmc_mount_config_t mount_config = { .format_if_mount_failed = true, .max_files = 5, .allocation_unit_size = 16 * 1024 }; /* 初始化SD卡 */ sdmmc_card_t *card; const char mount_point[] = MOUNT_POINT; ESP_LOGI(SD_TAG, "Initializing SD card"); ESP_LOGI(SD_TAG, "Using SDMMC peripheral"); sdmmc_host_t host = SDMMC_HOST_DEFAULT(); sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); slot_config.width = 4; slot_config.clk = CONFIG_EXAMPLE_PIN_CLK; slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD; slot_config.d0 = CONFIG_EXAMPLE_PIN_D0; slot_config.d1 = CONFIG_EXAMPLE_PIN_D1; slot_config.d2 = CONFIG_EXAMPLE_PIN_D2; slot_config.d3 = CONFIG_EXAMPLE_PIN_D3; slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; ESP_LOGI(SD_TAG, "Mounting filesystem"); ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card); if (ret != ESP_OK) { if (ret == ESP_FAIL) { ESP_LOGE(SD_TAG, "Failed to mount filesystem. " "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option."); } else { ESP_LOGE(SD_TAG, "Failed to initialize the card (%s). " "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret)); check_sd_card_pins(&config, pin_count); } return; } ESP_LOGI(SD_TAG, "Filesystem mounted"); /* 打印SD卡信息 */ sdmmc_card_print_info(stdout, card); } /** * @brief 获取文件索引 * * 1.每次开机后重置当前文件索引值 * 以该索引值新建日志文件、测试结果输出文件 * * 2.当用于存储索引的文件不存在时进行自动新建索引文件 */ esp_err_t sd_current_fileIndex_get(void) { /* 读取当前文件索引值 */ ESP_LOGI(SD_TAG, "Opening file %s", INDEX_FILE_NAME); /* 判断文件是否存在 */ if (access(INDEX_FILE_NAME, F_OK) == -1) { /*当文件不存在时创建文件*/ FILE *new_f = fopen(INDEX_FILE_NAME, "w"); fprintf(new_f, "0"); fclose(new_f); sdData.file_index = 0; ESP_LOGI(SD_TAG, "File not found, creating new file"); }else{ /* 当文件存在时直接获取当前索引值 */ FILE *f = fopen(INDEX_FILE_NAME, "r"); if (f == NULL) { ESP_LOGE(SD_TAG, "Failed to open file for reading"); return ESP_FAIL; } char line[EXAMPLE_MAX_CHAR_SIZE]; fgets(line, sizeof(line), f); fclose(f); sdData.file_index = atoi(line); ESP_LOGI(SD_TAG, "Read from file: '%s' , current Index = %d", line,sdData.file_index); } /* 重置索引值 */ FILE *new_index_f = fopen(INDEX_FILE_NAME, "w"); if (new_index_f == NULL) { return ESP_FAIL; } fprintf(new_index_f, "%d", sdData.file_index+1); fclose(new_index_f); return ESP_OK; } /** * @brief 向SD卡中写入测试结果 * * 1.文件头为 时间戳、设备类型、挡位、频率、流量体积 * 2.向总的测试结果文件写入测试结果 */ esp_err_t sd_testData_write(bool is_nom,float test_result) { if (is_test_mode_nom) { /* 获取当前挡位及呼吸频率 */ int32_t current_stage = lv_spinbox_get_value(ui_pageHome_spinboxStage); int32_t current_rate = lv_spinbox_get_value(ui_pageHome_spinboxRate); /* 打开当前测试文件 */ FILE *current_test_f = fopen(sdData.test_file_dir, "a+"); if (current_test_f == NULL) { ESP_LOGE(SD_TAG, "Failed to open file for writing"); return ESP_FAIL; } fprintf(current_test_f,"%lld,%s,%d,%d,%ld,%ld,%.3f\n",\ sdData.current_time,\ sdData.strftime_buf,\ is_nom,\ sdData.flux_test_result.current_device_type,\ current_stage,\ current_rate,\ test_result ); fclose(current_test_f); /* 打开总的测试结果文件 */ FILE *total_test_f = fopen(TEST_FILE_NAME, "a+"); if (total_test_f == NULL) { ESP_LOGE(SD_TAG, "Failed to open file for writing"); return ESP_FAIL; } fprintf(total_test_f,"%lld,%s,%d,%d,%ld,%ld,%.3f\n",\ sdData.current_time,\ sdData.strftime_buf,\ is_nom,\ sdData.flux_test_result.current_device_type,\ current_stage,\ current_rate,\ test_result ); fclose(total_test_f); } return ESP_OK; } /** * @brief 向指定目录中传入数据 * * 1.文件头为 时间戳、设备类型、挡位、频率、流量体积 * 2.向总的测试结果文件写入测试结果 */ void sd_append_bs_data(void) { /* 打开bs测试结果文件 */ FILE *bs_test_f = fopen(sdData.bs_file_dir, "a+"); if (bs_test_f == NULL) { ESP_LOGE(SD_TAG, "Failed to open file for writing"); return; } fprintf(bs_test_f,"%lld,%s,%d,%ld,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f\n",\ sdData.current_time,\ sdData.strftime_buf,\ lv_dropdown_get_selected(ui_pageHome_DropdownTestTypeBS),\ lv_spinbox_get_value(ui_pageHome_spinboxStageBS),\ sdData.flux_test_result.test_result[BS_RATE_15BPM],\ sdData.flux_test_result.test_result[BS_RATE_20BPM],\ sdData.flux_test_result.test_result[BS_RATE_25BPM],\ sdData.flux_test_result.test_result[BS_RATE_30BPM],\ sdData.flux_test_result.test_result[BS_RATE_35BPM],\ sdData.flux_test_result.test_result[BS_RATE_40BPM] ); fclose(bs_test_f); }