目录
ESP-ADF battery_service组件之voltage_monitor子模块创建与销毁函数详解
版本信息: v2.7-65-gcf908721
本章节分析的源码位于
/components/battery_service/monitors/voltage_monitor.c
文件
内部数据结构
电压监控模块内部定义了关键的结构体,用于实现电压监控和事件报告机制:
typedef struct {
SemaphoreHandle_t mutex; // 互斥锁,保护共享数据访问
vol_monitor_param_t *config; // 电压监控配置参数
vol_monitor_event_cb event_cb; // 事件回调函数
void *user_ctx; // 用户上下文数据
esp_timer_handle_t check_timer; // 定时器句柄,用于定期检查电压
int read_cnt; // 读取计数器
int report_start; // 报告启动标志
bool full_reported; // 满电量已报告标志
bool low_reported; // 低电量已报告标志
} vol_monitor_ctx_t;
结构体关系图
下面的图表展示了电压监控模块的核心数据结构及其关系:
这个图表展示了电压监控模块的核心数据结构及其关系:
vol_monitor_ctx_t
是核心结构体,维护电压监控的状态和配置vol_monitor_param_t
存储用户提供的配置信息,包括回调函数和阈值设置vol_monitor_event_t
定义了事件类型,用于上报电压状态- 监控模块使用定时器定期检查电压,并根据配置生成相应的事件
内部函数分析
vol_monitor_param_check
vol_monitor_param_check
函数用于检查配置参数的有效性。下面是其源码实现:
/**
* @brief 检查电压监控参数有效性
*
* 该函数验证配置参数的合法性,检查以下几个方面:
* 1. 必要的回调函数是否提供(init、deinit、vol_get)
* 2. 频率设置是否合理(read_freq > 0, report_freq >= 0)
* 3. 电压阈值设置是否合理(阈值非负,且低电量阈值小于满电量阈值)
*
* @param config 要检查的配置参数
* @return bool 参数有效返回true,否则返回false
*/
static bool vol_monitor_param_check(vol_monitor_param_t *config)
{
// 检查必要的回调函数是否提供
if (config->init == NULL) {
ESP_LOGE(TAG, "init NULL");
return false;
}
if (config->deinit == NULL) {
ESP_LOGE(TAG, "deinit NULL");
return false;
}
if (config->vol_get == NULL) {
ESP_LOGE(TAG, "vol_get NULL");
return false;
}
// 检查参数有效性
if (config->read_freq <= 0) {
ESP_LOGE(TAG, "Read freq <= 0");
return false;
}
if (config->report_freq < 0) {
ESP_LOGE(TAG, "report freq < 0");
return false;
}
if (config->vol_full_threshold < 0 ) {
ESP_LOGE(TAG, "vol_full_threshold < 0");
return false;
}
if (config->vol_low_threshold < 0 ) {
ESP_LOGE(TAG, "vol_low_threshold < 0");
return false;
}
// 检查阈值逻辑
if (config->vol_full_threshold != 0 && config->vol_low_threshold >= config->vol_full_threshold) {
ESP_LOGE(TAG, "vol_low_threshold >= vol_full_threshold");
return false;
}
return true;
}
这个函数虽然简短,但在电压监控模块中扮演着重要角色,它确保用户提供的配置参数符合预期,防止因参数错误导致的运行时异常。
vol_check_timer_hdlr
vol_check_timer_hdlr
是电压监控的核心函数,作为定时器回调定期执行。下面是其源码实现:
/**
* @brief 电压检查定时器回调函数
*
* 该函数是电压监控的核心,由定时器定期调用,完成以下主要操作:
* 1. 调用用户提供的电压读取函数获取当前电压
* 2. 根据报告频率设置决定是否触发定期报告事件
* 3. 检查电压是否低于低电量阈值,触发低电量事件
* 4. 检查电压是否高于满电量阈值,触发满电量事件
*
* 函数使用互斥锁保护共享数据访问,防止并发问题
* 为避免重复报告,低电量和满电量状态会通过标志位记录
*
* @param arg 回调函数参数,实际类型为vol_monitor_ctx_t指针
*/
static void vol_check_timer_hdlr(void *arg)
{
vol_monitor_ctx_t *vol_monitor = (vol_monitor_ctx_t *)arg;
if (vol_monitor == NULL || vol_monitor->event_cb == NULL || vol_monitor->config->user_data == NULL) {
return;
}
mutex_lock(vol_monitor->mutex);
// 获取当前电压
int voltage = vol_monitor->config->vol_get(vol_monitor->config->user_data);
// 处理定期报告
if (vol_monitor->report_start != 0 && ++vol_monitor->read_cnt % vol_monitor->report_start == 0) {
vol_monitor->read_cnt = 0;
vol_monitor->event_cb(VOL_MONITOR_EVENT_FREQ_REPORT, (void *)voltage, vol_monitor->user_ctx);
}
// 处理低电量事件
if (vol_monitor->config->vol_low_threshold != 0) {
if (vol_monitor->low_reported == false && voltage <= vol_monitor->config->vol_low_threshold) {
vol_monitor->event_cb(VOL_MONITOR_EVENT_BAT_LOW, (void *)voltage, vol_monitor->user_ctx);
vol_monitor->low_reported = true;
} else if (voltage > vol_monitor->config->vol_low_threshold) {
vol_monitor->low_reported = false;
}
}
// 处理满电量事件
if (vol_monitor->config->vol_full_threshold != 0) {
if (vol_monitor->full_reported == false && voltage >= vol_monitor->config->vol_full_threshold) {
vol_monitor->event_cb(VOL_MONITOR_EVENT_BAT_FULL, (void *)voltage, vol_monitor->user_ctx);
vol_monitor->full_reported = true;
} else if (voltage < vol_monitor->config->vol_full_threshold) {
vol_monitor->full_reported = false;
}
}
mutex_unlock(vol_monitor->mutex);
}
该函数是电压监控模块的核心组件,通过定时器定期调用,实现了电压采样、状态判断和事件触发的主要逻辑。它使用互斥锁保护共享数据,避免并发访问问题,并通过状态标志位避免重复触发同一事件。
创建与销毁函数分析
vol_monitor_create
vol_monitor_create
函数用于创建电压监控实例,分配必要的资源。下面是其源码实现:
/**
* @brief 创建电压监控实例
*
* 该函数完成以下主要操作:
* 1. 分配电压监控实例内存
* 2. 检查配置参数有效性
* 3. 调用用户提供的初始化函数
* 4. 创建互斥锁确保线程安全
* 5. 创建和启动定时器,用于定期检查电池电压
*
* 所有资源创建失败时会进行适当的清理,避免资源泄漏
*
* @param config 配置参数,包含电压读取函数、报告频率、阈值等信息
* @return vol_monitor_handle_t 成功返回电压监控实例句柄,失败返回NULL
*/
vol_monitor_handle_t vol_monitor_create(vol_monitor_param_t *config)
{
// 检查配置参数是否为NULL
AUDIO_NULL_CHECK(TAG, config, return NULL);
// 分配电压监控实例内存
vol_monitor_ctx_t *vol_monitor = audio_calloc(1, sizeof(vol_monitor_ctx_t));
AUDIO_MEM_CHECK(TAG, vol_monitor, return NULL);
// 检查参数有效性
if (!vol_monitor_param_check(config)) {
goto error;
}
// 保存配置并调用初始化函数
vol_monitor->config = config;
vol_monitor->config->init(vol_monitor->config->user_data);
// 创建互斥锁
vol_monitor->mutex = mutex_create();
/* 初始化定时器 */
if (vol_monitor->config->read_freq > 0) {
// 定义定时器配置
const esp_timer_create_args_t timer_args = {
.callback = vol_check_timer_hdlr, // 定时器回调函数
.arg = vol_monitor, // 回调函数参数
.dispatch_method = ESP_TIMER_TASK, // 调度方式
.name = "report", // 定时器名称
};
// 创建并启动定时器
if (esp_timer_create(&timer_args, &vol_monitor->check_timer) != ESP_OK
|| esp_timer_start_periodic(vol_monitor->check_timer, vol_monitor->config->read_freq * 1000 * 1000) != ESP_OK) {
goto error;
}
}
return vol_monitor;
// 错误处理部分
error:
ESP_LOGE(TAG, "vol_monitor_create failed");
// 释放已创建的资源
if (vol_monitor->check_timer != NULL) {
esp_timer_delete(vol_monitor->check_timer);
}
if (vol_monitor->mutex) {
mutex_destroy(vol_monitor->mutex);
}
if (vol_monitor) {
free(vol_monitor);
}
return NULL;
}
下面的时序图展示了 vol_monitor_create
函数的执行流程:
这个时序图清晰地展示了 vol_monitor_create
函数的执行流程,包括参数检查、内存分配、用户初始化函数调用、互斥锁创建、定时器配置以及错误处理的完整过程。通过这个图可以直观地理解函数内部的调用关系和数据流向。
vol_monitor_destroy
vol_monitor_destroy
函数用于销毁电压监控实例,释放所有资源。下面是其源码实现:
/**
* @brief 销毁电压监控实例并释放所有资源
*
* 该函数完成以下主要操作:
* 1. 停止并删除电压检查定时器
* 2. 调用用户提供的反初始化函数
* 3. 销毁互斥锁
* 4. 释放电压监控实例内存
*
* 函数确保所有在vol_monitor_create中分配的资源都被正确释放
*
* @param handle 电压监控句柄
* @return esp_err_t 成功返回ESP_OK,失败返回错误码
*/
esp_err_t vol_monitor_destroy(vol_monitor_handle_t handle)
{
// 检查句柄是否有效
AUDIO_NULL_CHECK(TAG, handle, return ESP_ERR_INVALID_ARG);
// 将句柄转换为内部实现类型
vol_monitor_ctx_t *vol_monitor = (vol_monitor_ctx_t *)handle;
// 停止并删除定时器
if (vol_monitor->check_timer != NULL) {
esp_timer_stop(vol_monitor->check_timer);
esp_timer_delete(vol_monitor->check_timer);
vol_monitor->check_timer = NULL;
}
// 调用用户反初始化函数
vol_monitor->config->deinit(vol_monitor->config->user_data);
// 销毁互斥锁
if (vol_monitor->mutex) {
mutex_destroy(vol_monitor->mutex);
}
// 释放实例内存
if (vol_monitor) {
free(vol_monitor);
}
ESP_LOGI(TAG, "vol monitor destroyed");
return ESP_OK;
}
下面的时序图展示了 vol_monitor_destroy
函数的执行流程:
这个函数是 vol_monitor_create
的配对函数,负责清理所有分配的资源,防止资源泄漏。它停止并删除定时器,调用用户提供的反初始化函数,销毁互斥锁,并释放实例内存,确保系统资源得到完全回收。
资源管理总结
电压监控模块的创建与销毁函数遵循了ESP-ADF组件的通用设计模式:
- 资源分配:通过create函数分配所有必要的资源,包括内存、系统对象和硬件资源
- 参数验证:在分配资源前进行严格的参数检查,确保配置有效
- 错误处理:在创建过程中任何步骤失败都会触发清理流程,释放已分配的资源
- 资源回收:通过destroy函数释放所有分配的资源,与create函数配对使用
- 状态保护:使用互斥锁等机制确保资源访问的线程安全性
这种设计确保了模块的健壮性和可靠性,防止资源泄漏和并发访问问题,使电压监控模块能够在嵌入式系统中安全高效地运行。