การเปลี่ยนไปใช้ RTOS บน RP2040
(blog.brixit.nl)- คอนโทรลเลอร์อุปกรณ์วิดีโอที่ใช้ Raspberry Pi Pico เป็นฐาน ต้องรองรับงานพร้อมกันจำนวนมากขึ้น จนโครงสร้างซอฟต์แวร์เริ่มรับไม่ไหวด้วยแค่ pico-sdk และการแยกงานด้วยดูอัลคอร์
- คอนโทรลเลอร์ต้องจัดการทั้ง RS-485 VISCA, Ethernet, ปุ่ม RGB 9 ปุ่ม, จอยสติ๊ก, จอแสดงผล รวมถึงต้องรองรับ DHCP, mDNS และโปรโตคอล ATEM บน UDP ด้วย
- FreeRTOS ทำให้บางส่วนพัฒนาได้ผ่าน scheduler และการสื่อสารระหว่าง task แต่ปัญหา
printfค้างและการขาด hardware abstraction ทำให้ดีบักและนำโค้ดกลับมาใช้ซ้ำได้ยาก - Apache NuttX มีทั้งเชลล์, ไฟล์ซิสเต็ม, device abstraction อย่าง
/dev/i2c0และการตั้งค่าแบบ Kconfig แต่ภายหลังพบว่าปัญหา I2C มาจากการตั้งค่าผิด ทำให้ข้อสรุปในช่วงนั้นส่วนใหญ่ใช้ไม่ได้ - Zephyr ติดที่ขนาดรีโพซิทอรี 5GB, SDK, board definition และความซับซ้อนของการ build สุดท้ายจึงเอนเอียงไปทางลองใช้ FreeRTOS ต่อ เพราะต่อเข้ากับสภาพแวดล้อมเดิมได้ง่ายที่สุด
ทำไมถึงเริ่มต้องใช้ RTOS
- โปรเจ็กต์ไมโครคอนโทรลเลอร์ขนาดเล็กหลายตัวกำลังถูกสร้างขึ้นโดยใช้บอร์ด Raspberry Pi Pico เป็นศูนย์กลาง
- Pico มี SDK ที่เหมาะสม ฮาร์ดแวร์ราคาถูก และรองรับการดีบักด้วย gdb/openocd จึงผสานกับ IDE ได้ง่าย
- โปรเจ็กต์ปัจจุบันคือ ฮาร์ดแวร์คอนโทรลเลอร์ สำหรับควบคุมอุปกรณ์วิดีโอหลายชิ้น
- อุปกรณ์ที่ควบคุมคือกล้อง PTZ แบบมีมอเตอร์ 2 ตัว, กล้องแบบคงที่ 1 ตัว และอุปกรณ์สวิตช์วิดีโอที่เชื่อมต่ออยู่
- คอนโทรลเลอร์กล้อง PTZ เดิมเป็นแผงไร้ยี่ห้อที่หน้าตาคล้าย Marshall VS-PTC-200
- เมื่อหลายปีก่อนมีราคา €650 แต่สัมผัสของปุ่มและคุณภาพของจอยสติ๊กแบบแอนะล็อกไม่ดีนัก
- ปุ่มจำนวนมากใช้กับกล้องที่ใช้งานอยู่ในปัจจุบันไม่ได้ และดูเหมือนถูกออกแบบมาสำหรับกล้องวงจรปิดมากกว่า
- เชื่อมต่อกับกล้องผ่าน บัส RS-485
- ตอนนี้การควบคุม ATEM video switcher ทำได้ผ่านซอฟต์แวร์แพเนลบนคอมพิวเตอร์เท่านั้น
- ฮาร์ดแวร์แพเนลของ Blackmagic Design มีราคาสูงมาก
องค์ประกอบฮาร์ดแวร์ของคอนโทรลเลอร์
- แบบขั้นต่ำกำหนดไว้ที่ ปุ่ม 9 ปุ่ม, จอยสติ๊ก และจอแสดงผลสำหรับส่วนติดต่อผู้ใช้
- หลังจากวนออกแบบฮาร์ดแวร์หลายรอบตลอด 1 ปี ก็ได้ PCB มา
- ปุ่ม RGB 9 ปุ่ม
- จอยสติ๊ก $10 ที่มีอยู่ในแผงลอกแบบ Marshall ด้วย
- TP8485E สำหรับสื่อสารกับกล้อง PTZ ผ่าน RS-485
- โมดูล Wiznet W5500 สำหรับสื่อสารกับวิดีโอสวิตเชอร์ผ่าน Ethernet
- หลังแก้บอร์ดเสร็จ ชิ้นส่วนฮาร์ดแวร์ทุกตัวทำงานได้แล้ว แต่ส่วนที่ยากกว่าคือซอฟต์แวร์
ซอฟต์แวร์ที่เริ่มหนักเกินไปสำหรับ pico-sdk อย่างเดียว
- เริ่มต้นด้วยรูปแบบเดิมของโปรเจ็กต์ RP2040 คือดึง pico-sdk มาใน cmake project
- เพื่อให้ระบบใช้งานได้ในระดับหนึ่ง จึงให้คอร์ที่สองของ Pico รับผิดชอบการจัดการโมดูล Wiznet และให้คอร์แรกจัดการ I/O ของส่วนติดต่อผู้ใช้
- สามารถทำได้ถึงขั้นให้ LED กระพริบ และทำ DHCP client ที่รันอยู่บนคอร์ที่สองได้
- แต่การทำส่วนที่เหลือของระบบซับซ้อนกว่านั้นมาก
- งานที่ต้องทำพร้อมกันเพิ่มขึ้นอย่างรวดเร็ว
- วาดส่วนติดต่อผู้ใช้บนจอให้ลื่นไหลพอสมควร
- ส่งคำสั่ง VISCAผ่านอินเทอร์เฟซ RS-485
- ตอบสนองต่ออินพุตจากปุ่ม
- รักษา network stack ที่มีหลายการเชื่อมต่อ
- ฝั่งเครือข่ายต้องมีงานเบื้องหลังแยกต่างหาก
- การรองรับ DHCP ตามมาตรฐานต้องติดตามเวลา lease หมดอายุ และสื่อสารกับ DHCP server เป็นครั้งคราวเพื่อรักษาสถานะ lease
- mDNS จำเป็นสำหรับค้นหา IP ของ ATEM video switcher แบบอัตโนมัติ และถ้าสามารถประกาศการมีอยู่ของคอนโทรลแพเนลได้ก็น่าจะดี
- โปรโตคอล ATEM นั้นเรียบง่าย แต่บางครั้งมีข้อมูลเข้ามาเกินขนาดบัฟเฟอร์ของโมดูล Wiznet และหากการส่ง UDP datagram หยุดลง ระยะ timeout ก่อนตัดการเชื่อมต่อจะสั้นมาก
- ภายใต้เงื่อนไขแบบนี้ การแยกงานด้วย RTOS ดูเหมาะสมกว่าการซ้อนลูปเพิ่มเอง
FreeRTOS: เรียบง่ายแต่ยังขาด abstraction
- ในทางเทคนิคแล้ว FreeRTOS ถูกรวมอยู่ใน pico-sdk แต่บทสอนส่วนใหญ่ใช้วิธีดาวน์โหลดสำเนาใหม่ จึงทำตามแบบนั้น
- จาก RTOS ที่ลองดู FreeRTOS ดูเรียบง่ายที่สุด และให้หลัก ๆ แค่ scheduler กับการสื่อสารระหว่าง task
- สร้าง task ด้วย
xTaskCreateและเริ่ม scheduler ด้วยvTaskStartScheduler - สามารถใช้ IPC แบบส่งสถานะปุ่มผ่าน queue ไปยัง LED task ได้
- สร้าง task ด้วย
- หลังลองใช้ไม่กี่วัน โค้ดเบสก็ถูกแยกเป็นหลาย task ทั้งที่ฟังก์ชันจริงยังมีไม่มาก
buttonsTask: poll GPIO expander ผ่าน I2C เพื่อตรวจอินพุตจากปุ่ม แล้วใส่ข้อความลงใน button queueledTask: ตั้งค่าสี RGB ของปุ่มแต่ละปุ่มจากข้อความในledQueuemainTask: รันเมนลูปที่อัปเดตสถานะของโปรเจ็กต์ตามอินพุตจากปุ่มnetworkTask: สื่อสารกับโมดูล WiznetdhcpTask:networkTaskสร้างขึ้นเมื่อมีการเชื่อมต่อสายเครือข่ายmdnsTask:dhcpTaskสร้างขึ้นหลังได้ IP addressatemTask: ถูกสร้างเมื่อ mDNS ได้รับการตอบกลับจากอุปกรณ์ ATEMviscaTask: ยังไม่ได้ทำอะไร แต่จะต้องส่งข้อมูลผ่านพอร์ต RS-485
- ทั้งที่ฮาร์ดแวร์ยังแทบไม่ทำอะไรเลยนอกจากโผล่ขึ้นมาบนเครือข่าย จำนวน task ก็เพิ่มมากแล้ว
- ปัญหาที่น่ารำคาญที่สุดคือ
printfทำให้ระบบค้างทุกครั้ง- gdb debugger ใช้งานได้ แต่ไม่เหมาะกับการตรวจสอบ DHCP traffic ด้วยการพิมพ์ log ออกมา
- FreeRTOS ไม่ได้ให้ hardware abstraction มา ทำให้โค้ดที่เขียนไว้สำหรับคุยกับชิปหลายตัวนำกลับมาใช้ซ้ำได้ไม่ง่าย
- ภายหลังพยายามสร้างโปรเจ็กต์ FreeRTOS ใหม่แบบสะอาดแล้วค่อยย้ายฟังก์ชันเข้าไป แต่ความลำบากในการดีบักแบบมืดบอดโดยไม่มี serial output ทำให้เริ่มมองหาตัวเลือกอื่น
Apache NuttX: โครงสร้างแบบ Unix และความผิดพลาดในการตั้งค่า
- Apache NuttX ดูคล้ายระบบปฏิบัติการทั่วไปมากกว่า และปฏิบัติต่อไมโครคอนโทรลเลอร์ราวกับเป็นระบบ Unix
- บทสอนแนะนำให้ดึง pico-sdk มาแล้วตั้งค่า environment variable
- แม้จะมี SDK อยู่ใน
/usr/shareและมี environment variable อยู่แล้ว แต่ build ก็ล้มเหลวเพราะ NuttX พยายามเขียนทับไฟล์version.hของ pico-sdk แล้วติดปัญหา permission
- แม้จะมี SDK อยู่ใน
- หลัง build เฟิร์มแวร์ NuttX ขั้นต่ำและเชื่อมต่อ serial port ก็มีเชลล์จริง ๆ ปรากฏขึ้น
- คำสั่งอย่าง
uptime,uname,uname -aใช้งานได้ - เวอร์ชันที่แสดงคือ
NuttX 12.5.1และเป้าหมายคือarm raspberrypi-pico
- คำสั่งอย่าง
- เพราะใช้แนวทางแบบ Unix จึงดูเหมือนว่าสามารถเขียนแอปพลิเคชันแล้วให้รันอัตโนมัติตอนบูตได้
- มีไฟล์ซิสเต็ม และฮาร์ดแวร์ถูกเปิดเผยผ่าน abstraction อย่าง
/dev/i2c0,/dev/adc0
- มีไฟล์ซิสเต็ม และฮาร์ดแวร์ถูกเปิดเผยผ่าน abstraction อย่าง
- ส่วนที่ชอบคือการตั้งค่าแบบ menuconfig/Kconfig
- เป็นแนวทางที่คุ้นเคยจากงานพัฒนา Linux
- มีระบบไดรเวอร์ฮาร์ดแวร์จริง และมีไดรเวอร์ของชิป GPIO expander ที่ใช้กับปุ่มอยู่แล้ว
- การตั้งค่า RP2040 pin mux ก็ระบุได้ใน menuconfig จึงไม่ต้องคอยเก็บค่าคงที่หมายเลขพินแยก หรือเขียนโค้ดเริ่มต้น I2C bus จำนวนมาก
- ยังรวมยูทิลิตีทดสอบ I2C เข้าไปในเฟิร์มแวร์ได้ด้วย
- ตอนแรกดูเหมือนว่า I2C พื้นฐานใช้งานไม่ได้
- แต่ในการอัปเดตภายหลังมีการแก้ไขว่าแท้จริงแล้วใช้งานได้ดี และที่บัส I2C พังเป็นเพราะการตั้งค่าผิด
- มีการระบุไว้อย่างชัดเจนว่าการประเมินส่วนที่เหลือเกี่ยวกับ NuttX ส่วนใหญ่อาจใช้ไม่ได้
- ในตอนนั้นยังไม่เข้าใจว่าจะบอก NuttX อย่างไรว่า GPIO button อยู่หลัง GPIO expander หรือจะเชื่อม GPIO expander เข้ากับ I2C bus อย่างไร
- หลัง
configure.shล้มเหลว รีโพซิทอรีก็อยู่ในสถานะไม่สอดคล้องกัน จนต้อง clone รีโพซิทอรี NuttX ใหม่หลายครั้ง- มีช่วงที่แม้แต่
distcleanก็ทำงานไม่ถูกต้องด้วยเหตุผลเดียวกัน
- มีช่วงที่แม้แต่
- แนวทางแบบ Unix-like ดูดีในตอนแรก แต่ก็ไม่ได้อยากจัดการกับ path ของไฟล์ซิสเต็มจำลองบนไมโครคอนโทรลเลอร์
- ระบบโปรดักชันก็ไม่ต้องการเชลล์ แค่ให้โค้ดรันได้ก็พอ
Zephyr: SDK ขนาดใหญ่และอุปสรรคเรื่อง board definition
- ตัวเลือกถัดไปคือ Zephyr ซึ่งมี Python utility สำหรับตั้งค่าโปรเจ็กต์
- ขั้นแรกต้องดึง Git repository ขนาดประมาณ 5GB ลงมา
- ในนั้นมี HAL library สำหรับชิปจำนวนมาก
- และยังไปกระทบกับการตั้งค่า cmake แบบ global ของผู้ใช้ด้วย
- บทสอนกำหนดให้ติดตั้ง Zephyr SDK
- Zephyr SDK มีทั้ง toolchain, compiler, assembler, linker และ build tool สำหรับสถาปัตยกรรมที่รองรับแต่ละแบบ
- ยังรวม host tool อย่าง QEMU และ OpenOCD ด้วย
- เนื่องจากมี ARM toolchain หลายชุดอยู่แล้ว จึงไม่อยาก build หรือนำของ prebuilt สำหรับ compiler ของทุกสถาปัตยกรรมมาเพิ่ม
- เพื่อ build โดยไม่ใช้ Zephyr SDK จึงตั้งค่า cross compile option
ZEPHYR_TOOLCHAIN_VARIANT=cross-compileCROSS_COMPILE=/usr/bin/arm-none-eabi-west build -p always -b sparkfun_pro_micro_rp2040 samples/basic/blinky
- Raspberry Pi Pico เองไม่ได้รับการรองรับจริง ๆ มีเพียงบอร์ดอื่นที่ใช้ SoC เดียวกันเท่านั้นที่รองรับ
- จึงถือว่าบอร์ดเหล่านั้นใกล้เคียงกันในทางปฏิบัติแล้วลองทำต่อ
- ไม่นานก็เจอปัญหาว่าเดโม
blinkybuild ไม่ผ่าน- เดโมต้องการนิยาม
led0ของ LED ที่จะให้กระพริบ - แต่ Sparkfun Pro Micro RP2040 ไม่มี GPIO LED ธรรมดา ใช้ WS2812B addressable LED แทน
- เดโมต้องการนิยาม
- จากนั้นทำตามคู่มือ custom board โดยคัดลอก board definition ของบอร์ดอื่นมาใช้ แต่แม้จะแก้ error และ warning ตอน build แล้ว ก็ยังไม่สามารถ build สำหรับบอร์ดเป้าหมายให้สำเร็จได้
สุดท้ายก็กลับมาที่ FreeRTOS
- ในบรรดา RTOS ทั้งสามตัว มีเพียง FreeRTOS ที่สามารถ build บางส่วนของแอปพลิเคชันจริงได้
- ปัญหา
printfน่าจะต้องแก้ด้วยการเปลี่ยนไปใช้ implementation อื่นของ printf ตามคำแนะนำออนไลน์ และเรียกฟังก์ชันอื่นในจุดที่จำเป็น - จากนี้มีแผนจะลองใช้ FreeRTOS ต่อไป
- เพราะเมื่อเทียบกับ RTOS อื่น ๆ แล้ว มันเป็นตัวเลือกเดียวที่ผสานเข้ากับสภาพแวดล้อมเดิมได้ค่อนข้างตรงไปตรงมา โดยไม่ต้องปรับสภาพแวดล้อมการพัฒนาให้เข้ากับ RTOS
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ดูเหมือนผู้เขียนบทความนี้จะมองว่า RTOS เป็นอะไรที่เหมือนกับสภาพแวดล้อม Arduino หรือเป็นสิ่งที่ลองคลำ ๆ ดูแล้วก็คาดหวังให้มันทำงานได้ ซึ่งส่วนใหญ่ไม่ใช่แบบนั้น
ทุกวันนี้ Arduino จำนวนไม่น้อยมี mbed หรือ FreeRTOS อยู่ข้างใน และก็มีวิธีเปิดให้ใช้งานได้ ดังนั้นสำหรับสไตล์ของผู้เขียน ทางนั้นอาจเหมาะกว่าก็ได้
Zephyr ใช้ง่ายและรองรับ CLion ได้ดี แต่จะคาดหวังให้ทุกอย่างทำงานได้โดยไม่ติดตั้ง toolchain เลยไม่ได้ Pi Pico ก็รองรับแน่นอน และตอนที่ลองใช้เองก็ไม่มีปัญหา
สรุปสั้น ๆ คือ FreeRTOS รองรับแทบทุกที่ แต่ไดรเวอร์ส่วนใหญ่จะเฉพาะ SoC/อุปกรณ์ จึงยุ่งยาก และ API ก็ไม่ได้เป็นมิตรนักแต่ก็ทำความคุ้นเคยได้ ถ้าจะใช้ Bluetooth ต้องไปหา stack เอง
Zephyr รองรับ hardware abstraction จริง ๆ และรองรับ SoC ส่วนใหญ่ แต่คุณอาจต้องทำงานกับบอร์ดอยู่บ้าง มี Bluetooth stack ให้ และอาจต้องเพิ่มการรองรับ HCI เล็กน้อย
NuttX ไม่ได้รองรับยอดเยี่ยมเท่าไร แต่ถ้าทำให้มันทำงานได้ก็เป็นตัวเลือกที่เจ๋งทีเดียว การสนับสนุนจากอุตสาหกรรมยังไม่แข็งแรงนัก ส่วน mbed ก็มี แต่ขอข้ามในที่นี้
ในฝั่ง RTOS จริง ๆ ปกติจะเลือก ตัวที่ผู้ผลิต SoC รองรับ เช่น ถ้าเป็น Nordic ก็ Zephyr ถ้าเป็น NXP ก็ FreeRTOS อะไรทำนองนั้น เพราะจะได้การสนับสนุนที่ดี
ผมยังไม่เคยเห็นโปรเจกต์เฟิร์มแวร์สำหรับอุปกรณ์ OEM ที่นักพัฒนาไม่เจอความลำบากกับ Zephyr เลย และยังไม่เคยเจอนักพัฒนาที่ทำเฟิร์มแวร์จริงของผลิตภัณฑ์ที่วางขายแล้วคิดว่า hardware abstraction ของ Zephyr ช่วยได้
ไม่ได้แปลว่าไม่มีคนแบบนั้น แต่ในช่วงราว ๆ 5 ปีที่ผ่านมา ผมยังไม่เคยเจอ
ในมุมมองผม Zephyr มีเอกสารการตลาดที่สวยมาก แต่เบื้องหลังความหรูนั้นคือ ความบวมเกินจำเป็น, การคอมไพล์ที่ช้ามาก และสภาพแวดล้อมที่เริ่มต้นใช้งานยาก
การติดตั้ง toolchain ทั้งระบบแบบ UNIX ดั้งเดิมนั้นเจ็บปวด และพูดตามตรงก็ไม่ใช่วิธีที่ฉลาดนัก
ถ้าใช้คนเดียวแล้วทำงานได้ดีก็คงดี แต่เมื่อมีนักพัฒนาหลายคนทำหลายโปรเจกต์ที่มีเป้าหมายต่างกัน ก็จะเสียเวลาไปมากกับการไล่ดูปัญหา build และ configuration
การใช้ Python ต่อไปกับเครื่องมือก็ไม่ได้ช่วยอะไร ผมไม่เข้าใจว่าทำไมถึงอยากใช้ภาษาที่พกปัญหาเรื่องเวอร์ชันของตัวเองมา และทำงานต่างกันไปในเครื่องของนักพัฒนาแต่ละคน
ผมทำ embedded development ทั้งเป็นงานอดิเรกและอาชีพมาราว 10 ปีแล้ว และไม่เข้าใจท่าทีที่ใช้เวลาหนึ่งสัปดาห์เพื่อทำให้สภาพแวดล้อมของทุกคนในโปรเจกต์เหมือนกัน แล้วยังไม่มองว่านั่นเป็นปัญหา
นี่คือปัญหาจริง น่าหงุดหงิด เสียเวลา และไม่จำเป็น
เครื่องมือควรเป็น ไบนารีที่ลิงก์แบบ static จะเขียนด้วย Rust, Go, C, C++ อะไรก็ได้ แต่ผมอยากให้ให้ความสำคัญกับเครื่องมือที่แข็งแรง ซึ่งเชื่อได้ว่าจะทำงานเหมือนกันโดยไม่ขึ้นกับสิ่งที่ติดตั้งอยู่ในคอมพิวเตอร์ มากกว่าการพัฒนาแบบแก้ขัด
Python ทำแบบนั้นไม่ได้ และการโมโหหรือปกป้องตัวเองแทนที่จะรับปัญหานี้อย่างจริงจังก็ไม่ได้ช่วยอะไร
ถึงอย่างนั้น สิ่งอย่าง PlatformIO ก็เป็นทิศทางที่ถูกต้อง ผมรู้ว่ามันเป็นโปรเจกต์ Python และบางครั้งก็เป็นปัญหา แต่ก็น้อยกว่าเครื่องมืออื่น ๆ และแนวคิดนั้นถูกต้อง
toolchain, SDK และไลบรารีควรถูกจัดการได้ การตั้งค่าโปรเจกต์ควรเรียบง่าย และการ build ควร ทำซ้ำให้ได้ผลเดิม ได้ทุกที่ทุกเวลา
ผมอยากให้อุตสาหกรรม embedded ตระหนักถึงคุณค่าของความพยายามจัดโครงสร้างร่วมกันมากขึ้น ผมรู้จักคนจำนวนมากที่ทำงานกับผู้ผลิต MCU รายใหญ่ แต่โดยมากพวกเขายุ่งกับการแก้ปัญหาเฉพาะหน้าของตัวเอง จนดูมีมุมมองต่อความต้องการของนักพัฒนาค่อนข้างแคบ ซึ่งทำให้ผิดหวังอยู่เสมอ
ผมมีโปรเจกต์คีย์บอร์ดที่ทำงานบน RP2040 และเฟิร์มแวร์เขียนด้วย Rust ขั้นตอนการแฟลชในกรณีที่มีแค่ repository แต่ยังไม่มี Rust toolchain มีดังนี้
(ติดตั้ง rustup)
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh$ rustup target add thumbv6m-none-eabi$ cargo install elf2uf2-rs* สลับคีย์บอร์ดเข้าสู่โหมด bootloader *$ cargo run --releaseในที่นี้ rustup และ Cargo จัดการ toolchain และการ build ให้เกือบทั้งหมด และทั้งสองตัวนี้ยอดเยี่ยมจริง ๆ โปรเจกต์ Python เทียบไม่ติดเลย
แค่รันคำสั่งเดียว ไม่ว่าจะบนเครื่อง local หรือ CI ก็จะตั้งค่าและทำงานด้วย build tools และไลบรารีเวอร์ชันเดียวกันในแบบที่เหมือนกันทุกประการ
ดังนั้นทุกครั้งที่ต้องกลับไปยุ่งกับ CMake และ Make แล้วดิ้นรนติดตั้งไลบรารี จึงรู้สึกทรมานมาก มันต่างจากวิธีอย่าง
compile 'library-name-here'เหลือเกินแต่เลือกเพราะ ระบบนิเวศของไลบรารี ต่างหาก ในงานวิเคราะห์ข้อมูล การทำ visualization และการคำนวณเชิงวิทยาศาสตร์ ไม่มีตัวเลือกในภาษาอื่นที่เทียบได้
สิ่งเหล่านี้เป็นพื้นที่ที่ไม่อยากเริ่มเขียนใหม่ตั้งแต่ต้น เพราะใส่บั๊กได้ง่ายในส่วนอย่างเสถียรภาพเชิงตัวเลข จนอาจให้ผลลัพธ์ถูกต้อง 99% แต่มีอีก 1% ที่ดูสมเหตุสมผลแต่ผิดโดยสิ้นเชิง
ส่วนตัวแล้วผมเริ่มย้ายโปรเจกต์ RP2040 ไปใช้ Rust กับ Embassy
Rust ต้องใช้เวลาปรับตัวนิดหน่อย แต่ก็ค่อนข้างชอบ แม้จะไม่ใช่ RTOS แต่ก็ช่วยตอบโจทย์หลายอย่างที่ทำให้เราต้องใช้ RTOS ได้
Rust กับ Cargo ช่วยขจัดความยุ่งยากในการ build และ flash RP2040 หรือ STM32 ได้ เป็นสภาพแวดล้อม embedded ที่สบายที่สุดเท่าที่เคยใช้มา
แอปพลิเคชันจำนวนมากไม่ได้ใช้ MPU อยู่แล้ว พอรวมกับ memory safety ของ Rust และความซับซ้อนโดยรวมของเฟิร์มแวร์ที่ลดลงเพราะไม่ต้องมี RTOS ก็น่าพิจารณา
async ของ Embassy executor ก็ทำงานได้ดีมาก ช่วยลดความเจ็บปวดในกระบวนการออกแบบ RTOS ไปได้เยอะ
ถ้าเพิ่ม PIO interface สำหรับ RP2040 เข้าไปด้วย โค้ดจะเรียบง่ายและสวยงามมาก และกลายเป็นชุดที่ทำให้สำเร็จได้ยากบนโปรเซสเซอร์อื่น
Pi Pico รองรับใน Zephyr 100% ไม่ได้ดูเอกสารหรือ? https://github.com/zephyrproject-rtos/zephyr/tree/main/board... https://docs.zephyrproject.org/latest/boards/raspberrypi/rpi...
เพิ่มเติมคือ ในหลายสถานการณ์ วิธีใช้งานที่ตั้งใจไว้ไม่ใช่การใช้ Zephyr ติดตั้งแบบ “main” ชุดเดียว แต่เป็นการใส่ external modules ที่ต้องใช้ไว้ใน
west.ymlของโปรเจกต์การให้หลายโปรเจกต์แชร์การติดตั้ง Zephyr ชุดเดียวกันเป็นอีกประเด็นหนึ่ง แต่การติดตั้ง toolchain และ HAL ทุกตัวที่เป็นไปได้ไม่ใช่วิธีเดียวเท่านั้น
ไม่ได้มีการพูดถึง ThreadX ซึ่งตอนนี้เป็นโอเพนซอร์สแล้ว
https://github.com/eclipse-threadx/threadx/
https://github.com/RIOT-OS/RIOT
ชอบการเปรียบเทียบตัวเลือก RTOS
ส่วนตัวมองว่า MicroPython เป็นเส้นทางที่ง่ายกว่า การทำ cooperative multitasking ด้วย
async/awaitเข้ากับผมได้ดีในโปรเจกต์ล่าสุด ผมจัดการสเต็ปเปอร์มอเตอร์ 6 ตัว, LED หลายดวง และการสแกนปุ่มทั้งหมด ซึ่งสำหรับผู้ใช้ก็ดูเหมือนเรียลไทม์
น่าแปลกใจที่หลายคนยังไม่เข้าใจจริง ๆ ว่าตอนนั้นเรามีทรัพยากรน้อยแค่ไหน แต่เราก็ยังใช้ภาษา high-level กัน
ตอนลองใช้ MicroPython บน ESP32 ผมชอบที่มันรองรับ interrupt handler ในแบบที่เป็น Python ดี คิดว่า RP2040 ก็น่าจะรองรับคล้ายกัน
ในกล้องจุลทรรศน์ของผม บอร์ด FluidNC รับผิดชอบการควบคุมสเต็ปเปอร์มอเตอร์ที่เกือบเรียลไทม์ ส่วนผมควบคุมผ่านโปรโตคอล serial แบบเบา ๆ
แต่ผมกำลังดู https://pypi.org/project/micropython-stepper/ อยู่ ซึ่งดูเหมือนจะใช้ hardware timer กับโค้ด Python
อยากลองใช้ Hubris ในโปรเจกต์จริงสักครั้ง (https://hubris.oxide.computer/reference/)
แนวทางสถาปัตยกรรมของมันค่อนข้างตรงกับสิ่งที่ผมมุ่งหวังในงาน embedded เพียงแต่ผมกำลังทำให้มันเจ็บปวดกว่าเดิมด้วย C
ก็ไม่ได้ต่างจากแนวทางที่ทำด้วย Erlang/Elixir ในสภาพแวดล้อม hosted มากนัก
ในสถานการณ์ที่หน่วยความจำจำกัดกว่าและรับหลาย stack ไม่ไหว Embassy ดูเป็นตัวเลือกที่ดี
โปรเจกต์ embedded ใหม่ควรเริ่มใน virtual machine เสมอ จริง ๆ คือเสมอ อย่าผสมเครื่องมือไว้ในระบบเดียวกัน
ในโปรเจกต์เชิงพาณิชย์ของผม นี่เป็นสาเหตุอันดับหนึ่งของปัญหาคุณภาพ
ถ้าเริ่มโปรเจกต์ด้วยชิปเซ็ตใหม่ เวนเดอร์ใหม่ ให้สร้าง VM ใหม่ ติดตั้งเฉพาะเครื่องมือของเวนเดอร์ลงใน VM นั้น แล้ว build จากตรงนั้น
งานพัฒนาเชิงทดลองทำบนเครื่อง local ที่ไม่ใช่ VM ของตัวเองก็ได้ แต่ release ต้องทำใน VM เท่านั้น และได้โปรด ซิงก์ VM กับ development workstation ไว้ด้วย
ตอนนี้กำลังเจ็บปวดหนักกับการต้องจัดการ build เฟิร์มแวร์ที่ต้องแก้ให้ได้ ในขณะที่นักพัฒนาต้นทางลาพักร้อน ไม่มีใครเข้าถึง workstation ของเขาได้, VM ที่เตรียมไว้ล้าหลังไป 6 เดือน และลูกค้าก็กำลังถามว่าทำไมต้องจ่ายค่าทีมทั้งทีมเพื่อสิ่งที่ดูเหมือนจะมีโปรแกรมเมอร์พิเศษบนโลกนี้แค่คนเดียวเท่านั้นที่ทำได้
ผมคิดว่า release ควรให้ระบบ CI เป็นคน build แบบติด release tag ใน git แล้วหลังผ่านการทดสอบก็ได้ binary ออกมา
ถ้าเลือก FreeRTOS ก็แทบจะไม่พลาดหนัก ๆ ณ จุดนี้ถือว่าเป็นมาตรฐานอุตสาหกรรมโดยพฤตินัยแล้ว
printf()ทำงานได้?ดูท่าจะค่อนข้างปวดหัว
เคยมีประสบการณ์คล้ายกับบทความต้นฉบับ
เลยสร้าง ตัวจับเวลาแบบกรีนเธรด ง่าย ๆ ขึ้นมาเอง
มันไม่ได้รองรับการจัดการโปรเซสจริง ๆ เหมือนเคอร์เนลแท้ ๆ และไม่ได้รับประกันอะไร แต่ช่วยให้ไปได้ไกลกว่าการทำ scheduling แบบ bare-metal และช่วยหลีกเลี่ยงความวุ่นวายของ RTOS ได้
มองได้ว่าเป็นเหมือน callback ของ timer ใน JavaScript ที่รับ struct context แบบเลือกใช้ได้ใน C
ทำให้สามารถจัดการงานต่าง ๆ ในรอบเวลาที่ต่างกันได้ เช่น การ query เซนเซอร์หลายชนิด การประมวลผลสัญญาณขาเข้า การตัดสินใจควบคุม และการส่งคำสั่งออกไป
ขอแนะนำอย่างยิ่งให้ลองวิธีแบบนี้ก่อน ก่อนที่จะทำให้ชีวิตพังด้วยสถาปัตยกรรมที่เชื่องช้าและเป็นนามธรรมแบบนั้น