一、do_min_word
void do_min_work(void)
{
timer_used_min = app_easy_timer(APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES, do_min_work);
current_unix_time += time_offset;
time_offset = 60;
// if (isconnected == 1)
// {
// GPIO_SetActive(GPIO_LED_PORT, GPIO_LED_PIN);
// GPIO_SetInactive(GPIO_LED_PORT, GPIO_LED_G_PIN);
// DEV_Delay_ms(5);
// }
arch_printf("current_unix_time:%d\n", current_unix_time);
do_time_show();
if (step == 0)
{
// do_img_save();
step = 1;
display();
}
// time_refresh_count++;
// if ((time_refresh_count >= Time_To_Refresh) && (g_tm.tm_min == 0 && g_tm.tm_hour == 0))
// {
// if (step == 0)
// {
// // do_img_save();
// step = 1;
// display();
// }
// }
// GPIO_SetActive(GPIO_LED_PORT, GPIO_LED_G_PIN);
// GPIO_SetInactive(GPIO_LED_PORT, GPIO_LED_PIN);
}
这段代码是一个定时器回调函数,用于每分钟执行一次系统时间更新和显示刷新操作。以下是对代码的详细解释:
void do_min_work(void)
{
// 1. 重新设置定时器,确保每分钟执行一次
timer_used_min = app_easy_timer(APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES, do_min_work);
// 2. 时间更新逻辑
current_unix_time += time_offset; // 累加当前时间(秒)
time_offset = 60; // 重置偏移量为60秒(1分钟)
// 3. 调试输出当前时间
arch_printf("current_unix_time:%d\n", current_unix_time);
// 4. 显示时间
do_time_show();
// 5. 状态机控制(仅在step=0时执行一次)
if (step == 0)
{
step = 1; // 切换状态
display(); // 更新显示内容
}
}
其中:APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES是一个宏,其定义如下 :
#define APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES 6000,这时是否少了一个0,不得而知。
代码关键点解释
-
定时器机制:
- 通过
app_easy_timer
函数设置一个每分钟触发的定时器 - 每次回调执行时会重新设置定时器,形成循环调用
APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES
应定义为 60000ms(1 分钟)
- 通过
-
时间维护:
current_unix_time
存储当前的 Unix 时间戳(秒)time_offset
初始为 60,每次累加后重置,确保每分钟递增 60 秒- 这种设计允许系统在无法获取 RTC 时通过软件维护时间
-
显示控制:
do_time_show()
:更新时间显示display()
:刷新整个显示内容- 使用
step
变量实现状态机控制,确保某些操作只执行一次
-
注释代码分析:
- 被注释的 LED 控制代码表明系统可能通过 LED 指示连接状态
do_img_save()
可能用于保存屏幕截图或图像数据- 时间刷新条件检查(午夜 0 点)被注释,可能用于每日特定操作
二、app_easy_timer
函数
timer_hnd app_easy_timer(const uint32_t delay, timer_callback fn)
{
// Sanity checks
ASSERT_ERROR(delay > 0); // Delay should not be zero
ASSERT_ERROR(delay <= KE_TIMER_DELAY_MAX); // Delay should not be more than maximum allowed
timer_hnd timer_id = set_callback(fn);
if (timer_id == EASY_TIMER_INVALID_TIMER)
{
return EASY_TIMER_INVALID_TIMER; //No timers available
}
// Create timer
create_timer(timer_id, delay);
return timer_id;
}
其中timer_hnd定义如下 :typedef uint8_t timer_hnd;
app_easy_timer 函数用于创建一个定时器,在指定的延时后执行回调函数。它提供了参数检查、资源分配和定时器初始化的功能。
timer_hnd app_easy_timer(const uint32_t delay, timer_callback fn)
{
// 1. 参数有效性检查
ASSERT_ERROR(delay > 0); // 延时必须大于0
ASSERT_ERROR(delay <= KE_TIMER_DELAY_MAX); // 延时不能超过系统最大限制
// 2. 分配定时器资源并注册回调函数
timer_hnd timer_id = set_callback(fn);
if (timer_id == EASY_TIMER_INVALID_TIMER)
{
return EASY_TIMER_INVALID_TIMER; // 没有可用的定时器资源
}
// 3. 创建并启动定时器
create_timer(timer_id, delay);
// 4. 返回定时器句柄供后续操作使用
return timer_id;
}
核心功能分析 1. 参数检查:
◦ delay > 0:确保延时时间有效
◦ delay <= KE_TIMER_DELAY_MAX:防止过大的延时值导致系统异常
◦ ASSERT_ERROR 通常是断言宏,参数不合法时会触发错误处理
2. 资源分配:
◦ set_callback(fn) 函数: ◦ 分配一个定时器 ID
◦ 将传入的回调函数 fn 与该 ID 关联
◦ 返回 EASY_TIMER_INVALID_TIMER 表示资源不足
3. 定时器初始化:
◦ create_timer(timer_id, delay) 函数:
◦ 使用分配的 ID 配置硬件 / 软件定时器
◦ 设置延时值 delay(单位通常为毫秒)
◦ 启动定时器开始倒计时
4. 错误处理:
◦ 当无可用定时器资源时,函数会立即返回错误码
◦ 上层应用需要检查返回值以处理错误情况
5.典型使用流程
1. 调用示例:
// 设置一个1000ms后执行的定时器
timer_hnd my_timer = app_easy_timer(1000, my_callback_function);
if (my_timer == EASY_TIMER_INVALID_TIMER) {
// 处理定时器创建失败的情况
}
2. 回调函数定义:
void my_callback_function(void) {
// 定时器到期时执行的代码
// 例如更新状态、触发事件等
}
6.注意事项
1). 定时器资源限制:
◦ 系统通常有最大定时器数量限制
◦ 未释放的定时器会持续占用资源
2). 延时精度:
◦ 实际延时可能受系统负载影响
◦ 依赖底层定时器硬件的精度
3). 回调函数约束:
◦ 避免在回调中执行耗时操作
◦ 确保回调函数可重入(如果支持多定时器并发)
4. ) 资源释放:
◦ 通常需要配套的定时器删除函数(如 delete_timer(timer_hnd))
◦ 不再使用的定时器应及时释放
这个函数提供了嵌入式系统中常用的定时器功能抽象,通过分离资源分配和定时器操作,使上层应用可以更方便地使用定时服务。
7.其中set_callback函数定义如下:
****************************************************************************************
* @brief Place a callback in the first available position in the timer callback array.
* @param[in] fn The callback to be added
* @return The handler of the timer for future reference. EASY_TIMER_INVALID_TIMER if
* there is no timer available
****************************************************************************************
*/
static timer_hnd set_callback(timer_callback fn)
{
for (int i = 0; i < APP_TIMER_MAX_NUM; i++)
{
if (timer_callbacks[i] == NULL)
{
timer_callbacks[i] = fn;
return APP_EASY_TIMER_IDX_TO_HND(i);
}
}
return EASY_TIMER_INVALID_TIMER;
}
这段代码实现了一个内部函数 set_callback,用于在定时器回调数组中分配一个空闲位置并注册回调函数。以下是对代码的详细解释:set_callback 是一个静态函数,用于从回调数组中分配一个未使用的索引,并将传入的回调函数 fn 存储在该位置。如果找不到空闲位置(即所有定时器都已被占用),则返回错误码。
static timer_hnd set_callback(timer_callback fn)
{
// 遍历定时器回调数组,查找空闲位置
for (int i = 0; i < APP_TIMER_MAX_NUM; i++)
{
// 如果发现某个位置为空(NULL),说明该位置未被使用
if (timer_callbacks[i] == NULL)
{
// 将回调函数存储到该位置
timer_callbacks[i] = fn;
// 将数组索引转换为定时器句柄并返回,
return APP_EASY_TIMER_IDX_TO_HND(i);
}
}
// 如果遍历完所有位置都没有找到空闲位置,返回错误码
return EASY_TIMER_INVALID_TIMER;
}
p
其中:timer_calback 定义如下 :
// Timer callback function type definition
typedef void (* timer_callback)(void);
此代码解析:这段代码定义了一个函数指针类型 timer_callback,用于表示定时器回调函数的原型。
类型定义:
◦ timer_callback 是一个函数指针类型,指向 无参数、无返回值 的函数。
函数原型要求:
◦ 回调函数必须符合 void function_name(void) 的形式。
◦ 例如:
void my_timer_handler(void) {
// 定时器触发时执行的代码
}
用途:
◦ 作为参数传递给定时器 API(如 app_easy_timer),用于注册定时执行的任务。
◦ 系统在定时器到期时,会通过该函数指针调用对应的回调函数。
典型应用场景
// 定义回调函数
void update_led(void) {
// 更新LED状态
}
// 创建定时器,注册回调
timer_hnd timer = app_easy_timer(1000, update_led); // 1秒后执行
技术要点
1. 函数指针语法:
◦ void (*)(void) 表示指向 “无参数、无返回值函数” 的指针。
◦ typedef 将该指针类型命名为 timer_callback,简化后续使用。
2. 与定时器系统的关联:
◦ 结合之前分析的 set_callback 函数,timer_callbacks 数组实际存储的就是 timer_callback 类型的函数指针。
◦ 当定时器到期时,系统会直接调用 timer_callbacks[index]()。
3. 兼容性要求: ◦ 所有注册的回调函数必须严格遵循 void(void) 原型,否则可能导致栈破坏或参数传递异常。
注意事项
1. 回调函数的执行环境:
◦ 回调函数可能在中断上下文或低优先级任务中执行,应避免耗时操作(如阻塞 IO)。
2. 参数传递限制:
◦ 由于函数原型固定为无参数,若需传递上下文,可通过全局变量或闭包技术(如 C++ lambda 捕获)。
3. 错误处理:
◦ 回调函数内部应包含必要的错误检查,避免因异常导致系统崩溃。
其中:timer_callbacks数组定义如下 :
// Array that holds the callback function of the active timers
static timer_callback timer_callbacks[APP_TIMER_MAX_NUM] __SECTION_ZERO("retention_mem_area0");
这段代码声明了一个静态数组 timer_callbacks,用于存储系统中所有活跃定时器的回调函数。以下是详细解释:
数据结构:
◦ timer_callbacks 是一个静态数组,大小为 APP_TIMER_MAX_NUM,表示系统最多支持的并发定时器数量。
◦ 数组元素类型为 timer_callback(即函数指针,指向 void (*)(void) 类型的函数)。
存储属性:
◦ static 关键字确保数组的作用域仅限于当前文件,避免外部访问。
◦ __SECTION_ZERO("retention_mem_area0") 是一个特殊的编译器指令,
用于:
◦ 将数组放置在名为 "retention_mem_area0" 的内存区域。
◦ __SECTION_ZERO 通常表示该区域在系统复位时不会被清零(即 “保留内存”),适用于需要在睡眠 / 唤醒周期中保持状态的场景。
初始化:
◦ 由于位于 __SECTION_ZERO 区域,数组初始值为全零(即所有元素初始化为 NULL),表示无活跃定时器。
关键技术点
1. 回调函数管理:
◦ 数组索引对应定时器 ID,例如 timer_callbacks[0] 对应第一个定时器的回调函数。
◦ set_callback 函数通过线性搜索找到第一个 NULL 位置,将回调函数存入并返回索引。
2. 保留内存的作用:
◦ 在低功耗系统中,部分内存区域可配置为 “保留” 状态,即使系统进入睡眠模式也不会断电。
◦ 定时器回调数组位于保留区,可确保:
◦ 睡眠唤醒后定时器状态不丢失。
◦ 无需重新初始化定时器配置。
3. 与硬件的关联:
◦ 实际硬件定时器可能通过 ID 与数组索引映射,
例如:
// 定时器触发时,通过索引调用对应回调
void hardware_timer_isr(uint8_t timer_idx) {
if (timer_callbacks[timer_idx] != NULL) {
timer_callbacks[timer_idx](); // 执行回调
}
}
应用场景
1. 嵌入式系统中的定时任务:
◦ 周期性数据采集(如传感器读数)。
◦ 状态机超时处理。
◦ 通信协议中的定时响应(如心跳包)。
2. 低功耗设计:
◦ 配合 RTC(实时时钟)实现长时间定时唤醒。
◦ 在休眠模式下保持关键定时器状态。
注意事项
1. 内存限制:
◦ APP_TIMER_MAX_NUM 受保留内存区域大小限制,需根据实际需求调整。
2. 线程安全:
◦ 多任务环境中修改数组需加锁保护,避免竞态条件。
3. 兼容性:
◦ __SECTION_ZERO 是特定编译器的扩展(如 GCC、ARMCC),不同平台语法可能不同。
总结 该数组是定时器系统的核心数据结构,通过将回调函数存储在保留内存区域,确保系统在休眠 / 唤醒周期中能持续管理定时任务。这种设计在资源受限的嵌入式系统中尤为重要,既能高效利用内存,又能保证关键状态不丢失。
其中宏定义如下 :
#define APP_EASY_TIMER_HND_TO_MSG_ID(timer_id) (timer_id - 1 + APP_TIMER_API_MES0)
#define APP_EASY_TIMER_MSG_ID_TO_HND(timer_msg) (timer_msg - APP_TIMER_API_MES0 + 1)
#define APP_EASY_TIMER_HND_TO_IDX(timer_id) (timer_id - 1)
#define APP_EASY_TIMER_IDX_TO_HND(timer_id) (timer_id + 1)
#define APP_EASY_TIMER_HND_IS_VALID(timer_id) ((timer_id > 0) && (timer_id <= APP_TIMER_MAX_NUM))