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