Kampis Elektroecke

IR-Empfänger für AVR

In diesem Artikel möchte ich zeigen, wie ein TSOP38238 IR-Empfänger zusammen mit einem AVR (oder einem anderen Mikrocontroller) genutzt werden kann um Daten einer Fernbedienung zu empfangen, um so den Mikrocontroller mittels IR-Fernbedienung zu steuern.

Grundlagen zur Kommunikation mittels Infrarot:

Für die Kommunikation mittels Infrarot werden verschiedene Verfahren kombiniert um so eine energiesparende und störungsarme Übertragung zu ermöglichen.

Für die Übertragungsstrecke (i. d. R. Luft) werden die einzelnen Informationen über eine Puls-Code-Modulation (PCM) mit einem 30 – 56 kHz Trägersignal moduliert und anschließend übertragen.

Durch die Modulation werden Übertragungsfehler reduziert und das System gegen den Einfluss von Tageslicht abgehärtet.

Bei den gesendeten Informationen handelt es sich um Daten, die in einem herstellerspezifischen Protokoll und mit einer bestimmten Codierung gesendet werden. Die Codierung bestimmt dabei, wie die Elemente einer Übertragung, in diesem Fall eine logische 0 oder eine logische 1, aussehen. Eine aufeinanderfolgende Serie von Pulsen wird Burst und eine aufeinanderfolgende Serie von Pausen wird Space genannt. 

Codierung Verlauf
Differentieller Manchester-Code
Puls-Distanz Code
Puls-Längen Code

Jede Übertragung wird mit einem Header, welcher aus einem Burst mit einer bestimmten Länge besteht, eingeleitet. Dieser Header wird für die automatische Verstärkungsregelung (Automatic Gain Control – AGC) des Empfängers genutzt wird.

Das implementierte Protokoll unterscheidet sich je nach Hersteller und viele der Funktionen sind nicht standardisiert. In diesem Artikel werde ich mich ausschließlich auf das Protokoll der Firma NEC beziehen. Für dieses Protokoll werden folgende Übertragungsparameter genutzt:

  • Puls-Längen Codierung
  • 38 kHz Trägersignal
  • Logische 0
    • 562,5 µs Burst
    • 562,5 µs Pause
  • Logische 1
    • 562,5 µs Burst
    • 1,6875 ms Pause

Dabei besteht eine komplette Nachricht immer aus den folgenden Komponenten:

  • Burst mit einer Länge von 9 ms als Header
  • Space mit einer Länge von 4,5 ms
  • 8-Bit Adresse des Empfängers
  • Invertierte Adresse
  • 8-Bit Kommando
  • Invertiertes Kommando
  • Burst mit einer Länge von 562,5 µs um das Ende einer Übertragung zu signalisieren

Für den Fall das ein Sender die gesendete Nachricht wiederholen möchte (z. B. falls bei einer Fernbedienung eine Taste länger gedrückt bleibt), wird nach dem Senden einer initialen Nachricht ein verkürzter Code, der sogenannte Repeat Code, gesendet. Dieser Code besteht aus den folgenden Komponenten:

  • Burst mit einer Länge von 9 ms
  • Space mit einer Länge von 2,25 ms
  • Burst mit einer Länge von 562,5 µs um das Ende einer Übertragung zu signalisieren

Typischerweise wird der Repeat Code 40 ms nach dem Ende der Nachricht gesendet und anschließend alle 108 µs wiederholt. In der Praxis sieht sieht das ganze dann folgendermaßen aus:

  • Gelb: Rohsignal, direkt mit von einem Photo-Transistor empfangen
  • Blau: Demoduliertes Signal aus dem IR-Empfänger

Implementierung des Protokolls auf dem Mikrocontroller:

Damit der Mikrocontroller gesendete IR-Nachrichten empfangen kann, wird ein geeigneter Empfänger benötigt. Bei der Auswahl des Empfängers ist darauf zu achten, dass der Empfänger für die verwendete Trägerfrequenz (hier 38 kHz) ausgelegt ist. Als Mikrocontroller soll ein XMega256A3BU verwendet werden, wobei der Empfänger am Pin 0 vom Port E angeschlossen wird.


Achtung:

Der Empfänger verwendet einen Open-Kollektor-Ausgang für die Datenübertragung. Dieser sorgt dafür, dass sämtliche Informationen invertiert werden!


Die Auswertung der Pulszeiten soll über den Timer D0 Timer erfolgen. Dazu wird der Timer so konfiguriert, dass dieser alle 50 µs einen Overflow-Interrupt erzeugen soll.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#define IR_TICK_INTERVAL 50
static Timer0_Config_t _IR_TimerConfig =
{
.Device = &TCD0,
.Prescaler = TIMER_PRESCALER_8
};
static Timer0_InterruptConfig_t _IR_TimerInterrupt =
{
.Device = &TCD0,
.Source = TIMER_OVERFLOW_INTERRUPT,
.InterruptLevel = INT_LVL_HI
};
static void _IR_Timer0OverflowCallback(void)
{
...
}
void IR_Init(void)
{
GPIO_SetDirection(GET_PERIPHERAL(IR_REC_INPUT), GET_INDEX(IR_REC_INPUT), GPIO_DIRECTION_IN);
_IR_TimerConfig.Period = F_CPU / ((((uint32_t)0x01) << (_IR_TimerConfig.Prescaler - 0x01)) * (1000000 / IR_TICK_INTERVAL));
_IR_TimerInterrupt.Callback = _IR_Timer0OverflowCallback
Timer0_Init(&_IR_TimerConfig);
Timer0_InstallCallback(&_IR_TimerInterrupt);
PMIC_EnableInterruptLevel(_IR_TimerInterrupt.InterruptLevel);
EnableGlobalInterrupts();
}
#define IR_TICK_INTERVAL 50 static Timer0_Config_t _IR_TimerConfig = { .Device = &TCD0, .Prescaler = TIMER_PRESCALER_8 }; static Timer0_InterruptConfig_t _IR_TimerInterrupt = { .Device = &TCD0, .Source = TIMER_OVERFLOW_INTERRUPT, .InterruptLevel = INT_LVL_HI }; static void _IR_Timer0OverflowCallback(void) { ... } void IR_Init(void) { GPIO_SetDirection(GET_PERIPHERAL(IR_REC_INPUT), GET_INDEX(IR_REC_INPUT), GPIO_DIRECTION_IN); _IR_TimerConfig.Period = F_CPU / ((((uint32_t)0x01) << (_IR_TimerConfig.Prescaler - 0x01)) * (1000000 / IR_TICK_INTERVAL)); _IR_TimerInterrupt.Callback = _IR_Timer0OverflowCallback Timer0_Init(&_IR_TimerConfig); Timer0_InstallCallback(&_IR_TimerInterrupt); PMIC_EnableInterruptLevel(_IR_TimerInterrupt.InterruptLevel); EnableGlobalInterrupts(); }
#define IR_TICK_INTERVAL			50

static Timer0_Config_t _IR_TimerConfig = 
{
    .Device = &TCD0,
    .Prescaler = TIMER_PRESCALER_8
};

static Timer0_InterruptConfig_t _IR_TimerInterrupt =
{
    .Device = &TCD0,
    .Source = TIMER_OVERFLOW_INTERRUPT,
    .InterruptLevel = INT_LVL_HI
};

static void _IR_Timer0OverflowCallback(void)
{
    ...
}

void IR_Init(void)
{
    GPIO_SetDirection(GET_PERIPHERAL(IR_REC_INPUT), GET_INDEX(IR_REC_INPUT), GPIO_DIRECTION_IN);

    _IR_TimerConfig.Period = F_CPU / ((((uint32_t)0x01) << (_IR_TimerConfig.Prescaler - 0x01)) * (1000000 / IR_TICK_INTERVAL)); 
    _IR_TimerInterrupt.Callback = _IR_Timer0OverflowCallback
    Timer0_Init(&_IR_TimerConfig);
    Timer0_InstallCallback(&_IR_TimerInterrupt);
    PMIC_EnableInterruptLevel(_IR_TimerInterrupt.InterruptLevel);
    EnableGlobalInterrupts();
}

In der ISR vom Overflow-Interrupt wird der Zustand des Empfängerpins eingelesen und ein Zähler inkrementiert und über einen Zustandsautomaten ausgewertet.

Zuerst wird der Zustandsautomat initialisiert, indem die Funktion

IR_GetMessage
IR_GetMessage aufgerufen wird:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bool IR_GetMessage(IR_Message_t* Message)
{
_IR_MessageBuffer = Message;
_IR_MessageBuffer->Valid = false;
_IR_TimerTicks = 0x00;
_IR_TimeOutTicks = 0x00;
_IR_RecState = IR_STATE_IDLE;
while(!_IR_MessageBuffer->Valid)
{
if(_IR_TimeOutTicks > (IR_TIMEOUT / IR_TICK_INTERVAL))
{
_IR_RecState = IR_STATE_STOP;
_IR_MessageBuffer->Valid = false;
return _IR_MessageBuffer->Valid;
}
}
return _IR_ProcessData();
}
bool IR_GetMessage(IR_Message_t* Message) { _IR_MessageBuffer = Message; _IR_MessageBuffer->Valid = false; _IR_TimerTicks = 0x00; _IR_TimeOutTicks = 0x00; _IR_RecState = IR_STATE_IDLE; while(!_IR_MessageBuffer->Valid) { if(_IR_TimeOutTicks > (IR_TIMEOUT / IR_TICK_INTERVAL)) { _IR_RecState = IR_STATE_STOP; _IR_MessageBuffer->Valid = false; return _IR_MessageBuffer->Valid; } } return _IR_ProcessData(); }
bool IR_GetMessage(IR_Message_t* Message)
{
    _IR_MessageBuffer = Message;
    _IR_MessageBuffer->Valid = false;
    _IR_TimerTicks = 0x00;
    _IR_TimeOutTicks = 0x00;
    _IR_RecState = IR_STATE_IDLE;
    while(!_IR_MessageBuffer->Valid)
    {
        if(_IR_TimeOutTicks > (IR_TIMEOUT / IR_TICK_INTERVAL))
        {
            _IR_RecState = IR_STATE_STOP;
            _IR_MessageBuffer->Valid = false;
            return _IR_MessageBuffer->Valid;
        }
    }

    return _IR_ProcessData();
}

Die Funktion erwartet als Parameter einen Zeiger auf ein Objekt vom Typ

IR_Message_t
IR_Message_t, welches folgendermaßen aufgebaut ist:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
typedef struct
{
uint8_t Length;
union
{
uint8_t Field[4];
uint32_t Value;
} Data;
bool Valid;
bool IsRepeat;
} __attribute__((packed)) IR_Message_t;
typedef struct { uint8_t Length; union { uint8_t Field[4]; uint32_t Value; } Data; bool Valid; bool IsRepeat; } __attribute__((packed)) IR_Message_t;
typedef struct
{
    uint8_t Length;
    union
    {
        uint8_t Field[4];
        uint32_t Value;
    } Data;
    bool Valid;
    bool IsRepeat;
} __attribute__((packed)) IR_Message_t;
Feld Funktion
Length Länge der Übertragung
Data Ein Union um die empfangenen Daten entweder als Array oder als 32-Bit Integer zur Verfügung zu stellen
Valid Flag um eine gültige Nachricht zu signalisieren
IsRepeat Flag um ein Repeat Code zu signalisieren

Wird innerhalb einer bestimmten Zeit keine gültige Nachricht empfangen, wird der Zustandsautomat angehalten, ein Timeout ausgelöst und die Funktion verlassen. Andernfalls werden die empfangenen Daten über die Funktion

_IR_ProcessData
_IR_ProcessData weiterverarbeitet.

Direkt nach der Initialisierung befindet sich der Zustandsautomat im Zustand

IR_STATE_IDLE
IR_STATE_IDLE, in dem der Automat auf eine steigende Flanke am Eingangspin wartet.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
case IR_STATE_IDLE:
{
if(InputState)
{
if(_IR_TimerTicks < IR_PROTOCOL_BURST)
{
_IR_TimeOutTicks++;
}
else
{
_IR_MessageBuffer->Length = 0x00;
_IR_RecState = IR_STATE_REC_SPACE;
}
_IR_TimerTicks = 0x00;
}
break;
}
case IR_STATE_IDLE: { if(InputState) { if(_IR_TimerTicks < IR_PROTOCOL_BURST) { _IR_TimeOutTicks++; } else { _IR_MessageBuffer->Length = 0x00; _IR_RecState = IR_STATE_REC_SPACE; } _IR_TimerTicks = 0x00; } break; }
case IR_STATE_IDLE:
{
    if(InputState)
    {
        if(_IR_TimerTicks < IR_PROTOCOL_BURST)
        {
            _IR_TimeOutTicks++;					
        }
        else
        {
            _IR_MessageBuffer->Length = 0x00;
            _IR_RecState = IR_STATE_REC_SPACE;
        }
        _IR_TimerTicks = 0x00;
    }
    break;
}

Dazu wertet der Automat zusätzlich noch den Zähler aus und wenn der Zählerstand zu niedrig ist, wird der Zähler zurückgesetzt, ein Zähler für den Timeout inkrementiert und anschließend die ISR verlassen. Falls der Zählerstand den Grenzwert überschreitet, ist ein gültiger Burst erkannt worden und der Zustandsautomat wechselt in den Zustand

IR_STATE_REC_SPACE
IR_STATE_REC_SPACE.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
case IR_STATE_REC_SPACE:
{
if(!InputState)
{
_IR_Data[_IR_MessageBuffer->Length++] = _IR_TimerTicks;
_IR_TimerTicks = 0x00;
_IR_RecState = IR_STATE_REC_SPACE;
}
else if(_IR_TimerTicks > (IR_TIMEOUT / IR_TICK_INTERVAL))
{
_IR_RecState = IR_STATE_STOP;
}
break;
}
case IR_STATE_REC_SPACE: { if(!InputState) { _IR_Data[_IR_MessageBuffer->Length++] = _IR_TimerTicks; _IR_TimerTicks = 0x00; _IR_RecState = IR_STATE_REC_SPACE; } else if(_IR_TimerTicks > (IR_TIMEOUT / IR_TICK_INTERVAL)) { _IR_RecState = IR_STATE_STOP; } break; }
case IR_STATE_REC_SPACE:
{
    if(!InputState)
    {
        _IR_Data[_IR_MessageBuffer->Length++] = _IR_TimerTicks;
        _IR_TimerTicks = 0x00;
        _IR_RecState = IR_STATE_REC_SPACE;
    }
    else if(_IR_TimerTicks > (IR_TIMEOUT / IR_TICK_INTERVAL))
    {
        _IR_RecState = IR_STATE_STOP;
    }
    break;
}

In diesem Zustand wertet der Zustandsautomat eine ankommenden Pause aus. Dazu wartet der Automat so lange bis eine fallende Flanke am Empfangspin auftritt. Wenn der Zählerstand einen bestimmten Wert überschreitet und der Empfangspin gesetzt ist, wird der Zustandsautomat angehalten und der Empfang beendet. Andernfalls wird der aktuelle Zählerstand im Array

_IR_Data
_IR_Data gespeichert, der Zähler zurückgesetzt und der Automat wechselt in den Zustand
IR_STATE_REC_BURST
IR_STATE_REC_BURST

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
case IR_STATE_REC_BURST:
{
if(InputState)
{
_IR_Data[_IR_MessageBuffer->Length++] = _IR_TimerTicks;
_IR_TimerTicks = 0x00;
_IR_RecState = IR_STATE_REC_SPACE;
}
break;
}
case IR_STATE_REC_BURST: { if(InputState) { _IR_Data[_IR_MessageBuffer->Length++] = _IR_TimerTicks; _IR_TimerTicks = 0x00; _IR_RecState = IR_STATE_REC_SPACE; } break; }
case IR_STATE_REC_BURST:
{
    if(InputState)
    {
        _IR_Data[_IR_MessageBuffer->Length++] = _IR_TimerTicks;
        _IR_TimerTicks = 0x00;
        _IR_RecState = IR_STATE_REC_SPACE;
    }
    break;
}

Dieser Zustand wertet die nachfolgende Pausenzeit eines Bits aus. Der Zustandsautomat wartet bis der Eingangspin gesetzt ist und somit eine steigende Flanke detektiert wurde. Dann wird der Zählerstand erneut im Array

_IR_Data
_IR_Data gespeichert und der Automat springt zurück in den Zustand
IR_STATE_REC_SPACE
IR_STATE_REC_SPACE.

Dieser Ablauf wiederholt sich so lange bis der letzte Burst ohne nachfolgende Pause gesendet wird. Dann springt der Zustandsautomaten aus dem Zustand

IR_STATE_REC_SPACE
IR_STATE_REC_SPACE in den Zustand
IR_STATE_REC_STOP
IR_STATE_REC_STOP, wo ein entsprechendes Flag gesetzt wird um das Ende der Datenübertragung zu signalisieren.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
case IR_STATE_STOP:
{
_IR_MessageBuffer->Valid = true;
break;
}
case IR_STATE_STOP: { _IR_MessageBuffer->Valid = true; break; }
case IR_STATE_STOP:
{
    _IR_MessageBuffer->Valid = true;
    break;
}

Wurde die Nachricht komplett empfangen und das

Valid
Valid-Flag gesetzt, wird die Funktion
_IR_ProcessData
_IR_ProcessData aufgerufen, wo die gespeicherten Nachricht ausgewertet wird.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
static bool _IR_ProcessData(void)
{
if(_IR_MessageBuffer->Length == 0x02)
{
_IR_MessageBuffer->IsRepeat = true;
_IR_MessageBuffer->Data.Value = 0x00;
if((_IR_Data[0x00] < ((IR_PROTOCOL_SPACE / 2) + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[0x00] > ((IR_PROTOCOL_SPACE / 2) - IR_PROTOCOL_TOLERANCE)))
{
_IR_MessageBuffer->Valid = true;
}
else
{
_IR_MessageBuffer->Valid = false;
}
}
else
{
_IR_MessageBuffer->IsRepeat = false;
if((_IR_Data[0] > (IR_PROTOCOL_SPACE + IR_PROTOCOL_TOLERANCE)) || (_IR_Data[0] < (IR_PROTOCOL_SPACE - IR_PROTOCOL_TOLERANCE)))
{
_IR_MessageBuffer->Valid = false;
}
else
{
...
}
}
return _IR_MessageBuffer->Valid;
}
static bool _IR_ProcessData(void) { if(_IR_MessageBuffer->Length == 0x02) { _IR_MessageBuffer->IsRepeat = true; _IR_MessageBuffer->Data.Value = 0x00; if((_IR_Data[0x00] < ((IR_PROTOCOL_SPACE / 2) + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[0x00] > ((IR_PROTOCOL_SPACE / 2) - IR_PROTOCOL_TOLERANCE))) { _IR_MessageBuffer->Valid = true; } else { _IR_MessageBuffer->Valid = false; } } else { _IR_MessageBuffer->IsRepeat = false; if((_IR_Data[0] > (IR_PROTOCOL_SPACE + IR_PROTOCOL_TOLERANCE)) || (_IR_Data[0] < (IR_PROTOCOL_SPACE - IR_PROTOCOL_TOLERANCE))) { _IR_MessageBuffer->Valid = false; } else { ... } } return _IR_MessageBuffer->Valid; }
static bool _IR_ProcessData(void)
{
    if(_IR_MessageBuffer->Length == 0x02)
    {
        _IR_MessageBuffer->IsRepeat = true;
        _IR_MessageBuffer->Data.Value = 0x00;
        if((_IR_Data[0x00] < ((IR_PROTOCOL_SPACE / 2) + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[0x00] > ((IR_PROTOCOL_SPACE / 2) - IR_PROTOCOL_TOLERANCE)))
        {
            _IR_MessageBuffer->Valid = true;
        }
        else
        {
            _IR_MessageBuffer->Valid = false;
        }
    }
    else
    {
        _IR_MessageBuffer->IsRepeat = false;
        if((_IR_Data[0] > (IR_PROTOCOL_SPACE + IR_PROTOCOL_TOLERANCE)) || (_IR_Data[0] < (IR_PROTOCOL_SPACE - IR_PROTOCOL_TOLERANCE)))
        {
            _IR_MessageBuffer->Valid = false;
        }
        else
        {
            ...
        }
    }

    return _IR_MessageBuffer->Valid;
}

Dazu prüft die Funktion zuerst die Länge des Nachrichtenobjektes. Falls die Länge 2 beträgt, so handelt es sich um einen Repeat Code und das

IsRepeat
IsRepeat-Flag des Nachrichtenobjekts wird gesetzt. Anschließend wird der gespeicherte Wert für die Pausenzeit überprüft und wenn dieser Wert innerhalb der Toleranz liegt (± 5 Ticks), wird die empfangene Nachricht als gültig markiert, indem das
Valid
Valid auf
true
true gesetzt wird.

Falls es sich nicht um einen Repeat Code handelt, so werden die gespeicherten Zeitinformationen ausgewertet und in Bitinformationen umgewandelt. Anschließend werden die empfangenen Daten noch auf Fehler überprüft, indem der nicht-invertierte und der invertierte Wert miteinander verglichen werden.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
for(uint8_t i = 0x01; i < (_IR_MessageBuffer->Length - 0x01); i += 0x02)
{
_IR_MessageBuffer->Data.Value <<= 0x01;
if((_IR_Data[i] < (IR_PROTOCOL_BIT_BURST + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i] > (IR_PROTOCOL_BIT_BURST - IR_PROTOCOL_TOLERANCE)) &&
(_IR_Data[i + 0x01] < (IR_PROTOCOL_BIT_ZERO + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i + 0x01] > (IR_PROTOCOL_BIT_ZERO - IR_PROTOCOL_TOLERANCE))
)
{
_IR_MessageBuffer->Data.Value |= 0x00;
}
else if((_IR_Data[i] < (IR_PROTOCOL_BIT_BURST + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i] > (IR_PROTOCOL_BIT_BURST - IR_PROTOCOL_TOLERANCE)) &&
(_IR_Data[i + 0x01] < (IR_PROTOCOL_BIT_ONE + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i + 0x01] > (IR_PROTOCOL_BIT_ONE - IR_PROTOCOL_TOLERANCE))
)
{
_IR_MessageBuffer->Data.Value |= 0x01;
}
}
if((_IR_MessageBuffer->Data.Field[0x00] == (~_IR_MessageBuffer->Data.Field[0x01])) &&
(_IR_MessageBuffer->Data.Field[0x02] == (~_IR_MessageBuffer->Data.Field[0x03])))
{
_IR_MessageBuffer->Valid = false;
}
else
{
_IR_MessageBuffer->Valid = true;
}
for(uint8_t i = 0x01; i < (_IR_MessageBuffer->Length - 0x01); i += 0x02) { _IR_MessageBuffer->Data.Value <<= 0x01; if((_IR_Data[i] < (IR_PROTOCOL_BIT_BURST + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i] > (IR_PROTOCOL_BIT_BURST - IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i + 0x01] < (IR_PROTOCOL_BIT_ZERO + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i + 0x01] > (IR_PROTOCOL_BIT_ZERO - IR_PROTOCOL_TOLERANCE)) ) { _IR_MessageBuffer->Data.Value |= 0x00; } else if((_IR_Data[i] < (IR_PROTOCOL_BIT_BURST + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i] > (IR_PROTOCOL_BIT_BURST - IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i + 0x01] < (IR_PROTOCOL_BIT_ONE + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i + 0x01] > (IR_PROTOCOL_BIT_ONE - IR_PROTOCOL_TOLERANCE)) ) { _IR_MessageBuffer->Data.Value |= 0x01; } } if((_IR_MessageBuffer->Data.Field[0x00] == (~_IR_MessageBuffer->Data.Field[0x01])) && (_IR_MessageBuffer->Data.Field[0x02] == (~_IR_MessageBuffer->Data.Field[0x03]))) { _IR_MessageBuffer->Valid = false; } else { _IR_MessageBuffer->Valid = true; }
for(uint8_t i = 0x01; i < (_IR_MessageBuffer->Length - 0x01); i += 0x02)
{
    _IR_MessageBuffer->Data.Value <<= 0x01;
    if((_IR_Data[i] < (IR_PROTOCOL_BIT_BURST + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i] > (IR_PROTOCOL_BIT_BURST - IR_PROTOCOL_TOLERANCE)) &&
       (_IR_Data[i + 0x01] < (IR_PROTOCOL_BIT_ZERO + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i + 0x01] > (IR_PROTOCOL_BIT_ZERO - IR_PROTOCOL_TOLERANCE))
    )
    {
        _IR_MessageBuffer->Data.Value |= 0x00;
    }
    else if((_IR_Data[i] < (IR_PROTOCOL_BIT_BURST + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i] > (IR_PROTOCOL_BIT_BURST - IR_PROTOCOL_TOLERANCE)) &&
        (_IR_Data[i + 0x01] < (IR_PROTOCOL_BIT_ONE + IR_PROTOCOL_TOLERANCE)) && (_IR_Data[i + 0x01] > (IR_PROTOCOL_BIT_ONE - IR_PROTOCOL_TOLERANCE))
    )
    {
        _IR_MessageBuffer->Data.Value |= 0x01;
    }
}
if((_IR_MessageBuffer->Data.Field[0x00] == (~_IR_MessageBuffer->Data.Field[0x01])) &&
   (_IR_MessageBuffer->Data.Field[0x02] == (~_IR_MessageBuffer->Data.Field[0x03])))
{
    _IR_MessageBuffer->Valid = false;
}
else
{
    _IR_MessageBuffer->Valid = true;
}

Die Auswertung der Daten ist damit komplett und kann nun getestet werden. Dazu habe ich ein kleines Beispielprogramm geschrieben, welches die Daten von einer Fernbedienung auswertet und die Hintergrundbeleuchtung des Displays ein- und ausschaltet:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include "Interfaces/IR-Remote/NEC_IR.h"
#include "Services/DisplayManager/DisplayManager.h"
char DisplayBuffer[32];
IR_Message_t Message;
int main(void)
{
SysClock_Init();
IR_Init();
DisplayManager_Init();
DisplayManager_Clear();
DisplayManager_DrawString(20, 0, "IR remote example");
while(1)
{
if(IR_GetMessage(&Message))
{
DisplayManager_ClearLine(2);
if(!Message.IsRepeat)
{
DisplayManager_DrawString(0, 8, "Button pressed!");
switch(Message.Data.Value)
{
case IR_REMOTE_KEY_1:
{
DisplayManager_SwitchBacklight(true);
break;
}
case IR_REMOTE_KEY_9:
{
DisplayManager_SwitchBacklight(false);
break;
}
default:
{
break;
}
}
sprintf(DisplayBuffer, "Data: 0x%lX", Message.Data.Value);
DisplayManager_DrawString(0, 16, DisplayBuffer);
}
else
{
DisplayManager_DrawString(0, 16, "Repeat");
}
}
}
return 0;
}
#include "Interfaces/IR-Remote/NEC_IR.h" #include "Services/DisplayManager/DisplayManager.h" char DisplayBuffer[32]; IR_Message_t Message; int main(void) { SysClock_Init(); IR_Init(); DisplayManager_Init(); DisplayManager_Clear(); DisplayManager_DrawString(20, 0, "IR remote example"); while(1) { if(IR_GetMessage(&Message)) { DisplayManager_ClearLine(2); if(!Message.IsRepeat) { DisplayManager_DrawString(0, 8, "Button pressed!"); switch(Message.Data.Value) { case IR_REMOTE_KEY_1: { DisplayManager_SwitchBacklight(true); break; } case IR_REMOTE_KEY_9: { DisplayManager_SwitchBacklight(false); break; } default: { break; } } sprintf(DisplayBuffer, "Data: 0x%lX", Message.Data.Value); DisplayManager_DrawString(0, 16, DisplayBuffer); } else { DisplayManager_DrawString(0, 16, "Repeat"); } } } return 0; }
#include "Interfaces/IR-Remote/NEC_IR.h"
#include "Services/DisplayManager/DisplayManager.h"

char DisplayBuffer[32];
IR_Message_t Message;

int main(void)
{
    SysClock_Init();
    IR_Init();
    DisplayManager_Init();
    DisplayManager_Clear();
    DisplayManager_DrawString(20, 0, "IR remote example");
    while(1)
    {
        if(IR_GetMessage(&Message))
        {
            DisplayManager_ClearLine(2);
            if(!Message.IsRepeat)
            {
                DisplayManager_DrawString(0, 8, "Button pressed!");
                switch(Message.Data.Value)
                {
                    case IR_REMOTE_KEY_1:
                    {
                        DisplayManager_SwitchBacklight(true);

                        break;
                    }
                    case IR_REMOTE_KEY_9:
                    {
                        DisplayManager_SwitchBacklight(false);

                        break;
                    }
                    default:
                    {
                        break;
                    }
                }
                sprintf(DisplayBuffer, "Data: 0x%lX", Message.Data.Value);
                DisplayManager_DrawString(0, 16, DisplayBuffer);
            }
            else
            {
                DisplayManager_DrawString(0, 16, "Repeat");
            }
        }
    }
    return 0;
}

Das komplette Beispiel könnt ihr euch in meinem GitHub-Repository herunterladen. Ggf. müssen die hinterlegten Codes noch der verwendeten Fernbedienung angepasst werden.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert