18.02.2014

Wejście analogowe

Obsługa wejść analogowych w NET MF jest bardzo prosta. Na STM32F4Discovery mamy dostępne 7 wejść analogowych, wszystkie mają rozdzielczość 12 bitów (0...4096):

Cpu.AnalogChannel.ANALOG_0: PA1
Cpu.AnalogChannel.ANALOG_1: PA2
Cpu.AnalogChannel.ANALOG_2: PA3
Cpu.AnalogChannel.ANALOG_3: PB0
Cpu.AnalogChannel.ANALOG_4: PB1
Cpu.AnalogChannel.ANALOG_5: PC4
Cpu.AnalogChannel.ANALOG_6: PC5

Do testów możemy po prostu do wejścia podłączyć potencjometr. Ja akurat miałem o wartości 10K, ale w zasadzie każdy może być powiedzmy w zakresie od 1K do 100K.

STM32F4Discovery AnalogInput PotencjometrSTM32F4Discovery AnalogInput Potencjometr

Inicjalizacja i użycie jest bardzo proste. Odczytujemy wartości bezpośrednio z wejścia. Kręcimy potencjometrem zmieniając wartość napięcia na wejściu w zakresie od 0 do 3V:

using (var analogInput = new AnalogInput(Cpu.AnalogChannel.ANALOG_0))
{
    for (;;)
    {
        double readVal = analogInput.Read();
        int rawVal = analogInput.ReadRaw();
        Debug.Print("Sample: " + readVal + " (" + rawVal + ")");

        Thread.Sleep(5000);
    }
}

Jak widać powyżej mamy dwie funkcje do odczytu: ReadRaw i Read. Funkcja ReadRaw zwraca wartość nieprzetworzoną (u nas w zakresie od 0 do 4096), a Read wartość przeskalowaną i przesuniętą (przetworzoną). Szczególnie druga funkcja jest bardzo użyteczna. Na przykład, aby otrzymać wartość napięcia na wejściu analogowym (od 0 do +3V) wystarczy ustawić następujące parametry:

using (var analogInput = new AnalogInput(Cpu.AnalogChannel.ANALOG_0))
{
    analogInput.Scale = 3;  //+3V
    analogInput.Offset = 0; //od 0

    for (;;)
    {
        double readVal = analogInput.Read();
        Debug.Print("U=" + readVal);
        Thread.Sleep(1000);
    }
}

Prawda, że proste? Parametr Offset pozwala przesunąć pozycję zerową. Na przykład gdyby pozycja środkowa potencjometru była dla nas wartością zerową i chcielibyśmy, aby wartości były z przedziału -100 w jednym skrajnym położeniu i +100 w drugim skrajnym położeniu (-100%..+100%), to trzeba ustawić Scale na 200, a Offset na -100.

Jeszcze jeden przykład. Sterowanie jasnością świecenia diody (przez PWM) w zależności od ustawienia potencjometru:

var analog = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);
analog.Scale = 100; //brightness: 0..100%

var pwm = new PWM(Cpu.PWMChannel.PWM_0, 300, 0, false);
pwm.Start();

double prev = Double.MinValue;
for (;;)
{
    double curr = analog.Read();

    if (Math.Abs(curr - prev) >= 1)
    {
        pwm.DutyCycle = ToDutyCycle(curr);
        prev = curr;
    }
}

Funkcja przeliczająca oczekiwaną jasność na wypełnienie taka jak w poprzednim wpisie o PWM:

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

    if (brightness > 99)
        return 1;

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

A niech będzie jeszcze jeden przykład, z użyciem interfejsu sieciowego, bo na razie nie było żadnego. Potencjometr przez wejście analogowe będzie sterował głośnością w XBMC uruchomionym na innym komputerze. Oczywiście trzeba w XBM włączyć Webserver, będziemy zdalnie wywoływać JSON-RPC_API.

Najważniejsza jest funkcja wysyłająca odpowiednio spreparowany request do XBMC. Trzeba jedynie pamiętać o tym, że wysyłając żądanie POST musimy ustawić odpowiednio ContentType, no i nie zapomnieć o użytkowniku i haśle do serwisu XBMC.

private static void SendXbmcVolume(int volume)
{
    const string xbmcHost = "192.168.1.2";
    const string xbmcPort = "8081";
    const string xbmcUser = "xbmc";
    const string xbmcPassword = "xbmc";

    string json = "{\"jsonrpc\": \"2.0\", " +
                    "\"method\": \"Application.SetVolume\", " +
                    "\"params\": {\"volume\": " + volume + "}, " +
                    "\"id\": 1}";

    const string xbmcRpc = @"http://" + xbmcHost + ":" + xbmcPort + "/jsonrpc";

    using (var request = (HttpWebRequest) WebRequest.Create(xbmcRpc))
    {
        byte[] content = Encoding.UTF8.GetBytes(json);

        request.Method = "POST";
        request.ContentType = "application/json";
        request.Accept = "application/json";
        request.ContentLength = content.Length;
        request.KeepAlive = false;
        request.Credentials = new NetworkCredential(xbmcUser,
                                                    xbmcPassword,
                                                    AuthenticationType.Basic);

        using (var stream = request.GetRequestStream())
            stream.Write(content, 0, content.Length);
    }
}

Wywołanie powyższej funkcji można zrealizować tak:

using (var analogInput = new AnalogInput(Cpu.AnalogChannel.ANALOG_0))
{
    analogInput.Scale = 100;

    double prevVal = Double.MinValue;
    for (;;)
    {
        double currentVal = analogInput.Read();

        if (Math.Abs(currentVal - prevVal) >= 1)
        {
            int volume = ToVolume(currentVal);
            SendXbmcVolume(volume);
            prevVal = currentVal;
        }
    }
}

Pozostaje jeszcze funkcja ToVolume. Dostosowuje ona wartość podawaną z potencjometru do logarytmicznej charakterystyki ucha ludzkiego. Funkcję naprędce wymyśliłem, aby tylko miała charakterystykę logarytmiczną, więc może nie być w pełni poprawna. Ale działa!
private static int ToVolume(double volume)
{
    double loud = 50*Math.Log10(volume);

    if (loud < 0)
        return 0;

    if (loud > 100)
        return 100;

    return (int) Math.Round(loud);
}

Pełny kot: DemoAnalogInput

17.02.2014

Wersja .NET MF 4.3 RTM (QFE1)

Dokładnie tydzień temu została opublikowana wersja QFE1 .NET Micro Framework 4.3. Najważniejsze zmiany to:
  •  poprawna kompilacja projektów zawierających kropkę w nazwie
  • dodanie BitConverter
  • poprawki w StringBuilder.Replace, Array.BinarySearch i klasie Uri

4.02.2014

Prawdziwy ethernet na ENC28J60

Może i mIP jest dobre na początek, ale na dłuższą metę nie da się tego używać. Dlatego postanowiłem udostępnić (na razie) skompilowaną wersję CLR dla STM32F4Discovery z obsługą ethernetu na ENC28J60. Od razu ostrzegam: nie wiem czy wszystko poprawnie chodzi. Pliki źródłowe udostępnię w późniejszym czasie.

Wszystko trzeba połączyć tak jak w opisie dla mIP: Ethernet na ENC28J60. Za pomocą jakiegoś kota z mIP można też sprawdzić czy wszystko jest dobrze połączone. Jeśli tak, to teraz wystarczy wgrać ER_CONFIG i ER_FLASH. Pliki znajdują się tutaj: OneWire+SDCard+Enc28j60.

No dobra. Szybkie sprawdzenie czy wszystko jest ok. Wyświetlamy informacje o dostępnych interfejsach i włączamy DHCP, tak aby automatycznie pobierać adres IP.

NetworkInterface[] eths =  NetworkInterface.GetAllNetworkInterfaces();
if(eths.Length == 0)
{
    Debug.Print("Ethernet error");
    return;
}

NetworkInterface networkInterface = eths[0];
networkInterface.EnableDhcp();

Debug.Print("Ethernet ok");

Mała dygresja na temat EnableDhcp. Po użyciu tej funkcji (jeśli wcześniej był statyczny IP, a taki jest domyślnie ustawiony) potrzebny jest reset płytki. Funkcja ta zmienia parametr w konfiguracji, a nie w czasie rzeczywistym odświeża adres z DHCP. Równie dobrze możemy to zrobić jednorazowo z MFDeploy w menu Target->Configuration->Network. Może jest jakaś poprawka na takie zachowanie tej funkcji, ale na razie nie sprawdzałem.
STM32F4Discovery network

3.02.2014

Przygotowanie do pracy raz jeszcze

Na stronie głównej projektu NET MF for STM32 nie znajdziemy już plików potrzebnych do uruchomienia STM32F4Discovery opisanych w Przygotowanie STM32F4Discovery do pracy z NETMF. Możemy je za to pobrać z wersji historycznej strony głównej: http://netmf4stm32.codeplex.com/wikipage?version=29