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.
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