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->value[i]);

}

arch_printf("\r\n");

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

• 参数:

◦ param->length:数据长度

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

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

if ((param->value[0] == 0xDD) && (param->length >= 5))

{

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

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);

}

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

• 数据格式:

◦ value[0]:命令头(0xDD)

◦ value[1-4]:Unix 时间戳(4 字节大端整数)

• 时间同步机制:

◦ 取消当前分钟定时器

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

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

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

else if (param->value[0] == 0xAA)

{

platform_reset(RESET_NO_ERROR);

}

• 功能:触发系统软复位

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

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

else if (param->value[0] == 0x101)

{

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

is_part = 0;

step = 1;

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

}

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

• 状态变量:

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

◦ step:状态机控制变量

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

else if (param->value[0] == 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);
   }
}
相关推荐
贺函不是涵12 分钟前
【沉浸式求职学习day36】【初识Maven】
java·学习·maven
一口一个橘子19 分钟前
[ctfshow web入门] web69
前端·web安全·网络安全
芯片SIPI设计20 分钟前
MIPI C-PHY 标准学习----一种通用多信号传输方案
c语言·开发语言·学习
Huazzi.33 分钟前
Ubuntu 22虚拟机【网络故障】快速解决指南
linux·网络·学习·ubuntu·bash·编程
【0931】36 分钟前
英语六级---2024.12 卷二 仔细阅读2
学习·英语
读心悦1 小时前
CSS:盒子阴影与渐变完全解析:从基础语法到创意应用
前端·css
m0_616188492 小时前
使用vue3-seamless-scroll实现列表自动滚动播放
开发语言·javascript·ecmascript
虾球xz2 小时前
游戏引擎学习第271天:生成可行走的点
c++·学习·游戏引擎
海尔辛2 小时前
学习黑客5 分钟读懂Linux Filesystem Interaction Continued
linux·服务器·学习
湛海不过深蓝2 小时前
【ts】defineProps数组的类型声明
前端·javascript·vue.js