22.05.2013

Pozytywka z PWM

Znalazłem jeszcze jedno zastosowanie PWM, dosyć rozrywkowe. Do wyjścia PWM można podłączyć głośniczek i odtwarzać proste melodie. Najpierw schemat:

STM32F4 Speaker

STM32F4Discovery Speaker
Rezystor może być inny, ale nie mniejszy niż 110, 120 omów. Z wyjścia STM32F407 można pobierać tylko 25mA prądu. Biorąc pod uwagę rezystancję głośniczka mamy równanie: 3V/(8ohm + 120ohm) =  0,023A. Tak więc przy tej rezystancji zmieścimy się w zakresie.

Teraz wystarczy tylko poszukać jakie częstotliwości odpowiadają poszczególnym dźwiękom i można odtworzyć dźwięki gamy:

//C, D, E, F, G, A, H, C 
var scale = new[]{261.6, 293.7, 329.2, 349.6, 
                  391.9, 440.0, 493.9, 523.2}; 
var pwm4 = new PWM(Cpu.PWMChannel.PWM_4, 50, 0, false);
pwm4.Start();

foreach (double note in scale)
{
    pwm4.Frequency = note;
    pwm4.DutyCycle = 0.5;
    Thread.Sleep(1000);
}

pwm4.Stop();


Aby zagrać jakąś prostą melodię trzeba się nieźle nawpisywać tych częstotliwości. Z pomocą przychodzi format RTTL lub MML. Na internecie można znaleźć mnóstwo melodyjek zapisanych w tych formatach.

Konstrukcję odtwarzacza RTTL rozpoczynamy od zdefiniowania interfejsu ISpeaker. Interfejs da możliwość dowolnej implementacji urządzenia wyjściowego. Jak widać są tylko dwie metody Play i Pause. Czyli to co głośniczek najlepiej umie robić: albo milczeć albo grać.

public interface ISpeaker
{
    void Pause();
    void Play(double frequency);
}

Teraz implementacja tego interfejsu na wyjściu PWM z podłączonym głośniczkiem:

public class PwmSpeaker : ISpeaker
{
    private readonly PWM _pwm;

    public PwmSpeaker(PWM pwm)
    {
        _pwm = pwm;
        _pwm.Frequency = 50;
        _pwm.DutyCycle = 0;
        _pwm.Start();
    }
            
    public void Play(double frequency)
    {
        _pwm.Frequency = frequency;
        _pwm.DutyCycle = 0.5;
    }

    public void Pause()
    {
        _pwm.Frequency = 50;
        _pwm.DutyCycle = 0;
    }
}

Przechodzimy do odtwarzacza RTTL. Całe zadanie polega na zdekodowaniu napisu w formacie RTTL na poszczególne częstotliwości dźwięków i czas ich trwania. Później trzeba to wysłać do ISpeakera.

Do dekodowania formatu RTTL na bardziej przyjazne dane służy statyczna metoda Parse klasy Rttl:

public static Rttl Parse(string rttlData)
{
    string[] sections = rttlData.Split(':');

    string name = sections[0].Trim();
    string[] tones = sections[2].Split(',');
    int duration = 4;
    int octave = 6;
    int bpm = 63;

    if (sections[1].Length > 0)
    {
        string[] controls = sections[1].Split(',');
        foreach (string item in controls)
        {
            string control = item.Trim();

            string valueStr = control.Substring(2, control.Length - 2);
            int value = Int32.Parse(valueStr);

            switch (control[0].ToLower())
            {
                case 'd':
                    duration = value;
                    break;

                case 'o':
                    octave = value;
                    break;

                case 'b':
                    bpm = value;
                    break;
            }
        }
    }

    var result = new Rttl
                        {
                            Name = name,
                            Tones = tones,
                            Duration = duration,
                            Octave = octave,
                            Bpm = bpm
                        };
    return result;
}

Jak już mamy wszystkie potrzebne dane to trzeba znaleźć tylko odpowiednie częstotliwości. Klasa RttlPlayer ma zdefiniowane tablice z częstotliwościami poszczególnych dźwięków. Wartości wypełnione są tylko dla pierwszej oktawy. Pozostałe oktawy zostaną uzupełnione poprzez podwojenie częstotliwości z oktawy poprzedzającej w statycznym konstruktorze.

private static readonly double[][] Scales = new[]
                                                {
                                                    new[]
                                                        {
                                                            //C, Cis, D, Dis, E, F, Fis, G, Gis, A, Ais, H 
                                                            261.6, 277.2, 293.7, 311.2, 329.2,
                                                            349.6, 370, 391.9, 415.3, 440.0, 466.2, 493.9
                                                        },
                                                    new double[12],
                                                    new double[12],
                                                    new double[12]
                                                };

static RttlPlayer()
{
    for (int i = 1; i < Scales.Length; i++)
        for (int j = 0; j < Scales[i].Length; j++)
            Scales[i][j] = 2*Scales[i - 1][j];
}

No teraz to wystarczy wszystko połączyć w całość w metodzie Play RttlPlayera.

public void Play(string rttlData)
{
    const int oneBpmWholeNote = 60*4*1000; //one bpm whole note in ms

    Rttl rttl = Rttl.Parse(rttlData);
    int wholeNote = oneBpmWholeNote/rttl.Bpm;

    foreach (string tone in rttl.Tones)
    {
        bool specialDuration;
        string durationStr, noteStr, scaleStr;
        ParseCommand(tone, out durationStr, out noteStr, out scaleStr, out specialDuration);

        int duration = rttl.Duration;
        if (durationStr.Length > 0)
            duration = Int32.Parse(durationStr);

        duration = specialDuration ? (3*wholeNote)/(2*duration) : wholeNote/duration;

        int freqIndex;
        switch (noteStr[0].ToLower())
        {
            case 'c':
                freqIndex = 0;
                break;

            case 'd':
                freqIndex = 2;
                break;

            case 'e':
                freqIndex = 4;
                break;

            case 'f':
                freqIndex = 5;
                break;

            case 'g':
                freqIndex = 7;
                break;

            case 'a':
                freqIndex = 9;
                break;

            case 'b':
                freqIndex = 11;
                break;

            default:
                freqIndex = -1;
                break;
        }

        if (noteStr.Length > 1)//#
            freqIndex++;

        int scale = rttl.Octave;
        if (scaleStr.Length > 0)
            scale = Int32.Parse(scaleStr);

        if (freqIndex >= 0)
        {
            double freq = Scales[scale - 4][freqIndex];

            Debug.Print("Playing: (" + tone + ")" + freq + " " + duration);
            _speaker.Play(freq);
            Thread.Sleep(duration);
            _speaker.Pause();
        }
        else
        {
            Debug.Print("Pausing: (" + tone + ") " + duration);
            _speaker.Pause();
            Thread.Sleep(duration);
        }
    }
}

A tak można odtwarzać melodyjki:

var pwm = new PWM(Cpu.PWMChannel.PWM_4, 50, 0, false);
var player = new RttlPlayer(new PwmSpeaker(pwm));
const string song = "Indiana:d=4,o=5,b=250:e,8p,8f,8g,8p,1c6,8p.,d,8p,8e,1f,p.,g,8p,8a,8b,8p,1f6,p,a,8p,8b,2c6,2d6,2e6,e,8p,8f,8g,8p,1c6,p,d6,8p,8e6,1f.6,g,8p,8g,e.6,8p,d6,8p,8g,e.6,8p,d6,8p,8g,f.6,8p,e6,8p,8d6,2c6";
player.Play(song);

Przykład odtwarzania formatu MTL można znaleźć w projekcie .NET Micro Framework Toolbox.

5.05.2013

PWM

Dzisiaj postanowiłem się pobawić PWM. W PWM chodzi o to, że sterujemy wypełnieniem sygnału prostokątnego przy stałej częstotliwości tego sygnału. Jak to wygląda na obrazkach można zobaczyć tutaj: http://arduino.cc/en/Tutorial/PWM. Na płytce STM32F4Discovery mamy dostępne 8 wyjść PWM:

PWMChannel.PWM_0: PD12 //GREEN LED
PWMChannel.PWM_1: PD13 //ORANGE LED

PWMChannel.PWM_2: PD14 //RED LED
PWMChannel.PWM_3: PD15 //BLUE LED
PWMChannel.PWM_4: PE9
PWMChannel.PWM_5: PE11
PWMChannel.PWM_6: PE13
PWMChannel.PWM_7: PE14


Najlepsze jest to, że pierwsze cztery wyjścia kanałów PWM to diody znajdujące się na płytce, więc aby zobaczyć jakieś efekty nic nie musimy podłączać.

Częstotliwość sygnału i jego wypełnienie możemy regulować na dwa sposoby: ustawiając częstotliwość (właściwość Frequency) i procentowe wypełnienie (właściwość DutyCycle) lub czas jednego okresu sygnału (właściwość Period) i czas trwania stanu wysokiego (właściwość Duration). Tutaj mała uwaga. Ustawiając DytyCycle wpisujemy procenty w postaci ułamka czyli jeśli chcemy 21% to ustawiamy 0.21. Natomiast ustawiając Period i Duration jednostkę wskazujemy we właściwości Scale (nano, mili, mikro sekundy). Przykład - dioda zielona mruga raz na sekundę:

var pwm0 = new PWM(Cpu.PWMChannel.PWM_0, 1, 0.5, false);
pwm0.Start();

No dobra, a czy można równocześnie ustawić czerwoną diodę żeby mrugała 2 razy szybciej? Chyba nic prostszego:

var pwm0 = new PWM(Cpu.PWMChannel.PWM_0, 1, 0.5, false);
pwm0.Start();

var pwm2 = new PWM(Cpu.PWMChannel.PWM_2, 2, 0.5, false);
pwm2.Start();

I co? Kicha! Zielona zaczęła mrugać tak samo jak czerwona. Popatrzymy do pliku platform_selector.h w katalogu Solutions\Discovery4 PK:

#define STM32F4_PWM_TIMER {4,4,4,4,1,1,1,1}
#define STM32F4_PWM_CHNL  {0,1,2,3,0,1,2,3}
#define STM32F4_PWM_PINS  {60,61,62,63,73,75,77,78} // D12-D15,E9,E11,E13,E14

Okazuje się, że pierwsze 4 wyjścia PWM obsługuje ten sam timer (cztery kolejne zresztą też, ale inny). Jeśli jest to ten sam timer to wszystkie wyjścia PWM działają z takim samym okresem. Przestawienie okresu (nie ważne czy robi się to przez właściwość Period czy Frequency) na inną wartość dla jednego wyjścia będzie miało wpływ na pozostałe kanały. Dodatkowo trzeba pamiętać, że DutyCycle nie pozostanie stałe przy zmianie częstotliwości. Czyli jeśli chcemy, aby wypełnienie było stałe na poziomie 50% to zmieniając częstotliwość trzeba równocześnie przypisać wartość 0.5 do DutyCycle. Trzeba o tym pamiętać szczególnie w pętlach, gdzie zmieniana jest częstotliwość lub okres. (http://netmf.codeplex.com/workitem/1749)


Jednym z zastosowań PWM jest regulacja jasności świecenia diody LED. Małe wypełnienie - dioda słabo świeci, duże wypełnienie - doda jasno świeci. Może to być wykorzystane do regulacji podświetlenia w wyświetlaczach. Prosty programik, który rozjaśnia i ściemnia diodę czerwoną:

public static void Main()
{
    var pwm2 = new PWM(Cpu.PWMChannel.PWM_2, 300, 0, false);
    pwm2.Start();

    const int minBright = 0;
    const int maxBright = 100;
    int bright = minBright;
    int step = 1;

    while (true)
    {
        pwm2.DutyCycle = ToDutyCycle(bright);

        bright += step;
        if (bright > maxBright || bright < minBright)
        {
            bright = bright > maxBright ? maxBright : minBright;
            step = -step;

            Thread.Sleep(1000);
        }

        Thread.Sleep(40);
    }
}

private static double ToDutyCycle(double brightness)
{
    return brightness/100;
}

Jak widać kot nie jest skomplikowany. Przelatujemy w pętli od 0 do 100 (to jasność) i przypisujemy wprost proporcjonalnie wypełnienie (dzielimy przez 100, aby otrzymać wartości w procentach: 0..1). Migotanie diody zostało zniwelowane poprzez ustawienie częstotliwości na 300 Hz tak, aby wykorzystać bezwładność oka ludzkiego. Przy mniejszych wartościach niektórzy ludzie mogą dostrzegać migotanie. Program jednak nie działa tak jak trzeba. Dioda bardzo szybko się rozjaśnia i bardzo jasno świeci przez większość czasu. Winę za to ponosi nieliniowa charakterystyka oka ludzkiego. Dlatego musimy skorygować wypełnienie.

Spotkałem dwie "szkoły" takiej korekcji. Jedna używa funkcji wykładniczej o podstawie e (podstawa logarytmu naturalnego), a druga funkcji wykładniczej o podstawie 10 (podstawa logarytmu dziesiętnego). Gdzieś wyczytałem, że oko ludzkie ma charakterystykę logarytmiczną o podstawie 10 więc druga może jest bardziej adekwatna. W praktyce moje oko nie zauważyło różnicy między nimi. Wykres poniżej przedstawia obie funkcje:
pwm brightness dimming curve
A tak trzeba je zapisać w kocie:
private static double ToDutyCycleExp(double brightness)
{
    if (brightness < 1)
        return 0;

    if (brightness > 99)
        return 1;

    return (0.383*System.Math.Exp(0.0555*brightness) - 0.196)/100;
}

private static double ToDutyCycle10(double brightness)
{
    if (brightness < 1)
        return 0;

    if (brightness > 99)
        return 1;

    return System.Math.Pow(10, (2.55*brightness - 1)/84.33 - 1)/100;
}

20.04.2013

Źródła PK bez instalacji MSI

Kod budzi się po zimowym letargu. Wiosna pozytywnie wpływa na chęci, a i czasu jest trochę więcej. Na początek coś prostego dla wprawy.

Często zachodzi potrzeba użycia czystych źródeł z różnych wersji PK. Jak je uzyskać z oryginalnych instalatorów, ale bez instalacji? Uruchamiamy msiexec w trybie instalacji administracyjnej na przykład poleceniem:

msiexec /a plik.msi /qb TARGETDIR=katalog

Przykład. Aby uzyskać źródła Porting Kit 4.2 z pliku MicroFrameworkPK.msi wykonujemy polecenie:

msiexec /a MicroFrameworkPK.MSI /qb TARGETDIR=c:\

Pliki zostaną wypakowane do katalogu c:\MicroFrameworkPK_v4_2

14.12.2012

Karta SD. Przygotowanie i kompilacja PK NET MF

Jak połączenia już mamy zrobione to teraz trzeba przygotować odpowiednio porting kit i skompilować. Aha. Kartę SD wcześniej formatujemy z życiem FAT32. Możemy też utworzyć na niej jakieś pliki. Najlepiej tekstowe. Kartę od razu wkładamy w holder. Przykład modyfikacji i kompilacji dla .NET Micro Framework PK 4.2 QFE2. W innej wersji PK powinno być podobnie.

Uruchamiamy SolutionWizard. Do czego służy i mniej więcej jak korzystać pokazałem wcześniej. Wybieramy modyfikację projektu Discovery4 i nic nie zmieniamy, aż do ekranu "Feature Selection". Tutaj zaznaczamy: "FileSystem" i "SD". A na następnym ekranie "Generate Template" dla tych dwóch nowych funkcjonalności. Dalej standardowo.

SolutionWizard- add FSSolutionWizard - add SD Card
 SolutionWizard - generate template

SolutionWizard zrobił dwie rzeczy w katalogu naszej solucji (C:\MicroFrameworkPK_v4_2\Solutions\Discovery4). Zmodyfikował plik TinyCLR\TinyCLR.proj oraz utworzył katalogi: DeviceCode\FileSystem_Config_PAL i DeviceCode\SD_Config_HAL. W pliku TinyCLR.proj dodał funkcjonalności: Stream, FileSystem i SD oraz odpowiednie drivery: Stream, SPOT_IO, fs_pal, SD_BL, FS_FAT, FileSystem_Config_PAL_Discovery4, SD_Config_HAL_Discovery4. Wywalił zaś zaślepki fs_pal_stubs i FS_Config_stubs. Nas tak naprawdę interesują dodane katalogi.

Katalog DeviceCode\SD_Config_HAL zawiera dwa pliki: proj i cpp. Plików proj nie ruszamy. Plik cpp zawiera konfigurację drivera SD. Czyli np. gdzie jest podłączona karta (który SPI), z jaka częstotliwością odbywa się komunikacja, który pin to CS (ChipSelect) itp. Wprowadzamy tam modyfikacje.

W linii 30 dodajemy:

#pragma arm section rwdata = "g_SD_DeviceRegisters"

Tak aby cały ten blok pragma wyglądał tak:

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section rwdata = "g_SD_BS_Config"
#pragma arm section rwdata = "g_SD_DeviceRegisters"
#endif

W linii 81 (po definicji struct SD_BLOCK_CONFIG g_SD_BS_Config) dodajemy:

struct SD_DEVICE_REGISTERS g_SD_DeviceRegisters;

Tak aby cały blok definicji struct wyglądał tak:

struct SD_BLOCK_CONFIG g_SD_BS_Config =
{
    {
        SD_WP_GPIO_PIN,            // GPIO_PIN             Pin;
        SD_WP_ACTIVE,              // BOOL                 ActiveState;
    },

     &g_SD_DeviceInfo,             // BlockDeviceinfo
};

struct SD_DEVICE_REGISTERS g_SD_DeviceRegisters;

W linii 83 zaraz po definicjach struct zmieniamy pin z GPIO_PIN_NONE na PB12(dziesiętnie = 28), SD_MSK_SAMPLE_EDGE na TRUE (dane na zboczu narastającym zegara), częstotliwość zegara, oraz z którego interfejsu SPI ma korzystać (SPI1=0, SPI2=1, SPI3=2).

#define SD_CS                    (GPIO_PIN)28 // PB12
#define SD_MSK_SAMPLE_EDGE       TRUE // important
#define SD_CLOCK_RATE_KHZ        400 //check 10000
#define SD_MODULE                1 // SPI2

Tak aby cała konfiguracja wyglądała tak:

#define SD_CS                    (GPIO_PIN)28 // PB12
#define SD_CS_ACTIVE             FALSE
#define SD_MSK_IDLE              TRUE
#define SD_MSK_SAMPLE_EDGE       TRUE // important
#define SD_16BIT_OP              FALSE
#define SD_CLOCK_RATE_KHZ        400 //check 10000
#define SD_CS_SETUP_USEC         0
#define SD_CS_HOLD_USEC          0
#define SD_MODULE                1 // SPI2
#define SD_INSERT_ISR_PIN        GPIO_PIN_NONE
#define SD_EJECT_ISR_PIN         GPIO_PIN_NONE
#define SD_LOW_VOLTAGE_FLAG      FALSE

Częstotliwość zegara można zostawić na 400 kHz lub spróbować wyższą. U mnie karta chodzi bez problemów z częstotliwością 10000 kHz. Dodatkowo dla bezpieczeństwa można ustawić SD_CS_SETUP_USEC i SD_CS_HOLD_USEC na 1.

Katalog DeviceCode\FileSystem_Config_PAL również zawiera dwa pliki. I tak jak poprzednio modyfikujemy tylko cpp. Najpierw dodajemy obsługę FAT32.

W linii 10 po definicjach pragma dodajemy:

extern FILESYSTEM_DRIVER_INTERFACE g_FAT32_FILE_SYSTEM_DriverInterface;
extern STREAM_DRIVER_INTERFACE g_FAT32_STREAM_DriverInterface;

W linii 23 i 28 zmieniamy g_AvailableFSInterfaces i g_InstalledFSCount:

FILESYSTEM_INTERFACES g_AvailableFSInterfaces[] =
{
    { &g_FAT32_FILE_SYSTEM_DriverInterface, &g_FAT32_STREAM_DriverInterface },
};

const size_t g_InstalledFSCount = 1;


To nie wszystko. Trzeba jeszcze dodać kod odpowiedzialny za jej podmontowanie. Inaczej nie będziemy jej widzieli w .NET MF.

W linii 12 po dodanych liniach extern dodajemy:

extern struct BlockStorageDevice  g_SD_BS;
extern struct IBlockStorageDevice g_SD_BS_DeviceTable;
extern struct BLOCK_CONFIG        g_SD_BS_Config;

A w linii 23 ciało procedury FS_MountRemovableVolumes:

void FS_MountRemovableVolumes()
{
 if (BlockStorageList::AddDevice( &g_SD_BS, &g_SD_BS_DeviceTable, &g_SD_BS_Config, TRUE ));
 {
  FS_MountVolume( "SD", 0, 0, &g_SD_BS );  
 }
}

Uff. Cały plik FS_config_Discovery4.cpp ma wyglądać tak:

#include <tinyhal.h>

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section rwdata = "g_AvailableFSInterfaces"
#endif

extern FILESYSTEM_DRIVER_INTERFACE g_FAT32_FILE_SYSTEM_DriverInterface;
extern STREAM_DRIVER_INTERFACE g_FAT32_STREAM_DriverInterface;

extern struct BlockStorageDevice  g_SD_BS;
extern struct IBlockStorageDevice g_SD_BS_DeviceTable;
extern struct BLOCK_CONFIG        g_SD_BS_Config;

void FS_AddVolumes()
{
}

void FS_MountRemovableVolumes()
{
 if (BlockStorageList::AddDevice( &g_SD_BS, &g_SD_BS_DeviceTable, &g_SD_BS_Config, TRUE ));
 {
  FS_MountVolume( "SD", 0, 0, &g_SD_BS );  
 }
}

FILESYSTEM_INTERFACES g_AvailableFSInterfaces[] =
{
    { &g_FAT32_FILE_SYSTEM_DriverInterface, &g_FAT32_STREAM_DriverInterface },
};
const size_t g_InstalledFSCount = 1;

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section rwdata
#endif

Kompilujemy zgodnie z tym co napisałem wcześniej: Kompilacja Porting Kit dla STM32F4Discovery. Tutaj mała uwaga. Można śmiało poprawiać pliki i  kompilować bez usuwania katalogu C:\MicroFrameworkPK_v4_2\BuildOutput za każdym razem. Wówczas kompilacja przebiega znacznie szybciej. W wyniku kompilacji otrzymujemy pliki ER_CONFIG + ER_FLASH do wgrania tradycyjnie za pomocą MFDeploy.

Po zaprogramowaniu STM32F4Discovery kolejna próba. Uruchamiamy program napisany kilka postów wcześniej. Program normalnie się uruchomi, a VolumeInfo będzie jak na obrazku poniżej.


SD Card in NET MF on STM32F4Discovery

13.12.2012

Karta SD. Podłączenie sprzętu

Kartę SD będziemy podłączać do STM32F4Discovery przez interfejs SPI. Wyprowadzeń SD card nie będę rozpisywał, bo można sobie popatrzeć na wikipedi jak to wygląda. Przechodzimy do sedna. Użyjemy następujących wyprowadzeń karty:
  • 1 - CS (Card Select)
  • 2 - MOSI (Data IN)
  • 3 - GND (Ground)
  • 4 - VCC (Power)
  • 5 - SCL (Clock)
  • 6 - GND (Ground)
  • 7 - MISO(Data Out)
SD card + STM32F4DiscoveryPodłączanie karty na pająka czy lutowanie bezpośrednio do styków karty pozostawiam hardkorowcom. Ja użyłem holdera do karty SD kupionego za grosze. Przylutowałem do golpinów wyprowadzenia holdera i do testów powinno wystarczyć. Zastosowanie holdera ma jeszcze jedną zaletę. Przez specjalne ustawienie styków łączących, podczas wkładania i wyciągania karty, podłączanie i odłączanie zasilania i sygnałów danych odbywa się w odpowiedniej, bezpiecznej kolejności . 

Jak mowa o zasilaniu to miałem pewne obawy czy napięcie +3V (takie VDD ma płytka STM32F4 Discovery) wystarczy. Wszędzie piszą o zasilaniu z +3.3V. Popatrzyłem więc do dokumentacji SanDiska. Oni dopuszczają napięcie zasilające w zakresie: od +2.7V do +3.6V. Teraz z doświadczenia już wiem, że +3.0V w zupełności wystarczy. A używam karty Kongstona 128MB.

Ostatnie dwa piny holdera na razie nie są wykorzystywane. Przedostatni pin podłączony jest do styków sygnalizujących obecność karty, a ostatni do styków zabezpieczenia przed zapisem. W kartach SD to programista musi zadbać o rozpoznanie stanu blokady zapisu poprzez ten pin i odpowiednią jego obsługę.

Jak już wcześniej wspomniałem będziemy używać interfejsu SPI. Płytka STM32F4Discovery ma 3 interfejsy SPI. Piny drugiego z kolei portu: SPI2, są wolne więc użyjemy ich: PB13-SCL, PB14-MISO, PB15-MOSI, oraz PB12 jako CS. Schemat podłączenia poniżej:

SD card + NET MF + STM32F4Discovery


SD card + NET MF + STM32F4Discovery

12.12.2012

Karta SD w .NET MF na STM32F4Discovery

No oczywiście obsługi karty SD (SD card) nie mamy w porcie .NET Micro Framework dla STM32F4 Discovery. Sprawdzić to można prostym programem. Najpierw pobieramy obsługiwane systemy plików, a następnie odpytujemy o dostępne zasoby (wolumeny). Kot wygląda tak:

using Microsoft.SPOT;
using Microsoft.SPOT.IO;

public class Program
{
    public static void Main()
    {
        string[] availFs = VolumeInfo.GetFileSystems();
        if (availFs.Length == 0)
        {
            Debug.Print("No FS found");
            return;
        }

        foreach (string fs in availFs)
            Debug.Print("Available FS: " + fs);

        VolumeInfo[] volumes = VolumeInfo.GetVolumes();
        if(volumes.Length == 0)
        {
            Debug.Print("No volumes found");
            return;
        }

        foreach (VolumeInfo volume in volumes)
            Debug.Print("Volume: " + volume.Name);
    }
}

W wyniku dostaniemy wyjątek:

An unhandled exception of type 'System.NotSupportedException' occurred in Program.exe

Czyli na 100% nie mamy obsługi żadnego FAT, a co za tym idzie karty SD, w porcie NET MF dla STM32F4Discovery. Trzeba dodać FS i SD do solucji. 

Przez chwilę myślałem, że się nie da i miałem zrezygnować, ale teraz mogę powiedzieć, że karta SD działa w NET MF na STM32F4 Discovery poprawnie. I dodanie jej wcale nie jest trudne. Największym problemem dla takiego lamera jak ja było rozgryzienie co i jak trzeba poupychać w solucji.


8.12.2012

Brak komunikacji z VS (zdechły kot)

Czasami może się zdarzyć, że coś sknocimy w naszym programie i Visual Studio nie będzie się mógł połączyć z płytką STM32F4 Discovery. Mnie się to zdarzyło kilka razy podczas zabaw z portem USB. 

Co wówczas robimy? Wciskamy na płytce przycisk user button (to ten niebieski), przytrzymujemy go i naciskamy przycisk resetu (to ten czarny). Puszczamy oba przyciski. Powinny zapalić się trzy diody na płytce (bez niebieskiej). Oznacza to, że uruchomił się jedynie tinybooter, a nie nasz program. Teraz odpalamy MFDeploy i przyciskiem Erase usuwamy deployment. Proste?