#include "battery.h" #include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali.h" #include "esp_adc/adc_cali_scheme.h" #include "esp_log.h" #include "esp_check.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_lvgl_port.h" #include "../ui/screens/ui_ScreenSet.h" // ScreenHome界面不关联电池电量显示 #include static const char *TAG = "BAT"; // ADC句柄 static adc_oneshot_unit_handle_t adc_handle = NULL; static adc_cali_handle_t cali_handle = NULL; static bool cali_enabled = false; // 当前电池数据 static uint32_t bat_voltage_mv = 0; static uint8_t bat_level = 0; // 锂电池放电曲线查找表(基于典型3.7V单节锂电池放电特性) // 电压单位:毫伏,电量单位:百分比 typedef struct { uint16_t voltage_mv; uint8_t level; } bat_curve_point_t; static const bat_curve_point_t bat_curve[] = { {4200, 100}, {4150, 95}, {4110, 90}, {4080, 85}, {4020, 80}, {3980, 75}, {3950, 70}, {3910, 65}, {3870, 60}, {3840, 55}, {3800, 50}, {3760, 45}, {3730, 40}, {3700, 35}, {3680, 30}, {3650, 25}, {3630, 20}, {3600, 15}, {3570, 10}, {3530, 5}, {3400, 2}, {3000, 0}, }; #define BAT_CURVE_SIZE (sizeof(bat_curve) / sizeof(bat_curve[0])) // 电压转电量(线性插值,提高精度) static uint8_t voltage_to_level(uint32_t voltage_mv) { // 超出上限 if (voltage_mv >= bat_curve[0].voltage_mv) { return 100; } // 低于下限 if (voltage_mv <= bat_curve[BAT_CURVE_SIZE - 1].voltage_mv) { return 0; } // 在查找表中线性插值 for (int i = 0; i < BAT_CURVE_SIZE - 1; i++) { if (voltage_mv >= bat_curve[i + 1].voltage_mv) { uint32_t v_range = bat_curve[i].voltage_mv - bat_curve[i + 1].voltage_mv; uint32_t l_range = bat_curve[i].level - bat_curve[i + 1].level; uint32_t v_offset = voltage_mv - bat_curve[i + 1].voltage_mv; return bat_curve[i + 1].level + (uint8_t)((v_offset * l_range) / v_range); } } return 0; } // 初始化ADC校准 static void battery_cali_init(void) { #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED // ESP32-S3 使用曲线拟合校准 adc_cali_curve_fitting_config_t cali_cfg = { .unit_id = ADC_UNIT_1, .chan = BAT_ADC_CHANNEL, .atten = ADC_ATTEN_DB_11, .bitwidth = ADC_BITWIDTH_DEFAULT, }; esp_err_t ret = adc_cali_create_scheme_curve_fitting(&cali_cfg, &cali_handle); #elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED // 备用:线性拟合校准 adc_cali_line_fitting_config_t cali_cfg = { .unit_id = ADC_UNIT_1, .atten = ADC_ATTEN_DB_11, .bitwidth = ADC_BITWIDTH_DEFAULT, }; esp_err_t ret = adc_cali_create_scheme_line_fitting(&cali_cfg, &cali_handle); #else esp_err_t ret = ESP_ERR_NOT_SUPPORTED; #endif if (ret == ESP_OK) { cali_enabled = true; ESP_LOGI(TAG, "ADC校准初始化成功"); } else { ESP_LOGW(TAG, "ADC校准不可用,将使用原始值换算"); } } esp_err_t battery_init(void) { // 初始化ADC单元 adc_oneshot_unit_init_cfg_t unit_cfg = { .unit_id = ADC_UNIT_1, .ulp_mode = ADC_ULP_MODE_DISABLE, }; ESP_RETURN_ON_ERROR(adc_oneshot_new_unit(&unit_cfg, &adc_handle), TAG, "ADC单元初始化失败"); // 配置ADC通道(11dB衰减,量程约0~2500mV) adc_oneshot_chan_cfg_t chan_cfg = { .atten = ADC_ATTEN_DB_11, .bitwidth = ADC_BITWIDTH_DEFAULT, }; ESP_RETURN_ON_ERROR(adc_oneshot_config_channel(adc_handle, BAT_ADC_CHANNEL, &chan_cfg), TAG, "ADC通道配置失败"); // 初始化校准 battery_cali_init(); ESP_LOGI(TAG, "电池ADC初始化完成 (GPIO%d, ADC1_CH%d, 分压比=%d)", PIN_BAT_ADC, BAT_ADC_CHANNEL, BAT_VOLTAGE_DIVIDER); return ESP_OK; } uint32_t battery_get_voltage_mv(void) { return bat_voltage_mv; } uint8_t battery_get_level(void) { return bat_level; } // 读取ADC并计算电池电压和电量 static void battery_read(void) { int adc_sum = 0; int valid_count = 0; // 多次采样取平均,滤除噪声 for (int i = 0; i < BAT_SAMPLE_COUNT; i++) { int raw; if (adc_oneshot_read(adc_handle, BAT_ADC_CHANNEL, &raw) == ESP_OK) { adc_sum += raw; valid_count++; } vTaskDelay(pdMS_TO_TICKS(2)); } if (valid_count == 0) { ESP_LOGE(TAG, "ADC采样全部失败"); return; } int adc_avg = adc_sum / valid_count; // 使用校准值或原始换算得到ADC引脚电压 int adc_voltage_mv = 0; if (cali_enabled) { adc_cali_raw_to_voltage(cali_handle, adc_avg, &adc_voltage_mv); } else { // 无校准时按3300mV参考电压线性换算 adc_voltage_mv = (adc_avg * 3300) / 4095; } // 乘以分压系数得到实际电池电压 bat_voltage_mv = (uint32_t)adc_voltage_mv * BAT_VOLTAGE_DIVIDER; // 查找表+插值计算电量百分比 bat_level = voltage_to_level(bat_voltage_mv); ESP_LOGI(TAG, "ADC原始值=%d, ADC电压=%dmV, 电池电压=%lumV, 电量=%d%%", adc_avg, adc_voltage_mv, (unsigned long)bat_voltage_mv, bat_level); } // 更新UI电量显示(线程安全) static void battery_update_ui(void) { if (!lvgl_port_lock(100)) { return; } char buf[8]; snprintf(buf, sizeof(buf), "%d%%", bat_level); // 只更新ScreenSet界面的电量圆弧和标签 if (ui_ArcPowerLevel) { lv_arc_set_value(ui_ArcPowerLevel, bat_level); } if (ui_LabelPowerLevel) { lv_label_set_text(ui_LabelPowerLevel, buf); } // ScreenHome界面的Arc1和Label1保持默认值,不关联电池电量 lvgl_port_unlock(); } // 电池监控任务 static void battery_monitor_task(void *pvParameters) { while (1) { battery_read(); battery_update_ui(); vTaskDelay(pdMS_TO_TICKS(BAT_MONITOR_INTERVAL_MS)); } } void battery_monitor_start(void) { xTaskCreate(battery_monitor_task, "bat_mon", 4096, NULL, 3, NULL); ESP_LOGI(TAG, "电池监控任务已启动,更新间隔%dms", BAT_MONITOR_INTERVAL_MS); }