Vývoj USB ovládača – 5. časť
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu
Reklama
Reklama

Vývoj USB ovládača – 5. časťVývoj USB ovládača – 5. časť

 

Vývoj USB ovládača – 5. časť

Google       Google       18. 11. 2013       15 066×

V tejto časti si vytvoríme GUI klientsku aplikáciu v jazyku C#, ktorej základom bude rozhranie na komunikáciu medzi ovládačom zariadenia a .Net aplikáciou vytvorené s pomocou PInvoke (Platform Invoke).

Reklama
Reklama

Úvod

V tomto kroku máme funkčné USB zariadenie a ovládač s ním komunikujúci pomocou libusb. Ďalším krokom je vytvorenie koncovej aplikácie, pomocou ktorej môže používateľ hardvér ovládať. Pre zmenu som si vybral platformu .NET a jazyk C#, vďaka ktorým bude jednoduchšie demonštrovať princípy.

Aplikácia bude tvorená jedným oknom, v ktorom sa bude zobrazovať stav tlačidiel na zariadení, teplota procesora a napätie na potenciometri. Taktiež bude obsahovať prvky na ovládanie LED diód, na vypísanie textu a odoslanie bitmapy na LCD. Pri vytvorení okna sa aplikácia pokúsi otvoriť spojenie s USB zariadením; pokiaľ zariadenie nebude pripojené, skončí s chybovým hlásením.

Celú komunikáciu so zariadením vložíme do jedného objektu, ktorý bude pomocou P/Invoke (Platform Invoke) komunikovať s natívnou knižnicou ovládača vytvorenou v minulom dieli tohto seriálu. Tento objekt alebo wrapper sa bude taktiež starať o spracovanie vzniknutých chýb a namiesto číselných hodnôt vrátených ovládačom vyhodí príslušnú výnimku.

P/Invoke – Platform Invoke

Pokiaľ v .NET programujete už nejaký ten čas, je dosť možné, že ste sa stretli s potrebou importovať funkcie natívnych knižníc. V C/C++ by ste toho dosiahli jednoducho prilinkovaním knižnice k vášmu programu alebo by ste knižnicu načítali pomocou funkcie LoadLibrary, získali adresu funkcie volaním GetProcAddress a pretypovaním by vám vznikol odkaz na funkciu, ktorú by ste zavolali ako každú inú. V .Net to ale funguje trochu inak, na volanie funkcií v natívnych knižniciach musíme využiť DllImport (System.Runtime.InteropServices v knižnici System.Core).

V prvom kroku si v zdrojovom súbore zadeklarujeme použitie názvoslovia:

using System.Runtime.InteropServices;

Potom nám stačí v triede deklarovať statickú metódu nasledovným spôsobom:

/// <summary>
/// Vyčistí obrazovku jednou farbou
/// </summary>
/// <param name="color">Farba</param>
/// <returns></returns>
[DllImport("DeviceWrapper.dll",
 CallingConvention = CallingConvention.Cdecl)]
private static extern uint ClearScreen(uint color);

Čím prekladač vie, že metóda ClearScreen je externá a nachádza sa v knižnici DeviceWrapper.dll (to je náš ovládač). Každá takto vložená funkcia musí byť označená ako static extern s atribútom DllImport.

Naša klientska aplikácia bude importovať všetky funkcie exportované v ovládači DeviceWrapper, nebudú sa ale používať priamo. Napr. funkcia ClearScreen preberá jeden argument, ktorým je 24bitové číslo reprezentujúce farbu. Návratová hodnota je taktiež číslo (0 v prípade, že funkcia uspeje alebo iná hodnota popisujúca chybu). My ale túto metódu zabalíme v objekte (wrapper) tak, aby parameter farby nebol číslo, ale typ System.Color, a odstránime návratový typ, zmeníme ju na void. V prípade, že volanie funkcie ClearScreen z knižnice DeviceWrapper.dll zlyhá, nevrátime volajúcemu kód chyby, ale vyhodíme výnimku. Je to omnoho prijateľnejší spôsob. Týmto spôsobom dosiahneme to, aby bola metóda ClearScreen volaná vždy v správnom kontexte.

/// <summary>
/// Vyčistí obrazovku
/// </summary>
/// <param name="color">Farba</param>
public void ClearScreen(Color color)
{
    // Spojenie so zariadením musí byť otvorené
    if (!isOpen)
        throw new DeviceFailException(
            DeviceFailException.DeviceError.NotOpen);

    // Zavolať funkciu ClearScreen z DeviceWrapper.dll
    uint result = ClearScreen((uint)color.ToArgb());

    // Pokiaľ volanie zlyhalo, vyhodíme výnimku s kódom chyby
    if (result != 0)
        throw new DeviceFailException(
            (DeviceFailException.DeviceError)result);
}

Rozhranie na komunikáciu s ovládačom

Základ aplikácie bude tvorený jedným objektom – rozhraním na komunikáciu s ovládačom. Pri štarte aplikácie sa vytvorí inštancia tohto objektu, otvorí sa spojenie s USB zariadením a ďalšia komunikácia s ním už bude prebiehať iba pomocou tohto objektu. Pri ukončení aplikácie sa objekt odstráni, tým sa zatvorí spojenie s ovládačom a zariadením. Na obrázku je znázornené, aké metódy bude objekt obsahovať a ďalšie pomocné štruktúry, triedy a dátové typy.

Je dôležité uvedomiť si, že dátové štruktúry (pakety) sú rovnaké pre zariadenie, ovládač aj koncovú aplikáciu. Preto pri vývoji ovládača pracujeme s rovnakými dátovými typmi v zariadení aj na strane hosta. Táto klientska aplikácia definuje štyri takéto štruktúry, a to usbapp_bulk_out_t, usbapp_control_in_t, usbapp_control_out_t a usbapp_interrupt_in_t.

Ďalej potrebujeme definovať výnimky, ktoré objekt vyhodí pri chybnej komunikácii alebo pri nesprávnom použití rozhrania. Na to nám budú slúžiť dve triedy, DeviceFailException, ktorá bude vyhodená, ak dôjde k chybe v ovládači alebo zariadení, a DeviceNotOpenException, ktorú rozhranie vyhodí, ak sa programu nepodarí nadviazať spojenie so zariadením.

Nakoniec zadefinujeme štruktúry a enumy, ktoré poskytnú informácie z paketov cieľovému používateľovi rozhrania. Ako príklad uvediem nastavenie LED a LCD.

Aplikácia potrebuje odoslať zariadeniu príkaz, aby rozsvietilo modrú a zelenú LED diódu, zaplo LCD displej a taktiež aby posunulo obsah LCD displeja o 50 px. Keďže každé nastavenie v pakete je číselné, užívateľ nášho rozhrania by musel poznať, aké čísla pripadajú na modrú a zelenú LED. Taktiež by musel vedieť, akým príznakom zapne LCD displej. Preto je dôležité, aby naše rozhranie tieto čísla skrylo za enum dátové typy ako napr. zoznam LED diód a ich číselné hodnoty:

/// <summary>
/// Nastavenia LED diód
/// </summary>
public enum Led
{
    None = 0,
    Blue = 0x1,
    Green = 0x2,
    Amber = 0x4
};

Potom môže užívateľ jednoduchým spôsobom zostaviť nastavenia a odoslať ich zariadeniu:

// Vytvoriť nastavenie LED a LCD
Device.LedLcdOptions ledLcd = new Device.LedLcdOptions(
    Device.Led.Blue | Device.Led.Green, // Modrá a zelená LED
    0, // Oranžová nebude svietiť
    Device.DisplayOption.DisplayOn, // Zapnúť LCD displej
    0, // Displej nebude podsvietený
    50); // Obsah displeja bude posunutý o 50 px
 
// Odoslať nastavenie zariadeniu
device.SetupLedLcd(ledLcd);

Metóda Device.SetupLedLcd(LedLcdOptions options) skontroluje, či je spojenie na zariadenie otvorené a ak áno, zostaví paket usbapp_control_out_t z nastavení, ktoré užívateľ predal ako argument options. Zároveň skontroluje platnosť nastavení a to, či hodnota PWM a hodnota podsvietenia LCD displeja neprekročia max. hodnoty stanovené USB zariadením. Následne paket posunie ovládaču volaním funkcie SetupDevice z knižnice ovládača DeviceWrapper.dll.
Tá vytvorí USB paket, jeho dáta naplní obsahom štruktúry usbapp_control_out_t a odošle ho zariadeniu, ktoré paket prijme a spracuje. Akonáhle host potvrdí príjem paketu zariadením, funkcia SetupDevice vráti hodnotu určujúcu, či prenos prebehol v poriadku alebo sa vyskytla chyba.

Naše rozhranie túto vrátenú hodnotu porovná a pokiaľ zistí, že počas prenosu nastala chyba, vyhodí výnimku.

/// <summary>
/// Nastavi LED a LCD
/// </summary>
/// <param name="options">LED a LCD nastavenia</param>
public void SetupLedLcd(LedLcdOptions options)
{
    if (!isOpen)
        throw new DeviceFailException(
            DeviceFailException.DeviceError.NotOpen);
 
    // Vytvoriť paket obsahujúci nastavenia LED a LCD
    usbapp_control_out_t data = new usbapp_control_out_t();
    data.leds = options.Leds;
    data.pwmLedDuty = options.PwmLedDuty > MaxPwm ?
        MaxPwm : options.PwmLedDuty;
    data.display = options.LcdOptions;
    data.backlight = options.Backlight > MaxBacklight ?
        MaxBacklight : options.Backlight;
    data.displayScroll = options.LcdScroll;

    // Posunúť paket ovládaču, ktorý ho odošle
    uint result = SetupDevice(ref data);

    // Vyhodiť výnimku ak počas prenosu vznikla chyba
    if (result != 0)
        throw new DeviceFailException(
            (DeviceFailException.DeviceError)result);
}

Obdobným spôsobom trieda Device poskytuje prístup k ostatným funkciám ovládača. V princípe je potrebné iba „zabaliť“ volania ovládača takým spôsobom, aby klientska aplikácia na platforme .NET mohla využívať tieto metódy v súlade s konvenciami .NET a taktiež aby bolo možné spracúvať výnimky namiesto chybových kódov.

Front end – používateľské rozhranie

Na otestovanie funkčnosti poslúži jednoduchá GUI aplikácia, ktorá pri svojom štarte skontroluje, či je k počítaču pripojené naše zariadenia a ak áno, vytvorí klientske rozhranie pre komunikáciu s týmto zariadením. Používateľ tak bude mať možnosť jednoducho odosielať príkazy zariadeniu pomocou nami vytvoreného rozhrania.

Komunikácia bude pozostávať z troch častí:

  1. Príkazy odosielané klientom do zariadenia – nastavenie LED a vykresľovanie na LCD
  2. Pravidelná aktualizácia hodnôt z ADC prevodníka
  3. Vlákno bežiace na pozadí aplikácie čakajúce na prerušenia zo strany zariadenia

Keďže väčšinu práce bude mať na starosti rozhranie v triede Device, GUI aplikácii zostáva konvertovať vstupné hodnoty od používateľa a zobrazovať výstup zo zariadenia a čo je podstatnejšie, spracovanie chýb. V našom prípade pri vzniku chyby program zobrazí bližšie informácie o príčine vzniku chyby a následne sa ukončí.

Záver

A na záver si môžete stiahnuť zdrojový kód aplikácie a pozrieť ukážku toho, ako zariadenie a klientska aplikácia fungujú v praxi:

×Odeslání článku na tvůj Kindle

Zadej svůj Kindle e-mail a my ti pošleme článek na tvůj Kindle.
Musíš mít povolený příjem obsahu do svého Kindle z naší e-mailové adresy kindle@programujte.com.

E-mailová adresa (např. novak@kindle.com):

TIP: Pokud chceš dostávat naše články každé ráno do svého Kindle, koukni do sekce Články do Kindle.

2 názory  —  2 nové  
Hlasování bylo ukončeno    
3 hlasy
Google
Autor sa venuje programovaniu v jazykoch C#, C/C++, Delphi a v poslednej dobe sa taktiež zaujíma o vývoj hardvéru.
Web    

Nové články

Obrázek ke článku RAD Studio a Windows Store

RAD Studio a Windows Store

RAD Studio je první vývojové prostředí se zabudovanou podporou balení aplikací typu Win32 a Win64 pro jejich umístění a šíření prostřednictvím Windows 10 Store.

Reklama
Reklama
Obrázek ke článku Testujte na 2 400+ Android a iOS zařízení

Testujte na 2 400+ Android a iOS zařízení

V dnešní době, kdy většina softwaru pro mobilní aplikace je tvořena a distribuována průběžně, mnohdy do celého světa je třeba zajistit také průběžnou automatizaci testování mobilního softwaru. V případě mobilních aplikací pro Android a iOS začíná být problém, jak testovat na obrovském množství kombinací HW variant, rozměrů, edic operačních systémů různých výrobců v různých částech světa na reálných zařízení. Simulátory a emulátory nejsou většinou to pravé. Pokud již testuji, jak si udělat vlastní beta distribuci opravdovým reálným testerům napříč platformami?

Obrázek ke článku Funkcie main vo Windows API

Funkcie main vo Windows API

V tretej časti seriálu o Windows API budeme hovoriť o funkčných prototypoch main. Funkčný prototyp je tvorený názvom funkcie a typom signatúry, pričom sa vynecháva telo funkcie. 

loadingtransparent (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })();
Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032017 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý