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

Brak komentarzy:

Prześlij komentarz