DA14585墨水屏学习(2)

发布于:2025-05-12 ⋅ 阅读:(26) ⋅ 点赞:(0)

一、user_svc2_wr_ind_handler函数

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 是否足够,可能导致越界访问。  

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

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

4.  错误处理:

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

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

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

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

二、app_easy_timer_cancel函数

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


网站公告

今日签到

点亮在社区的每一天
去签到