ทดลอง Zephyr RTOS #1 บอร์ด Xiao nRF52840 Sense
ผมรู้จัก Zephyr ในฐานะของ real-time operating system (RTOS) จากการไปฟังสัมมนาไมโครคอนโทรลเลอร์ค่าย Nordic มาสักพักแล้ว จุดแตกต่างจาก RTOS ค่ายอื่นที่รับรู้จากตอนนั้นคือ การเป็น RTOS ที่ตอบโจทย์ด้าน low-power ซึ่งเป็นเรื่องที่แปลกเพราะ RTOS น่าจะถูกกระตุ้นด้วย timer interrupt ที่ค่อนข้างถี่ (> 100 Hz) เพื่อให้สามารถตอบสนองต่อเหตุการณ์ต่างๆได้ทัน แต่หลังจากนั้นก็ไม่ได้ลองอะไรเพิ่มเติม เพราะเครื่องมือและขั้นตอนของค่าย Nordic (nRF Connect SDK) ที่หุ้ม Zephyr SDK ของค่าย Nordic ค่อนข้างจะวุ่นสักหน่อย
พอเห็นข่าว embedded Swift ในงาน WWDC2024 เลยไปดูที่ blog และ repository ของโครงการ เลยเห็นว่ายังมีอีกหลายบอร์ดที่ใช้งานได้ รวมถึง nRF52840 ที่ใช้การ build โค้ดของ Swift บน Zephyr SDK อีกที เลยคิดว่าอยากมาลอง Zephyr ก่อนเผื่อจะไปลอง embedded Swift อีกที ส่วนหนึ่งที่ช่วยตัดสินใจคือโค้ดไฟกระพริบแบบ Swift ดูแล้วไม่ขัดตาเท่าไร
@main
struct Main {
static func main() {
// Note: & in Swift is not the "address of" operator, but on a global variable declared in C
gpio_pin_configure_dt(&led0, GPIO_OUTPUT | GPIO_OUTPUT_INIT_HIGH | GPIO_OUTPUT_INIT_LOGICAL)
while true {
gpio_pin_toggle_dt(&led0)
k_msleep(100)
}
}
}
ติดตั้ง Zephyr SDK
ผมเลือกจะติดตั้ง Zephyr SDK จากเว็บของโครงการเอง แทนที่จะใช้ผ่าน nRF Connect SDK เพราะตัว Zephyr RTOS เป็นอีกค่ายหลักที่รองรับสถาปัตยกรรมของไมโครคอนโทรลเลอร์เยอะมาก (ARM, RISC-V, MIPS, x86) รวมถึง Xtensa ซึ่งก็คือส่วนคอร์ของ ESP32 ดังนั้นการติดตั้ง Zephyr SDK จะทำให้สามารถพัฒนาโค้ดสำหรับไมโครคอนโทรลเลอร์ได้ทุกค่าย
Zephyr SDK รองรับการติดตั้งทั้ง MS Windows, Ubuntu และ Mac แต่เสียดายที่ไม่รองรับ Windows Subsystem for Linux (WSL) การติดตั้งในกรณี MS Windows จะใช้ Chocolatey เป็น package manager ขั้นตอนในการติดตั้งมีดังนี้
- ติดตั้ง Windows Terminal จาก Microsoft Store
- เปิดใช้ PowerShell ในระดับ admin แล้วติดตั้ง Chocolatey ผ่าน command ที่อยู่ในเว็บ จากนั้นทดสอบด้วยคำสั่ง
choco --version
- พิมพ์คำสั่งเพื่อติดตั้งเครื่องมือต่างๆ
choco feature enable -n allowGlobalConfirmation
choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System'
choco install ninja gperf python310 git dtc-msys2 wget 7zip
4. สลับเป็น PowerShell แบบ user แล้วพิมพ์คำสั่งเพื่อสร้าง workspace
pip install west
cd <workspace folder>
west init zephyrproject
cd zephyrproject
west update
west zephyr-export
pip install -r .\zephyr\scripts\requirements.txt
5. ดาวน์โหลด Zephyr SDK จาก github repo แล้วติดตั้ง
7z x zephyr-sdk-0.16.8_windows-x86_64.7z
cd .\zephyr-sdk-0.16.8\
.\setup.cmd
6. ใช้คำสั่ง west boards
เพื่อแสดงรายการของบอร์ดที่ใช้ Zephyr ได้ เราสามารถใช้ option -n
เพื่อเลือกแสดงเฉพาะบอร์ดที่เกี่ยวข้องได้
ทดลอง Zephyr กับฮาร์ดแวร์
โครงสร้างของ Zephyr project นอกจากโค้ด main.c ในโฟลเดอร์ /src จะประกอบด้วย 2 ไฟล์สำหรับกำหนดเงื่อนไขในการ build โค้ด ได้แก่
- ไฟล์ CMakeLists.txt ใช้กำหนดเครื่องมือและตำแหน่งไฟล์ต่างๆสำหรับการ build เฟิร์มแวร์ ตัวอย่างของ CMakeLists.txt ใน blinky แสดงถึงการกำหนดเงื่อนไขของเครื่องมือ เช่น รุ่นของโปรแกรม CMake สำหรับการ build และตำแหน่ง/ชื่อของไฟล์ซอร์สโค้ด
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(blinky)
target_sources(app PRIVATE src/main.c)
- ไฟล์ prj.conf ใช้กำหนดเงื่อนไขต่างๆในการ build ผ่านกลไก Kconfig ตัวอย่างของ blinky เป็นการกำหนดว่าใช้ GPIO
CONFIG_GPIO=y
ส่วนซอร์สโค้ด main.c จะใช้การ include ไฟล์ header จากกลุ่ม zephyr/*.h เพื่อเรียกใช้ API ที่เกี่ยวข้อง ทั้งนี้ ฮาร์ดแวร์ของบอร์ดจะถูกแปลงจากกลไก devicetree เช่น มาโคร GPIO_DT_SPEC(...)
ใช้สำหรับมองค่าของฮาร์ดแวร์จากการตั้งค่าในไฟล์นามสกุล .dtsi (Device Tree Structure Include ระดับตัวชิพ) และ .dts (Device Tree Source ระดับบอร์ด) ในโฟลเดอร์ zephyrproject\zephyr\boards\…
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS 1000
/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)
/*
* A build error on this line means your board is unsupported.
*/
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void) {
int ret;
bool led_state = true;
if (!gpio_is_ready_dt(&led)) {
return 0;
}
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
return 0;
}
while (1) {
ret = gpio_pin_toggle_dt(&led);
if (ret < 0) {
return 0;
}
led_state = !led_state;
printf("LED state: %s\n", led_state ? "ON" : "OFF");
k_msleep(SLEEP_TIME_MS);
}
return 0;
}
ผมมีบอร์ด Xiao nRF52840 Sense ของ Seeed Studio พร้อมกับบอร์ด Expansion Board Base ในสต็อก เลยเลือกเอามาลอง Zephyr เพราะมีอยู่หลายฮาร์ดแวร์ให้ลองได้ทั้งในส่วน Bluetooth เซ็นเซอร์ IMU และไมค์โครโฟนบนบอร์ด Xiao BLE เอง นิยามของบอร์ด Xiao BLE ในโครงการ Zephyr จะมองเป็นบอร์ดชื่อ xiao_ble
การทดสอบ blinky กับบอร์ด Xiao BLE เริ่มจาก copy โฟลเดอร์ blinky จากโค้ดตัวอย่างใน zephyrproject\zephyr\samples\basic\ มาในโฟลเดอร์ app จากนั้นจึงใช้คำสั่ง west build
ในการ build เฟิร์มแวร์จากโค้ด ผลจากการ build จะเป็นไฟล์ zephyr.uf2 ในโฟลเดอร์ app\build\zephyr
cd app/
west build -p always -b xiao_ble blinky
บอร์ด Xiao BLE ไม่มีหัวต่อโปรแกรม/ดีบั๊ก J-Link มาด้วย จึงต้องใช้การกระตุ้นให้บอร์ดทำงานในโหมด UF2 bootloader เพื่อให้เชื่อมต่อเป็น USB mass storage รองรับการโปรแกรมด้วยการย้ายไฟล์ zephyr.uf2 มาที่ไดร์ฟจำลอง ผมชอบบอร์ด Xiao ที่มีขนาดเล็ก แต่ก็ไม่ชอบตรงที่ปุ่ม reset เล็กมาก (ต้องใช้ดินสอกดเพราะนิ้วก็ใหญ่ไป) จึงแนะนำว่าการมีบอร์ด Expansion Board Base จะช่วยได้มาก เพราะการบังคับบอร์ดให้เข้าโหมด UF2 bootloader ต้องกดปุ่ม reset 2 ครั้งต่อกัน
หลังจากตัวบอร์ด Xiao BLE เข้าโหมด UF2 bootloader แล้ว จะมีไดร์ฟจำลองชื่อ XIAO-SENSE เพิ่มขึ้น เราสามารถใช้การลากไฟล์ zephyr.uf2 เข้าไปที่ไดร์ฟจำลอง หรือใช้คำสั่ง west flash -r uf2
เพื่อโปรแกรมบอร์ด หลังจากบอร์ดถูกโปรแกรมแล้ว จะเห็น LED สีแดงกระพริบ รวมทั้งมีการพิมพ์ข้อความสถานะ LED ออกทาง serial port (USB CDC)
終わりに (ในตอนท้าย)
บทความนี้เล่าถึงขั้นตอนในการทดลอง Zephyr RTOS กับบอร์ด Xiao nRF52840 Sense ซึ่งแม้จะเสียเวลาและเปลืองที่ไปหน่อยตอนติดตั้ง Zephyr SDK และ toolchain ต่างๆ แต่การทดลอง blinky ก็ค่อนข้างง่าย … เมื่อเทียบกับตอนทดลองใช้ nRF Connect SDK ซึ่งอาจจะเพราะต้องไปเรียนขั้นตอนของ GUI ด้วย ในขณะที่วิธีการหลักของ Zephyr จะเน้นการใช้คำสั่ง west
ควบคุมการ build เฟิร์มแวร์
การกำหนดรายละเอียดของฮาร์ดแวร์ในรูปของ devicetree ดูแล้วอาจจะเข้าใจยากหน่อย (ตอนดูไฟล์ .dtsi และ .dts) แต่ก็ทำให้ส่วนโค้ดค่อนข้างดูง่ายขึ้นพอสมควร หากมีเวลาคงจะขยับไปลองส่วนของ Bluetooth เพิ่ม เพราะเป็นจุดแข็งของ Zephyr RTOS ที่ทำให้ค่าย Nordic เลือกใช้ Zephyr RTOS เป็นแพลตฟอร์มของการเขียนโค้ด