การสื่อสารกับบอร์ด MCU ผ่านพอร์ต USB ปฏิบัติการเกี่ยวกับวิศวกรรมคอมพิวเตอร์ (01204223) ผศ.ดร.ชัยพร ใจแก้ว ภาควิชาวิศวกรรมคอมพิวเตอร์ คณะวิศวกรรมศาสตร์ มหาวิทยาลัยเกษตรศาสตร์
สถาปัตยกรรม USB อุปกรณ์ด้านหนึ่งทำหน้าที่เป็นโฮสท์ (Host) อีกด้านหนึ่งทำหน้าที่เป็นดีไวซ์ (Device) ไม่ว่าจะเขียนข้อมูลไปยังดีไวซ์ หรืออ่านข้อมูลจากดีไวซ์ ฝั่งโฮสท์ต้องมีการส่งคำร้องขอ (request) ไปก่อนเสมอ Request PC - Notebook - USB Host USB Device - Flash drive - Mouse - Game controller - etc. Device Driver App USB Cable Response
โครงสร้างของคำร้องขอ ประกอบด้วยข้อมูล 5 ส่วน RequestType (1 ไบท์) – ทิศทางการส่งข้อมูล Request (1 ไบท์) – หมายเลขคำร้องขอ Value (2 ไบท์) – พารามิเตอร์ประกอบคำร้องเพิ่มเติม Index (2 ไบท์) – พารามิเตอร์ประกอบคำร้องเพิ่มเติม Length (2 ไบท์) – จำนวนไบท์ของข้อมูลที่ต้องการส่งให้หรือรับจากดีไวซ์
โค้ดตัวอย่าง ดาวน์โหลดได้จาก http://www.cpe.ku.ac.th/~cpj/204223/usb-example.tgz แตกไฟล์ไว้ในเครื่องของตนโดยเรียกคำสั่งเชลล์ ในไฟล์ตัวอย่างประกอบด้วย ไลบรารี V-USB main.c –ตัวอย่างเฟิร์มแวร์ฝั่งดีไวซ์ practicum.py – ไพธอนมอดูลสำหรับฝั่งโฮสท์ peri.py – ไฟล์เริ่มต้นสำหรับทำแบบฝึกหัด test-usb.py – โค้ดสำหรับทดสอบแบบฝึกหัด Makefile $ tar zxf usb-example.tgz
เฟิร์มแวร์ฝั่งดีไวซ์ (บอร์ด MCU) อาศัยไลบรารี V-USB จำลองกลไก USB ด้วยซอฟต์แวร์ แก้ไขการตั้งค่าในไฟล์ usbconfig.h เปลี่ยน USB_CFG_DEVICE_NAME ให้เป็น Practicum Group XX โดย XX เป็นหมายเลขกลุ่ม (อย่าลืมระบุความยาวสตริงให้ถูกต้อง) โค้ดในเมนลูปมีการเรียกฟังก์ชัน usbPoll() เพื่อตรวจสอบคำร้องขอจากฝั่งโฮสท์ เมื่อได้รับคำร้องขอ ไลบรารี V-USB จะเรียกฟังก์ชัน usbFunctionSetup() โดยอัตโนมัติ ต้องเขียนฟังก์ชันนี้ขึ้นมาเอง
ตัวอย่างฟังก์ชัน usbFunctionSetup usbMsgLen_t usbFunctionSetup(uint8_t data[8]) { usbRequest_t *rq = (void *)data; static uint16_t returnedData; if (rq->bRequest == 0) /* Do something */ return 0; } else if (rq->bRequest == 1) usbMsgPtr = (uchar*) &returnedData; return sizeof(returnedData); ตรวจสอบหมายเลขคำร้อง ตอบสนองคำร้องขอที่ไม่ขอข้อมูลคืน - ให้ฟังก์ชันคืนค่า 0 ตอบสนองคำร้องขอที่ต้องการข้อมูลคืน - ให้ตัวแปร usbMsgPtr ชี้ที่ตำแหน่งของ ข้อมูลที่ต้องการส่งให้โฮสท์ - ให้ฟังก์ชันคืนค่าจำนวนไบท์ที่ต้องการ ส่งให้โฮสท์
โครงสร้างคำร้องขอ มีขนาด 8 ไบท์ นิยามไว้แล้วในสตรัค usbRequest_t (ในไฟล์ usbdrv/usbdrv.h) ดังนี้ uchar ถูกนิยามให้เป็นชนิดข้อมูล unsigned char (ซึ่งเทียบเท่ากับ uint8_t) ส่วน usbWord_t นิยามเป็นชนิด union ดังนี้ typedef struct usbRequest{ uchar bmRequestType; /* 1 ไบท์ */ uchar bRequest; /* 1 ไบท์ */ usbWord_t wValue; /* 2 ไบท์ */ usbWord_t wIndex; /* 2 ไบท์ */ usbWord_t wLength; /* 2 ไบท์ */ }usbRequest_t; typedef union usbWord{ unsigned word; uchar bytes[2]; }usbWord_t;
โปรแกรมฝั่งโฮสท์ (ไพธอน) อาศัยไลบรารี PyUSB ทดสอบโดยเรียกคำสั่ง import usb ในไพธอน (Linux) หากไม่พบให้ติดตั้งโดยใช้คำสั่ง (MacOS) หากไม่พบให้ดาวน์โหลด libusb1.0 จาก SourceForge มอดูล practicum.py เตรียมคลาส McuBoard ไว้ให้ใช้งานได้สะดวกขึ้น $ sudo apt-get install python-usb >>> from practicum import McuBoard >>> b = McuBoard() >>> help(b)
ตัวอย่างการส่งคำร้อง >>> from practicum import McuBoard >>> b = McuBoard() # สั่งให้ LED สีเขียว (หมายเลข 2) บน Peripheral board ติด >>> b.usb_write(0, index=2, value=1) # อ่านสถานะสวิตช์บน Peripheral board >>> b.usb_read(1, length=1)
แบบฝึกหัด แก้ไขเฟิร์มแวร์ (main.c) เพื่อเพิ่มคำร้องขอหมายเลข 2 ส่งค่าแสง 10 บิตกลับมายังโฮสท์ (2 ไบท์) ค่าใน tuple ที่ส่งคืนคือ (ไบท์ต่ำ, ไบท์สูง) แก้คลาส PeriBoard ในมอดูล peri.py ที่สืบเชื้อสายมาจากคลาส McuBoard ใน practicum.py โดยให้มีเมท็อดดังนี้ setLed(self, led_no, led_value) – เซ็ตสถานะ LED ตามหมายเลข led_no ที่ระบุให้เป็นไปตาม led_value (0 = ดับ, 1 = ติด) setLedValue(self, value) – นำ 3 บิตล่างของ value แสดงผลบน LED โดยให้สีแดงเป็นบิตขวาสุด เขียวเป็นบิตซ้ายสุด getSwitch(self) – คืนค่า True เมื่อสวิตช์ถูกกด False เมื่อปล่อย getLight(self) – คืนค่าแสงในช่วง 0-1023
โปรแกรมสำหรับทดสอบ (test-usb.py) from peri import PeriBoard from time import sleep b = PeriBoard() count = 0 while True: b.setLedValue(count) sw = b.getSwitch() light = b.getLight() if sw is True: state = "PRESSED" else: state = "RELEASED" print "LEDs set to %d | Switch state: %-8s | Light value: %d" % ( count, state, light) sleep(0.25) count = (count + 1) % 8
โครงงานปลายภาค แต่ละกลุ่มพัฒนาโครงงานขนาดเล็ก เพิ่มฮาร์ดแวร์จาก peripheral board ที่มีอยู่ หรือทำบอร์ดใหม่หากต้องการ พัฒนาเฟิร์มแวร์สำหรับ MCU เชื่อมต่อกับซอฟต์แวร์บนฝั่งพีซีผ่านพอร์ต USB กำหนดส่งและนำเสนอ: แจ้งให้ทราบภายหลัง ดูตัวอย่างโครงงานของรุ่นพี่ได้จากเว็บ http://cloud3.cpe.ku.ac.th/practicum