SMARTWATCH 学习手册有 C 基础 · 嵌入式从零开始
支线二 / CH582M 开发与蓝牙桥接 / 03

蓝牙和串口怎样共用主循环

串口能收到字节以后,为什么没有直接在中断里发送蓝牙?先看主循环每轮在做什么,再认识项目使用的事件处理方式。

接续上一篇:用熟悉的串口,接上另一颗主控

1 · 循环每次都要回来

Main_Circulation 中的实际循环

while (1) {
    TMOS_SystemProcess();
    app_uart_process();
}

第一项让厂商提供的事件系统处理当前可执行的工作,第二项处理自己的串口交接。两项都需要不断获得执行机会。如果在其中一项写一个永远不返回的等待,另一项就没有机会继续。

因此“我只是在等一个字节”也会影响无线连接。这里延续裸机中每次做一点、做完回来的思路;不是看到 Task 一词就应在回调里放 while(1)。

2 · 先约定处理函数,再提交一个待办事件

TMOS 是本工程蓝牙库使用的任务和事件调度接口。先把它理解为“登记处理函数,发生事情时标记一个事件,循环中再调用处理函数”。这与此前的函数指针回调能够连起来。

Peripheral_Init 中的两处调用 · 中间其他初始化省略

Peripheral_TaskID =
    TMOS_ProcessEventRegister(Peripheral_ProcessEvent);
tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT);

登记后得到的编号表示这组事件交给谁处理;事件值表示具体要做哪件事。事件编号不携带收到的全部字节,那些字节仍保存在队列里。

3 · UART 中断如何把工作交出来

中断先保存数据并设置 uart_rx_flag。app_uart_process 检查标志,安排 UART_TO_BLE_SEND_EVT,再清标志。真正发送在 Peripheral_ProcessEvent 的对应分支执行。于是中断能尽快结束,主循环也能继续处理库的工作。

app_uart_process 内部的片段 · 外围保留项目原有临界区

tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2);
uart_rx_flag = false;

最后一个数字是库计时单位,不应直接写成“2 毫秒”。需要换算时查询当前版本接口说明;教学上先掌握它表示稍后安排事件。保护标志的短临界区由项目代码处理,不把这一段孤立复制出去替换函数。

4 · 一次事件处理完,怎样表达还剩什么

Peripheral_ProcessEvent 用 events & 某事件值判断这一位是否存在,处理后返回剩余事件。按位与用于检查,按位异或在已经确认该位存在时清除该位。返回值不代表业务成功或失败,而是交回仍待处理的事件集合。

事件位处理示意 · MY_EVENT 是讲解用名称,未加入生产工程

if (events & MY_EVENT) {
    /* 完成本次有界工作,然后返回。 */
    return events ^ MY_EVENT;
}

如果蓝牙暂时不能接收新的发送请求,当前代码会安排之后再试。把“等资源”变成未来一次事件,能让本轮处理函数先返回。

5 · 与 FreeRTOS 的任务区别在哪里

问题本工程 TMOS 应用处理主线 FreeRTOS 任务
入口通常怎样写处理一次事件并返回各自循环,可阻塞等待
业务执行会不会因普通等待自动交接不能据此假定;长时间停留会阻碍循环按内核调度规则切换
怎样延后工作再次安排事件任务延时、通知或同步对象
动手观察

沿 事件处理源码 找出 UART_TO_BLE_SEND_EVT 的登记、触发和处理位置。画出“中断保存 → 标志 → 主循环安排 → 事件发送”的顺序,再进入下一篇的蓝牙服务。

资料与代码