Rdzleo 99aef2c4e5 1、新增电池电量ADC检测;
2、ScreenSet界面的电量进度条实施更新和电量文本显示实施更新
2026-02-10 10:42:03 +08:00

235 lines
6.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 <stdio.h>
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);
}