SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (MTB & µPython)
Loading...
Searching...
No Matches
I1 — การ bring-up BLE และกฎวิทยุเดียว
variant ที่ใช้ได้
mtb-mpy และ mtb-only

บล็อกการ bring-up BLE ใน proj_cm33_ns/main.c ไม่ขึ้นกับ BENTO_HAS_MPY ส่วนตัวกันและ binding ฝั่ง MicroPython ที่แสดงไว้เป็นข้อความนั้นเป็น mtb-mpy เท่านั้น

Warning
สิ่งที่ต้องอ่านก่อนทั้งกลุ่ม — อ่านก่อนสิ่งอื่นใด โมดูล Bento Buddy / BLE ทั้งหมดคอมไพล์ออกจาก build ปริยาย: ENABLE_PAGE_BENTO_BUDDY ?= 0 (proj_cm33_ns/Makefile:64, :305) และบล็อก BLE ทั้งบล็อกรวมถึงข้อความ [boot] อยู่ภายใน #if ENABLE_PAGE_BENTO_BUDDY (main.c:36, :325) ขั้นแรกคือ make getlibs ใน proj_cm33_ns (ดึง btstack และเฟิร์มแวร์ BT) แล้วจึงสร้างใหม่ด้วย ENABLE_PAGE_BENTO_BUDDY=1 หากไลบรารีขาดหาย build จะหยุดที่ $(error) ของ Makefile ที่ :353-359 — ตัว error นั้นเองคือสิ่งที่สังเกตได้ ซึ่งหัวข้อนี้ใช้แสดงสถานะที่ยังไม่มีสิ่งที่ต้องมีก่อน: ENABLE_PAGE_BENTO_BUDDY=1 requires btstack-integration. Run 'make getlibs' to fetch the AIROC BLE host stack. ไม่มีสิ่งใดในบทนี้หรือใน I2 — ส่วนที่เรียกใช้ได้ของโปรโตคอล NUS ที่สังเกตได้บน build ปริยาย

เป้าหมายของหัวข้อนี้

ยก BLE NUS stack ขึ้นตามลำดับ 2 ชั้นที่เฟิร์มแวร์ใช้ เข้าใจว่าเหตุใด task ที่จ่ายไฟให้ชิปจึงต้องเป็นเจ้าของ ble_nus_init และได้เห็นกฎการใช้วิทยุเดียวปฏิเสธ WiFi จากฝั่ง Python — พร้อมกับ build เดียวที่ข้ามกฎนั้นไป

ลำดับการทำงานจริงของเฟิร์มแวร์

ชั้นที่ 1 — task ที่จ่ายไฟให้ชิป ยก WL_REG_ON ขึ้นหลังรอให้นิ่ง 1.5 s จากนั้นรออีก 50 ms ให้ตัวควบคุม HCI จ่ายไฟขึ้น แล้วจึงเรียก bento_buddy_request_start():

static void chip_power_then_ble_task(void *arg)
{
(void)arg;
vTaskDelay(pdMS_TO_TICKS(1500));
printf("[boot] Asserting WL_REG_ON (P%u.%u) for CYW55513...\r\n",
CYBSP_WIFI_WL_REG_ON_PORT_NUM, (unsigned)CYBSP_WIFI_WL_REG_ON_PIN);
Cy_GPIO_Write(CYBSP_WIFI_WL_REG_ON_PORT, CYBSP_WIFI_WL_REG_ON_PIN, 1U);
/* Murata 2FY datasheet: WL_REG_ON to module-ready ≈ 5 ms. Give 50 ms
* for the HCI controller firmware to finish its internal power-up. */
vTaskDelay(pdMS_TO_TICKS(50));
printf("[boot] WL_REG_ON asserted — bringing up BLE NUS stack\r\n");
extern int bento_buddy_request_start(void);
printf("[boot] bento_buddy_request_start rc=%d\r\n", rc);
vTaskDelete(NULL);
}

bento_buddy_request_start คืนค่า 0 = เพิ่งเริ่มทำงาน, 1 = ทำงานอยู่แล้ว, −1 = init ล้มเหลวหรือ mutex หมดเวลา (ble_nus_lazy.c:152-156) ฟังก์ชันนี้จับ mutex ด้วย timeout 1000 ms (:161) — เรียกได้จาก FreeRTOS task เท่านั้น ห้ามเรียกจาก main() หรือจาก ISR เจ้าของ ble_nus_init ตอนบูตต้องเป็น task เฉพาะตัวนี้ การเรียกจาก context อื่นล้มเหลวด้วย state=ERROR (main.c:326-334)

ชั้นที่ 2 — ตัวจัดคิววิทยุ init หลังจากที่จัดคิว auto-start แล้ว เพื่อไม่ให้ชิงกับ init ของ AIROC set_on_state เป็นการกำหนดค่าเปล่า ๆ และต้องมาก่อน init ซึ่งเป็นตัวจุดการเปลี่ยนสถานะครั้งแรก (radio_scheduler.c:319):

#if ENABLE_PAGE_BENTO_BUDDY
/* Two-layer BLE bring-up:
*
* 1. bento_buddy_auto_start_install — the legacy 3-s-delayed task
* that brings up the AIROC BLE host stack. Proven path: kept as
* the boot-time owner of ble_nus_init. Smoke tests showed that
* calling ble_nus_init from any other context (notably the
* radio_scheduler worker) fails with state=ERROR even with the
* same 3-s delay — the original task's stack/priority is what
* the AIROC HCI bring-up actually needs.
*
* 2. radio_scheduler — runtime arbiter for BLE↔Wi-Fi mode switches.
* Initialised AFTER auto_start so it doesn't race the AIROC init.
* Phase 1 only services the verbs (radio.status / radio.switch /
* wifi.set_creds) and persists creds; the actual swap-radio path
* is exercised by user action, not at boot. Saved-creds auto-Wi-Fi
* moves to Phase 2 once the persistence hooks (LFS boot_mode +
* LCD long-press) are wired. */
{
/* Replace bento_buddy_auto_start_install with our chip-power-then-BLE
* variant — the legacy task skipped the WL_REG_ON / WCM init step,
* which CYW55513 needs for BLE controller bring-up. */
install_chip_power_then_ble();
extern void nus_radio_emit_state_event(const struct radio_status_s *st);
.persist_boot_mode = NULL,
.load_boot_mode = NULL,
};
(void)radio_scheduler_init(&cfg);
}
#endif

radio_scheduler_init เรียกได้ก่อน scheduler เริ่มทำงาน (ใช้ mutex และคิวแบบ static กับ xTaskCreate) คืนค่า true เมื่อสำเร็จหรือเมื่อ init ซ้ำ และคืน false เฉพาะเมื่อ xTaskCreate ล้มเหลว persist_boot_mode/load_boot_mode เป็น NULL ได้ (ค่าสำรองคือ RADIO_BOOT_AUTO) ตัวทำงานของมันหน่วง 3000 ms ก่อนเริ่มให้บริการคิว nus_radio_emit_state_event ใช้เป็นพอยน์เตอร์ฟังก์ชันเท่านั้น ห้ามเรียกโดยตรง

ตัวรับ IPC ฝั่ง CM33 ต้องพร้อมก่อนการแตะจาก CM55 ครั้งแรก บน mtb-mpy มีการลงทะเบียนตอนบูตที่ mpy_main.c:604-623 (ipc_bento_buddy_rx_init() ซึ่งพิมพ์ [MPY] bento_buddy IPC RX init: OK) และยังลงทะเบียนตัวเองแบบ lazy ภายใน bento_buddy_request_start ด้วย (ble_nus_lazy.c:183-186) — การลงทะเบียนซ้ำสองครั้งมีบันทึกไว้และยอมรับได้

การหยุดเป็นแบบนุ่ม bento_buddy_request_stop() (ble_nus_lazy.c:227-245) เป็น void และไม่มีสัญญาณบอก error ตัว AIROC stack ยังคงอยู่ในหน่วยความจำ การเริ่มครั้งต่อไปจึงใช้เส้นทาง ble_nus_rearm_advertising() (:176) แทนการ init ใหม่ binding ฝั่ง MicroPython อยู่ที่ modbentobuddy.c:29-44 (bento_buddy.start() โยน OSError เมื่อได้ −1 ส่วน stop() คืน None)

กฎวิทยุเดียว CYW55513 มีเส้นทาง RF เพียงเส้นเดียว ตัวกันฉบับหลักคือช่วง marker [ble_radio_scheduler_single_rf_guard] ที่ modwifi.c:54-81 (mtb-mpy) ยกมาที่นี่โดยตัดออกหนึ่งช่วงและกำกับไว้:

#if defined(BENTO_HAS_BLE_NUS) && (BENTO_HAS_BLE_NUS == 1) \
&& !(defined(BENTO_HAS_DUAL_BAND) && (BENTO_HAS_DUAL_BAND == 1))
// ... elided: 14-line rationale comment, modwifi.c:56-69 — no COEX
// arbiter in this build, so WiFi while BLE advertises means RF
// contention and eventually a WHD HardFault; the DualBand variant
// (BENTO_HAS_DUAL_BAND=1) time-slices on the on-die COEX block and
// skips this guard ...
#include "../ble_nus/radio_scheduler.h"
bool wifi_permitted = (rm == RADIO_MODE_WIFI_ACTIVE
|| rm == RADIO_MODE_WIFI_FAILED /* retry path */);
if (!wifi_permitted) {
mp_raise_msg(&mp_type_OSError,
MP_ERROR_TEXT("WiFi unavailable: BLE is active. Use the "
"Desktop Buddy or call bento_buddy.stop() "
"to switch radio mode first."));
}
#endif
radio_mode_t radio_scheduler_get_mode(void)
อ่านโหมดโดยไม่ต้องจับ lock — เป็นตัวกัน (guard) มาตรฐานของความเป็นเอกสิทธิ์ระหว่าง Wi-Fi กับ BLE บน R...
radio_mode_t
Definition radio_scheduler.h:34
@ RADIO_MODE_WIFI_FAILED
Definition radio_scheduler.h:41
@ RADIO_MODE_SWITCHING_TO_WIFI
Definition radio_scheduler.h:38
@ RADIO_MODE_WIFI_ACTIVE
Definition radio_scheduler.h:39

(modwifi.c:54-81 โดยตัดคอมเมนต์ช่วงที่ระบุออก แสดงเป็นข้อความเพราะไฟล์นั้นไม่ได้ส่งมาในแพ็กเกจ mtb-only สังเกต #include "../ble_nus/radio_scheduler.h" ที่ modwifi.c:70 ด้วย — โค้ดที่ลอกตัวกันนี้ไปใช้ต้องมีบรรทัดนั้น) radio_scheduler_get_mode() เป็นการอ่าน enum ตัวเดียวแบบไม่ใช้ lock เรียกได้จากทุก context และคืนค่า RADIO_MODE_UNKNOWN ก่อน init ข้อยกเว้น: ภายใต้ BENTO_HAS_DUAL_BAND == 1 ตัวกันนี้คอมไพล์ออกไป เพราะตัวจัดสรร COEX บนไดแบ่งเวลาให้ WiFi และ BLE

ทีละขั้น

ขั้นที่ 0 — สร้างด้วย flag นี้

cd proj_cm33_ns && make getlibs
make build -j ENABLE_PAGE_BENTO_BUDDY=1

สิ่งที่ควรสังเกต หากไม่ได้ทำ getlibs build จะหยุดที่ $(error) ที่ยกมาข้างบน หากทำแล้วทั้ง 3 คอร์จะรายงานว่าเสร็จสมบูรณ์

ขั้นที่ 1 — เฝ้าดูการ bring-up 2 ชั้นบนคอนโซล

แฟลช ตัดไฟแล้วจ่ายไฟใหม่ เปิดคอนโซลที่ 115200

สิ่งที่ควรสังเกต ตามลำดับนี้ และเป็นสตริงเหล่านี้พอดี:

  • [boot] Asserting WL_REG_ON (Pu.u) for CYW55513...
  • [boot] WL_REG_ON asserted — bringing up BLE NUS stack
  • [boot] bento_buddy_request_start rc=d — คาดว่าจะได้ rc=0 ในการบูตเย็น (cold boot)

บน mtb-mpy บรรทัด [MPY] bento_buddy IPC RX init: OK มาก่อนทั้ง 3 บรรทัดนี้ ส่วน glyph BLE บนแถบบนตามการวนถาม (polling) ble_nus_get_state() ทุก 500 ms (บท I2)

ขั้นที่ 2 — ทำให้กฎวิทยุเดียวทำงาน (mtb-mpy, build ที่ไม่ใช่ DualBand)

>>> import wifi
>>> wifi.connect("ssid", "pass")

สิ่งที่ควรสังเกต OSError: WiFi unavailable: BLE is active. Use the Desktop Buddy or call bento_buddy.stop() to switch radio mode first. และไม่มีบรรทัด [wifi-glue] ปรากฏ — การปฏิเสธเกิดขึ้นก่อน app_wifi_init()

ขั้นที่ 3 — สลับโหมดแล้วลองใหม่

>>> import bento_buddy; bento_buddy.stop()

สิ่งที่ควรสังเกต ฝั่งเดสก์ท็อปได้รับ event bento.radio.state (บท I2 แสดงรูปร่างของมัน) ขณะที่ตัวจัดคิวเปลี่ยนสถานะ จากนั้น wifi.connect() ครั้งถัดไปจะทำงานต่อได้ และบรรทัดลองใหม่ [wifi-glue] จะปรากฏ (wifi_init.c:225-242) การเรียก bento_buddy.start() หลังจากนั้นจะไปเส้นทาง rearm และคืนค่า 1 หาก stack ยังคงอยู่ในหน่วยความจำ

กับดัก

Warning
bento_buddy_auto_start_install ถูกแทนที่ไปแล้ว main.c:344-347 เข้ามาแทนเพราะ "the legacy task skipped the WL_REG_ON / WCM init step" และมันไม่มีผู้เรียกที่ใดเลย ให้ใช้ install_chip_power_then_ble() เป็นต้นแบบ
การเรียก radio_scheduler_set_on_state หลัง radio_scheduler_init ทำให้พลาดการเปลี่ยนสถานะครั้งแรก
bento_buddy_request_stop กลืนการหมดเวลาของ mutex ไปอย่างเงียบ ๆ ให้ยืนยันด้วย ble_nus_get_state() ไม่ใช่ด้วยค่าที่คืนมา
ห้ามสมมติว่าเป็น DualBand บน build Bento Buddy ปริยายของ AI Kit กฎนี้บังคับใช้จริงมีเฉพาะอิมเมจที่ BENTO_HAS_DUAL_BAND==1 เท่านั้นที่ใช้วิทยุทั้งสองพร้อมกันได้

กล่อง variant

mtb-mpy mtb-only
บล็อกการ bring-up main.c:325-357 เหมือนกัน เหมือนกัน
การ start/stop จากโค้ดผู้ใช้ bento_buddy.start()/stop() task ที่เขียนเองเรียก bento_buddy_request_start()/stop() หรือปุ่มบนหน้า CM55 ผ่าน IPC
การปฏิเสธด้วยกฎวิทยุเดียว OSError ฝั่ง Python ไม่มีเส้นทางฝั่ง Python การเขียนทับ app_wifi_* ฝั่ง C ใน wifi_init.c ไม่มีตัวกันจากตัวจัดคิว — โค้ดที่เขียนเองต้องถาม radio_scheduler_get_mode() เอง
คอนโซล บรรทัด RX-init ของ [MPY] กับบรรทัด [boot] บรรทัด [boot] กับ [HB]