From 99aef2c4e50894af43b8b93483f8d788f572b3c4 Mon Sep 17 00:00:00 2001 From: Rdzleo Date: Tue, 10 Feb 2026 10:42:03 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=96=B0=E5=A2=9E=E7=94=B5=E6=B1=A0?= =?UTF-8?q?=E7=94=B5=E9=87=8FADC=E6=A3=80=E6=B5=8B=EF=BC=9B=202=E3=80=81Sc?= =?UTF-8?q?reenSet=E7=95=8C=E9=9D=A2=E7=9A=84=E7=94=B5=E9=87=8F=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=9D=A1=E5=AE=9E=E6=96=BD=E6=9B=B4=E6=96=B0=E5=92=8C?= =?UTF-8?q?=E7=94=B5=E9=87=8F=E6=96=87=E6=9C=AC=E6=98=BE=E7=A4=BA=E5=AE=9E?= =?UTF-8?q?=E6=96=BD=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/CMakeLists.txt | 2 + main/battery/battery.c | 234 +++++++++++++++++++++++++++++++++ main/battery/include/battery.h | 29 ++++ main/gpio/include/gpio.h | 1 + main/main.c | 8 ++ 5 files changed, 274 insertions(+) create mode 100644 main/battery/battery.c create mode 100644 main/battery/include/battery.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 5b18395..c1b3fc8 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -8,6 +8,7 @@ idf_component_register( "./fatfs/fatfs.c" "./pages/pages.c" "./ble/ble.c" + "./battery/battery.c" "./ui/ui.c" "./ui/components/ui_comp_hook.c" "./ui/screens/ui_ScreenHome.c" @@ -31,6 +32,7 @@ idf_component_register( "./fatfs/include/" "./pages/include/" "./ble/include/" + "./battery/include/" "./ui/" "./ui/screens/" # "./axis/include/" diff --git a/main/battery/battery.c b/main/battery/battery.c new file mode 100644 index 0000000..e584cb5 --- /dev/null +++ b/main/battery/battery.c @@ -0,0 +1,234 @@ +#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" +#include "../ui/screens/ui_ScreenHome.h" +#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界面的电量圆弧和标签 + if (ui_Arc1) { + lv_arc_set_value(ui_Arc1, bat_level); + } + if (ui_Label1) { + lv_label_set_text(ui_Label1, buf); + } + + 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", 2048, NULL, 3, NULL); + ESP_LOGI(TAG, "电池监控任务已启动,更新间隔%dms", BAT_MONITOR_INTERVAL_MS); +} diff --git a/main/battery/include/battery.h b/main/battery/include/battery.h new file mode 100644 index 0000000..f829ed9 --- /dev/null +++ b/main/battery/include/battery.h @@ -0,0 +1,29 @@ +#pragma once +#include "esp_err.h" +#include + +// 电池ADC引脚配置 +#define PIN_BAT_ADC 3 // GPIO3 +#define BAT_ADC_CHANNEL ADC_CHANNEL_2 // ADC1_CH2 + +// 分压比(实际电池电压 = ADC测量电压 * 此系数) +// 根据硬件电路中的分压电阻调整,1:1分压器设为2 +#define BAT_VOLTAGE_DIVIDER 2 + +// 采样次数(取平均值,提高精度) +#define BAT_SAMPLE_COUNT 32 + +// 监控间隔(毫秒) +#define BAT_MONITOR_INTERVAL_MS 5000 + +// 初始化电池ADC检测 +esp_err_t battery_init(void); + +// 获取电池电压(毫伏) +uint32_t battery_get_voltage_mv(void); + +// 获取电池电量百分比(0-100) +uint8_t battery_get_level(void); + +// 启动电池监控任务(周期性读取ADC并更新UI) +void battery_monitor_start(void); diff --git a/main/gpio/include/gpio.h b/main/gpio/include/gpio.h index ad7fde4..4dfc5cf 100644 --- a/main/gpio/include/gpio.h +++ b/main/gpio/include/gpio.h @@ -24,6 +24,7 @@ #define SPI_LCD_HOST SPI2_HOST // SPI 主机编号 2 #define PIN_MOTOR_EN -1 // 电机使能 +#define PIN_BAT_ADC 3 // 电池ADC检测引脚(ADC1_CH2) void test_display(esp_lcd_panel_handle_t panel_handle); void test_gpio(); \ No newline at end of file diff --git a/main/main.c b/main/main.c index c84ef0c..b27f03c 100644 --- a/main/main.c +++ b/main/main.c @@ -16,6 +16,7 @@ #include "fatfs.h" #include "pages.h" #include "ble.h" +#include "battery.h" #include "ui/ui.h" @@ -196,6 +197,13 @@ void app_main(void) ESP_LOGI("MAIN", "10. 渲染步骤 %d 已完成", i+1);// 渲染步骤已完成 } ESP_LOGI("MAIN", "UI渲染完成");// UI渲染完成 + + // 初始化电池ADC检测并启动监控任务 + ESP_ERROR_CHECK(battery_init()); + ESP_LOGI("MAIN", "10. 电池ADC已初始化"); + battery_monitor_start(); + ESP_LOGI("MAIN", "11. 电池监控任务已启动"); + ESP_LOGI("MAIN", "系统初始化完成成功!");// 系统初始化完成成功 // =====================================================================