Rdzleo 75586b3744 ESP32-S3按键版电子吧唧功能完整实现
1、按键驱动重构:从GPIO中断改为iot_button组件,支持BOOT/KEY2的单击/双击/长按6种事件;
2、新增按键导航管理器(key_nav):9种上下文状态机,统一分发按键事件到对应界面业务逻辑;
3、BLE模块改造:广播默认关闭由按键触发,新增设备间图片传输(GATT Client),支持扫描配对→MTU协商→分包发送;
4、新增6个UI界面:配对(Peiwang)、更新(Update)、发送等待(ImageShar)、接收等待(ImageReception)、发送中(Sharing)、接收中(Receiving);
5、新增电池指示器组件(battery_ui):支持多界面真实电量显示,3秒渐隐效果;
6、Home界面重构:移除手势事件,改为airhub背景图+电池指示器;
7、Img界面重构:移除触摸事件,新增删除二次确认边框机制;
8、禁用ScreenSet/ScreenChar界面(保留文件),禁用触摸初始化(保留代码可恢复);
9、sleep_mgr简化:移除ScreenSet亮度UI依赖,按键唤醒由key_nav统一处理;
10、新增9张图片资源(airhub背景、配对、传输状态、电池图标等);

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 17:35:05 +08:00

218 lines
5.9 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/battery_ui.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;
}
// 更新所有界面的电池指示器
battery_ui_update_level(bat_level);
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);
}