✨ 新增功能 - 按键驱动模块:GPIO中断+软件去抖,支持BOOT和KEY2按键事件回调 - 休眠管理器:10秒无操作自动休眠,触摸/按键唤醒,UI集成开关 - 电池电量监控:GPIO3 ADC实时检测,ScreenSet界面圆弧显示 🐛 Bug修复 - 修复FreeRTOS任务栈溢出导致内存损坏(battery任务栈2048→4096) - 修复图片文件名损坏问题(改用静态缓冲区替代动态分配) - 修复触摸中断引脚配置错误(GPIO4→GPIO5,匹配V1.0硬件) - 修复开机闪烁问题(调整背光初始化时序,UI渲染后再点亮) 🎨 界面优化 - ScreenSet恢复为标准Screen切换方式(移除浮动面板架构) - 亮度调节支持0%(完全关闭)和10-100%范围 - ScreenHome界面电量显示独立(不关联实时电量) - 手势导航优化:下拉显示设置,上滑返回主界面 ⚡ 性能优化 - 启动时间优化:从650ms缩短至170ms - 内存管理优化:图片列表使用静态数组(10×32字节) - 任务栈配置调优:battery 4096, button 3072, sleep_mgr 3072 📝 其他改进 - CMakeLists.txt添加新模块编译配置 - 添加硬件版本兼容性注释(GPIO引脚说明) - 完善函数注释和错误日志输出 - sdkconfig配置更新 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
229 lines
6.2 KiB
C
229 lines
6.2 KiB
C
#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 <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界面的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);
|
||
}
|