DA14585墨水屏学习(2)-ELink墨水屏电子纸社区-FPGA CPLD-ChipDebug

DA14585墨水屏学习(2)

 

一、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("rn");     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 %dn", 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(“rn”);
• 功能:将接收到的字节流以 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 %dn”,
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);    } }
请登录后发表评论

    没有回复内容