2025-04-18 18:59:53 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @file FluxSD.c
|
|
|
|
|
|
* @brief SD卡源文件
|
|
|
|
|
|
*
|
|
|
|
|
|
* 用于存储测试日志
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author wang xiang en
|
|
|
|
|
|
* @date 2025-04-18
|
|
|
|
|
|
* @version 版本号
|
|
|
|
|
|
* @copyright 版权声明((C)2025, YUWELL MEDTECH Co.ltd)
|
2025-03-07 19:07:31 +08:00
|
|
|
|
*/
|
|
|
|
|
|
#include "FluxSD.h"
|
|
|
|
|
|
|
2025-04-21 14:06:52 +08:00
|
|
|
|
/* 用于信息存储的SD卡结构体 */
|
|
|
|
|
|
struct SD_Data sdData;
|
|
|
|
|
|
|
2025-04-18 18:59:53 +08:00
|
|
|
|
/* SD卡配置 */
|
2025-03-15 09:37:07 +08:00
|
|
|
|
const static char *SD_TAG = "SD_TEST";
|
2025-03-26 18:59:32 +08:00
|
|
|
|
|
2025-03-07 19:07:31 +08:00
|
|
|
|
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,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-04-18 18:59:53 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 等待SD引脚状态改变
|
|
|
|
|
|
*
|
|
|
|
|
|
* SD卡引脚检测子函数
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param[in] i 序号值
|
|
|
|
|
|
* @param[in] level 检测的电平
|
|
|
|
|
|
* @param[in] timeout 超时时间
|
|
|
|
|
|
* @return 返回值说明
|
|
|
|
|
|
*/
|
2025-03-07 19:07:31 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-18 18:59:53 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief SD卡引脚检测
|
|
|
|
|
|
*
|
|
|
|
|
|
* 测试SD卡引脚驱动能力及配置情况
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param[in] config 配置情况
|
|
|
|
|
|
* @param[in] pin_count 引脚数目
|
|
|
|
|
|
*/
|
2025-04-19 09:30:40 +08:00
|
|
|
|
static void check_sd_card_pins(pin_configuration_t *config, const int pin_count)
|
2025-03-07 19:07:31 +08:00
|
|
|
|
{
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "Testing SD pin connections and pullup strength");
|
2025-03-07 19:07:31 +08:00
|
|
|
|
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]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-19 09:30:40 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief SD卡写测试
|
|
|
|
|
|
*
|
|
|
|
|
|
* 向特定路径写入一段字符串
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param[in] path 需要写入的路径
|
|
|
|
|
|
* @param[in] data 要写入的数据
|
|
|
|
|
|
*/
|
2025-03-07 19:07:31 +08:00
|
|
|
|
static esp_err_t s_example_write_file(const char *path, char *data)
|
|
|
|
|
|
{
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "Opening file %s", path);
|
2025-03-26 18:59:32 +08:00
|
|
|
|
FILE *f = fopen(path, "a");
|
2025-03-07 19:07:31 +08:00
|
|
|
|
if (f == NULL) {
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGE(SD_TAG, "Failed to open file for writing");
|
2025-03-07 19:07:31 +08:00
|
|
|
|
return ESP_FAIL;
|
|
|
|
|
|
}
|
|
|
|
|
|
fprintf(f, data);
|
|
|
|
|
|
fclose(f);
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "File written");
|
2025-03-07 19:07:31 +08:00
|
|
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-19 09:30:40 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 向SD卡中写入日志
|
|
|
|
|
|
*
|
|
|
|
|
|
* 向已定路径写入一段字符串
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param[in] data 要写入的数据
|
|
|
|
|
|
*/
|
2025-04-21 14:06:52 +08:00
|
|
|
|
esp_err_t example_write_log(char *data)
|
2025-03-26 18:59:32 +08:00
|
|
|
|
{
|
2025-04-21 14:06:52 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "Opening file %s", sdData.log_file_dir);
|
|
|
|
|
|
|
|
|
|
|
|
FILE *f = fopen(sdData.log_file_dir, "a+");
|
2025-03-26 18:59:32 +08:00
|
|
|
|
if (f == NULL) {
|
|
|
|
|
|
ESP_LOGE(SD_TAG, "Failed to open file for writing");
|
|
|
|
|
|
return ESP_FAIL;
|
|
|
|
|
|
}
|
2025-04-21 14:06:52 +08:00
|
|
|
|
fprintf(f, "%s - %s\n",sdData.strftime_buf,data);
|
2025-03-26 18:59:32 +08:00
|
|
|
|
fclose(f);
|
2025-04-21 14:06:52 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "File written to log file %s",sdData.log_file_dir);
|
2025-03-26 18:59:32 +08:00
|
|
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-19 09:30:40 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief SD卡读测试
|
|
|
|
|
|
*
|
|
|
|
|
|
* 读取一段字符串
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param[in] path 读取的文件所在的路径
|
|
|
|
|
|
*/
|
2025-03-07 19:07:31 +08:00
|
|
|
|
static esp_err_t s_example_read_file(const char *path)
|
|
|
|
|
|
{
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "Reading file %s", path);
|
2025-04-21 14:06:52 +08:00
|
|
|
|
|
|
|
|
|
|
/* 创建文件控制句柄 */
|
2025-03-07 19:07:31 +08:00
|
|
|
|
FILE *f = fopen(path, "r");
|
|
|
|
|
|
if (f == NULL) {
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGE(SD_TAG, "Failed to open file for reading");
|
2025-03-07 19:07:31 +08:00
|
|
|
|
return ESP_FAIL;
|
|
|
|
|
|
}
|
2025-04-21 14:06:52 +08:00
|
|
|
|
|
2025-03-07 19:07:31 +08:00
|
|
|
|
char line[EXAMPLE_MAX_CHAR_SIZE];
|
2025-04-21 14:06:52 +08:00
|
|
|
|
fgets(line, sizeof(line), f);//整行读取
|
|
|
|
|
|
|
2025-03-26 18:59:32 +08:00
|
|
|
|
//fread(line, sizeof(line), f);
|
2025-03-07 19:07:31 +08:00
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
|
|
|
|
// strip newline
|
|
|
|
|
|
char *pos = strchr(line, '\n');
|
|
|
|
|
|
if (pos) {
|
|
|
|
|
|
*pos = '\0';
|
|
|
|
|
|
}
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "Read from file: '%s'", line);
|
2025-03-07 19:07:31 +08:00
|
|
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-19 09:30:40 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief SD卡初始化
|
|
|
|
|
|
*
|
|
|
|
|
|
* 初始化SD读写总线,将文件系统挂载到固定挂载点。
|
|
|
|
|
|
*
|
|
|
|
|
|
*/
|
2025-03-07 19:07:31 +08:00
|
|
|
|
void flux_sd_init(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
esp_err_t ret;
|
|
|
|
|
|
|
|
|
|
|
|
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
2025-03-08 14:45:49 +08:00
|
|
|
|
.format_if_mount_failed = true,
|
2025-03-07 19:07:31 +08:00
|
|
|
|
.max_files = 5,
|
|
|
|
|
|
.allocation_unit_size = 16 * 1024
|
|
|
|
|
|
};
|
2025-04-21 14:06:52 +08:00
|
|
|
|
|
|
|
|
|
|
/* 初始化SD卡 */
|
2025-03-07 19:07:31 +08:00
|
|
|
|
sdmmc_card_t *card;
|
|
|
|
|
|
const char mount_point[] = MOUNT_POINT;
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "Initializing SD card");
|
2025-03-07 19:07:31 +08:00
|
|
|
|
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "Using SDMMC peripheral");
|
2025-03-07 19:07:31 +08:00
|
|
|
|
|
|
|
|
|
|
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;
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "Mounting filesystem");
|
2025-03-07 19:07:31 +08:00
|
|
|
|
ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
|
|
|
|
|
|
|
|
|
|
|
|
if (ret != ESP_OK) {
|
|
|
|
|
|
if (ret == ESP_FAIL) {
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGE(SD_TAG, "Failed to mount filesystem. "
|
2025-03-07 19:07:31 +08:00
|
|
|
|
"If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
|
|
|
|
|
|
} else {
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGE(SD_TAG, "Failed to initialize the card (%s). "
|
2025-03-07 19:07:31 +08:00
|
|
|
|
"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
|
|
|
|
|
|
check_sd_card_pins(&config, pin_count);
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-15 09:37:07 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "Filesystem mounted");
|
|
|
|
|
|
|
2025-04-19 09:30:40 +08:00
|
|
|
|
/* 打印SD卡信息 */
|
2025-03-07 19:07:31 +08:00
|
|
|
|
sdmmc_card_print_info(stdout, card);
|
|
|
|
|
|
|
2025-03-15 09:37:07 +08:00
|
|
|
|
#ifdef CONFIG_WRITE_TEST
|
|
|
|
|
|
/* File write test example */
|
2025-03-07 19:07:31 +08:00
|
|
|
|
const char *file_hello = MOUNT_POINT"/hello.txt";
|
|
|
|
|
|
char data[EXAMPLE_MAX_CHAR_SIZE];
|
2025-03-13 14:04:19 +08:00
|
|
|
|
|
2025-03-07 19:07:31 +08:00
|
|
|
|
snprintf(data, EXAMPLE_MAX_CHAR_SIZE, "%s %s!\n", "Hello how are you", card->cid.name);
|
2025-03-13 14:04:19 +08:00
|
|
|
|
|
2025-03-07 19:07:31 +08:00
|
|
|
|
ret = s_example_write_file(file_hello, data);
|
|
|
|
|
|
if (ret != ESP_OK) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-03-15 09:37:07 +08:00
|
|
|
|
#endif
|
2025-03-10 18:07:34 +08:00
|
|
|
|
|
2025-04-21 14:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 获取文件索引
|
|
|
|
|
|
*
|
2025-04-21 17:46:58 +08:00
|
|
|
|
* 1.每次开机后重置当前文件索引值
|
2025-04-21 14:06:52 +08:00
|
|
|
|
* 以该索引值新建日志文件、测试结果输出文件
|
|
|
|
|
|
*
|
2025-04-21 17:46:58 +08:00
|
|
|
|
* 2.当用于存储索引的文件不存在时进行自动新建索引文件
|
2025-04-21 14:06:52 +08:00
|
|
|
|
*/
|
|
|
|
|
|
esp_err_t sd_current_fileIndex_get(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* 读取当前文件索引值 */
|
|
|
|
|
|
ESP_LOGI(SD_TAG, "Opening file %s", INDEX_FILE_NAME);
|
|
|
|
|
|
|
2025-04-21 17:46:58 +08:00
|
|
|
|
/* 判断文件是否存在 */
|
|
|
|
|
|
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;
|
2025-04-21 14:06:52 +08:00
|
|
|
|
|
2025-04-21 17:46:58 +08:00
|
|
|
|
ESP_LOGI(SD_TAG, "File not found, creating new file");
|
2025-04-21 14:06:52 +08:00
|
|
|
|
|
2025-04-21 17:46:58 +08:00
|
|
|
|
}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);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2025-04-21 14:06:52 +08:00
|
|
|
|
|
|
|
|
|
|
/* 重置索引值 */
|
2025-04-21 17:46:58 +08:00
|
|
|
|
FILE *new_index_f = fopen(INDEX_FILE_NAME, "w");
|
|
|
|
|
|
if (new_index_f == NULL)
|
2025-04-21 14:06:52 +08:00
|
|
|
|
{
|
|
|
|
|
|
return ESP_FAIL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-21 17:46:58 +08:00
|
|
|
|
fprintf(new_index_f, "%d", sdData.file_index+1);
|
|
|
|
|
|
fclose(new_index_f);
|
2025-04-21 14:06:52 +08:00
|
|
|
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 向SD卡中写入测试结果
|
|
|
|
|
|
*
|
|
|
|
|
|
* 1.文件头为 时间戳、设备类型、挡位、频率、流量体积
|
|
|
|
|
|
* 2.向总的测试结果文件写入测试结果
|
|
|
|
|
|
*/
|
|
|
|
|
|
esp_err_t sd_testData_write(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* 打开当前测试文件 */
|
|
|
|
|
|
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,%.3f\n",\
|
|
|
|
|
|
sdData.current_time,\
|
|
|
|
|
|
sdData.strftime_buf,\
|
|
|
|
|
|
sdData.flux_test_result.current_device_type,\
|
|
|
|
|
|
sdData.flux_test_result.current_stage,\
|
|
|
|
|
|
sdData.flux_test_result.current_rate,\
|
|
|
|
|
|
sdData.flux_test_result.current_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,%.3f\n",\
|
|
|
|
|
|
sdData.current_time,\
|
|
|
|
|
|
sdData.strftime_buf,\
|
|
|
|
|
|
sdData.flux_test_result.current_device_type,\
|
|
|
|
|
|
sdData.flux_test_result.current_stage,\
|
|
|
|
|
|
sdData.flux_test_result.current_rate,\
|
|
|
|
|
|
sdData.flux_test_result.current_test_result
|
|
|
|
|
|
);
|
|
|
|
|
|
fclose(total_test_f);
|
|
|
|
|
|
|
|
|
|
|
|
return ESP_OK;
|
2025-03-15 09:37:07 +08:00
|
|
|
|
}
|