title: "YetAnotherUSB Oscilloscope: ปลดล็อกพลัง 350kSps ของ Arduino Zero/MKR ด้วย USB Isochronous"
โปรเจกต์ Oscilloscope ส่วนใหญ่ที่ใช้บอร์ดตระกูล Arduino Zero หรือ MKR มักจะติดข้อจำกัดสำคัญอยู่สองประการ:
- การใช้ Sound Card ของคอมพิวเตอร์: ซึ่งจำกัด Bandwidth และความแม่นยำของแรงดันไฟฟ้า
- การใช้พอร์ต USB ผ่าน Protocol Bulk Transfer: แม้จะเป็น USB 2.0 แต่ Protocol นี้ถูกจำกัดความเร็วไว้ที่ 64 bytes ต่อมิลลิวินาที (ตามมาตรฐาน USB 2.0 Full Speed CDC) ซึ่งไม่เพียงพอสำหรับการส่งข้อมูลความละเอียดสูงแบบ Real-time
บทความนี้จะพาคุณไปพบกับแนวทางการวิศวกรรมเพื่อทำลายขีดจำกัดดังกล่าว และดึงประสิทธิภาพสูงสุดของชิป SAMD21 ออกมาใช้งานจริง
ความท้าทายในเชิงตัวเลข (The Engineering Challenge)
หัวใจหลักของ Arduino Zero/MKR คือชิป SAMD21G18 (ARM Cortex-M0+) ซึ่งมีโมดูล ADC (Analog-to-Digital Converter) ขนาด 12-bit ที่สามารถทำ Sampling Rate ได้สูงถึง 350kSps (350,000 ตัวอย่างต่อวินาที) นี่คือสเปกที่น่าทึ่งมากสำหรับไมโครคอนโทรลเลอร์ระดับนี้ แต่คำถามคือเราจะส่งข้อมูลมหาศาลนี้เข้าคอมพิวเตอร์เพื่อแสดงผลที่ 60Hz (ทุกๆ 16ms) ได้อย่างไร?
ลองคำนวณดู:
- ที่ 60Hz หรือ 16ms ต่อเฟรม หากเราต้องการ Sampling ที่ 350kSps
- เราจะมีข้อมูลทั้งหมด: 5,600 Samples ต่อ 16ms
- ข้อมูล 1 Sample ประกอบด้วย: Overhead + ข้อมูล ADC (12-bit) + Timestamp (12-bit) รวมแล้วประมาณ 3 ถึง 6 bytes
- ดังนั้น ข้อมูลที่ต้องส่งคือ: 5,600 x (3 ถึง 6) = 16,800 ถึง 33,600 bytes ต่อ 16ms
เปรียบเทียบกับขีดจำกัดของ USB 2.0 Full Speed:
- USB Bulk Transfer: ส่งได้ 64 bytes/ms ≈ 1,024 bytes/16ms (ไม่พออย่างมาก!)
- USB Isochronous Transfer: ส่งได้ 1,023 bytes/ms ≈ 16,368 bytes/16ms (เข้าใกล้เป้าหมายมากขึ้น)
ภาพด้านล่างแสดงคุณสมบัติหลักและการจัดการข้อมูลของแอปพลิเคชันนี้:

การออกแบบระบบ (System Architecture)
เพื่อให้ได้ประสิทธิภาพสูงสุด เราจำเป็นต้องใช้เทคนิค Multi-threading ทั้งในฝั่ง Hardware และ Software เพื่อให้การรับข้อมูล (Data Capture) และการแสดงผล (Rendering) ทำงานขนานกันไปได้
แอปพลิเคชันนี้แบ่งการทำงานออกเป็น 3 Thread หลัก:
- C# Graphical User Interface (UI): รับผิดชอบส่วนติดต่อผู้ใช้ แยกออกมาจากงานหลักเพื่อให้ความหน่วงของ UI ไม่ไปรบกวนกระบวนการรับส่งข้อมูล
- C++ DLL (OpenGL): ใช้ Native C DLL ในการหุ้ม (Wrap) หน้าต่าง OpenGL เพื่อให้การวาดกราฟเส้นสัญญาณเป็นหน้าที่ของ GPU โดยตรง ซึ่ง OpenGL เป็น Library ที่มีประสิทธิภาพสูงและรองรับหลาย Platform
- C++ DLL (WinUSB): ส่วนนี้สำคัญที่สุด คือการใช้ Driver
winusb.sysเพื่อจัดการการรับส่งข้อมูลแบบ Isochronous.- ทำไมต้อง Isochronous? เพราะเป็น Mode ที่เน้น "Streaming" ข้อมูลเป็นหลัก โดยยอมเสียสละเรื่อง Data Integrity Check (การตรวจสอบความถูกต้องของข้อมูลซ้ำ) เพื่อแลกกับความเร็วและความต่อเนื่องของ Bandwidth ทำให้เราส่งข้อมูลได้ถึง 1023 bytes ทุกๆ 1ms

วิเคราะห์ประสิทธิภาพจากการทดสอบจริง
จากการวิเคราะห์ผ่าน Wireshark และการ Monitor ระบบ พบข้อมูลที่น่าสนใจดังนี้:
- เราสามารถทำ Sampling ได้ที่ประมาณ 270kSps ในขณะที่ระบบกำลังรัน (สาเหตุที่ลดลงจาก 350kSps เพราะ CPU ต้องแบ่งทรัพยากรไปจัดการ USB Stack ของ Arduino ด้วย)
- การรวบรวมและส่งข้อมูลขนาด 1023 bytes ผ่านพอร์ต USB ใช้เวลาประมาณ 6.7ms ซึ่งคิดเป็น 264 samples ต่อชุดข้อมูล
- กระบวนการเตรียมข้อมูล (Transform), สร้างกราฟ (Build) และวาดลงจอ (Draw) ใช้เวลารวมเพียง 2.1ms (0.15 + 0.35 + 1.6ms ตามลำดับ)
- Total Frame Rate ที่วัดได้ใน OpenGL อยู่ที่ประมาณ 9.2ms ซึ่งเร็วกว่าเป้าหมายที่ 16ms มาก ทำให้เราสามารถเพิ่มจำนวน Sample ต่อเฟรมเป็น 2048 ได้โดยที่ภาพยังลื่นไหล
ขั้นตอนการติดตั้งและเตรียมอุปกรณ์
แอปพลิเคชันนี้มีความเป็น "Experimental" สูง ดังนั้นการติดตั้งจึงต้องอาศัยทักษะทางเทคนิคระดับหนึ่ง:
- ฝั่ง Arduino: บอร์ดต้องมีระบบ Isochronous USB ติดตั้งไว้ ซึ่งปกติ ArduinoCore ทั่วไปจะไม่รองรับ ในโปรเจกต์นี้มี Code สำหรับขยายความสามารถ (Extension) ของ ArduinoCore มาให้ เพื่อให้บอร์ดรองรับพอร์ต Isochronous โดยที่ยังสามารถใช้
Serial.printได้ตามปกติ - ฝั่ง Windows (Driver): คุณต้องติดตั้งไฟล์
WinUSB.infโดยต้องรัน Windows ใน Test Mode เนื่องจากตัว Driver ยังไม่มี Digital Certificate ที่เป็นทางการ - ฝั่ง Software: Source Code บน GitHub ยังไม่ได้ทำการ Compile (Uncompiled) เพื่อสนับสนุนให้นักพัฒนาท่านอื่นนำไปปรับแต่ง (Adjust) และ Build ขึ้นมาเองตามความเหมาะสมของระบบ
ข้อจำกัดและการพัฒนาในอนาคต
โปรเจกต์ "YetAnotherUSB Oscilloscope" พัฒนาขึ้นบน Visual Studio 2019 ด้วย .NET Framework ล่าสุด และทดสอบบนระบบ 64-bit เท่านั้น:
- ความเสถียร: ระบบ Multi-threading ยังไม่ Robust เต็มที่ อาจเกิด Infinite Loop ได้ในกรณีที่ดึงสาย USB ออกกะทันหันระหว่างอ่านค่า
- ฟีเจอร์ที่ขาดหาย: ปัจจุบันยังไม่มีระบบ FFT (Fast Fourier Transform), Triggers ที่ซับซ้อน และความสามารถในการบันทึกข้อมูล (Memorizing)
หวังว่าโปรเจกต์นี้จะเป็นแรงบันดาลใจและเป็นฐานข้อมูลที่ดีสำหรับวิศวกรหรือ Maker ที่ต้องการดึงพลังแฝงของ Arduino ออกมาใช้งานในระดับสูงครับ!
Keep Making!