3.09.2012

Odbiornik podczerwieni cd. - dekodowanie RC5

Jak już wcześniej napisałem, chyba najpowszechniejszym protokołem transmisji używanym w pilotach TV jest protokół RC5. Jak mamy piloty Philipsa lub "no name" to prawdopodobnie używają RC5. Można również taki pilot zakupić. Najtańsze już za około 10, 15 zł. Spróbujemy zdekodować taki sygnał.

Bardzo dokładnie protokół został opisany na stronie http://www.sbprojects.com/. Najważniejsze informacje jakie musimy wiedzieć:
  • bit ramki to pojedynczy impuls niski lub wysoki
  • ramka zawsze zaczyna się od bitu 0
  • pojedynczy bit ramki trwa 889us
  • każdy impuls powyżej 1.2*889us to dwa bity
  • odbieramy 28 bitów ramki
  • bity ramki zakodowane są kodem menchester - występują tylko pary 01 i 10
  • pary 01 zamieniamy na 1, a 10 na 0
  • po zamianie bitów ramki otrzymujemy 14 bitów danych
  • dane zawsze zaczynają się dwoma jedynkami i bitem toggle
  • adres urządzenia to kolejne 5 bitów, a kod przycisku to następne 6
Dobra. Sprawdzamy krótkim programikiem czy nasz pilot (piloty) używają RC5. Wykrywamy długość pierwszego impulsu 1 i następującego 0. Jeśli długości będą zbliżone do 889us, to jest to RC5 (pokaże się informacja w oknie output).

public class Program
{
    private const int MinWidth = 640; //us
    private const int MaxWidth = 1140; //us

    private static DateTime _nextCommand = DateTime.MinValue;
    private static int _okImpulses;

    public static void Main()
    {
        using(var receiver = new IRReceiver(Stm32F4Discovery.FreePins.PB5))
        {
            receiver.Pulse += ConsumePulse;
            Thread.Sleep(Timeout.Infinite);
        }
    }

    private static void ConsumePulse(TimeSpan width, bool state)
    {
        DateTime now = DateTime.Now;
        if (now < _nextCommand)
            return;

        long usWidth = width.TotalMicroseconds();
        //czekamy na 1
        if (state)
        {
            if (usWidth > MinWidth && usWidth < MaxWidth)
                _okImpulses++;
        }
        else
        {
            if (_okImpulses == 1)
            {
                if (usWidth > MinWidth && usWidth < MaxWidth)
                    Debug.Print("RC5!");

                _okImpulses = 0;
                _nextCommand = now.AddMilliseconds(500);
            }
        }
    }
}

Jeśli mamy zgodny pilot to budujemy dekoder RC5 do podglądania kodów klawiszy. Najciekawsza jest procedura dekodująca impulsy. Algorytm mógłby się opierać na tablicy 28 bitów ramki, które po zebraniu dekodowalibyśmy na 14 bitów danych. Jednak tablica nie jest potrzebna. Bity zbieramy i dekodujemy parami.

private void ConsumePulse(TimeSpan width, bool state)
{
    long usWidth = width.TotalMicroseconds();

    //poczatek ramki
    if (usWidth > NextFrame || _cnt == 0)
    {
        _cnt = 1;
        _prevBit = false; //zawsze zaczyna sie od 0
        _frame = 0;
        return;
    }

    if (_cnt == 0)
        return;

    //jesli szerokosc impulsu szersza niz 1 bit to dwa bity
    int bitCnt = usWidth > MaxOneBitTime ? 2 : 1;
    for (int i = 1; i <= bitCnt; i++)
    {
        _cnt++;

        if (_cnt%2 == 0)
            DecodeMenchester(_prevBit, state); //co dwa bity dekodujemy bit ramki
        else
        {
            //jesli mamy przedostatni bit to nie czekamy na ostatni mamy całą ramke
            if (_cnt == 27)
            {
                _cnt++;
                //ostatni bit jest przeciwienstwem przedostatniego
                //tutaj błąd w dekodowaniu menchester nie moze się pojawić
                DecodeMenchester(state, !state);
                //mamy ramke
                OnFrame(_frame);
                //zaczynamy od nowa
                _cnt = 0;
            }
            else
                _prevBit = state; //to tylko kolejny bit
        }
    }
}

private void DecodeMenchester(bool bit0, bool bit1)
{
    //kontrola czy jest ok
    if (!(bit0 ^ bit1))
    {
        Debug.Print("Invalid frame data");
        //jesli nie jest ok to zaczynamy ramke od nowa
        _cnt = 0;
    }

    //dekodowanie menchester: 01->1 , 10->0
    if (bit0) 
        return;

    _frame |= 1 << (Framelength - _cnt/2);
}

Jeśli zbierzemy bity całej ramki, to dekodujemy je na adres i kod przycisku i przekazujemy poprzez zdarzenie zainteresowanym.

private void OnFrame(int frame)
{
    int command = (frame & 0x3F);
    bool toggle = (frame & 0x0800) > 0;
    bool extended = (frame & 0x1000) == 0;
    int address = (frame & 0x1F) >> 6;
    //korekta dla extended RC5
    if (extended)
        command |= (1 << 6);

    //Debug.Print(" Addr:" + address + " Cmd:" + command + " Toggle: " + toggle);
    var args = new FrameEventArgs
                   {
                       Command = command,
                       Address = address,
                       Toggle = toggle
                   };

    if (Frame != null)
        Frame(this, args);
}

Krótki, główny programik pozwoli nam wyświetlić te informacje w oknie output.

public class Program
{
    private static DateTime _nextCommand = DateTime.MinValue;

    public static void Main()
    {
        using(var receiver = new IRReceiver(Stm32F4Discovery.FreePins.PB5))
        {
            var detector = new RC5Decoder(receiver);
            detector.Frame += (s, f) =>
                                  {
                                      DateTime now = DateTime.Now;
                                      if (now < _nextCommand)
                                          return;

                                      Debug.Print("Addr:" + f.Address +
                                                  " Cmd:" + f.Command +
                                                  " Toggle: " + f.Toggle);

                                      _nextCommand = now.AddMilliseconds(500);
                                  };

            Thread.Sleep(Timeout.Infinite);
        }
    }
}

Pełny kot: DemoIRReceiverRC5

1.09.2012

Odbiornik podczerwieni (IRReceiver)

Bardzo łatwo można zbudować układ do sterowania STM32F4Discovery za pomocą pilota telewizyjnego (TV Remote). Na początek trochę teorii. 

Najbardziej powszechne są dwa rodzaje nadajników i odbiorników podczerwieni: 36kHz i 38kHz. Częstotliwości te są używane do modulowania impulsów danych. Odbiornik 36kHz nie będzie reagował na piloty 38kHz i na odwrót. Pilot wysyła impulsy, które po odebraniu przez odbiornik można zamienić na kod przycisku naciśniętego na pilocie. Sposób w jaki są zakodowane impulsy danych (liczba impulsów i ich szerokości), to protokół transmisyjny. Piloty mogą mieć różne protokoły transmisji (zależy od firmy). Najbardziej powszechnym jest chyba protokół RC5, wymyślony przez Philipsa. Więcej o tym można poczytać tutaj: TR Remote Control Theory

Ja akurat zakupiłem (w ciemno bo nie wiedziałem jakie mam piloty) odbiornik podczerwieni na 36kHz: TSOP31236, ale jest podobny na 38kHz: TSOP31238. Zresztą chyba i inne się nadadzą tylko trzeba w notach katalogowych popatrzeć jak je podłączyć. Ja mój podłączyłem tak jak na obrazku poniżej (kondensator i rezystor do testów można sobie darować):

TVIR + STM32F4Discovery
TVIR + STM32F4Discovery
Teraz kot. Konstruujemy klasę, która podłączona pod nóżkę odbiornika będzie informowała (EventHandler) o odebranych danych. Interesuje nas stan (1 lub 0) oraz czas trwania każdego impulsu. Obsługujemy każdą zmianę sygnału na pinie odbiornika (InterruptEdgeBoth).
public class IRReceiver : IDisposable
{
    public delegate void PulseEventHandler(TimeSpan width, bool state);
    public event PulseEventHandler Pulse;

    private readonly InterruptPort _receiverPort;
    private long _lastTick = DateTime.Now.Ticks;

    public IRReceiver(Cpu.Pin pin)
    {
        _receiverPort = new InterruptPort(pin, false,
                                          Port.ResistorMode.PullUp,
                                          Port.InterruptMode.InterruptEdgeBoth);

        _receiverPort.OnInterrupt += PortInterrupt;
    }

    public void Dispose()
    {
        _receiverPort.Dispose();
    }

    private void PortInterrupt(uint port, uint state, DateTime time)
    {
        long current = time.Ticks;
        TimeSpan pulseWidth = TimeSpan.FromTicks(current - _lastTick);
        _lastTick = current;

        //Debug.Print("pulse " + pulseWidth.TotalMicroseconds()+ "us " + state);

        if (Pulse != null) 
            Pulse(pulseWidth, state == 1);
    }
}

Teraz program, który będzie reagował na impulsy z IRReceiver. Na razie bez rozpoznawania protokołu transmisyjnego i dekodowania impulsów. Będziemy włączać i wyłączać diody na płytce STM32F4Discovery dowolnym klawiszem na pilocie. Nie możemy jednak reagować na każdy impuls, bo w ramce transmisyjnej może ich być kilka czy kilkanaście i diody będą miały losowy stan. Robimy więc opóźnienie, tak aby reagować na pierwszy impuls, a na kolejny dopiero po 500 milisekundach.

public class Program
{
    private const int DelayBetweenCommands = 500; //ms

    public static void Main()
    {
        var leds = new[]
                       {
                           new OutputPort(Stm32F4Discovery.LedPins.Green, true),
                           new OutputPort(Stm32F4Discovery.LedPins.Orange, true),
                           new OutputPort(Stm32F4Discovery.LedPins.Red, true),
                           new OutputPort(Stm32F4Discovery.LedPins.Blue, true)
                       };

        var receiver = new IRReceiver(Stm32F4Discovery.FreePins.PB5);

        DateTime nextCommand = DateTime.MinValue;
        receiver.Pulse += (width, state) =>
                              {
                                  DateTime now = DateTime.Now;
                                  if (now < nextCommand)
                                      return;

                                  nextCommand = now.AddMilliseconds(DelayBetweenCommands);
                                  Toggle(leds);
                              };

        Thread.Sleep(Timeout.Infinite);
    }

    private static void Toggle(OutputPort[] leds)
    {
        bool state = !leds[0].Read();
        foreach (OutputPort led in leds)
            led.Write(state);
    }
}

Piloty w dłoń i celujemy w odbiornik. Naciskamy dowolne klawisze. U mnie w domu na trzy piloty dwa były 36kHz, a jeden prawdopodobnie 38kHz.

Pełny kot: DemoIRReceiver

28.08.2012

Termometr z LCD na STM32F4Discovery

Bez rozłączania poprzedniego układu (z DS18B20), możemy do płytki podłączyć wyświetlacz LCD. Najprościej podłączyć LCD zgodny z HD44780. Ja akurat miałem pod ręką YM1602C. Łączymy według schematu poniżej.
HD44780 + STM32F4Discovery

HD44780 + STM32F4Discovery
No dobra. Zapalamy wyświetlacz. Tylko jak? na szczęście ktoś już za nas zrobił bibliotekę do obsługi takich wyświetlaczy: μLiquidCrystal. Aha. Jest jeden problem. Jak już ściągniemy źródła, to trzeba w projekcie μLiquidCrystal zmienić framework na 4.2 (domyślnie jest 4.1 bodajże). No to start. Testujemy LCD krótkim programikiem.

var lcdProvider = new GpioLcdTransferProvider(Stm32F4Discovery.Pins.PD1, Stm32F4Discovery.Pins.PD2,
                                              Stm32F4Discovery.Pins.PD9, Stm32F4Discovery.Pins.PD11,
                                              Stm32F4Discovery.Pins.PD10, Stm32F4Discovery.Pins.PD8);

var lcd = new Lcd(lcdProvider);
lcd.Begin(16, 2); //columns, rows

//znaki specjalne
//http://www.quinapalus.com/hd44780udg.html
var customCharacters = new[]
                           {
                               new byte[] {0x00, 0x0a, 0x15, 0x11, 0x11, 0x0a, 0x04, 0x00}, //serce
                               new byte[] {0x04, 0x02, 0x01, 0x1f, 0x01, 0x02, 0x04, 0x00} //strzalka
                           };

//ladowanie znakow specjalnych
for (int i = 0; i < customCharacters.Length; i++)
    lcd.CreateChar(i, customCharacters[i]);

lcd.Clear();
lcd.Write("* Hello World! *");
Thread.Sleep(3000);

lcd.Clear();
lcd.WriteByte(0); //pierwszy znak specjalny
Thread.Sleep(2000);
lcd.WriteByte(1); //drugi znak specjalny
Thread.Sleep(3000);

//nastepna linia
lcd.SetCursorPosition(0, 1);
lcd.Write("#     Bye...   #");

 Pełny kot: DemoLcd

Na wyświetlaczu powinny pojawić się jakieś napisy. No niestety polskich znaków nie wyświetlimy. Jak wszystko jest OK to robimy kolejny display do termometru z poprzedniego postu. Najważniejsza jest procedura, która wyświetla temperaturę.
public void ShowTemperature(float temperature)
{
    _lcd.SetCursorPosition(0, 0);
    string txt = "Temperatura:";
    int padsCnt = Columns - txt.Length;
    _lcd.Write(txt + new string(' ', padsCnt));

    _lcd.SetCursorPosition(0, 1);

    string tempStr = temperature.ToString("F2");
    padsCnt = Columns - tempStr.Length;
    _lcd.Write(tempStr);
    _lcd.WriteByte(0);

    if (padsCnt > 0)
    {
        var pads = new String(' ', padsCnt);
        _lcd.Write(pads);
    }
}

Dodajemy jeszcze LCD do listy wyświetlaczy.

public Display()
{
    var debugDisplay = new DebugDisplay();
    var ledDisplay = new FourLedDisplay();
    var lcd = new LcdDisplay();
    _displays = new IDisplay[] {debugDisplay, ledDisplay, lcd};
}

W taki oto sposób mamy termometr z 3 wyświetlaczami: w oknie debug, na diodach z płytki i na LCD.

Pełny kot: DemoDS18B20

26.08.2012

Termometr na DS18B20 (1-Wire) i STM32F4Discovery

DS18B20 + STM32F4Discovery
DS18B20 + STM32F4Discovery
W internecie jest wiele bibliotek do DS18B20 i wiele przykładów jak pobierać informację o temperaturze. Moja biblioteka radzi sobie dobrze z wartościami ujemnymi i obsługuje zarówno DS18B20 jak i DS18S20. Poniżej zamieszczam tylko samo sedno biblioteki, czyli metody do odczytu temperatury z czujnika i konwersji na wartość.

Komunikacja z DSem musi odbywać się według ustalonej sekwencji: inicjalizacja (reset linii 1-Wire), wysłanie komendy ROM (np. match=0x55), wysłanie funkcji. Poniżej pomocnicza metoda, która taką sekwencję uruchamia.
private void RunSequence(byte command)
{
    if(_bus.TouchReset() == 0) //0 = no devices, 1 = device(s) exist
        throw new IOException("DS18X20 communication error");

    Write(MatchROMCommand);
    Write(_presensePulse);
    Write(command);
}

private void Write(params byte[] sendValues)
{
    foreach (byte sendValue in sendValues)
        _bus.WriteByte(sendValue);
}

Właściwa funkcja odczytu temperatury wysyła 2 komendy. Pierwsza inicjuje pomiar temperatury, a druga go pobiera. Właściwie to układ zwraca 2 bajty, które trzeba dopiero na temperaturę zamienić. Wyliczenie wartości temperatury uzależnione jest od typu układu i uwzględnia znak.
public float GetTemperature()
{
    RunSequence(ConvertTCommand);

    DateTime timeBarier = DateTime.Now.AddMilliseconds(TimeoutMiliseconds);
    while(_bus.ReadByte() == 0)
    {
        if(DateTime.Now > timeBarier)
            throw new IOException("DS18X20 read timeout");
    }

    RunSequence(ReadScratchpadCommand);

    int reading = _bus.ReadByte() | (_bus.ReadByte() << 8); //lsb msb
    bool minus = (reading & 0x8000) > 0;
    if (minus)
        reading = (reading ^ 0xffff) + 1; //uzupelnienie do 2 (U2)

    float result = _sSeries
                       ? CalculateS(reading)
                       : CalculateB(reading);
    if(minus)
        result = -result;

    return result;
}

private static float CalculateB(int reading)
{
    float result = 6*reading + reading/4; // multiply by (100 * 0.0625) or 6.25
    result = result/100;
    return result;
}

private static float CalculateS(int reading)
{
    float result = (reading & 0x00FF)*0.5f;
    return result;
}

No dobra. Czas temperaturę wyświetlić. Wyświetlacz będzie implementował interfejs IDisplay (od razu dla przykładu najprostsza implementacja):
public interface IDisplay
{
    void ShowTemperature(float temperature);
    void ShowError(string message);
}

public class DebugDisplay : IDisplay
{
    public void ShowTemperature(float temperature)
    {
        Debug.Print("Temperatura: " + temperature.ToString("F2") + " °C");
    }

    public void ShowError(string message)
    {
        if (message == null) 
            throw new ArgumentNullException("message");

        Debug.Print("Error: " + message);
    }
}

Dodajemy jeszcze jedną klasę pomocniczą implementującą IDisplay. Z niej będziemy korzystali w programie. Dzięki temu, w przyszłości, łatwo będzie można dodać nowe wyświetlacze. Można sobie nawet wyobrazić taką sytuację, że w zależności od np. ustawionych zworek raz będzie działał jeden wyświetlacz, a raz inny.
public class Display : IDisplay
{
    private readonly IDisplay[] _displays;

    public Display()
    {
        var debugDisplay = new DebugDisplay();
        _displays = new IDisplay[] {debugDisplay};
    }

    public void ShowTemperature(float temperature)
    {
        foreach (IDisplay display in _displays)
            display.ShowTemperature(temperature);
    }

    public void ShowError(string message)
    {
        if (message == null) 
            throw new ArgumentNullException("message");

        foreach (IDisplay display in _displays)
            display.ShowError(message);
    }
}

Teraz wszystko trzeba poskładać w całość. Na początku głównej procedury programu mamy inicjalizację OneWire i wykrycie czujników. W moim przypadku zawsze jeden, więc bierzemy pierwszy lepszy.
var op = new OutputPort(Stm32F4Discovery.FreePins.PA15, false);
var ow = new OneWire(op);

IDisplay display = new Display();
DS18X20[] devices = DS18X20.FindAll(ow);
if (devices.Length == 0)
{
    display.ShowError("Brak DS18B20");
    return;
}

DS18X20 tempDev = devices[0];

Samo sedno programu stanowi kot wykonywany w nieskończoność, który pobiera temperaturę i wyświetla wartość na wyświetlaczu. Dzięki przechwyceniu wyjątku jest odporny na błędy transmisji (np. wyciągnięcie kabelka sygnałowego).

for (;;)
{
    float currentTemp = 0;
    string exceptionMsg = String.Empty;
    try
    {
        currentTemp = tempDev.GetTemperature();
    }
    catch(IOException ex)
    {
        exceptionMsg = ex.Message;
    }

    if (exceptionMsg.Length == 0)
        display.ShowTemperature(currentTemp);
    else
        display.ShowError(exceptionMsg);

    Thread.Sleep(1000);
}

Jeśli wszystko mamy dobrze podłączone i .NET MF z OneWire w STM32F4Discovery, to otrzymamy w oknie debug informacje o aktualnej temperaturze:

Temperatura: 26.75 °C
Temperatura: 26.81 °C

Możemy też skonstruować i dodać do programu drugi wyświetlacz - na diodach LED, które znajdują się na płytce. Każda kolejna dioda będzie się zapalać po przekroczeniu określonej temperatury. Najciekawszym elementem klasy jest chyba konstruktor, w którym określamy progi zapalania się poszczególnych diod oraz procedura realizująca wyświetlanie temperatury.
public FourLedDisplay()
{
    var blueLed = new OutputPort(Stm32F4Discovery.LedPins.Blue, false);
    var greenLed = new OutputPort(Stm32F4Discovery.LedPins.Green, false);
    var orangeLed = new OutputPort(Stm32F4Discovery.LedPins.Orange, false);
    var redLed = new OutputPort(Stm32F4Discovery.LedPins.Red, false);

    _ranges = new[]
                  {
                      new LedRange(blueLed, 20),
                      new LedRange(greenLed, 25),
                      new LedRange(orangeLed, 30),
                      new LedRange(redLed, 35)
                  };
}

public void ShowTemperature(float temperature)
{
    if(_timer != null)
        _timer.Change(Timeout.Infinite, BlinkPeriod);

    foreach (LedRange range in _ranges)
        range.Check(temperature);
} 

Nowy wyświetlacz musimy jeszcze tylko dodać do listy w konstruktorze klasy Display.
public Display()
{
    var debugDisplay = new DebugDisplay();
    var ledDisplay = new FourLedDisplay();
    _displays = new IDisplay[] {debugDisplay, ledDisplay};
}

Pełny kot: DemoDS18B20

21.08.2012

OneWire i STM32F4Discovery - mówisz i masz

Kompilacja PK opanowana. Czas dodać 1-Wire. Bułka z masłem. Żeby się za dużo nie narobić trzeba zlikwidować błędy w PK (wersja QFE2). Otwieramy (może być notatnik lub inny zaawansowany edytor xml) plik C:\MicroFrameworkPK_v4_2\Framework\Features\Analog_DA_HAL.libcatproj. Guid w linijce Guid zmieniamy z  54DABCD1-8C9D-485c-8C48-8ECEE7D27454 na

00E400EA-0018-00CA-B59A-A2B3F9586139

Jest to ten sam guid z linijki MFComponent z pliku C:\MicroFrameworkPK_v4_2\DeviceCode\Drivers\Stubs\Processor\stubs_DA\dotNetMF.proj. Po prostu w pliku Analog_DA_HAL.libcatproj jest pomylony.

Następnie otwieramy plik C:\MicroFrameworkPK_v4_2\Framework\Features\OneWire_PAL.libcatproj i w linijce z StubLibrary zmieniamy guid z 24E1C771-7E4F-471C-A85C-78D693C259B6 na

238A3F72-46C6-4267-88BE-D15C09594103

A ten guid odpowiada ProjGuid z pliku C:\MicroFrameworkPK_v4_2\DeviceCode\pal\OneWire\Stubs\dotNetMF.proj (stubs to nic innego jak zaślepki). Nie wiem skąd się wziął w oryginale guid od zaślepki piezo? (C:\MicroFrameworkPK_v4_2\DeviceCode\pal\piezo\stubs\dotNetMF.proj).

W tym samym pliku zmieniamy jeszcze jeden guid. W linijce FeatureAssociations jest guid C9D30638-8B83-42B7-90A6-C96899B185F1 projektu SD (z pliku C:\MicroFrameworkPK_v4_2\Framework\Features\SD.featureproj) zamiast guidu

3401dd69-cab5-45fc-a759-1d1eded247c7

z projektu C:\MicroFrameworkPK_v4_2\Framework\Features\OneWire.featureproj.

Uff! Najgorsze za nami. Odpalamy program C:\MicroFrameworkPK_v4_2\tools\bin\SolutionWizard\SolutionWizard.exe i nic nie zmieniamy...

... aż do tego miejsca. Tutaj zaznaczamy OneWire.
Wygląd jak na obrazku poniżej oznacza, że wszystko jest ok. Tutaj zaznaczmy "Show All Choices" i ...
... patrzymy (lub zaznaczmy) czy jest wybrany dobry pal.
To wszystko. SolutionWizard zmienił dokładnie 2 pliki: C:\MicroFrameworkPK_v4_2\Solutions\Discovery4\TinyBooter\TinyBooter.proj (uzupełnił stub od analaog_DA) oraz C:\MicroFrameworkPK_v4_2\Solutions\Discovery4\TinyCLR\TinyCLR.proj (dodał wpisy od OneWire). Kto ciekawy co się zmieniło niech sobie porówna. Kompilujemy PK dokładnie tak samo jak wcześniej opisałem. W wyniku otrzymujemy pliki:
Wgrywamy je na płytkę przy pomocy ST_LINK i MFDeploy, tak jak opisałem wcześniej. Czas na test. Uruchamiamy ponownie krótki programik:
var op = new OutputPort(Stm32F4Discovery.FreePins.PA15, false);
var ow = new OneWire(op);

ArrayList devices = ow.FindAllDevices();
Debug.Print("Found " + devices.Count);
Wynik w postaci: Found 0 bez żadnego wyjątku to bardzo dobry znak! OneWire powinno działać bez problemu. Montujemy więc szybko układ z czujnikiem temperatury DS18B20.

1-wire stm32f4discovery
Uruchomienie programu takiego jak powyżej pokaże Found 1. OneWire działa poprawnie.

Jeden kabelek USB

Denerwujące jest podłączanie dwóch kabelków USB do STM32F4Discovery. Teoretycznie USB CN1 jest potrzebne tylko podczas wgrywania pliku tinybooter za pomocą ST-LINK. Praktycznie zawsze musi być podpięte, bo z tego gniazda brane jest zasilanie. Ale istnieje na to sposób. Wystarczy podłączyć PA9 do +5V, jak na rysunku poniżej:
stm32f4discovery
Teraz wystarczy, że będziemy podpinać USB CN5 i wszystko powinno ładnie działać. Nie wiem tylko czy podłączenie na raz CN1 i CN5 jest bezpieczne. Na wszelki wypadek ja wpinam albo CN1 albo CN5 - nigdy obydwa, bo i nie mam na razie takiej potrzeby.

20.08.2012

Kompilacja Porting Kit dla STM32F4Discovery

Postanowiłem skompilować PK. Na razie czysty, bez żadnych zmian, aby nabrać wprawy i zobaczyć jak to wszystko pójdzie.

Podstawową sprawą jest użycie do kompilacji Keil MDK-ARM w wersji Standard. Dla wersji 4.12 na 100% poprawnie się wszystko skompiluje. Program instalujemy na dysku C, reszta standardowo. Powinna powstać taka struktura katalogów: C:\Keil\ARM. Następnie trzeba ściągnąć i zainstalować Porting Kit 4.2 (RTM QFE2). PK również trzeba zainstalować na dysku C (powstanie katalog C:\MicroFrameworkPK_v4_2). Teraz trzeba ściągnąć NETMF for STM32 (F4 Edition) Release 4.2 QFE2 RTM i wypakować wszystko jak idzie do katalogu PK, tak aby nadpisać lub dodać nowe pliki i katalogi. Nas interesuje katalog C:\MicroFrameworkPK_v4_2\Solutions\Discovery4.

PK jest przygotowane do kompilacji. Otwieramy okno konsoli cmd i przechodzimy do katalogu C:\MicroFrameworkPK_v4_2. Uruchamiamy polecenie:  setenv_mdk 4.12
Teraz można odpalić właściwą kompilację poleceniem:

msbuild /p:flavor=DEBUG;memory=FLASH Solutions\Discovery4\dotNetMF.proj

Zamiast DEBUG można podać RELEASE - zależy którą wersję chce się otrzymać. Rozpocznie się kompilacja.
Na koniec zobaczymy podsumowanie. Nie powinno być żadnych błędów.
Interesujące są 3 pliki:
  1. C:\MicroFrameworkPK_v4_2\BuildOutput\THUMB2FP\MDK4.12\le\FLASH\debug\Discovery4\bin\Tinybooter.hex
  2. C:\MicroFrameworkPK_v4_2\BuildOutput\THUMB2FP\MDK4.12\le\FLASH\debug\Discovery4\bin\tinyclr.hex\ER_CONFIG
  3. C:\MicroFrameworkPK_v4_2\BuildOutput\THUMB2FP\MDK4.12\le\FLASH\debug\Discovery4\bin\tinyclr.hex\ER_FLASH
Pliki wgrywamy standardowo, zgodnie z opisem w: Przygotowanie STM32F4Discovery do pracy.
Jeśli kompilujemy od nowa trzeba usunąć katalog C:\MicroFrameworkPK_v4_2\BuildOutput.