Als zusätzliches Feature manches Mikrocontroller-Projektes bietet es sich an, dieses noch fernsteuerbar auszulegen. Als Protokoll möchte ich das recht verbreitete RC5 verwenden, da dazu sehr viele Informationen im Netz verfügbar sind. Auch verwenden viele moderne Applikationen dieses Protokoll, so dass auch das Zweckentfremden so mancher Fernbedienung möglich ist. Dem Controller ist es ja prinzipiell relativ egal, woher er das Signal erhält, man muss ihn nur daraufhin "trainieren", je nach Signal eine bestimmte Aktion auszuführen oder auch einfach nicht.

Zum Senden eines Signal dient in meinem Beispiel eine normale Infrarot-LED, welche über einen Transistor, welcher mit deinem MC verbunden ist, angesteuert wird. Als Empfänger dient ein TSOP 17xx. Die beiden 'x' stehen für die Empfangsfrequenz in kHz. In meinem Beispiel wird ein TSOP 1736 verwendet. Prinzipiell ist es zwar auch möglich, ein 38kHz Signal damit zu dekodieren, jedoch sinken dabei Reichweite und Störsicherheit ab. Der TSOP 1736 enthält intern einen Demodulator, welcher das Nutzsignal wieder von der Trägerfrequenz trennt und vorverstärkt. Dieses kann dann direkt abgegriffen werden.

Zunächst soll der Aufbau eines RC5-Signals betrachtet werden. Eine Signalfolge besteht laut Spezifikation aus 14 Bits. Diese sind auf einen entsprechenden Träger aufmoduliert. Hierbei hat jedes Bit eine feste Zuordnung. Die ersten zwei Bits sind die so genannten Startbits. Sie sind im Normalfall immer gleich 1. Danach folgt ein Toggelbit. Dieses wechselt bei jedem Tastendruck seinen logischen Zustand. Wird eine Taste dauerhaft gedrückt bleibt es konstant. Danach folgen 5 Bits welche für die Geräteadresse stehen sowie 6 Bits, welche den eigentlichen Befehl enthalten.
Selbstverständlich ist auch die zeitliche Abfolge des Sendevorganges festgelegt. Die gesamte Signalzeit beträgt etwa 114ms. Die Dauer eines einzelnen Bits 1,778ms. So erhält man eine Sendezeit von 1,778ms*14=25ms für das eigentliche Signal sowie 89ms Pause.
Alle Bits werden biphasencodiert gesendet. D.h., dass immer in der Mitte der Bitzeit der logische Zustand des Signals wechselt. Ein Wechsel von Low auf High bedeutet hierbei eine logische 0 sowie ein Wechsel von High auf Low eine logische 1. Das klingt zwar vielleicht unlogisch, liegt jedoch im invertierten Signal des Empfängers begründet.

Die Darstellung der Empfängerschaltung ist zwar eigentlich überflüssig weil trivial, soll hier nun aber doch erfolgen. Sie kann alternativ auch dem Datenblatt des Infrarotempfängers entnommen werden.

Nachdem der Empfang des Signal geklärt ist, soll es nun um dessen Erzeugung gehen, für den Fall das keine RC5-Fernbedienung jedoch ein MC verfügbar ist.
Die Frequenzerzeugung kann mittels PWM oder einfachen Warteschleifen erfolgen. Hierzu reicht eigentlich ein sehr kleiner Mikrocontroller. Sämtliche ATMega-Modelle sind hierfür reichlich überdimensioniert,
ein ATTiny sollte mehr als ausreichend sein. Dieser befindet sich ständig im Sleep-Mode und wacht nur auf Tastendruck auf, sendet das Signal ein paar mal und kehrt danach in den Sleep-Mode zurück. So sollte ein Batteriebetrieb über eine längere Dauer ohne Probleme möglich sein. Da sich jedoch die meisten Projekte dieser Seite auf ATMegas beziehen habe ich, da die Sache nur Bastelzwecken dient, auch hierfür einen ATMega8 verwendet.
Da eine Infrarot-Diode Ströme von etwa 100mA (je nach Modell) benötigt, sollte von einer direkten Ansteuerung abgesehen werden, da ansonsten die Lebensdauer des MC drastisch sinken würde. Deshalb wird als Treiber ein npn-Transistor eingesetzt. Dessen Basis kann über einen Vorwiderstand direkt an den MC angeschlossen werden. An den Kollektor wird dann die IR-LED geklemmt, gegebenenfalls sollte noch ein Widerstand in Serie geschalten werden, um die LED für Überlastungen zu schützen. Dieser errechnet sich recht einfach nach dem ohmschen Gesetz: Rv = (Us - Uce - Uf)/If = (5V-0,7V-1,5V)/0,13A = 21,5Ω. Die genauen Werte für Flussspannung und Flussstrom können dem Datenblatt der IR-LED entnommen werden, die Kollektor-Emitter-Spannung dem Datenblatt des Transistors
Die Schaltung könnte beispielsweise die folgende Form haben.


Bevor ich mich auf das RC5-Protokoll gestürzt habe, habe ich zunächst ein paar einfach Testalgorithmen implementiert, welche für einfach Fernsteuerungsaufgaben schon durchaus ausreichend sein können. Wenn es lediglich um die Fernsteuerung eines Roboters geht, reichen unter Umständen bereits 10 voneinander unterscheidbare Signale aus, welche als 10 verschiedene Befehle interpretiert werden können.
Die Basis der Signale bildet prinzipiell ein 36kHz Trägersignal. Dieses muss zwangläufig als erstes bereitgestellt werden. Ein beispielhafte Implementierung der Trägererzeugung ist nachfolgend angegeben.

//36kHz Träger
TCCR1A = (1<<COM1A1) | (1<<COM1A0) | (1<<WGM11);
TCCR1B = (1<<WGM12) | (1<<WGM13) | (1<<CS10);
OCR1A = 111;
ICR1 = 222;

Das Beispiel gilt nur für eine Taktfrequenz von 8MHz. Es wird der auf Seite 97 im Datenblatt des ATMega8 aufgelistete Modus 14 verwendet. ICR1 definiert die obere Grenze des Zählers. Es wird ein Prescaler von 1 verwendet, d.h. der Counter läuft mit Systemtakt. Es ergibt sich nun eine Frequenz von 8MHz/1/222=36,036kHz. Setzt man im Datenrichtungsregister für PORTB nun noch das zu PB1 korrespondierende Bit, so liegt am Pin ein 36kHz Signal an. Mittels OCR1A kann die Pulsweite variiert werden, in diesem Fall wurde ein Verhältnis von 1:1 gewählt.

Auf den so gewonnen Träger kann nun ein "Nutzsignal" aufmoduliert werden. Zu beachten ist, dass dessen Frequenz logischerweise ein ganzes Stück geringer sein muss als die des Trägers.
Hier ist ein Beispiel, wie man einen Timer für den Sender bzw. Empfänger konfigurieren könnte. Der Empfänger-Interrupt wird 8fach häufiger aufgerufen als der Sender-Interrupt, um eine gute Abtastung des Signals gewährleisten zu können.

//Timer-Interrupt alle 2,048ms zur Signalerzeugung --> 1/(8MHz/256/64)
TCCR0 = (1<<CS01) | (1<<CS00);
TIMSK |= (1<<TOIE0);

//Abtastung realisieren
//Timer-Interrupt alle 0,256ms zum Signalempfang --> 1/(8MHz/256/8)

TCCR0 |= (1<<CS01);
TIMSK |= (1<<TOIE0);

Die ISR für den Sender muss nun für eine bestimmte Zeitdauer den Träger auf den Sender geben. Die Dauer wird durch die Variable pulse festgelegt. Die Variable empty dient der Sendefunktion als Indikator dafür, ob ein Sendevorgang noch am laufen ist oder bereits abgeschlossen wurde. So wird vermieden, dass pulse während eines Sendevorgang verändert wird.

volatile unsigned char empty = 0;
volatile unsigned char pulse = 0;

SIGNAL (SIG_OVERFLOW0) {
    if (pulse) {
        DDRB |= (1<<PB1);
        empty = 1;
        pulse--;
    }
    if (!pulse) {
        empty = 0;
        DDRB &= ~(1<<PB1);
    }
}

void ir_send (unsigned char byte) {
    if (!empty)
        pulse = byte;
}

Die ISR für den Empfänger muss nun darauf warten, dass am Ausgang des TSOP Low-Pegel anliegt. Ist dies der Fall, kann begonnen werden, die Pulslänge auszuzählen. Je nach Pulslänge kann dann das Signal als jeweiliger Befehl interpretiert werden. Die Auswertung kann jederzeit um weitere Pulslängen erweitert werden, der Übersichtlichkeit halber sind hier nur zwei Varianten angegeben.

volatile unsigned short timecount = 0;
volatile unsigned char empty = 0;
volatile unsigned char rec_byte = 0;

SIGNAL (SIG_OVERFLOW0) {
   
//-----<allgemein>-------
    if (!(PINB & (1<<PB0))) { //Low-Pegel = log. 1 am Sender
        timecount++;
        empty = 1;
    }
    else
        empty = 0;
    if ((!empty) && (timecount)) {
       
//-----<Auswertung>------ //8fache Abtastung
        if ((timecount > 70) && (timecount < 90)) {
//pulse war 10
            rec_byte = 10;
        }
        if ((timecount > 150) && (timecount < 170)) {
//pulse war 20

            rec_byte = 20;
        }
        timecount = 0;
    }
}

unsigned char ir_receive (void) {
   
//Empfangsroutine
    return rec_byte;
}

Ein Testfunktion in der das alles vereint ist, kann hier oder unter der Rubrik Programme angesehen werden. In ihr wird oben per #define festgelegt, ob es sich um Sender oder Empfänger handeln soll. Man hätte das Programm auch auf zwei Dateien verteilen können. Diese Variante fand ich aber noch unübersichtlicher beim debuggen als die verwendete.

Eine komplette RC5-Infrarotroutine wird in Kürze nachgereicht, sie funktioniert zwar bereits, aber zum Teil gibt es leider noch Timing-Probleme. Sobald meine Lösung frei von Bugs ist, werde ich sie samt Erklärung hier vorstellen.

 

Zurück zur Startseite.