ผลต่างระหว่างรุ่นของ "การจำลองบอร์ด MCU เป็นอุปกรณ์ USB"

จาก Theory Wiki
ไปยังการนำทาง ไปยังการค้นหา
แถว 1: แถว 1:
ที่ผ่านมานั้นเราใช้พอร์ท USB เป็นเพียงแหล่งจ่ายพลังงานและโปรแกรมแฟลช เราสามารถสั่งงานไมโครคอนโทรลเลอร์ให้จำลองการทำงานเป็นอุปกรณ์ USB ความเร็วต่ำ เพื่อที่จะสามารถสื่อสารกับโปรแกรมที่ทำงานอยู่บนเครื่องคอมพิวเตอร์ได้
+
ที่ผ่านมานั้นเราใช้พอร์ท USB เป็นเพียงแหล่งจ่ายพลังงานและโปรแกรมแฟลชเท่านั้น วิกินี้อธิบายถึงขั้นตอนการทำให้ไมโครคอนโทรลเลอร์จำลองตัวเองเป็นอุปกรณ์ USB ความเร็วต่ำ เพื่อที่จะสามารถสื่อสารกับโปรแกรมที่ทำงานอยู่บนเครื่องคอมพิวเตอร์ได้
 +
 
 +
ใช้งานเป็น [http://vusb.wikidot.com/usb-device-classes custom class device] ซึ่งเป็นประเภทอุปกรณ์...
  
 
ในที่นี้เราจะใช้โอเพนซอร์สไลบรารีชื่อ [http://www.obdev.at/products/vusb/index.html V-USB] (หรือ AVRUSB) ที่พัฒนาโดยบริษัท [http://www.obdev.at/index.html Objective Development]
 
ในที่นี้เราจะใช้โอเพนซอร์สไลบรารีชื่อ [http://www.obdev.at/products/vusb/index.html V-USB] (หรือ AVRUSB) ที่พัฒนาโดยบริษัท [http://www.obdev.at/index.html Objective Development]
 +
 +
== ขั้นตอนการใช้งานไลบรารี V-USB ==
 +
* ดาวน์โหลดซอร์สโค้ดจาก [http://www.obdev.at/downloads/vusb/vusb-20090822.tar.gz]
 +
* แตกไฟล์ .tar.gz ที่ดาวน์โหลดมาโดยใช้คำสั่ง
 +
tar zxf vusb-20090822.tar.gz
 +
* คัดลอกไดเรคตอรี <code>usbdrv</code> ที่อยู่ในไดเรคตอรี <code>vusb-2009-0822</code> ไปวางในไดเรคตอรีโปรเจ็คของตน
 +
* ภายในไดเรคตอรีโปรเจ็คของตนเอง ย้ายไฟล์ <code>usbconfig-prototype.h</code> มาวางไว้ด้านนอก และเปลี่ยนชื่อให้เป็น <code>usbconfig.h</code>
 +
mv usbdrv/usbconfig-prototype.h ../usbconfig.h
 +
:ไฟล์นี้จะเก็บข้อมูลการตั้งค่าเกี่ยวกับอุปกรณ์ USB ที่จะให้ไมโครคอนโทรลเลอร์จำลองตัวเองขึ้นมา
 +
* ในไฟล์หลักของโปรเจ็ค เรียกใช้คำสั่ง <code>#include</code> ต่อไปนี้ไว้ที่ตอนต้นของโปรแกรม
 +
#include <avr/io.h>
 +
#include <avr/interrupt.h>  /* for sei() */
 +
#include <util/delay.h>    /* for _delay_ms() */
 +
#include <avr/pgmspace.h>  /* required by usbdrv.h */
 +
#include "usbdrv.h"
 +
* ในส่วนของฟังก์ชัน <code>main()</code> ต้องมีโครงสร้างหลักดังนี้ (สามารถใส่โค้ดอื่นเพิ่มได้ตามที่ต้องการ)
 +
int main()
 +
{
 +
    usbInit();
 +
    usbDeviceDisconnect();
 +
    _delay_ms(300);    /* fake USB disconnect for > 250 ms */
 +
    usbDeviceConnect();
 +
    sei();              /* enable interrupts */
 +
    while (1)          /* main event loop */
 +
    {               
 +
        usbPoll();      /* คำสั่งนี้ต้องถูกเรียกอย่างน้อยที่สุดทุก ๆ 50ms */
 +
    }
 +
    return 0;
 +
}
 +
* นิยามฟังก์ชัน <code>usbFunctionSetup</code> เพื่อประมวลผลข้อมูลที่รับมาจากเครื่องคอมพิวเตอร์ผ่านทางพอร์ท USB
 +
usbMsgLen_t usbFunctionSetup(uchar data[8])
 +
{
 +
    :
 +
}
 +
:ฟังก์ชันนี้จะถูกเรียกทำงานโดยอัตโนมัติจากฟังก์ชัน <code>usbPoll</code> เมื่อทางฝั่งคอมพิวเตอร์ส่งคำสั่งผ่านมาทางพอร์ท USB
 +
 +
== การคอมไพล์และลิ้งค์โปรแกรมรวมกับ V-USB ==
 +
สร้าง Makefile ต่อไปนี้เพื่อคอมไพล์โปรแกรมและดำเนินการลิ้งค์เข้ากับไลบรารี V-USB ซึ่งตัวอย่าง Makefile นี้สมมติว่าไฟล์หลักของโปรเจ็คคือ <code>main.c</code>
 +
MCU=atmega168
 +
F_CPU=16000000L
 +
TARGET=main.hex
 +
OBJS=usbdrv/usbdrv.o usbdrv/usbdrvasm.o
 +
CFLAGS=-Wall -Os -DF_CPU=$(F_CPU) -Iusbdrv -I. -mmcu=$(MCU)
 +
 +
all: $(TARGET)
 +
 +
flash: $(TARGET)
 +
    avrdude -p atmega168 -c usbasp -U flash:w:$(TARGET)
 +
 +
%.hex: %.elf
 +
    avr-objcopy -j .text -O ihex $< $@
 +
 +
%.elf: %.o $(OBJS)
 +
    avr-gcc $(CFLAGS) -o $@ $?
 +
 +
%.o: %.c
 +
    avr-gcc -c $(CFLAGS) $<
 +
 +
%.o: %.S
 +
    avr-gcc $(CFLAGS) -x assembler-with-cpp -c -o $@ $<
 +
 +
clean:
 +
    rm -f $(OBJS)
 +
    rm -f $(TARGET)
 +
    rm -f *~
 +
 +
สังเกตดูในกฎการสร้างไฟล์ <code>main.elf</code> ขึ้นมาจาก <code>main.o</code> บรรทัดที่มีการเรียกใช้ <code>avr-gcc</code> ได้มีการนำเอา <code>$(OBJS)$</code> (ซึ่งได้แก่ไฟล์
 +
<code>usbdrv/usbdrv.o</code> และ <code>usbdrv/usbdrvasm.o</code>) ลิ้งค์รวมเข้าไปด้วย ในที่นี้ตัวแปรพิเศษ <code>$@</code> แทน target ซึ่งหมายถึง
 +
<code>main.elf</code> ส่วนตัวแปรพิเศษ <code>$?</code> แทนรายการของ dependency ทั้งหมด
 +
 +
== โครงสร้างของคำสั่ง USB ที่รับจากฝั่งคอมพิวเตอร์ ==
 +
ในสถาปัตยกรรม USB นั้นการสื่อสารจะถูกเริ่มจากฝั่งคอมพิวเตอร์ (ฝั่งโฮสท์) ไปยังฝั่งอุปกรณ์ USB เสมอไม่ว่าจะมีการอ่านข้อมูลจากอุปกรณ์ USB หรือเขียนข้อมูลไปยังอุปกรณ์ USB ก็ตาม
 +
ข้อมูลคำสั่งที่ถูกส่งจากโฮสท์ เรียกว่า USB Request มีโครงสร้างตามที่นิยามไว้ในไฟล์ <code>usbdrv/usbdrv.h</code> ดังนี้
 +
typedef struct usbRequest{
 +
    uchar      bmRequestType;  /* 1 ไบท์ */
 +
    uchar      bRequest;      /* 1 ไบท์ */
 +
    usbWord_t  wValue;        /* 2 ไบท์ */
 +
    usbWord_t  wIndex;        /* 2 ไบท์ */
 +
    usbWord_t  wLength;        /* 2 ไบท์ */
 +
}usbRequest_t;
 +
* <code>bmRequestType</code> ประกอบด้วยฟิลด์ย่อย 3 ฟิลด์ดังต่อไปนี้
 +
:* บิต 7 ทิศทางการส่งข้อมูล (Data Phase Transfer Direction)
 +
::* 0 = จากคอมพิวเตอร์ไปอุปกรณ์ USB (Host to Device)
 +
::* 1 = จากอุปกรณ์ USB มายังคอมพิวเตอร์ (Device to Host)
 +
:* บิต 6..5 ประเภทคำสั่ง (Type)
 +
::* 0 = Standard
 +
::* 1 = Class
 +
::* 2 = Vendor
 +
:* บิต 4..0 ผู้รับ (Recipient)
 +
::* 0 = Device
 +
::* 1 = Interface
 +
::* 2 = Endpoint
 +
::* 3 = Other
 +
* <code>bRequest</code> ระบุหมายเลขคำสั่ง ซึ่งต้องถูกออกแบบไว้ล่วงหน้าแล้วว่าอุปกรณ์ USB ของเราจะรองรับคำสั่งอะไรบ้าง โดยเฟิร์มแวร์ต้องประมวลผลคำสั่งเหล่านี้ได้ถูกต้อง
 +
* <code>wValue</code> และ <code>wIndex</code>
 +
* <code>wLength</code> กำหนดขนาดของข้อมูลเพิ่มเติมที่จะส่งจากฝั่งโฮสท์หรือจากอุปกรณ์ USB หากไม่มีข้อมูลเพิ่มเติม ค่านี้จะถูกเซ็ตเป็นศูนย์
 +
 +
อุปกรณ์ USB
 +
 +
== ตัวอย่างโปรแกรม ==
 +
เพื่อให้เห็นภาพของการใช้งานไลบรารี V-USB มากขึ้น เราลองสร้างตัวอย่างเฟิร์มแวร์อย่างง่ายขึ้นมาพร้อมทั้งโปรแกรมที่ทดลองสั่งงานจากฝั่งคอมพิวเตอร์ (ฝั่งโฮสท์) ด้วยภาษาไพธอน
 +
ในสถาปัตยกรรม USB นั้นการสื่อสารจะถูกกำหนดโดยฝั่งโฮสท์เสมอไม่ว่าจะมีการอ่านข้อมูลจากอุปกรณ์ USB หรือเขียนข้อมูลไปยังอุปกรณ์ USB ก็ตาม
 +
ดังนั้นหน้าที่ของเฟิร์มแวร์คือตีความคำสั่งที่ส่งมาจากโฮสท์และเตรียมข้อมูลที่จะส่งกลับไปให้โฮสท์
 +
 +
เราจะให้ตัวเฟิร์มแวร์จำลองตัวเป็นอุปกรณ์ USB ที่รองรับการสั่งงานจากโฮสท์ 2 คำสั่ง ดังนี้
 +
* '''คำสั่ง SET_LED''' สั่งให้ LED ดวงที่ระบุติดหรือดับ มีรายละเอียดของคำสั่งดังนี้
 +
:* กำหนดให้หมายเลขคำสั่ง (request number) คือ 0
 +
:* การใช้งานจะระบุให้อุปกรณ์ USB เป็นเอาท์พุท
 +
:* ส่งพารามิเตอร์มาให้ 2 ไบท์ ไบท์แรกระบุตำแหน่งของ LED (0, 1 หรือ 2) ส่วนไบท์ที่สองระบุว่าจะให้ LED ดวงดังกล่าวติด (หากมีค่า 0) หรือดับ (หากมีค่าอื่นที่ไม่ใช่ศูนย์)
 +
* '''คำสั่ง GET_SWITCH''' สั่งให้บอร์ด MCU รายงานสถานะการกดปุ่มสวิตช์กลับมา มีรายละเอียดของคำสั่งดังนี้
 +
:* กำหนดให้หมายเลขคำสั่ง (request number) คือ 1
 +
:* การใช้งานจะระบุให้อุปกรณ์ USB เป็นอินพุท
 +
:* ไม่มีพารามิเตอร์ใดส่งไป แต่ตัวบอร์ด MCU ส่งรายงานกลับมา 1 ไบท์ บอกสถานะของสวิตช์ (0 คือไม่ถูกกด 1 คือถูกกด)
 +
 +
===การตั้งค่าให้อุปกรณ์ USB===
 +
* อุปกรณ์ USB ทุกตัวจะต้องมีการระบุค่า Vendor ID (VID) และ Product ID (PID) อย่างละ 16 บิต
 +
ซึ่งไม่สามารถตั้งเองได้ตามใจชอบเนื่องจากระบบปฏิบัติการจะอาศัยตัวเลขคู่นี้ในการเลือกโปรแกรมไดรเวอร์ที่จะมาควบคุมอุปกรณ์ USB
 +
http://www.voti.nl/docs/usb-pid.html
 +
ตั้งค่า VID ให้เป็น 0x16c0
 +
 +
===โปรแกรมฝั่งเฟิร์มแวร์===
 +
เพื่อเป็นแนวปฏิบัติที่ดีในการเขียนโปรแกรม เราจะนิยามคำสั่งเหล่านี้ไว้ที่ส่วนหัวของ <code>main.c</code> ดังนี้
 +
#define SET_LED    0
 +
#define GET_SWITCH 1
 +
 +
ภายในฟังก์ชัน <code>usbFunctionSetup</code> สร้างโค้ดสำหรับประมวลผลคำสั่งที่รับมาจากโฮสท์ดังนี้
 +
usbMsgLen_t usbFunctionSetup(uchar data[8])
 +
{
 +
    usbRequest_t *rq = (void *)data;
 +
    static uchar dataBuffer[4];  /* buffer must stay valid when usbFunctionSetup returns */
 +
 +
    if(rq->bRequest == SET_LED)
 +
    {
 +
        dataBuffer[0] = rq->wValue.bytes[0];
 +
        dataBuffer[1] = rq->wValue.bytes[1];
 +
        dataBuffer[2] = rq->wIndex.bytes[0];
 +
        dataBuffer[3] = rq->wIndex.bytes[1];
 +
        usbMsgPtr = dataBuffer;        /* tell the driver which data to return */
 +
        return 4;
 +
    }else if(rq->bRequest == CUSTOM_RQ_SET_STATUS){
 +
        if(rq->wValue.bytes[0] & 1){    /* set LED */
 +
            LED_PORT_OUTPUT |= _BV(LED_BIT);
 +
        }else{                          /* clear LED */
 +
            LED_PORT_OUTPUT &= ~_BV(LED_BIT);
 +
        }
 +
    }else if(rq->bRequest == CUSTOM_RQ_GET_STATUS){
 +
        dataBuffer[0] = ((LED_PORT_OUTPUT & _BV(LED_BIT)) != 0);
 +
        usbMsgPtr = dataBuffer;        /* tell the driver which data to return */
 +
        return 1;                      /* tell the driver to send 1 byte */
 +
    }
 +
    return 0;  /* default for not implemented requests: return no data back to host */
 +
}
 +
 +
 +
 +
== การโปรแกรมฝั่งเครื่องคอมพิวเตอร์ ==
 +
ติดตั้ง PyUSB เพื่อให้ติดต่อกับอุปกรณ์ USB โดยใช้ภาษาไพธอนได้
 +
sudo apt-get install python-usb
 +
 +
== ข้อมูลเพิ่มเติม ==
 +
* [http://vusb.wikidot.com/ V-USB Documentation Wiki]
 +
* [http://www.beyondlogic.org/usbnutshell/usb1.htm USB in a NutShell]

รุ่นแก้ไขเมื่อ 04:11, 26 สิงหาคม 2552

ที่ผ่านมานั้นเราใช้พอร์ท USB เป็นเพียงแหล่งจ่ายพลังงานและโปรแกรมแฟลชเท่านั้น วิกินี้อธิบายถึงขั้นตอนการทำให้ไมโครคอนโทรลเลอร์จำลองตัวเองเป็นอุปกรณ์ USB ความเร็วต่ำ เพื่อที่จะสามารถสื่อสารกับโปรแกรมที่ทำงานอยู่บนเครื่องคอมพิวเตอร์ได้

ใช้งานเป็น custom class device ซึ่งเป็นประเภทอุปกรณ์...

ในที่นี้เราจะใช้โอเพนซอร์สไลบรารีชื่อ V-USB (หรือ AVRUSB) ที่พัฒนาโดยบริษัท Objective Development

ขั้นตอนการใช้งานไลบรารี V-USB

  • ดาวน์โหลดซอร์สโค้ดจาก [1]
  • แตกไฟล์ .tar.gz ที่ดาวน์โหลดมาโดยใช้คำสั่ง
tar zxf vusb-20090822.tar.gz
  • คัดลอกไดเรคตอรี usbdrv ที่อยู่ในไดเรคตอรี vusb-2009-0822 ไปวางในไดเรคตอรีโปรเจ็คของตน
  • ภายในไดเรคตอรีโปรเจ็คของตนเอง ย้ายไฟล์ usbconfig-prototype.h มาวางไว้ด้านนอก และเปลี่ยนชื่อให้เป็น usbconfig.h
mv usbdrv/usbconfig-prototype.h ../usbconfig.h
ไฟล์นี้จะเก็บข้อมูลการตั้งค่าเกี่ยวกับอุปกรณ์ USB ที่จะให้ไมโครคอนโทรลเลอร์จำลองตัวเองขึ้นมา
  • ในไฟล์หลักของโปรเจ็ค เรียกใช้คำสั่ง #include ต่อไปนี้ไว้ที่ตอนต้นของโปรแกรม
#include <avr/io.h>
#include <avr/interrupt.h>  /* for sei() */
#include <util/delay.h>     /* for _delay_ms() */
#include <avr/pgmspace.h>   /* required by usbdrv.h */
#include "usbdrv.h"
  • ในส่วนของฟังก์ชัน main() ต้องมีโครงสร้างหลักดังนี้ (สามารถใส่โค้ดอื่นเพิ่มได้ตามที่ต้องการ)
int main()
{
    usbInit();
    usbDeviceDisconnect();
    _delay_ms(300);     /* fake USB disconnect for > 250 ms */
    usbDeviceConnect();
    sei();              /* enable interrupts */
    while (1)           /* main event loop */
    {                
        usbPoll();      /* คำสั่งนี้ต้องถูกเรียกอย่างน้อยที่สุดทุก ๆ 50ms */
    }
    return 0;
}
  • นิยามฟังก์ชัน usbFunctionSetup เพื่อประมวลผลข้อมูลที่รับมาจากเครื่องคอมพิวเตอร์ผ่านทางพอร์ท USB
usbMsgLen_t usbFunctionSetup(uchar data[8])
{
    :
}
ฟังก์ชันนี้จะถูกเรียกทำงานโดยอัตโนมัติจากฟังก์ชัน usbPoll เมื่อทางฝั่งคอมพิวเตอร์ส่งคำสั่งผ่านมาทางพอร์ท USB

การคอมไพล์และลิ้งค์โปรแกรมรวมกับ V-USB

สร้าง Makefile ต่อไปนี้เพื่อคอมไพล์โปรแกรมและดำเนินการลิ้งค์เข้ากับไลบรารี V-USB ซึ่งตัวอย่าง Makefile นี้สมมติว่าไฟล์หลักของโปรเจ็คคือ main.c

MCU=atmega168
F_CPU=16000000L
TARGET=main.hex
OBJS=usbdrv/usbdrv.o usbdrv/usbdrvasm.o
CFLAGS=-Wall -Os -DF_CPU=$(F_CPU) -Iusbdrv -I. -mmcu=$(MCU)

all: $(TARGET)

flash: $(TARGET)
    avrdude -p atmega168 -c usbasp -U flash:w:$(TARGET)

%.hex: %.elf
    avr-objcopy -j .text -O ihex $< $@

%.elf: %.o $(OBJS)
    avr-gcc $(CFLAGS) -o $@ $?

%.o: %.c
    avr-gcc -c $(CFLAGS) $<

%.o: %.S
    avr-gcc $(CFLAGS) -x assembler-with-cpp -c -o $@ $<

clean:
    rm -f $(OBJS)
    rm -f $(TARGET)
    rm -f *~

สังเกตดูในกฎการสร้างไฟล์ main.elf ขึ้นมาจาก main.o บรรทัดที่มีการเรียกใช้ avr-gcc ได้มีการนำเอา $(OBJS)$ (ซึ่งได้แก่ไฟล์ usbdrv/usbdrv.o และ usbdrv/usbdrvasm.o) ลิ้งค์รวมเข้าไปด้วย ในที่นี้ตัวแปรพิเศษ $@ แทน target ซึ่งหมายถึง main.elf ส่วนตัวแปรพิเศษ $? แทนรายการของ dependency ทั้งหมด

โครงสร้างของคำสั่ง USB ที่รับจากฝั่งคอมพิวเตอร์

ในสถาปัตยกรรม USB นั้นการสื่อสารจะถูกเริ่มจากฝั่งคอมพิวเตอร์ (ฝั่งโฮสท์) ไปยังฝั่งอุปกรณ์ USB เสมอไม่ว่าจะมีการอ่านข้อมูลจากอุปกรณ์ USB หรือเขียนข้อมูลไปยังอุปกรณ์ USB ก็ตาม ข้อมูลคำสั่งที่ถูกส่งจากโฮสท์ เรียกว่า USB Request มีโครงสร้างตามที่นิยามไว้ในไฟล์ usbdrv/usbdrv.h ดังนี้

typedef struct usbRequest{
    uchar       bmRequestType;  /* 1 ไบท์ */
    uchar       bRequest;       /* 1 ไบท์ */
    usbWord_t   wValue;         /* 2 ไบท์ */
    usbWord_t   wIndex;         /* 2 ไบท์ */
    usbWord_t   wLength;        /* 2 ไบท์ */
}usbRequest_t;
  • bmRequestType ประกอบด้วยฟิลด์ย่อย 3 ฟิลด์ดังต่อไปนี้
  • บิต 7 ทิศทางการส่งข้อมูล (Data Phase Transfer Direction)
  • 0 = จากคอมพิวเตอร์ไปอุปกรณ์ USB (Host to Device)
  • 1 = จากอุปกรณ์ USB มายังคอมพิวเตอร์ (Device to Host)
  • บิต 6..5 ประเภทคำสั่ง (Type)
  • 0 = Standard
  • 1 = Class
  • 2 = Vendor
  • บิต 4..0 ผู้รับ (Recipient)
  • 0 = Device
  • 1 = Interface
  • 2 = Endpoint
  • 3 = Other
  • bRequest ระบุหมายเลขคำสั่ง ซึ่งต้องถูกออกแบบไว้ล่วงหน้าแล้วว่าอุปกรณ์ USB ของเราจะรองรับคำสั่งอะไรบ้าง โดยเฟิร์มแวร์ต้องประมวลผลคำสั่งเหล่านี้ได้ถูกต้อง
  • wValue และ wIndex
  • wLength กำหนดขนาดของข้อมูลเพิ่มเติมที่จะส่งจากฝั่งโฮสท์หรือจากอุปกรณ์ USB หากไม่มีข้อมูลเพิ่มเติม ค่านี้จะถูกเซ็ตเป็นศูนย์

อุปกรณ์ USB

ตัวอย่างโปรแกรม

เพื่อให้เห็นภาพของการใช้งานไลบรารี V-USB มากขึ้น เราลองสร้างตัวอย่างเฟิร์มแวร์อย่างง่ายขึ้นมาพร้อมทั้งโปรแกรมที่ทดลองสั่งงานจากฝั่งคอมพิวเตอร์ (ฝั่งโฮสท์) ด้วยภาษาไพธอน ในสถาปัตยกรรม USB นั้นการสื่อสารจะถูกกำหนดโดยฝั่งโฮสท์เสมอไม่ว่าจะมีการอ่านข้อมูลจากอุปกรณ์ USB หรือเขียนข้อมูลไปยังอุปกรณ์ USB ก็ตาม ดังนั้นหน้าที่ของเฟิร์มแวร์คือตีความคำสั่งที่ส่งมาจากโฮสท์และเตรียมข้อมูลที่จะส่งกลับไปให้โฮสท์

เราจะให้ตัวเฟิร์มแวร์จำลองตัวเป็นอุปกรณ์ USB ที่รองรับการสั่งงานจากโฮสท์ 2 คำสั่ง ดังนี้

  • คำสั่ง SET_LED สั่งให้ LED ดวงที่ระบุติดหรือดับ มีรายละเอียดของคำสั่งดังนี้
  • กำหนดให้หมายเลขคำสั่ง (request number) คือ 0
  • การใช้งานจะระบุให้อุปกรณ์ USB เป็นเอาท์พุท
  • ส่งพารามิเตอร์มาให้ 2 ไบท์ ไบท์แรกระบุตำแหน่งของ LED (0, 1 หรือ 2) ส่วนไบท์ที่สองระบุว่าจะให้ LED ดวงดังกล่าวติด (หากมีค่า 0) หรือดับ (หากมีค่าอื่นที่ไม่ใช่ศูนย์)
  • คำสั่ง GET_SWITCH สั่งให้บอร์ด MCU รายงานสถานะการกดปุ่มสวิตช์กลับมา มีรายละเอียดของคำสั่งดังนี้
  • กำหนดให้หมายเลขคำสั่ง (request number) คือ 1
  • การใช้งานจะระบุให้อุปกรณ์ USB เป็นอินพุท
  • ไม่มีพารามิเตอร์ใดส่งไป แต่ตัวบอร์ด MCU ส่งรายงานกลับมา 1 ไบท์ บอกสถานะของสวิตช์ (0 คือไม่ถูกกด 1 คือถูกกด)

การตั้งค่าให้อุปกรณ์ USB

  • อุปกรณ์ USB ทุกตัวจะต้องมีการระบุค่า Vendor ID (VID) และ Product ID (PID) อย่างละ 16 บิต

ซึ่งไม่สามารถตั้งเองได้ตามใจชอบเนื่องจากระบบปฏิบัติการจะอาศัยตัวเลขคู่นี้ในการเลือกโปรแกรมไดรเวอร์ที่จะมาควบคุมอุปกรณ์ USB http://www.voti.nl/docs/usb-pid.html ตั้งค่า VID ให้เป็น 0x16c0

โปรแกรมฝั่งเฟิร์มแวร์

เพื่อเป็นแนวปฏิบัติที่ดีในการเขียนโปรแกรม เราจะนิยามคำสั่งเหล่านี้ไว้ที่ส่วนหัวของ main.c ดังนี้

#define SET_LED    0
#define GET_SWITCH 1

ภายในฟังก์ชัน usbFunctionSetup สร้างโค้ดสำหรับประมวลผลคำสั่งที่รับมาจากโฮสท์ดังนี้

usbMsgLen_t usbFunctionSetup(uchar data[8])
{
   usbRequest_t *rq = (void *)data;
   static uchar dataBuffer[4];  /* buffer must stay valid when usbFunctionSetup returns */

    if(rq->bRequest == SET_LED)
    {
        dataBuffer[0] = rq->wValue.bytes[0];
        dataBuffer[1] = rq->wValue.bytes[1];
        dataBuffer[2] = rq->wIndex.bytes[0];
        dataBuffer[3] = rq->wIndex.bytes[1];
        usbMsgPtr = dataBuffer;         /* tell the driver which data to return */
        return 4;
    }else if(rq->bRequest == CUSTOM_RQ_SET_STATUS){
        if(rq->wValue.bytes[0] & 1){    /* set LED */
            LED_PORT_OUTPUT |= _BV(LED_BIT);
        }else{                          /* clear LED */
            LED_PORT_OUTPUT &= ~_BV(LED_BIT);
        }
    }else if(rq->bRequest == CUSTOM_RQ_GET_STATUS){
        dataBuffer[0] = ((LED_PORT_OUTPUT & _BV(LED_BIT)) != 0);
        usbMsgPtr = dataBuffer;         /* tell the driver which data to return */
        return 1;                       /* tell the driver to send 1 byte */
    }
    return 0;   /* default for not implemented requests: return no data back to host */
}


การโปรแกรมฝั่งเครื่องคอมพิวเตอร์

ติดตั้ง PyUSB เพื่อให้ติดต่อกับอุปกรณ์ USB โดยใช้ภาษาไพธอนได้

sudo apt-get install python-usb

ข้อมูลเพิ่มเติม