DA14585墨水屏学习

一、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,不得而知。

代码关键点解释

  1. 定时器机制

    • 通过app_easy_timer函数设置一个每分钟触发的定时器
    • 每次回调执行时会重新设置定时器,形成循环调用
    • APP_PERIPHERAL_CTRL_TIMER_DELAY_MINUTES应定义为 60000ms(1 分钟)
  2. 时间维护

    • current_unix_time存储当前的 Unix 时间戳(秒)
    • time_offset初始为 60,每次累加后重置,确保每分钟递增 60 秒
    • 这种设计允许系统在无法获取 RTC 时通过软件维护时间
  3. 显示控制

    • do_time_show():更新时间显示
    • display():刷新整个显示内容
    • 使用step变量实现状态机控制,确保某些操作只执行一次
  4. 注释代码分析

    • 被注释的 LED 控制代码表明系统可能通过 LED 指示连接状态
    • do_img_save()可能用于保存屏幕截图或图像数据
    • 时间刷新条件检查(午夜 0 点)被注释,可能用于每日特定操作

二、app_easy_timer函数

timer_hnd app_easy_timer(const uint32_t delay, timer_callback fn)
{// Sanity checksASSERT_ERROR(delay > 0);                  // Delay should not be zeroASSERT_ERROR(delay <= KE_TIMER_DELAY_MAX); // Delay should not be more than maximum allowedtimer_hnd timer_id = set_callback(fn);if (timer_id == EASY_TIMER_INVALID_TIMER){return EASY_TIMER_INVALID_TIMER; //No timers available}// Create timercreate_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);                  // 延时必须大于0ASSERT_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))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/83091.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务调试问题总结

本地环境调试。 启动本地微服务&#xff0c;使用公共nacos配置。利用如apifox进行本地代码调试解决调试问题。除必要的业务微服务依赖包需要下载到本地。使用mvn clean install -DskipTests进行安装启动前选择好profile环境进行启动&#xff0c;启动前记得mvn clean清理项目。…

C#学习第22天:网络编程

网络编程的核心概念 1. 套接字&#xff08;Sockets&#xff09; 定义&#xff1a;套接字是网络通信的基本单元&#xff0c;提供了在网络中进行数据交换的端点。用途&#xff1a;用于TCP/UDP网络通信&#xff0c;支持低级别的网络数据传输。 2.协议 TCP&#xff08;Transmiss…

TWASandGWAS中GBS filtering and GWAS(1)

F:\文章代码\TWASandGWAS\GBS filtering and GWAS README.TXT 请检查幻灯片“Vitamaize_update_Gorelab_Ames_GBS_filtering_20191122.pptx”中关于阿姆斯&#xff08;Ames&#xff09;ID处理流程的详细信息。 文件夹“Ames_ID_processing”包含了用于处理阿姆斯ID的文件和R…

图像处理篇---opencv实现坐姿检测

文章目录 前言一、方法概述使用OpenCV和MediaPipe关键点检测角度计算姿态评估 二、完整代码实现三、代码说明PostureDetector类find_pose()get_landmarks()cakculate_angle()evaluate_posture() 坐姿评估标准&#xff08;可进行参数调整&#xff09;&#xff1a;可视化功能&…

.Net HttpClient 使用代理功能

HttpClient 使用代理功能 实际开发中&#xff0c;HttpClient 通过代理访问目标服务器是常见的需求。 本文将全面介绍如何在 .NET 中配置 HttpClient 使用代理&#xff08;Proxy&#xff09;功能&#xff0c;包括基础使用方式、代码示例、以及与依赖注入结合的最佳实践。 注意…

【学习路线】 游戏客户端开发入门到进阶

目录 游戏客户端开发入门到进阶&#xff1a;系统学习路线与推荐书单一、学习总原则&#xff1a;从底层出发&#xff0c;项目驱动&#xff0c;持续迭代二、推荐学习路线图&#xff08;初学者→进阶&#xff09;第一阶段&#xff1a;语言基础与编程思维第二阶段&#xff1a;游戏开…

精益数据分析(57/126):创业移情阶段的核心要点与实践方法

精益数据分析&#xff08;57/126&#xff09;&#xff1a;创业移情阶段的核心要点与实践方法 在创业的浩瀚征程中&#xff0c;每一个阶段都承载着独特的使命与挑战。今天&#xff0c;我们继续秉持共同进步的理念&#xff0c;深入研读《精益数据分析》&#xff0c;聚焦创业的首…

015枚举之滑动窗口——算法备赛

滑动窗口 最大子数组和 题目描述 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 原题链接 思路分析 见代码注解 代码 int maxSubArray(vector<int>& num…

微软系统 红帽系统 网络故障排查:ping、traceroute、netstat

在微软&#xff08;Windows&#xff09;和红帽&#xff08;Red Hat Enterprise Linux&#xff0c;RHEL&#xff09;等系统中&#xff0c;网络故障排查是确保系统正常运行的重要环节。 ping、traceroute&#xff08;在Windows中为tracert&#xff09;和netstat是三个常用的网络…

解构认知边界:论万能方法的本体论批判与方法论重构——基于跨学科视阈的哲学-科学辩证

一、哲学维度的本体论批判 &#xff08;1&#xff09;理性主义的坍缩&#xff1a;从笛卡尔幻想到哥德尔陷阱 笛卡尔在《方法论》中构建的理性主义范式&#xff0c;企图通过"普遍怀疑-数学演绎"双重机制确立绝对方法体系。然而哥德尔不完备定理&#xff08;Gdel, 19…

【网络入侵检测】基于源码分析Suricata的IP分片重组

【作者主页】只道当时是寻常 【专栏介绍】Suricata入侵检测。专注网络、主机安全&#xff0c;欢迎关注与评论。 目录 目录 1.概要 2. 配置信息 2.1 名词介绍 2.2 defrag 配置 3. 代码实现 3.1 配置解析 3.1.1 defrag配置 3.1.2 主机系统策略 3.2 分片重组模块 3.2.1…

二分查找的边界问题

前言 二分查找(Binary Search)是一种高效的查找算法&#xff0c;时间复杂度为O(log n)。它适用于已排序的数组或列表。本文将详细介绍二分查找的两种常见写法&#xff1a;闭区间写法和左闭右开区间写法。 一、二分查找基本思想 二分查找的核心思想是"分而治之"&am…

重庆医科大学附属第二医院外科楼外挡墙自动化监测

1.项目概述 重庆医科大学附属第二医院&#xff0c;重医附二院&#xff0c;是集医疗、教学、科研、预防保健为一体的国家三级甲等综合医院。前身为始建于1892年的“重庆宽仁医院”。医院现有开放床位 1380张&#xff0c;年门诊量超过百万人次&#xff0c;年收治住院病人4.5万人…

【Redis实战篇】秒杀优化

1. 秒杀优化-异步秒杀思路 我们来回顾一下下单流程 当用户发起请求&#xff0c;此时会请求nginx&#xff0c;nginx会访问到tomcat&#xff0c;而tomcat中的程序&#xff0c;会进行串行操作&#xff0c;分成如下几个步骤 1、查询优惠卷 2、判断秒杀库存是否足够 3、查询订单…

【idea】调试篇 idea调试技巧合集

前言&#xff1a;之前博主写过一篇idea技巧合集的文章&#xff0c;由于技巧过于多了&#xff0c;文章很庞大&#xff0c;所以特地将调试相关的技巧单独成章, 调试和我们日常开发是息息相关的&#xff0c;用好调试可以事半功倍 文章目录 1. idea调试异步线程2. idea调试stream流…

postman 用法 LTS

postman 用法 LTS File ---- View ---- Show Postman Console

MySQL 数据库故障排查指南

MySQL 数据库故障排查指南 本指南旨在帮助您识别和解决常见的 MySQL 数据库故障。我们将从问题识别开始&#xff0c;逐步深入到具体的故障类型和排查步骤。 1. 问题识别与信息收集 在开始排查之前&#xff0c;首先需要清晰地了解问题的现象和范围。 故障现象&#xff1a; 数…

用AI写简历是否可行?

让AI批量写简历然后投简历是绝对不行的&#xff01;&#xff01;&#xff01; 为什么不行&#xff0c;按照 "招聘经理" 工作经历举例&#xff1a; ai提示词&#xff1a;请帮我写一份招聘经理的工作经历内容&#xff1a; 招聘经理 | XXX科技有限公司 | 2020年…

【从零实现JsonRpc框架#1】Json库介绍

1.JsonCpp第三方库 JSONCPP 是一个开源的 C 库&#xff0c;用于解析和生成 JSON&#xff08;JavaScript Object Notation&#xff09;数据。它提供了简单易用的接口&#xff0c;支持 JSON 的序列化和反序列化操作&#xff0c;适用于处理配置文件、网络通信数据等场景。 2.Jso…

Ubuntu——执行echo $USE什么都不显示

问题&#xff1a;“执行 echo $USER 什么都不显示”&#xff1f; 一、原因分析 环境变量 $USER 未正确设置 $USER 是系统自动定义的环境变量&#xff0c;通常用于表示当前登录的用户名。若该变量未设置或为空&#xff0c;执行 echo $USER 会无输出。可能场景&#xff1a; 用户通…