Infrarot Auslöser für Nikon Kameras

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

von Martin Matysiak

Einleitung

Innenansicht - Ja, das ist ein SSOP Controller, wo eigentlich ein DIP Controller sein sollte - ich hatte keine mehr übrig und der letzte ist durch einen kleinen Fehler gestorben ;-)

Während eines Moments der Langeweile habe ich mich dazu entschieden, eine Infrarot-Fernbedienung für meine Nikon Digitalkamera zu bauen. Dabei stellte sich heraus, dass es sich dabei um ein extrem einfaches Projekt handelt, perfekt, wenn man einen Tag lang nichts zu tun hat. Die Fernbedienung hat eine Reichweite von mindestens 15 - 20 Meter, mehr konnte ich bislang nicht testen.

Die Fernbedienung besteht hauptsächlich aus einem Mikrocontroller, der per Knopfdruck mit Strom versorgt wird. Daraufhin wird eine Infrarot-LED mit einer Frequenz von ca. 38kHz angesteuert, die ein bestimmtes Leuchtmuster an die Kamera sendet. Die Fernbedienung ist funktionsgleich zu Nikon's ML-L3 und funktioniert somit mit einer vielzahl von Nikon Kameras.

Hardware

Für die Fernbedienung wird nur eine geringe Anzahl an Bauteilen benötigt. Wer öfter mit Elektronischen Schaltungen bastelt, wird vermutlich alle Bauteile lagernd haben. Nachfolgend eine genaue Auflistung:

  • Plastikgehäuse für die Schaltung (am besten, wenn es ein Fernbedienungsgehäuse wie im obigen Foto ist)
  • Ein Stück Loch- oder Streifenrasterplatine
  • 1x Atmel ATTiny13 Mikrocontroller
  • 1x 8 MHz Quarzoszillator
  • 1x 3V Knopfzelle (z.B. CR2032, vorzugsweise mit Lötfahnen)
  • 1x Taster
  • 1x Infrarot-LED
  • 1x ca. 1 bis 2Ω Widerstand
  • 1x 100Ω Widerstand
  • 1x 10 kOhm Widerstand
  • 1x BC547 NPN Transistor (oder äquivalentes Modell)
  • 2x 100 nF Kondensator

Theoretisch sollte die Fernbedienung auch mit dem internen Oszillator funktionieren. In der Praxis erwies sich der Einsatz eines externen Quarzoszillators allerdings deutlich zuverlässiger. Hier der Schaltplan der Fernbedienung:

Schaltplan

Ich werde hier kein Platinenlayout zur Verfügung stellen, da das Layout stark von der Wahl der Bauteile und des Gehäuses abhängt. Außerdem habe ich nicht erwähnt, ob ich DIP oder SMD Bauteile verwendet habe - such dir einfach die Form aus, die dir am besten passt! Ich persönlich habe eine Mischung aus beidem verwendet, da ich nur Bauteile verwenden wollte, die ich bereits lagernd hatte.

Software

Bevor ich den eigentlichen Quellcode vorstelle, möchte ich ein paar Worte über das Protokoll verlieren, welches die Fernbedienung einsetzt. Das Infrarot-Signal wird mit einer Frequenz von etwa 38,4 Kilohertz moduliert. Das bedeutet, wenn die LED "an" ist, leuchtet sie nicht durchgehend, sondern blinkt 38400 mal in der Sekunde. Weiterhin muss die LED zwei mal in einem ganz bestimmten Muster leuchten (mit einer Pause von 63ms), damit sie von der Kamera erkannt wird. Im Quellcode nenne ich dieses Muster "burst". Glücklicherweise haben sich bereits einige Leute mit dieser Thematik befasst und das Signal der Original-Fernbedienung genaustens analysiert. Ich habe die Daten von BigMike's Webseite[1] genommen.

Der Quellcode für den Mikrocontroller ist relativ simpel. Da der Schalter einfach die Stromversorgung aktiviert und nicht etwa als digitaler Eingang agiert, ist das einzige, was der Controller tun muss, direkt nach dem Start das richtige Muster auszugeben. Diese Tatsache spiegelt sich in der extrem kurzen Hauptmethode wieder:

int main(void) {
  // Initialize port
  DDRB |= (1 << IO_IR);
 
  burst();
  _delay_ms(63);
  burst();
   
  while(1) {}
}

Das war einfach, nicht wahr? Viel interessanter ist die burst Methode:

#define F_CPU 8000000UL

// DO NOT USE PB3! PIN IS USED AS CLOCK INPUT!
#define IO_IR PB4

#define LED_ON() PORTB |= (1 << IO_IR)
#define LED_OFF() PORTB &=~(1 << IO_IR)

// Length of a clock in microseconds (ca. 38,4kHz)
#define CLOCK_DURATION 9

// Number of clocks per data burst
#define BURST_LENGTH 3088

#include<avr/io.h>
#include<math.h> // fuer delay.h im avrstudio 5.1
#include<util/delay.h>
#include<avr/interrupt.h>

// Clock-counts at which the LED state has to be toggled
// The values are determined by experimenting, they work
// with a 8MHz crystal oscillator
const uint16_t thresholds[8] = {2,164, 2564, 2597, 2727, 2760, 3054, 3087};
 
// Sends a single data burst (two of them are needed)
void burst() {
  uint16_t clock = 0;
  uint8_t current_threshold = 0;
  uint8_t status = 0;
     
  while(clock++ < BURST_LENGTH) {
    if (clock == thresholds[current_threshold]) {
      status ^= 1;
      current_threshold++;
    }
     
    if (clock & status) {
      LED_ON();
    } else {
      LED_OFF();
    }      
         
    _delay_us(CLOCK_DURATION);
  }
     
  LED_OFF();
  return;
}

Nun, was passiert hier? Ein Durchlauf der while-Schleife benötigt ungefähr 13 Mikrosekunden, wodurch die Modulationsfrequenz von 38.4 kHz erreicht wird. Während jeder Iteration wird ein Zähler inkrementiert. Dieser Zähler entspricht der Anzahl der bisher erfolgten Takte. Pro Durchlauf wird er mit einem Array von Schwellwerten verglichen. Wird ein Schwellwert erreicht, schaltet der LED Zustand von "an" auf "aus" oder umgekehrt (wobei "an" wie vorhin erwähnt Blinken mit 38.4 kHz bedeutet).

Damit die LED also wirklich leuchtet, müssen zwei Bedingungen gelten:

  • Der aktuelle Takt ist ungerade (dadurch wird das Blinken mit Modulationsfrequenz erreicht)
  • Der Zustand der LED ist "an"

Auch wenn die status Variable ein 8-bit Integer ist, verwende ich nur das niederwertigste Bit zur Indikation des Zustandes. Der Zähler wird bei jedem Schleifendurchlauf inkrementiert, das heißt, dass das niederwerigste Bit des Zählers bei jedem Durchlauf zwischen Null und Eins springt. Nun kann man einfach die beiden Variablen UND-verknüpfen - wenn das Ergebnis der Verknüpfung eine Eins ist, heißt es, dass beide Bedingungen erfüllt sind und die LED leuchten kann.

Einzelnachweise