DA14585墨水屏学习(2)

一、user_svc2_wr_ind_handler函数

cpp 复制代码
void user_svc2_wr_ind_handler(ke_msg_id_t const msgid,
                              struct custs1_val_write_ind const *param,
                              ke_task_id_t const dest_id,
                              ke_task_id_t const src_id)
{

    // sprintf(buf2,"HEX %d :",param->length);
    arch_printf("cmd HEX %d:", param->length);
    for (int i = 0; i < param->length; i++)
    {
        arch_printf("%02X", param->value[i]);
    }
    arch_printf("\r\n");
    if ((param->value[0] == 0xDD) && (param->length >= 5))
    {
        current_unix_time = (param->value[1] << 24) + (param->value[2] << 16) + (param->value[3] << 8) + (param->value[4] & 0xff);
        tm_t tm = {0};
        transformTime(current_unix_time, &tm);
        app_easy_timer_cancel(timer_used_min);
        time_offset = 60 - tm.tm_sec;
        timer_used_min = app_easy_timer(time_offset * 100, do_min_work);
        arch_printf("%d-%02d-%02d %02d:%02d:%02d %d\n", tm.tm_year + YEAR0,
                    tm.tm_mon + 1,
                    tm.tm_mday,
                    tm.tm_hour,
                    tm.tm_min,
                    tm.tm_sec,
                    tm.tm_wday);
    }
    else if (param->value[0] == 0xAA)
    {
        platform_reset(RESET_NO_ERROR);
    }
    else if (param->value[0] == 0x101)
    {
        do_rili_show();
			is_part = 0;
        step = 1;
        display();
    }
    else if (param->value[0] == 0xE2)
    {
        do_time_show();
        is_part = 0;
        step = 1;
        display();
        
    }
}

这段代码实现了一个蓝牙服务写入指示处理函数 user_svc2_wr_ind_handler,用于解析来自客户端的命令并执行相应操作。以下是对代码的详细解释:

函数概述

该函数是一个事件处理回调,当接收到自定义蓝牙服务(custs1)的写请求时被触发。

它会解析命令数据并执行以下操作:

  1. 打印接收到的原始数据(HEX 格式)

  2. 根据命令类型(首字节)执行不同逻辑

◦ 0xDD:设置系统时间

◦ 0xAA:重启设备

◦ 0x101:显示日历

◦ 0xE2:显示时间

核心逻辑解析

1. 数据接收与打印

arch_printf("cmd HEX %d:", param->length);

for (int i = 0; i < param->length; i++)

{

arch_printf("%02X", param->valuei);

}

arch_printf("\r\n");

• 功能:将接收到的字节流以 HEX 格式打印输出

• 参数:

◦ param->length:数据长度

◦ param->value\[\]:数据内容

2. 时间设置命令(0xDD)

if ((param->value0 == 0xDD) && (param->length >= 5))

{

// 从字节流中解析Unix时间戳(大端格式)

current_unix_time = (param->value1 << 24) + (param->value2 << 16) +

(param->value3 << 8) + (param->value4 & 0xff);

// 转换为本地时间结构

tm_t tm = {0};

transformTime(current_unix_time, &tm);

// 重新同步定时器,确保整分钟触发

app_easy_timer_cancel(timer_used_min);

time_offset = 60 - tm.tm_sec;

timer_used_min = app_easy_timer(time_offset * 100, do_min_work);

// 打印格式化时间

arch_printf("%d-%02d-%02d %02d:%02d:%02d %d\n",

tm.tm_year + YEAR0, tm.tm_mon + 1, tm.tm_mday,

tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);

}

• 功能:通过蓝牙设置系统时间

• 数据格式:

◦ value0:命令头(0xDD)

◦ value1-4:Unix 时间戳(4 字节大端整数)

• 时间同步机制:

◦ 取消当前分钟定时器

◦ 计算距离下一个整分钟的剩余秒数(time_offset)

◦ 重新设置定时器,确保 do_min_work 在整分钟触发

3. 系统重启命令(0xAA)

else if (param->value0 == 0xAA)

{

platform_reset(RESET_NO_ERROR);

}

• 功能:触发系统软复位

• 参数:RESET_NO_ERROR 表示正常重启(无错误)

4. 日历显示命令(0x101)

else if (param->value0 == 0x101)

{

do_rili_show(); // 显示日历内容

is_part = 0;

step = 1;

display(); // 更新屏幕显示

}

• 功能:切换到日历显示模式

• 状态变量:

◦ is_part:可能表示部分更新标志

◦ step:状态机控制变量

5. 时间显示命令(0xE2)

else if (param->value0 == 0xE2)

{

do_time_show(); // 显示时间内容

is_part = 0;

step = 1;

display(); // 更新屏幕显示

}

• 功能:切换到时间显示模式

• 与日历命令类似,但调用 do_time_show() 而非 do_rili_show()

潜在问题与优化建议

  1. 命令格式检查:

◦ 对于 0x101 和 0xE2 命令,未检查 param->length 是否足够,可能导致越界访问。

  1. 定时器精度: ◦ time_offset * 100 可能是笔误,应为 time_offset * 1000(毫秒转换)。

  2. 状态管理: ◦ is_part 和 step 作为全局变量,多命令并发时可能引发状态混乱。

  3. 错误处理:

◦ 未处理未知命令(首字节非 0xDD/0xAA/0x101/0xE2)的情况。

应用场景 该处理函数常见于蓝牙低功耗(BLE)设备中,用于接收手机 APP 发送的控制命令,实现:

• 时间同步(通过 NTP 或手机时间)

• 远程重启设备 • 界面显示切换(时钟 / 日历) 通过蓝牙协议栈的 GATT 服务,客户端可以向设备写入特定命令,触发相应功能。

二、app_easy_timer_cancel函数

cpp 复制代码
void app_easy_timer_cancel(const timer_hnd timer_id)
{
    if APP_EASY_TIMER_HND_IS_VALID(timer_id)
    {
        if ((timer_callbacks[APP_EASY_TIMER_HND_TO_IDX(timer_id)] != NULL) &&
            (timer_callbacks[APP_EASY_TIMER_HND_TO_IDX(timer_id)] != timer_canceled_handler))
        {
            // Remove the timer from the timer queue
            ke_timer_clear(APP_EASY_TIMER_HND_TO_MSG_ID(timer_id), TASK_APP);

            timer_callbacks[APP_EASY_TIMER_HND_TO_IDX(timer_id)] = timer_canceled_handler;
            /*
                Send a message to the kernel in order to clear the timer callback function and
                free the respective position in the timers callback array.
                The app_easy_timer_cancel() function cannot guarantee if a timer has entered
                the message queue or not. Therefore a message must be sent to the kernel and
                inform it about the requested cancel operation.
            */
            struct cancel_timer_struct *req = KE_MSG_ALLOC(APP_CANCEL_TIMER, TASK_APP, TASK_APP,
                                                           cancel_timer_struct);

            req->timer_id = timer_id;
            ke_msg_send(req);
        }
        else
        {
            ASSERT_WARNING(0);
        }
   }
   else
   {
       ASSERT_WARNING(0);
   }
}
相关推荐
Pedantic15 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘15 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆15 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师16 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆16 小时前
VSCode自动格式化三要素
前端
爱勇宝17 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen17 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user205855615181320 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode20 小时前
Redis 在生产项目的使用
前端·后端
LiaCode20 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端