10.07.2017

TinyCLR OS od GHI Electronics

Od jakiegoś czasu na stronie GHI Electronics pojawiały się wzmianki o tym, że pracują nad nowym rozwiązaniem w stylu .NET Micro Framework. Nazwali to TinyCLR OS. Natomiast kilka dni temu pojawiła się informacja o wypuszczeniu wersji Preview 5 oraz o udostępnieniu kodu, który pozwala wygenerować TinyCLR OS na dowolną płytkę z procesorem STMF4. Żart? Nie!

To działa!

Bez problemu wygenerowałem dla poczciwej płytki STM32F4Discovery TinyCLR OS!. Bez problemu zrobiłem projekt w Visual Studio 2017 i bez problemu uruchomiłem prosty program z mrugającą diodą! Wszystko się debuguje, restartuje, przerywa itp. Bez najmniejszego problemu. Przygotowanie portu zajęło mi 15 minut, a kompilacja 5 sekund! TinyCLR OS będzie sukcesywnie rozwijany i uzupełniany o nowe biblioteki i peryferia.

Poniżej kilka przydatnych linków:
Przy rozpoczynaniu pracy trzeba się trzymać dokumentacji. Aha. Trzeba pamiętać o wgraniu sterownika USB, bo w dokumentacji nie ma o tym wzmianki.

TinyCLR OS Library trzeba podłączyć jako repozytorium lokalne NuGeta np.w ten sposób: How to Install a local sources NuGet package or a Prerelease package in Visual Studio

29.12.2015

Wyświetlacz LCD HD44780 na I2C

Wyświetlacze LCD zgodne z HD44780 niewielkim kosztem można przystosować do sterowania poprzez interfejs I2C. Uprości się wówczas sposób podłączania takiego wyświetlacza do układu. Zamiast sześciu linii GPIO wystarczy dwa przewody magistrali I2C plus zasilanie. Taki moduł można za niewielkie pieniądze zakupić i samemu przylutować do LCD lub od razu kupić wyświetlacz z wlutowanym adapterem. Adaptery przeważnie zbudowane są na układzie PCF8574. Najczęściej spotykane są takie adaptery jak na obrazku poniżej:

I2C LCD module
A tutaj adapter przylutowany do wyświetlacza LCD:

Uwaga! Inne adaptery (inaczej wyglądające) lub z innym  układem niż PCF8574T (na przykład z PCF8574TA) mogą mieć inaczej podłączone wyprowadzenia lub inny adres na magistrali I2C niż 0x27. Adapter do STM32F4Discovery (port dla NET MF 4.2) podłączamy następująco:

GND  -  GND
VCC  -  +5V
SDA  -  PB9
SCL  -  PB6

Teraz wystarczy tylko napisać nowy sterownik do biblioteki μLiquidCrystal i po kłopocie. Musimy zacząć od tego, jak jest podłączony układ PCF8574 do wyświetlacza. Dla adaptera takiego jak powyżej bity wartości wysłanej na interfejs I2C mają następujące znaczenie: 

                  (MSB) P7 P6 P5 P4 P3 P2 P1 P0 (LSB)
                        |  |  |  |   |  |  |  |
                   D7 __|  |  |  |   |  |  |  |__ RS (0 - cmd, 1 - data)
                   D6 _____|  |  |   |  |  |_____ RW (0 - write, 1 - read)
                   D5 ________|  |   |  |________ E  (falling edge)
                   D4 ___________|   |___________ Backlight (on/off)


Cała robota sprowadza się więc do napisania procedury, która będzie odpowiednio ustawiać poszczególne bity w wartości wysyłanej na interfejs I2C. Dodatkową komplikacją jest tryb 4 bitowej pracy wyświetlacza: najpierw trzeba wysłać 4 starsze bity wartości, a potem młodsze. Implementacja sterownika może wyglądać tak:

using MicroLiquidCrystal;
using Microsoft.SPOT.Hardware;

public class Pcf8574 : ILcdTransferProvider
{
    private readonly I2CDevice _device;
    private readonly I2CDevice.Configuration _config;
    private readonly I2CDevice.I2CTransaction[] _transactions;

    public bool FourBitMode
    {
        get { return true; }
    }

    public Pcf8574(I2CDevice device, ushort address = 0x27)
    {
        _device = device;
        _config = new I2CDevice.Configuration(address, 100);

        I2CDevice.I2CTransaction tran = I2CDevice.CreateWriteTransaction(new byte[1]);
        _transactions = new[] {tran};
    }

    public void Send(byte data, bool mode, bool backlight)
    {
        //Hi 4 bits
        int send = data & 0xF0;

        if (backlight)
            send |= 0x08; //P3

        if (mode)
            send |= 0x01; //P0

        Send((byte) (send | 0x04)); //E = 1
        Send((byte) send); //E = 0

        //Lo 4 bits + backlight + mode
        send = (data << 4) | (send & 0x0F);
        Send((byte) (send | 0x04)); //E = 1
        Send((byte) send); //E = 0
    }

    private void Send(byte data)
    {
        lock (_device)
        {
            _device.Config = _config;
            _transactions[0].Buffer[0] = data;
            _device.Execute(_transactions, 100);
        }
    }
}

Może wyjaśnienia troszkę wymaga konstruktor oraz druga procedura Send. W konstruktorze tworzymy i zapamiętujemy transakcję dla I2C tak, aby nie tworzyć jej za każdym razem. W procedurze Send przypisujemy bezpośrednio wartość do bufora tej transakcji. Dodatkowo w tej procedurze blokujemy I2CDevice i ponownie przypisujemy konfigurację. Pozwala to na współdzielenie magistrali I2C przez inne peryferia. Jeśli zawsze będziemy się trzymali takiego wzorca, żadne dodatkowe opakowania dla I2CDevice nie będą potrzebne. Poniżej przykład użycia:

using (I2CDevice device = new I2CDevice(null))
{
    Pcf8574 provider = new Pcf8574(device);

    Lcd lcd = new Lcd(provider);
    lcd.Begin(16, 2);

    lcd.Write("ABCDEFGHIJKLMNOP");
    lcd.SetCursorPosition(0, 1);
    lcd.Write("abcdefghijklmnop");
}


15.08.2015

Więcej interop: watchdog (IWDG)

Nasz mikrokontroler ma wbudowany system zabezpieczający przed zawieszeniem systemu. Nazywa się to watchdog. Właściwie to ma dwie takie funkcje: IWDG - independent watchdog i WWDG - window watchdog. Znacznie prostszy w użyciu i adekwatny do nieprzewidywalnego czasu działania NET MF jest IWDG. Spróbujemy zatem zaimplementować obsługę takiego watchdoga. W watchdogu chodzi o to, aby cyklicznie co jakiś czas poinformować go, że nasz program działa poprawnie. W przypadku, gdy watchdog nie dostanie takiego sygnału to mikrokontroler samoczynnie się zrestartuje. Nasza implementacja będzie bardzo zbliżona do tej na stronie stm32f4-discovery.com: Library 20- Independent watchdog timer on STM32F4.

A więc do dzieła. Do naszego projektu STM32F4Helper dodajemy nowy plik: Watchdog.cs. Statyczna klasa Watchdog będzie miała dwie funkcje: Start i Reset. Nasz mikrokontroler ma jeszcze jedną dodatkowa informację, która może być bardzo użyteczna: ostatni reset wykonał watchdog. Tą informację zwrócimy przez statyczną właściwość: LastReset. Tak więc cały "kadłubek" klasy wygląda tak:

namespace STM32F4Helper
{
    public static class Watchdog
    {
        public static bool LastReset { get { throw new NotImplementedException(); } } 

        public static void Start(TimeSpan period)
        {
            throw new NotImplementedException();
        }

        public static void Reset()
        {
            throw new NotImplementedException();
        }
    }
}

No dobra uzupełniamy funkcje. Aha. W dokumentacji zobaczymy, że do poprawnego działania watchdoga musimy odpowiednio ustawić rejestry prescallera (IWDG_PR) i licznika (IWDG_RLR). Te wartości obliczymy po stronie kodu zarządzanego na podstawie parametru period metody Start. Jeśli nie popełniłem żadnego błędu to cała klasa powinna wyglądać tak:

namespace STM32F4Helper
{
    public static class Watchdog
    {
        public static bool LastReset { get { return LastResetIwdg(); }  } 

        public static void Start(TimeSpan period)
        {
            SetTimings(period);
            StartIwdg();
        }

        public static void Reset()
        {
            ResetIwdg();
        }

        private static void SetTimings(TimeSpan period)
        {
            const int kHzLsi = 32000;

            long usPeriod = (period.Ticks * 1000) / TimeSpan.TicksPerMillisecond;
            int[] dividers = { 4, 8, 16, 32, 64, 128, 256 };
            for (int i = 0; i < dividers.Length; i++)
            {
                int usMin = (dividers[i] * 1000 * 1000) / kHzLsi;
                if (usPeriod >= usMin)
                {
                    long counter = usPeriod / usMin - 1;
                    if (counter < 0 || counter > 0xFFF)
                        continue;

                    SetupIwdg(i, (int)counter);
                    return;
                }
            }

            throw new InvalidOperationException("Invalid period (0.125..32768 ms).");
        }

        [MethodImpl(MethodImplOptions.InternalCall)]
        private static extern void SetupIwdg(int divider, int counter);

        [MethodImpl(MethodImplOptions.InternalCall)]
        private static extern void StartIwdg();

        [MethodImpl(MethodImplOptions.InternalCall)]
        private static extern void ResetIwdg();

        [MethodImpl(MethodImplOptions.InternalCall)]
        private static extern bool LastResetIwdg();
    }
}

Teraz standardowo generujemy pliki po stronie native. I uzupełniamy funkcje w pliku *_Watchdog.cpp. Oczywiście zmiany z innych plików (dotNetMF.proj, *_Native.cpp, *_Native.h) musimy odpowiednio przenieść do już istniejącej biblioteki. Funkcje po stronie native będą wyglądały tak:

#include "STM32F4Helper_Native.h"
#include "STM32F4Helper_Native_STM32F4Helper_Watchdog.h"

using namespace STM32F4Helper;

void Watchdog::SetupIwdg( INT32 param0, INT32 param1, HRESULT &hr )
{
  IWDG->KR = 0x5555;
  IWDG->PR = param0;
  IWDG->RLR = param1;
  
  RCC->CSR |= RCC_CSR_RMVF;
}

void Watchdog::StartIwdg( HRESULT &hr )
{
  IWDG->KR = 0xCCCC;
  IWDG->KR = 0xAAAA;
}

void Watchdog::ResetIwdg( HRESULT &hr )
{
  IWDG->KR = 0xAAAA;
}

INT8 Watchdog::LastResetIwdg( HRESULT &hr )
{
    INT8 retVal = 0;
    
    if (RCC->CSR & RCC_CSR_WDGRSTF)
       retVal = 1;
     
    return retVal;
}

I już. Zostaje tylko skompilowanie solucji, wgranie przez MFDeploy obrazów hex i przetestowanie. Na przykład takim programem. Diody informują po restarcie jaki był powód: czerwona - watchdog, zielona - zasilanie. Naciśnięcie przycisku wymusza blokadę programu, tak aby zadziałał watchdog.

public class Program
{
    public static void Main()
    {
        OutputPort redLed = new OutputPort(Stm32F4Discovery.LedPins.Red, false);
        OutputPort greenLed = new OutputPort(Stm32F4Discovery.LedPins.Green, false);
        OutputPort blueLed = new OutputPort(Stm32F4Discovery.LedPins.Blue, false);
        InputPort button = new InputPort(Stm32F4Discovery.ButtonPins.User, false, Port.ResistorMode.PullDown);

        OutputPort led = STM32F4Helper.Watchdog.LastReset ? redLed : greenLed;
        led.Write(true);
        Thread.Sleep(1000);
        led.Write(false);

        STM32F4Helper.Watchdog.Start(new TimeSpan(0, 0, 0, 5, 0));
            
        for (;;)
        {
            STM32F4Helper.Watchdog.Reset();

            Thread.Sleep(500);
            blueLed.Write(!blueLed.Read());
                
            if (button.Read())
            {
                while (button.Read())
                {
                }

                while (true)
                {
                    blueLed.Write(!blueLed.Read());
                    Thread.Sleep(100);

                    if (button.Read())
                    {
                        while (button.Read())
                        {
                        }

                        blueLed.Write(false);
                        break;
                    }
                }
            }
        }
    }
}

6.08.2015

Serwo Tower Pro SG90

Tower Pro SG-90 to małe, lekkie i przede wszystkim tanie, uniwersalne serwo. Bez problemu da się podłączyć do STM32F4Discovery. Urządzenie ma 3 przewody połączeniowe: brązowy, czerwony i żółty. Brązowy podłączamy do masy (GND), czerwony do zasilania (+5V), a żółty do wyjścia PWM na płytce. Ja wybrałem wyjście PWMChannel.PWM_0 (czyli pin PD12 - zielona dioda LED).
Tower Pro SG90

Sterowanie kątem obrotu odbywa się poprzez zmianę szerokości impulsu na wejściu PWM. Według dokumentacji częstotliwość sygnału sterującego powinna wynosić 50Hz (okres 20ms), natomiast szerokość impulsu dla wychyleń -90..+90 stopni powinna zawierać się w przedziale: 1ms..2ms. Po zbadaniu skrajnych położeń orczyka wyszło mi, że szerokość impulsu powinna być z przedziału 0.53ms..2.35ms. Do takiego sterowania nadaje się drugi sposób regulacji parametrów PWM: zmiana właściwości Period (czas jednego okresu sygnału) oraz Duration (czas trwania stanu wysokiego). Poniżej kot, który wychyla orczyk do skrajnych położeń:

public static void Main()
{
    const uint period = 20000;    //20ms in us
    const uint minDuration = 530; //0.53ms in us
    const uint maxDuration = 2350;//2.35ms in us
    const uint midDuration = minDuration + (maxDuration - minDuration)/2;

    var servo = new PWM(Cpu.PWMChannel.PWM_0, period, minDuration,
                        PWM.ScaleFactor.Microseconds, false);
    servo.Start();

    for (;;)
    {
        servo.Duration = minDuration;
        Thread.Sleep(2000);

        servo.Duration = midDuration;
        Thread.Sleep(2000);

        servo.Duration = maxDuration;
        Thread.Sleep(2000);

        servo.Duration = midDuration;
        Thread.Sleep(2000);
    }
}

Jeśli byśmy chcieli sterować kątem obrotu podając stopnie, to przyda się jedna bardzo fajna funkcja z Arduino. Funkcja "map":

static uint Map(uint x, uint minX, uint maxX, uint outMin, uint outMax)
{
    return (x - minX)*(outMax - outMin)/(maxX - minX) + outMin;
}

A użyć jej możemy tak (orczyk zmienia położenie co 30 stopni):

public static void Main()
{
    const uint period = 20000; //20ms in us
    const uint minDuration = 530; //0.53ms in us
    const uint maxDuration = 2350; //2.35ms in us

    var servo = new PWM(Cpu.PWMChannel.PWM_0, period, minDuration,
                        PWM.ScaleFactor.Microseconds, false);
    servo.Start();

    uint step = 30;
    for (uint angle = 0;; angle += step)
    {
        if (angle > 180)
        {
            step = (uint) -step;
            continue;
        }

        servo.Duration = Map(angle, 0, 180, minDuration, maxDuration);
        Thread.Sleep(2000);
    }
}

4.08.2015

Bliskie spotkanie z .NET Micro Framework 4.4

Jakiś czas temu dla wersji NET MF 4.4 (4.4 Beta 2 is here!) pojawiła się solucja dla STM32F4Discovery. Postanowiłem więc skompilować SDK oraz solucję. Może się komuś przyda. Pliki dostępne są w repozytorium: MicroFrameworkSDK i obrazów hex. Najpierw zainstalować trzeba SDK z pliku MicroFrameworkSDK.msi, a następnie rozszerzenie vsix dla odpowiedniej wersji Visual Studio. Pliki hex wgrywamy standardowo poprzez MFDeploy. Instalacja przebiega gładko. Natomiast później można napotkać pewne problemy, które opiszę poniżej.

Na systemie Windows 8 i Visual Studio 2015 nie powinno być większych problemów. Schody mogą się pojawić na wersji Windows 7 i Visual Studio 2013.

Problem 1: sterownik USB.
W tej wersji NET MF nie są wymagane jakieś specjalne sterowniki. System sam powinien wykryć i zainstalować sterownik WinUSB dla STM32F4Discovery. Czasami może się automatycznie podpiąć jakiś inny sterownik. Wówczas trzeba go usunąć i pozwolić systemowi na zainstalowanie domyślnego. Jak ma to poprawnie wyglądać jest poniżej:

Windows 8Windows 7

W pewnych przypadkach na Windows 7 sterowniki nie zostaną zainstalowane automatycznie. Trzeba wówczas samemu pobrać sterownik i zainstalować ręcznie. Sterownik Microsoft - Other hardware - WinUsb Device można pobrać ze strony http://catalog.update.microsoft.com (tylko Internet Explorer).

Problem 2: MetaDataProcessor exited with code -1073741515.
Jeśli używamy tylko Visual Studio 2013 (nie mamy zainstalowanego Visual Studio 2015), to prawdopodobnie, przy pierwszej kompilacji, dostaniemy komunikat:


Ręczne uruchomienie MetaDataProcessor.exe pozwoli na uzyskanie dokładniejszej informacji o błędzie: The program can't start because api-ms-win-crt-runtime-l1-1-0.dll is missing. Musimy zainstalować Visual C++ Redistributable for Visual Studio 2015.

Problem 3: zmienione piny.
W solucji zostały zmienione niektóre piny w stosunku do poprzedniej wersji. Różnice poniżej:

I2C pins: scl=PB8 sda=PB9

Brak PWMChannel4 .. PWMChannel7

AnalogChannel0: pin=PA0
AnalogChannel1: pin=PA1
AnalogChannel2: pin=PA2
AnalogChannel3: pin=PA3
AnalogChannel4: pin=PF6
AnalogChannel5: pin=PF7
AnalogChannel6: pin=PF8
AnalogChannel7: pin=PF9
AnalogChannel8: pin=PF10
AnalogChannel9: pin=PF3
AnalogChannel10: pin=PC0
AnalogChannel11: pin=PC1
AnalogChannel12: pin=PC2
AnalogChannel13: pin=PC3
AnalogChannel14: pin=PF4
AnalogChannel15: pin=PF5

COM1: (rx, tx, cts, rts)=(PB7, PB6, PP15, PP15)
COM2: (rx, tx, cts, rts)=(PD6, PD5, PD3, PD4)
COM3: (rx, tx, cts, rts)=(PC11, PC10, PD11, PD12)
COM4: (rx, tx, cts, rts)=(GPIO_NONE, GPIO_NONE, GPIO_NONE, GPIO_NONE)
COM5: (rx, tx, cts, rts)=(GPIO_NONE, GPIO_NONE, GPIO_NONE, GPIO_NONE)
COM6: (rx, tx, cts, rts)=(GPIO_NONE, GPIO_NONE, GPIO_NONE, GPIO_NONE)

To wszystko. Możemy spróbować sił z NET MF 4.4. Jeśli pojawią się jakieś poprawki będę próbował na bieżąco kompilować i SDK i solucję.

17.05.2015

Dodawanie nowych funkcji (Interop w .NET Micro framework))

Przychodzi taki czas, gdy czegoś nam w .NET MF brakuje. Potrzebna nam jest jakaś funkcja, a jej nie ma. Jeśli to prosta funkcja, bardzo łatwo sami możemy rozszerzyć nasz system. Jedynym wymaganiem jest umiejętność kompilacji naszej solucji. Bardzo przystępny przewodnik, o tworzeniu takiej dodatkowej biblioteki, znajduje się na stronie: Using Interop in the .NET Micro Framework. Bazując na tym przykładzie zbudujemy własny interop.

Na pewno przyda się jakaś dokumentacja mikrokontrolera STM32F407, bo taki ma nasza płytka:
W pierwszym dokumencie wyczytamy (w punkcie 39.1), że każdy mikrokontroler posiada, zapisany w pamięci, unikalny identyfikator. Taki identyfikator możemy użyć np. przy komunikacji z serwerem do identyfikacji urządzenia. Spróbujemy więc dodać możliwość odczytania tego identyfikatora. Unikalny id znajduje się pod adresem 0x1FFF7A10 i składa się z 96 bitów (12 bajtów). Możemy go zobaczyć w ST-Link:


Tylko jak ten identyfikator odczytać? Na przykład tak: Reading the STM32 unique device ID in C. Wystarczy przenieść ten przykład do naszej solucji. Ale po kolei.

Musimy utworzyć nowy projekt, który będzie nasza biblioteką. Ja projekt nazwałem STM32F4Helper i dodałem jedną klasę: Device. Ta klasa będzie miała jedną statyczna metodę: GetGuid. W tej procedurze zamienimy liczby (pokazane na obrazku) w guid. Od razu trzeba też zaplanować funkcję zewnętrzną (GetUuid32), którą pobierzemy te wartości z pamięci. Cała klasa może wyglądać tak:

using System.Runtime.CompilerServices;

namespace STM32F4Helper
{
    public static class Device
    {
        public static string GetGuid()
        {
            var buffer = new uint[3];
            GetUuid32(buffer);
            string result = buffer[0].ToString("X8")
                            + "-" + (buffer[1] >> 16).ToString("X4")
                            + "-" + (buffer[1] & 0xFFFF).ToString("X4")
                            + "-" + "0000"
                            + "-" + buffer[2].ToString("X8") + "0000";
            return result;
        }

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private static extern void GetUuid32(uint[] buffer);
    }
}

Ustawiamy w projekcie opcję generowania native stub i kompilujemy projekt.



We wskazanym katalogu powstanie cześć natywna naszej biblioteki. Nie trzeba się przejmować ilością tych plików. Tak naprawdę, dla prostych funkcji, trzeba uzupełnić tylko plik *Device.cpp (świadomie użyłem  tutaj gwiazdki, aby pokazać, że nazwa pliku wcale nie jest taka skomplikowana, tylko trzeba odpowiednio patrzeć). Pliki przenosimy do katalogu solucji, aby powstała taka struktura:


W katalogu managed można sobie umieścić projekt części zarządzanej biblioteki lub wynik jej kompilacji czyli pliki z katalogu bin\debug lub bin\release. W docelowym projekcie będziemy mogli wówczas dodawać referencję tylko do dll, a nie cały projekt (ale o tym kiedy indziej). Musimy mieć na uwadze to, że jeśli do biblioteki dodamy nowe funkcje lub klasy, to trzeba ponownie wygenerować część natywną.

Przed kompilacją umiejętnie trzeba zmienić niektóre pliki, no i uzupełnić funkcję. Najpierw plik STM32F4Helper.featureproj.  Możemy uzupełnić element description i musimy zmienić ścieżkę w elemencie RequiredProjects na taką:

<!--MMP_DAT_CreateDatabase Include="$(SPOCLIENT)\Solutions\Discovery4\Libraries\STM32F4Helper\Managed\$(ENDIANNESS)\STM32F4Helper.pe" /-->
<RequiredProjects Include="$(SPOCLIENT)\Solutions\Discovery4\Libraries\STM32F4Helper\Native\dotnetmf.proj" />
Dodatkowo komentujemy lub usuwamy z tego pliku element MMP_DAT_CreateDatabase. Dopóki do docelowego programu będziemy dodawać referencję do całego projektu to tej linii nie potrzebujemy.

Dokładamy też od razu naszą bibliotekę do TinyCLR.proj w naszej solucji. Do grupy featureproj dodajemy STM32F4Helper.featureproj:

<Import Project="$(SPOCLIENT)\Solutions\Discovery4\Libraries\STM32F4Helper\Native\STM32F4Helper.featureproj" />

Poniżej całego featureproj dodajemy pozycję z driverlibs:

<ItemGroup>
  <DriverLibs Include="STM32F4Helper.$(LIB_EXT)" />
  <RequiredProjects Include="$(SPOCLIENT)\Solutions\Discovery4\Libraries\STM32F4Helper\Native\dotNetMF.proj" />
</ItemGroup>

Pozostało tylko uzupełnić naszą natywną funkcję. Jak widać nie jest zbyt skomplikowana:

#include <stdint.h>
#include "STM32F4Helper_Native.h"
#include "STM32F4Helper_Native_STM32F4Helper_Device.h"

#define STM32_UUID ((uint32_t *)0x1FFF7A10)

using namespace STM32F4Helper;

void Device::GetUuid32( CLR_RT_TypedArray_UINT32 param0, HRESULT &hr )
{
  param0[0] = STM32_UUID[0];
  param0[1] = STM32_UUID[1];
  param0[2] = STM32_UUID[2];
}

Teraz trzeba tylko skompilować solucję (TinyCLR.proj) i wgrać na płytkę przy pomocy MFDeploy. W programie można teraz użyć takiej funkcji:

using Microsoft.SPOT;
using STM32F4Helper;

namespace DemoInterop
{
    public class Program
    {
        public static void Main()
        {
            string guid = Device.GetGuid();
            Debug.Print("Guid: " + guid);
        }
    }
}
 

A wynik działania jest taki:
Guid: 002B001C-3231-4713-0000-383031370000

26.04.2015

.NET Micro framework w projektach komeryjnych

Czy .NET Micro Framework da się użyć w projektach komercyjnych? Myślę, że tak. Przy dobrze napisanym programie nigdy nie miałem problemu ze stabilnością czy zawieszaniem się układu. Jednak do celów profesjonalnych chyba użyłbym jednego z modułów od GHI Electronics i ich bibliotek np: G400-S (Flash: 1.4MB, RAM: 92MB) czy G120 (Flash: 2.87MB, RAM: 13.67MB).

Sceptykom "czy da się tego użyć profesjonalnie" polecam dwa linki: