學完本章你將能夠:
用 Event Group 廣播喚醒、故障、OTA、睡眠等系統級旗標。
設定單次與週期軟體定時器,取代任務裡的阻塞 |HAL_Delay|。
實作「喚醒後 5 秒聆聽窗,逾時回 idle」的產品行為。
讓 Monitor 任務依旗標決定是否餵看門狗。
Queue 適合「這筆資料給那一個消費者」。有些事情卻是很多任務都要知道 :已經喚醒、匯流排故障、準備升級。Event Group 用一組位元表達這些事實。
系統事件位元
#define EVT_WAKE (1U << 0) #define EVT_LISTEN (1U << 1) #define EVT_FAULT (1U << 2) #define EVT_OTA (1U << 3) #define EVT_SLEEP_REQ (1U << 4) #define EVT_AUDIO_OK (1U << 5) #define EVT_HEALTHY (1U << 6) /* Monitor 判定健康(第 12 章) */ #define EVT_GW_DIRTY (1U << 7) /* 狀態已變,閘道需回推(第 13 章) */
ASR 任務辨識到喚醒詞:xEventGroupSetBits(g_evt, EVT_WAKE | EVT_LISTEN)。 Device 任務可點亮「聆聽」LED;Monitor 可暫停進低功耗;5 秒後 Timer 回呼清掉 |EVT_LISTEN|。
等待時可指定「任一」或「全部」:
EventBits_t b = xEventGroupWaitBits( g_evt, EVT_WAKE | EVT_FAULT, pdFALSE, /* 不自動清除,讓其他人也能讀到 */ pdFALSE, /* 任一 bit 即可 */ portMAX_DELAY); if (b & EVT_FAULT) comms_send_fault();
Event Group 的位元數與 |TickType_t| 有關(24 或 8 個使用者位)。 本專案用不到那麼多。清位元用 |xEventGroupClearBits|;從 ISR 設定 要用 |xEventGroupSetBitsFromISR|(內部可能開 timer daemon 任務代勞)。
軟體定時器
FreeRTOS Timer 在 Tmr Svc 任務裡執行回呼——回呼裡不可 阻塞。適合:關蜂鳴器、關聆聽窗、LED 呼吸燈下一步、週期餵狗。
static TimerHandle_t s_listen_tmr;
static void listen_timeout(TimerHandle_t t) (void)t; xEventGroupClearBits(g_evt, EVT_LISTEN); dev_light_listen_led(0);
void voice_on_wake(void) xEventGroupSetBits(g_evt, EVT_WAKE | EVT_LISTEN); dev_light_listen_led(1); xTimerReset(s_listen_tmr, 0);
建立時:
s_listen_tmr = xTimerCreate("listen", pdMS_TO_TICKS(5000), pdFALSE, NULL, listen_timeout);
|pdFALSE| 表示單次。蜂鳴器 200 ms 同理:ON 之後 reset 一個 200 ms 單次 timer,回呼裡 OFF。Device 任務不再 |vTaskDelay(200)| 擋住後續 命令。
與 Watchdog 的分工
第 12 章才接上真正的 IWDG,但協議現在就要定:Monitor 任務只有在 沒有 |EVT_FAULT| 且各關鍵任務最近有「心跳」時才餵狗。心跳 可用另一個 Event Group,或每任務更新 |g_heartbeat[id] = now|。 Timer 週期餵狗是危險的——任務都死了 timer daemon 若還活著,狗會被騙。
實驗:喚醒窗與蜂鳴定時
firmware/lab06_event_group/。序列埠送 W 模擬喚醒: 聆聽 LED 亮 5 秒後自動熄。送 B 蜂鳴 200 ms(timer,非 delay)。 送 F 置位 FAULT,Monitor 印警告且不再印「healthy」。
為「全部關閉」加上 300 ms 的視覺確認:三顆 LED 同時閃一下。用一個 單次 timer 實作,禁止在 Command 任務裡 delay。再讓 FAULT 發生時自動 |dev_all_off()|——安全策略應在同步層就存在,不要等 AI 章才補。