WebElektronika

USB-CDC kommunikáció PIC18F mikrovezérlőkkel

person access_time 2014.07.21
Megismerkedünk most gyakorlati oldalról az USB-CDC használatával. Készítünk egy projektet az MpLab segítségével, amelynek segítségével LED-eket kapcsolunk be, illetve ki, de megnézzük azt is, hogy hogyan tudunk a mikrovezérlőből beolvasni string-et.


A Microchip különböző PIC mikrokontrollereket gyárt, amelyek képesek az USB kommunikációra, akár HID (Human Interface Device) használatával, akár CDC-ben (Communication Device Class). A PIC18-as családban (pl.: PIC18F67J50, PIC18F85J50, PIC18F14K50, stb), de akár a PIC32MX-eseknél (pl.: PIC32MX440F128L, PIC32MX795F512L, stb) is bőven találunk olyan eszközöket, amelyek képesek az USB protokoll használatára.

Készítsünk el közösen egy USB-s projektet, amelynek segítségével LED-eket tudunk ki-/bekapcsolni. Karaktereket fogunk küldeni, a kiküldött karakterektől függ, hogy melyik LED-et kapcsoljuk be/ki. Tehát az USB CDC-t fogjuk alkalmazni. A PC-n futó program virtuális soros porton keresztül csatlakozik a mikrovezérlős panelhez. Ennek a kommunikációs megoldásnak a maximális sebessége kb 64KB/s.
Ezt akár egy általunk megvalósított tesztpanelon, vagy a Microchip által gyártott rengeteg fejlesztői board egyikén is kipróbálhatjuk.
Az általunk választott PIC18F család fejlesztői kártyájának a leírása, illetve a kapcsolási rajza innen tölthető le.

A Microchip által kínált USB Stack (és egyéb stack, illetve alkalmazások) innen tölthető le. Bármelyik USB módot is használnánk (pl.: CDC, HID, stb), a Microchip úgy oldotta meg az adott példaalkalmazását, hogy a main() függvény felépítése mindig ugyanaz. Itt tudunk választani, hogy interrupt megoldást, vagy a polling-olást alkalmazzuk a projektünk elkészítésekor.
Polling-olásnál az USBDeviceTasks() függvény mindig meghívásra kerül a végtelen ciklusban, ha megszakítást használunk, akkor csak a megszakításnál kerül ez a függvény futtatásra. 

#if defined(__18CXX)
void main(void)
#else
int main(void)
#endif
{   
    InitializeSystem();

    #if defined(USB_INTERRUPT)
        USBDeviceAttach();
    #endif

    while(1)
    {
        #if defined(USB_POLLING)
        USBDeviceTasks();
        #endif

        ProcessIO();        
    }
}

Látható a main() függvénynél, hogy feltételes fordítás van, tehát csak az IT használatánál kerül sor az USBDeviceAttach() függvény használatára.

A while(1) végtelen ciklusban állandóan meghívjuk a ProcessIO() függvényt, ebben fogjuk megvalósítani azt a programrészletet, amely a LED-ek állapotáért fog felelni.

Telepítsük a Microchip Application Library-t, majd azután indítsuk el az MpLab-ot. Hozzunk létre egy projektet az MpLab-on belül, és a projekt felépítése a következő legyen (1. ábra).


1. ábra   Az USB-CDC projekt felépítése
 

Az USB CDC projektben található *.C és *.H file-ok megtalálhatók a telepített Microchip Application Library-ban.

A telepített Microchip Application Library-ban konkrét projekt is található, az. "..\..\USB\Device-CDC-Basic Demo\Firmware" elérési útvonalon. Kattintsunk a "USB Device - CDC - Basic Demo -  C18 - Low Pin Count USB Development Kit.mcp" projektfile-ra, ekkor az MpLab elindul és betöltődik az USB CDC projekt. A "main.c" file-ban találjuk meg a main() és a ProcessIO() függvényeket.

Ahhoz, hogy virtuális soros kommunikációt tudjunk kialakítani az USB porton keresztül, a következő függvényeket használhatjuk (1. táblázat).
Fordítsuk le a projektünket (CTRL + F10). Ha sikerült, akkor egészítsük ki a ProcessIO() függvényt az USB Stack-ben található függvényekkel. Ezeket a függvényeket a "usb_function_cdc.c" file-ban találjuk meg.

Függvény neve Iránya Leírás
getsUSBUSART() PC -> PIC string olvasása a PC-ről
putrsUSBUSART() PIC -> PC adatküldés a programmemóriából, \n-nel
putsUSBUSART()
PIC -> PC adatküldés az adatmemóriából \n-nel
mUSBUSARTTxRom() PIC -> PC adatküldés programmemóriából, a hossz ismert
mUSBUSARTTxRAM() PIC -> PC adatküldés az adatmemóriából, a hossz ismert
mUSBUSARTIsTxTrfReady()    készen áll az újabb adatküldésre a PIC?
mCDCGetRxLength()   az olvasott string hossza

1. táblázat   Főbb USB-CDC kommunikációt megvalósító függvények
 

A "getUSBUSART" függvény meghívásakor a paraméterlistájában átadunk egy max. 64 elemű karaktertömböt (pl.: USB_Out_Buffer) és egy egész számot. Az "USB_Out_Buffer" tömbben tároljuk el azokat a karaktereket, amelyeket a számítógépről küldünk a PIC felé.

Ezt a getUSBUSART() függvényt írjuk be a ProcessIO() függvény törzsébe.
Az eredeti ProcessIO() látható itt, majd a módosított megoldást mutatja a következő rész.

void ProcessIO(void)
{
    if((USBDeviceState < CONFIGURED_STATE) || (USBSuspendControl==1)) return;

    if(mDataRdyUSART())
    {
        USB_Out_Buffer[NextUSBOut] = getcUSART();
        ++NextUSBOut;
        USB_Out_Buffer[NextUSBOut] = 0;
    }

  CDCTxService();
}


A módosított ProcessIO() (kiemelve a feltételvizsgálat) :

void ProcessIO(void)
{
    if((USBDeviceState < CONFIGURED_STATE) || (USBSuspendControl==1)) return;

    if(mDataRdyUSART())
    {
        USB_Out_Buffer[NextUSBOut] = getcUSART();
        ++NextUSBOut;
        USB_Out_Buffer[NextUSBOut] = 0;
    }

if (getsUSBUSART(USB_Out_Buffer,1))

    {
        if (USB_Out_Buffer[0] == 'x')
            LATC = 0B00000110;
        else
            LATC = 0B00000000;
    }

  CDCTxService();
}

A végtelen ciklusban állandóan meghívott ProcessIO() törzsében található getsUSBUSART() függvény belsejében feltételvizsgálat történik. Ha az USB_Out_Buffer karaktertömb 0. eleme "x", akkor a mikrovezérlő C portjának az 1. és 2. bitje logikai 1-be kerül. Egyéb esetben az összes C portbit logikai nulla értéket kap.
Természetesen egyszerre nem csak egy karakter vihető át, és figyelhető egy feltételes szerkezettel, hanem maximum 64 karakter.

Ha nem fogjuk tudni, hogy mennyi karaktert küldünk át a vezérlés során (pl.: ez a szám változik vagy nem rögzítjük), akkor a mCDCGetRxLength() függvény visszatérési értékét figyelhetjük, mert ez a függvényérték adja vissza az átküldött karaktersorozat hosszát.

Ha nem csak LED-eket szeretnénk be-/kikapcsolni, hanem mikrovezérlőből beolvasni adatot az USB-n keresztül, akkor ismét módosítani kell az eredeti ProcessIO() függvényt. Az első táblázatban lévő  függvények egyikét alkalmazhatjuk, attól függően, hogy ismert hosszúságú karaktersorozatot kívánunk átküldeni, illetve el kell dönteni azt is, hogy honnan küldjük az adatot.

void ProcessIO(void)
{
   
if((USBDeviceState < CONFIGURED_STATE) || (USBSuspendControl==1)) return;

    if(mDataRdyUSART())
    {
        USB_Out_Buffer[NextUSBOut] = getcUSART();
        ++NextUSBOut;
        USB_Out_Buffer[NextUSBOut] = 0;
    }

    if (getsUSBUSART(USB_Out_Buffer,1))
    {
        i
f (USB_Out_Buffer[0] == 'x')
            
LATC = 0B00000110;
        
else

            LATC = 0B00000000;
    }
    if(mUSBUSARTIsTxTrfReady())
        putrsUSBUSART("WebElektronika");
    
CDCTxService();
}

Először beállítjuk a LED-ek (mikrovezérlő C port-ja) állapotát, majd a putrsUSBUSART függvény használatával a "WebElektronika" karaktersorozatot küldük a PIC18F-ből a számítógép felé.

Ha nem karaktersorozatot szeretnénk átküldeni, hanem egész számot (pl.: AD átalakító értéke), akkor átalakítást kell végeznünk. Erről részletesebben itt olvashat.