Department of InformatiX
Microsoft .NET Micro Framework Tools & Resources

Váš první interop krok za krokem

S .NET Micro Frameworkem v4.0, Board Support Package od Device Soutions a GCC. Aktuální verze balíčku nepodporuje Solution Wizard, takže je potřeba udělat integraci ručně.

Pro detailnější a pokročilejší návod na interop včetně Solution Wizardu doporučuji Stevův článek Using Interop in the .NET Micro Framework V3.0.

Připravte si prostředí

  1. Pokud ještě nemáte, nainstalujte si Visual Studio a .NET Micro Framework SDK, chcete-li ladit testovací aplikaci. Abychom to měli vše zdarma, budu používat express edici. Potřebujete jak C# tak C++. SDK je k dispozici zde.
    • C++ ve Visual Studio 2010 RC zatím nefunguje. Potřebujete 2008 verzi C++, je mi líto.
  2. Nainstalujte porting kit. Ke stažení je na Microsoft Download Centru, verze 4.0.1681.0 je zde (zabalená, cca. 40 MB) – instalaci začnete spuštěním MicroFrameworkPK.MSI.
    • Vřele doporučuji ponechat nabízenou instalační cestu (obvykle C:\MicroFrameworkPK_v4_0), nativní věcičky obvykle vůbec nekamarádí s mezerami a znaky mimo ASCII.
    • Také se ujistěte, že instalujete verzi porting kitu, která je board support balíčkem podporovaná.
    • Ve volitelné instalaci není na výběr žádná jiná možnost navíc než změna instalační cesty.
  3. Důležité: Nainstalujte síťové (150 MB) a kryptografické (0.5 MB) knihovny. Meridian má architekturu ARM, takže potřebujeme verzi pro ARM (opět, spusťte MSI soubory).
    • Pokud tento krok vynecháte, vše bude zákeřně fungovat až do okamžiku, kdy nahrajete svůj firmware do zařízení. Procesor pak bude zkoušet nabootovat, ale vždy rychle skončí.
    • Znovu bych doporučil nechat navrženou instalační cestu (obvykle shodná s instalací porting kitu); žádná doplňující nastavení.
  4. Nainstalujte board support balíček. Ten můžete stáhnout na stránkách Device Solutions.
    • Nechte stejnou cestu jakou měl porting kit, balíček se do něj integruje.
  5. Teď když zkontrolujete Device Solutions BSP documentation, která se objevila v nabídce Start pod Device Solutions BSP, zjistíte, že musíme povolit PowerShellu nepodepsané skripty (Getting Started\Installing the BSP). Z nabídky Start spusťte Windows PowerShell s administrátorskými právy. Objeví se modrá příkazová řádka. Napište set-executionpolicy remotesigned, stiskněte enter a potvrďte, že si jste jisti.
    • Do Windows starších než Windows Vista resp. Windows Server 2008 však musíte PowerShell doinstalovat. Odkazy na stažení najdete v KB 968929.
    • Pokud nemůžete Windos PowerShell v nabídce Start najít, je ve složce Příslušenství.
  6. Nainstalujte nativní kompilátor. Balíček (BSP) podporuje momentálně dva, ARM RVDS 3.1 a GCC 4.2, přičemž verze bývá dost podstatná. Jelikož dnes děláme zadarmo, zůstaneme u GCC. Ve zmíněné Device Solutions BSP Documentation je odkaz na tu správnou verzi GCC, a to tento (70 MB). Důležité: změňte instalační cestu na C:\CodeSourcery nebo jinou jednoduchou bez mezer! ...vítejte v nativním světe pro embedded zařízení.
    • Nenechte se zmást nápisem G++, to hádám znamená GNU.

Od teď dále předpokládám, že jste nainstalovali porting kit, síťové a kryptografické knihovny a BSP do složky C:\MicroFrameworkPK_v4_0, GCC do složky C:\CodeSourcery. Pokud jste zvolili jiné umístění, nahraďte v dalším textu cesty odpovídajícím způsobem.

Kompilujeme poting kit

  1. Spusťte příkazovou řádku a přepněte se do složky porting kitu, tj. C:\MicroFrameworkPK_v4_0.
    • Pro ty, kteří tento trik neznají: stačí napsat cd m a stisknout klávesu Tab k doplnění celého názvu složky.
  2. Nastavte systémové proměnné: Napište setenv_gcc C:\CodeSourcery a stisknete enter. Napíše to:
    Setting environment for using Microsoft Visual Studio 2008 x86 tools.
    setting vars for GCC compiler.
    • Pokud používáte kompilátor RVDS, použijte místo toho setenv_RVDS3.1 C:\CodeSourcery.
  3. Teď zkompilujeme porting kit. Napište msbuild build.dirproj /p:flavor=release a stiskněte enter. Trochu trpělivosti, trvá to přes čtvrt hodiny na mém 2 × 2.16 GHz / 3 GB RAM. Neměly by se vyskytnout žádné chyby, jen pár varování (já měl 171).
    • Pro prohlédnutí výstupu můžete kdykoliv stisknout klávesu Pause.
    • Pokud chcete skrolovat více řádky dozadu, klikněte pravým tlačítkem na titulek okna příkazové řádky a v menu vyberte Vlastnosti. Na záložce Rozvržení ve skupině Vyrovnávací paměť zobrazení zvyšte hodnotu Výška. Maximum je 9999, ale to stejně nestačí na všechen výstupní text během kompilace.
    • Několika červených řádek se není třeba lekat. Pravděpodobně jsou způsobeny tím, že nejsou k dispozici zdrojové soubory od síťových/kryptografických knihoven, ale v kompilaci to nevadí.
    • Pokud jste přeci jen narazili na závažné chyby, ujistěte se, že jste nastavili systémové proměnné a že skript setenv_gcc potrvdil verzi C++, kterou používáte. Jste-li na pochybách, zcela odstraňte složku BuildOutput a pokuste se spustit kompilaci znovu. Pokud to nepomůže, můžete mi zkusit poslat chyby e-mailem.
  4. Vytvoříme RAM verzi Tahoe-II firmwaru. Jak říká dokumentace, potřebujeme RAM verzi bootloaderu, aby bylo možné nový firmware nahrát (bootloader se nemůže sám přepsat, když běží z paměti FLASH). Napište buildmf TAHOEII RAM a stiskněte enter. Tahle akce mi trvala téměř 3 minuty, žádné chyby, 92 varování.
    • Jako svůj cíl jsem si zvolil Tahoe-II, jelikož má displej a je tak snadno poznat, když se něco pokazí. Pokud vytváříte firmware pro jiná zařízení, použijte příslušný parametr, např. MERIDIANP nebo TAHOE.
  5. Poslední krok je snadný: Vytvořte si složku pro své interop projekty, např. C:\MicroFrameworkPK_v4_0\MyInterops. Můžete už také zavřít příkazový řádek.

Teď už se můžeme pustit do vývoje, nic z výše uvedeného již nebude třeba dělat znovu.

Vytváříme interop knihovnu

Náš cíl bude velmi primitivní; v podstatě uděláme třídu, která posílá na výstupní pin impulsy. Očekáváme, že:

  1. Spusťte Visual C# Express (resp. Visual Studio) a vytvořte nový Micro Framework projekt typu Class Library, nazvaný MyFirstLibrary.
  2. Přejmenujte vygenerovaný Class1.cs na ImpulseSender.cs.
  3. Přidejte referenci na assembly Microsoft.SPOT.Hardware.
  4. Do ImpulseSender.cs napište C# část třídy:

    using Microsoft.SPOT.Hardware; using System.Runtime.CompilerServices; namespace MyFirstLibrary { public class ImpulseSender { uint _pin; // Nativní kód podporuje jen primitivní typy, takže nemůžeme (snadno) předat ani OutputPort ani Cpu.Pin. // Cpu.Pin zabírá čtyři bajty a jak uvidíme později, uint je typ potřebný na nativní straně. public ImpulseSender(OutputPort port) { _pin = (uint)port.Id; // číslo pinu je dostatečné k identifikaci portu na nativní straně, jak rovněž uvidíme } // každou metodu kterou se chystáme implementovat nativním kódem je potřeba deklarovat jako extern a označit následujícím atributem: [MethodImpl(MethodImplOptions.InternalCall)] public extern void WritePulse(bool positive, int durationCycles); } }

  5. Ve vlastnostech projektu zaškrtněte Generate native stubs for internal methods (na záložce .NET Micro Framework).

    Do prvního pole se vyplňuje text, kterým budou začínat všechny názvy generovaných souborů. Jak uvádí Steve, měli byste vytvářet unikátní názvy souborů v celém porting kitu. Pro náš příklad použijeme Tutorial.

    Do druhého pole se uvádí název složky, do které se budou soubory generovat. Výchozí by mělo být Stubs; pokud máte pole prázdné, vyplňte te jej.

    Screenshot vlastnosti projektů

    • Pokud nevyplníte žádné počáteční jméno pro soubory, použije se název projektu.
    • Složka (cesta) pro generování souborů se bere jako relativní vzhledem ke složce celého projektu.
  6. Zkompilujte projekt. Vytvoří se složka Stubs společně s těmito soubory:
    • dotNetMF.proj
    • MyFirstLibrary.featureproj
    • Tutorial.cpp
    • Tutorial.h
    • Tutorial_MyFirstLibrary_ImpulseSender.cpp
    • Tutorial_MyFirstLibrary_ImpulseSender.h
    • Tutorial_MyFirstLibrary_ImpulseSender_mshl.cpp

    Jejich prozkoumáním snadno shledáte, že ty které se podobají naší třídě jsou soubor s deklaracemi – Tutorial_MyFirstLibrary_ImpulseSender.h, a soubor s vlastním kódem – Tutorial_MyFirstLibrary_ImpulseSender.cpp..

    Soubor s deklaracemi - hlavička - obsahuje podpisy metod, které jsme deklarovali jako extern. Všimněte si, že názvy parametrů se nezachovávají, ale můžeme je přejmenovat ručně (je však třeba tak učinit v obou souborech). Také stojí za zmínku, že hlavička obsahuje metody pro přístup k proměnným deklarovaných v C# kódu:

    #ifndef _TUTORIAL_MYFIRSTLIBRARY_IMPULSESENDER_H_ #define _TUTORIAL_MYFIRSTLIBRARY_IMPULSESENDER_H_ namespace MyFirstLibrary { struct ImpulseSender { // Helper Functions to access fields of managed object static UINT32& Get__pin( CLR_RT_HeapBlock* pMngObj ) { return Interop_Marshal_GetField_UINT32( pMngObj, Library_Tutorial_MyFirstLibrary_ImpulseSender::FIELD___pin ); } // Declaration of stubs. These functions are implemented by Interop code developers static void WritePulse( CLR_RT_HeapBlock* pMngObj, INT8 param0, INT32 param1, HRESULT &hr ); }; } #endif //_TUTORIAL_MYFIRSTLIBRARY_IMPULSESENDER_H_

    Soubor s kódem pak jen prahne po naší implementaci:

    #include "Tutorial.h" #include "Tutorial_MyFirstLibrary_ImpulseSender.h" using namespace MyFirstLibrary; void ImpulseSender::WritePulse( CLR_RT_HeapBlock* pMngObj, INT8 param0, INT32 param1, HRESULT &hr ) { }

  7. Přejmenujte parametry stejně jako je máme v C# kódu. To sice není vůbec nezbytné, ale jako dobrý zvyk vám to pomůže udržet projekt snadno čitelný, zvláště pak když máte hromadu souborů se spoustou metod. Navíc věřím, že budoucí verze SDK bude umět názvy zachovat. Máme tedy

    static void WritePulse( CLR_RT_HeapBlock* pMngObj, INT8 positive, INT32 durationCycles, HRESULT &hr );

    v hlavičce a

    void ImpulseSender::WritePulse( CLR_RT_HeapBlock* pMngObj, INT8 positive, INT32 durationCycles, HRESULT &hr ) { }

    v kódu.
    • Všimněte si, že booleovský parametr je reprezentován jedním bajtem. To proto, že jazyk C nemá nativní podporu typu booelan. Nula znamená false a jakákoliv jiná hodnota znamená true.
  8. Nyní projekt uložte, tam kam obvykle projekty ukládáte.
    • Pokud využíváte funkci dočasných projektů, budete pravděpodobně muset projekt zkompilovat (a parametry přejmenovat) znovu po uložení, aby se soubory vygenerovaly na nové místo. Můžete také samozřejmě pracovat z dočasného umístění.

Integrujeme interop knihovnu do porting kitu

Pokaždé když jsou zmíněné soubory vygenerovány, jsou označený kontrolní hodnotou. Tato hodnota se kontroluje za běhu, aby se zajistilo, že nativní a C# části sedí k sobě. Je tedy rozumné vzít kopii jak nativní tak řízené části a schovat je odděleně, aby se zamezilo škodám způsobeným nechtěnou kompilací knihovny (což by samozřejmě znovu soubory přegenerovalo, takže byste o všechen svůj nativní kód přišli).

  1. Vytvořte v porting kitu složku pro svůj projekt: C:\MicroFrameworkPK_v4_0\MyInterops\MyFirstLibrary.
  2. V této složce vytvořte další dvě podsložky: ManagedCode a NativeCode.
  3. Všechny soubory ze složky Bin\Release projektu knihovny do složky ManagedCode, kterou jsme teď vytvořili.
    • Vývojové nástroje řady Express neznají konfigurace. Může se stát, že místo toho najdete soubory ve složce Bin\Debug, pokud kompilujete konfiguraci debug ve Visual Studiu.
  4. Zkopírujte všechny soubory ze složky Stubs do právě vytvořené NativeCode. Výsledkem tohoto snažení by mělo být toto:
    Screenshot seznamu souborů
  5. Otevřete C:\MicroFrameworkPK_v4_0\MyInterops\MyFirstLibrary\NativeCode\MyFirstLibrary.featureproj ve svém oblíbeném xml editoru. Vypadá zhruba takto:

    <Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <FeatureName>MyFirstLibrary</FeatureName> <Guid>{48327512-e47d-402e-8b6a-ea6ca831491e}</Guid> <!-- nejspíš máte jiné guid --> <Description> </Description> <Groups> </Groups> </PropertyGroup> <ItemGroup> <InteropFeature Include="MyFirstLibrary" /> <DriverLibs Include="MyFirstLibrary.$(LIB_EXT)" /> <MMP_DAT_CreateDatabase Include="$(BUILD_TREE_CLIENT)\pe\MyFirstLibrary.pe" /> <RequiredProjects Include="Stubs\Stubs\dotnetmf.proj" /> </ItemGroup> </Project>

  6. Upravte cesty tak aby ukazovaly na naši kopii a zakomentujte MMP_DAT_CreateDatabase – zahrnul by knihovnu přímo do ROM obrazu, což není zrovna praktické během vývoje.

    <ItemGroup> <InteropFeature Include="MyFirstLibrary" /> <DriverLibs Include="MyFirstLibrary.$(LIB_EXT)" /> <!-- <MMP_DAT_CreateDatabase Include="$(SPOCLIENT)\MyInterops\MyFirstLibrary\ManagedCode\MyFirstLibrary.pe" /> --> <RequiredProjects Include="$(SPOCLIENT)\MyInterops\MyFirstLibrary\NativeCode\dotNetMF.proj" /> </ItemGroup>

    • $(SPOCLIENT) odkazuje na místo instalace porting kitu, v našem případě C:\MicroFrameworkPK_v4_0.
  7. MyFirstLibrary.featureproj uložte a zavřete. dotNetMF.proj necháme beze změn.
  8. Otevřete C:\MicroFrameworkPK_v4_0\Solutions\TAHOEII\TinyCLR\TinyCLR.proj ve svém oblíbeném xml editoru.
    • Pokud jste se rozhodli pro firmware pro jiné zařízení (již během kompilace RAM verze), použijte příslušnou cestu, tj. se složkami MERIDIANP nebo TAHOE.

    Obsah vypadá následovně:

    <Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> ... <!-- couple of properties and imports --> </PropertyGroup> <ItemGroup> ... </ItemGroup> <Import Condition="" Project="$(SPOCLIENT)\Framework\Features\core.featureproj" /> <Import Condition="" Project="$(SPOCLIENT)\Framework\Features\Hardware.featureproj" /> <Import Condition="" Project="$(SPOCLIENT)\Framework\Features\NativeEventDispatcher.featureproj" /> <Import Project="$(SPOCLIENT)\Framework\Features\BlockStorage.featureproj" /> ... <!-- lots of project imports --> <Import Project="$(SPOCLIENT)\Solutions\TAHOEII\DeviceCode\Interop\DeviceSolutions.SPOT.Hardware.TahoeII.featureproj" /> <!-- BOD A --> <Import Project="$(SPOCLIENT)\tools\targets\Microsoft.SPOT.System.Interop.Settings" /> <ItemGroup> <RequiredProjects Include="$(SPOCLIENT)\DeviceCode\drivers\LargeBuffer\stubs\dotnetmf.proj"/> <DriverLibs Include="LargeBuffer_hal_stubs.$(LIB_EXT)"/> </ItemGroup> ... <!-- tons of item groups --> <ItemGroup> <RequiredProjects Include="$(SPOCLIENT)\DeviceCode\Pal\Ink\dotNetMF.proj" /> <DriverLibs Include="Ink_pal.$(LIB_EXT)" /> </ItemGroup> <!-- BOD B --> <Import Project="$(SPOCLIENT)\tools\targets\Microsoft.SPOT.System.Targets" /> </Project>

  9. Zakomponujeme náš projekt do porting kitu. Na konci části s importama featureproj souborů, tedy do místa označeného BOD A přidejte import našeho featureproj souboru:

    <Import Project="$(SPOCLIENT)\MyInterops\MyFirstLibrary\NativeCode\MyFirstLibrary.featureproj" />

    A na konci části s import group, tedy do místa BOD B, vložte naši item group:

    <ItemGroup> <RequiredProjects Include="$(SPOCLIENT)\MyInterops\MyFirstLibrary\NativeCode\dotNetMF.proj"/> <DriverLibs Include="MyFirstLibrary.$(LIB_EXT)"/> </ItemGroup>

  10. Toť vše, uložte a zavřete soubor.

Implementujeme nativní část

  1. Otevřete naši kopii vygenerovaného souboru s kódem, C:\MicroFrameworkPK_v4_0\MyInterops\MyFirstLibrary\NativeCode\Tutorial_MyFirstLibrary_ImpulseSender.cpp. Potřebujeme zhruba něco takového:

    void ImpulseSender::WritePulse( CLR_RT_HeapBlock* pMngObj, INT8 positive, INT32 durationCycles, HRESULT &hr ) { if (durationCycles < -1) { // throw ArgumentOutOfRangeException } // něco jako OutputPort.Write(true) pro pozitivní impuls, false pro negativní if (durationCycles == -1) return; // nechat nastavenou hodnotu while (--durationCycles >= 0) { // prázdný cyklus } // něco jako OutputPort.Write(false) pro pozitivní impuls, true pro negativní }

  2. Část s vyhozením výjimky je snadná, Steve ji popsal ve svém článku: Parametr hr je způsob jakým vyjádřit nějakou chybovou situaci. Výchozí hodnota je S_OK, takže ji potřebujeme změnit:

    if (durationCycles < -1) { hr = CLR_E_OUT_OF_RANGE; return; }

  3. Co se týče záležitosti s manipulací pinů, zjevně by nám dost pomohlo vědět, jak vypadá kód ve třídě OutputPort. Tak to pojďmě zjistit. Naivní, avšak fungující přístup: Vyhledejte v porting kitu (tj. C:\MicroFrameworkPK_v4_0) soubory s názvem obsahujícím OutputPort:
    Screenshot výsledků hledání
    První soubor je evidentně mezivýsledek kompilace, takže ten nám mnoho nepomůže. Také poslední tři vypadají jako prázdné, vygenerované soubory zrovna jako ty, jaké jsme měli my - kromě toho jsou ve složce BuildOutput, která vznikla během naší kompilace porting kitu. Zbývá nám tedy jediný, C:\MicroFrameworkPK_v4_0\CLR\Libraries\SPOT_Hardware\spot_hardware_native_Microsoft_SPOT_Hardware_OutputPort.cpp, což je shodou okolností ten, který hledáme:

    ////////////////////////////////////////////////////////////////////////... // Copyright (c) Microsoft Corporation. All rights reserved. ////////////////////////////////////////////////////////////////////////... #include "SPOT_Hardware.h" HRESULT Library_spot_hardware_native_Microsoft_SPOT_Hardware_OutputPort::Write___VOID__BOOLEAN( CLR_RT_StackFrame& stack ) { NATIVE_PROFILE_CLR_HARDWARE(); TINYCLR_HEADER(); bool state; CLR_RT_HeapBlock* pThis = stack.This(); FAULT_ON_NULL(pThis); if(pThis[ Library_spot_hardware_native_Microsoft_SPOT_Hardware_NativeEventDispatcher::FIELD__m_disposed ].NumericByRef().s1 != 0) { TINYCLR_SET_AND_LEAVE(CLR_E_OBJECT_DISPOSED); } state = stack.Arg1().NumericByRef().u1 != 0; // Test if flag says it is output port if (pThis[ Library_spot_hardware_native_Microsoft_SPOT_Hardware_Port::FIELD__m_flags ].NumericByRefConst().s4 & GPIO_PortParams::c_Output) { // Writes value to the port. ::CPU_GPIO_SetPinState( pThis[ Library_spot_hardware_native_Microsoft_SPOT_Hardware_Port::FIELD__m_portId ].NumericByRefConst().u4, state ); } else { TINYCLR_SET_AND_LEAVE(CLR_E_INVALID_OPERATION); } TINYCLR_NOCLEANUP(); } ...

    Jak je vidět tento soubor má odlišnou hlavičku a pravidla, byl pravděpodobně napsán již před delší dobou, když ještě nebyla možnost soubory automaticky vygenerovat. Test zrušení objektu nás také momentálně nezajímá, ten je tam protože OutputPort dědí Port, který dědí NativeEventDispatcher, což je poněkud pokročilejší téma na tento návod. Hle, ::CPU_GPIO_SetPinState je přesně to, co hledáme. Buďme ještě trochu zvídavější a zkusme zjistit, jakou má metoda signaturu. Pokud zkusíme najít soubor se jménem CPU_GPIO_SetPinState, nic nenajdeme. Je třeba nezoufat a přitvrdit - prohledáme obsah souborů stisknutím File Contents :
    Sreenshot výsledků hledání
    Sháníme jen signaturu, takže to, co nás zejména zajímá, jsou hlavičkové soubory. Ovladače block storage pravděpodobně není to co hledáme (když je prohlédnete, najdete jen volání této metody), za to CPU_CPIO_decl.h vypadá dobře i podle jména. Když jej otevřete, najdete tam signatury všech I/O metod, včetně té, která nás zajímá:

    void CPU_GPIO_SetPinState ( GPIO_PIN Pin, BOOL PinState );

    Najít význam GPIO_PIN už je trochu náročnější (nakonec byste našli typedef UINT32 GPIO_PIN; v C:\MicroFrameworkPK_v4_0\DeviceCode\Include\tinyhal.h), ale bez toho se nakonec obejdeme, neboť v kódu OutputPort je již dostatečná nápověda, a to konverze parametru pomocí .NumericByRefConst().u4. Pokud autoři porting kitu nebyli škodolibí, jedná se bez pochyb o čtyři bajty bez znaménka (unsigned), což je přesně UINT32.
    • To je ten důvod deklarovat v C# číslo pinu jako uint, a předejít tak zbytečným konverzím na nativní straně.
  4. Teď už je celkem jasné, jak měnit hodnotu pinu v našem kódu:

    ::CPU_GPIO_SetPinState(pin, positive != 0); // impulse start ... ::CPU_GPIO_SetPinState(pin, positive == 0); // impulse end

    Stejně tak v C# kódu musíme ale i zde kompilátoru napovědět, kde CPU_GPIO_SetPinState najde. Jak je to vyřešeno v implementaci OutputPort? Letmým pohledem na začátek souboru najdeme #include "SPOT_Hardware.h", což je to, co musíme přidat i do našeho kódu.
    • Jen proto, že v C# přijímáme už vytvořenou instanci OutputPortu, nemusíme pin inicializovat a kontrolovat. Kdybychom brali přímo Cpu.Pin, byla by to naše starost.
  5. Poslední věc, která zbývá vyřešit je, jak dostat číslo pinu. Vzpomeňte si, že jsme v C# k tomuto účelu použili proměnnou _pin a ve vygenerované hlavičce, Tutorial_MyFirstLibrary_ImpulseSender.h, spatřili toto:

    // Helper Functions to access fields of managed object static UINT32& Get__pin( CLR_RT_HeapBlock* pMngObj ) ...

    Takže potřebujeme jen někde sehnat ukazatel na CLR_RT_HeapBlock, ale to je hned první parametr, který naše WritePulse metoda dostává. Teď už nám nic nebrání pomocnou funkci zavolat:

    UINT32 pin = Get__pin(pMngObj);

    A je to – soubor uložte a můžete jej zavřít společně se všemi, které jsme pootvírali.

Kompletní obsah Tutorial_MyFirstLibrary_ImpulseSender.cpp vypadá tedy následovně:

//----------------------------------------------------------------------------- // // ** WARNING! ** // This file was generated automatically by a tool. // Re-running the tool will overwrite this file. // You should copy this file to a custom location // before adding any customization in the copy to // prevent loss of your changes when the tool is // re-run. // //----------------------------------------------------------------------------- #include "SPOT_Hardware.h" #include "Tutorial.h" #include "Tutorial_MyFirstLibrary_ImpulseSender.h" using namespace MyFirstLibrary; void ImpulseSender::WritePulse( CLR_RT_HeapBlock* pMngObj, INT8 positive, INT32 durationCycles, HRESULT &hr ) { if (durationCycles < -1) { hr = CLR_E_OUT_OF_RANGE; return; } UINT32 pin = Get__pin(pMngObj); ::CPU_GPIO_SetPinState(pin, positive != 0); // začátek impulsu if (durationCycles == -1) return; // nechat nastavenou hodnotu while (--durationCycles >= 0) { // prázdný cyklus } ::CPU_GPIO_SetPinState(pin, positive == 0); // konec impulsu }

Poznámka: čekací cyklus, který jsme vytvořil, dělá tuto funkci blokující a žádná další vlákna se nedostanou ke slovu, dokud neskončí. To by nemělo pro naše účely vadit – kdybyste chtěli udělat desetivteřinový puls, udělali byste to byli snadno celé v C#.

Kompilujeme vlastní firmware

Předpokládám, že kompilujeme pro Tahoe-II, v jiném případě nahraďte všechny výskyty TAHOEII za vaši platformu.

  1. Spusťte příkazovou řádku a přepněte se do složky porting kitu, tj. C:\MicroFrameworkPK_v4_0.
  2. Nastavte systémové proměnné. Napište setenv_gcc C:\CodeSourcery a stiskněte enter, objeví se potvrzení.
  3. Z kopmilujte (FLASH verzi) firmware. Napište buildmf TAHOEII a stiskněte enter. Tím se spustí kompilace, u mne trvala téměř 3 minuty, žádné chyby, 92 varování. Bylo mi přiděleno číslo verze 4.0.40220.34067, vám pravděpodobně jiné.
  4. Dejte dohromady zkompilované soubory. Napište release a stiskněte enter. Tím se potřebné soubory posbírají a nakopírují do složky C:\MicroFrameworkPK_v4_0\Release\TAHOEII\TAHOEII_4.0.40220.34067\ (resp. vaše číslo), neměla by nastat žádná chyba a můžete ukončit příkazovou řádku.

Nahráváme vlastní firmware

Nahraďte všude 4.0.40220.34067 za vaši verzi firmwaru.

  1. Pokud jste tak ještě neučinili, připojte k počítači Tahoe-II.
  2. Ve zmíněné složce jsou tři podsložky, Bin, FirmwareUpdate_TAHOEII_4.0.40220.34067 a BootRecovery_TAHOEII_4.0.40220.34067. BootRecovery je pro případy nouze, kdy musíte přehrát celý firmware procesoru přes sériový port. Ve FirmwareUpate je běžný průvodce na přehrání firmware, který můžete stáhnout ze stránkách Device Solutions. Ve složce bin je kompletní výstup z kompilace, který momentálně k ničemu nepotřebujeme. Spusťte MeriianFirmwareDeploy.exe ze složky FirmwareUpdate.
    • Ve stávajících release notes je poznámka, že průvodce pro boot recovery není funkční. Nezkoušejte ho, dělal jsem s ním nějaké psí kusy a deska se mnou přestala mluvit. Pokud potřebujete udělat recovery update, stáhněte si poslední recovery co najdete (i kdyby to byla 3.0) a použijte tu.
  3. Objeví se uvítací stránka průvodce. Stiskněte Next, přijměte licenční podmínky (samozřejmě jen pokud s nimi souhlasíte) a znovu Next.
    Screenshot průvodce
    • Nezdá se, že by automatická detekce zařízení na této stránce fungovala. Pokud jste desku připojili později, spusťte průvodce znovu.
  4. Důležité: Pokud je to poprvé, co nahráváte svůj firmware vytvořený pomocí GCC, musíte zaškrtnout Update Configuration. V opačném případě to není potřeba.
  5. Stiskněte Next pro zahájení nahrávání firmware. Napoprvé to bude probíhat asi takto:
    1. Updating firmware...
    2. Tahoe-II se restartuje (do TinyBooteru).
    3. Deploying TinyBooterDecompressorRAM...
    4. Connecting to TinyBooter...
    5. V tuto chvíli by se mělo Tahoe-II restartovat, ale nějak se k tomu nemá, jen se vypne displej.
    6. Failed to connect to device! message box appears.
    7. Evidentně musíme s restartem pomoci, ale očekává se od nás spuštění TinyBooteru. To lze udělat udělat podržením zároveň SW5 a SW9 a během toho stisknutím tlačítka RESET.
    8. Tahoe-II restartuje a na displeji se objeví uvítací hlášky TinyBooteru.
    9. Na okně s chybovou hláškou stiskněte Retry.
    10. Teď už se nahrávání dokončí, uvidítě spoustu deploying, checking, erasing, writing...
    11. Po tom nejdelším, ER_FLASH, se objeví Update complete a po chvíli se průvodce přepne na stránku Finished, ale Tahoe zůstane opět s černým displejem. Mám takové podezření, že tu chvilku průvodce čeká na další restart.
    12. Průvodce teď můžete zavřít.
    13. Musíme tedy znovu pomoct s restartováním. Stisknete na desce tlačítko RESET.
    14. Tahoe-II blikne na displeji nějaké info a znovu zhasne.
    15. Nezoufat, a stisknout znovu RESET.
    16. TAHOEII v4.0.40220.34067 Build Date: Feb 11 2010 13:30:53 Waiting for debug commands...
      No není to žůžo?

    Další nahrávání firmware (tj. bez zaškrtnutého Update Configuration) už probíhá celé bez problémů a nevyžaduje žádnou pomoc.
    • Ke spuštění TinyBooteru na Meridianu/P je třeba podržet uživatelské tlačítko a stisknout reset.

Zkoušíme interop knihovnu

Naše knihovna zasílá impulsi na OutputPort. Abychom ji vyzkoušeli, potřebujeme buď LEDku s odporem, jak je uvedeno v prvních krůčkách s GPIO (nebo použít vestavěnou v případě Meridianu/P), nebo připojit osciloskop.

  1. Spusťte Visual C# Express resp. Visual Studio a vytvořte nový Micro Framework projekt typu Console Application.
  2. Přidejte referenci na naši kopii C:\MicroFrameworkPK_v4_0\MyInterops\MyFirstLibrary\ManagedCode\MyFirstLibrary.dll (k nalezení použijte záložku Browse). Je důležité aby to byla tato kopie, protože musí přesně sedět k nativní části, s kterou jsme zkompilovali firmware.
  3. Přidejte referenci na Microsoft.SPOT.Hardware assembly, abychom se dostali k třídě OutputPort.
  4. Nyní napíšeme do Class1.cs nějaký zkušební kód:

    using System; using System.Threading; using Microsoft.SPOT; using Microsoft.SPOT.Hardware; using MyFirstLibrary; namespace MFConsoleApplication1 { public class Program { public static void Main() { Thread.Sleep(5000); // bezpečnostní pauza OutputPort port = new OutputPort( (Cpu.Pin)49 /* GPO3 na Tahoe-II */, false /* výchozí hodnota */); ImpulseSender pulsar = new ImpulseSender(port); try { pulsar.WritePulse(true /* pozitivní */, -5 /* neplatný durationCycle */); } catch (ArgumentOutOfRangeException) { Debug.Print("Chyceno!"); } pulsar.WritePulse(true, 0); // nejkratší možný impuls for (int i = 1000; i < 100000; i *= 2) pulsar.WritePulse(true, i); pulsar.WritePulse(true, -1); // nekonečný impuls, ekvivalentní s port.Write(true); pulsar.WritePulse(false, int.MaxValue); // negativní impuls, ten nejdelší možný Debug.Print("Hotovo!"); } } }

    Bezpečnostní pauza není nutné, ale může vám při podobných pokusech zachránit nějaký ten recovery update. Pokud uděláte něco špatně, v C# nebo mnohem pravděpodobněji v C++, nemáte žádnou šanci se k desce znovu připojit Visual Studiem ani MFDeploy, jelikož se vaše aplikace spouští ihned jakmile desku zapnete nebo zrestartujete, aniž byste měli možnost prosadit debugger nebo něco jiného.
  5. Nezapomeňte ve vlastnostech projektu vybrat USB a Meridian_a7e70ea2 (resp. kombinaci pro vaše zařízení).
  6. A teď už si zbývá jen užít krokování programem.

Snímek z osciloskopu

Jen poznámka: tak krátké časy, s jakými si hrajeme tady, dost záleží na tom, jestli se jedná o debug nebo release konfiguraci, jestli je připojen debugger apod. Příklad je určen spíše pro demonstrativní účely.

Pokud chcete nyní upravit kód na nativní straně, stačí změnit soubor Tutorial_MyFirstLibrary_ImpulseSender.cpp a znovu projít kroky Kompilujeme vlastní firmware a Nahráváme vlastní firmware.

Hodně štěstí!

Doufám, že brzy uvidíme nějaké vaše nativní knihovny...

Comments
Comment Jan Kučera 11.02.2010 15:58:32
Okay, who first does see the problem with above example? Extra points for suggesting a fix.
Comment Richard 05.03.2010 3:04:44
My experience is that the above process does not work using Power Shell (installed Powershell on XP). Is my setup wrong? Process works if I open a VS2008 command Window and then use that as the shell.
Comment Jan Kučera 05.03.2010 12:56:58
You need the PowerShell only in step 5 of preparing the environment,, to set the execution policy. All other commands belong to the standard (or Visual Studio) command prompt window. Is that what caused the confusion?
Comment Richard 07.03.2010 20:11:21
yes. I tried to run build cmd scripts in Power Shell prmpt. :-) Thanks for confirming I should be using normal cmd or VS cmd.
Comment Richard 11.03.2010 10:40:35
Do you have any problems running MF programs on TAHOEII after you install the GCC Build of MF4? What about Windows and USB. Do you get errors from Windows during USB PnP device enumeration with GCC build of MF4? Any other problems?
Comment Jan Kučera 11.03.2010 11:11:04
I haven't faced any problems with either GCC build or USB PnP so far. The only difference when using GCC is a larger footprint.
Comment Ondra Vrzal 22.03.2010 18:06:43
very nice tutorial :). i ran into problems by using different versions of MF SDK and porting kit, so TAHOEII was not build because of several errors. after using links provided i managed to build TAHOEII :). I tried to run 2 application and one didn't worked with this new build opposed to the latest firmware from device solutions :(.
Sign in using Live ID to be able to post comments.