Suche

A     B     C     D     E     F     G     H     I     J     K     L     M     N     O     P     Q     R     S     T     U     V     W     X     Y     Z


Formelsammlung
Alle Tests
 F7 F9




Lokdecoder 2


const byte DCC_PIN = PB2; const byte MY_ADDRESS = 15;
const byte OUT_FWD = PB0;   // Pin 5
const byte OUT_REV = PB1;   // Pin 6

// --------------------
// DCC Zustände
// --------------------

enum State {
   SEARCH_PREAMBLE,
   READ_BYTE,
   READ_TRUNC_BIT
};

State state = SEARCH_PREAMBLE;
byte oneCount = 0;
byte bitCount = 0;
byte currentByte = 0;

byte phase = 0;

byte address = 0;
byte command = 0;
byte data = 0;
byte xorByte = 0;

// --------------------
// Timing
// --------------------

unsigned long lastTime = 0;

// ====================================
// SETUP
// ====================================

void setup()
{  pinMode(DCC_PIN, input);

 pinMode(OUT_FWD, OUTPUT);
 pinMode(OUT_REV, OUTPUT);

 analogWrite(OUT_FWD, 0);
 analogWrite(OUT_REV, 0);

} // ====================================
// HAUPTSCHLEIFE
// ====================================

void loop()
{
 // Flankenerkennung (muss noch validiert werden)
 if (digitalRead(DCC_PIN) == HIGH)
  {
   while (digitalRead(DCC_PIN) == HIGH);

   unsigned long now = micros();
   unsigned long dt = now - lastTime;
 lastTime = now;

   byte bit;

   // --------------------
   // Bitentscheidung
   // --------------------
   if (dt < 80)
    bit = 1;    else
    bit = 0;

   // ====================================
   // 1. PRÄAMBEL SUCHE
   // ====================================
   if (state == SEARCH_PREAMBLE)
    {
    if (bit == 1)
     {
     oneCount++;
    }
   else
    {
    if (oneCount >= 10)
     {
     state = READ_BYTE;
     phase = 0;

     bitCount = 0;
     currentByte = 0;
    }

     oneCount = 0;
    }
   }

   // ====================================
   // 2. BYTE LESEN
   // ====================================
  else if (state == READ_BYTE)
   {
   currentByte <<= 1;
   currentByte |= bit;

   bitCount++;

   if (bitCount == 8)
    {
    // Byte je nach Phase zuordnen
    if (phase == 0)
     {
     address = currentByte;

     if (address != MY_ADDRESS)
     {
     state = SEARCH_PREAMBLE;
     }
   else
     {
     state = READ_TRUNC_BIT;
     phase = 1;
     }
    }    else if (phase == 1)
    {
    command = currentByte;
    state = READ_TRUNC_BIT;
    }
   else if (phase == 2)
    {
    data = currentByte;
    state = READ_TRUNC_BIT;
    }
   else if (phase == 3)
    {
    xorByte = currentByte;

    // XOR prüfen
    if ((address ^ command ^ data) == xorByte)
     {
    executePacket();
   }

   state = SEARCH_PREAMBLE;
   }

   bitCount = 0;
   currentByte = 0;
   }>br>   }

  // ===================================
  // 3. TRENNBIT (0 oder 1 nach Byte)
  // ===================================
 else if (state == READ_TRUNC_BIT)
  {
  // DCC: 0 = nächstes Byte folgt
  //   1 = Paketende

  if (bit == 1 && phase < 3)
   {
   state = SEARCH_PREAMBLE;
   return;
   }

  phase++;
  state = READ_BYTE;
  }
 }
}

// =========================================
// AUSWERTUNG
// =========================================
void executePacket()
{
 // 128 Fahrstufenformat (0x3F)
if (command == 0x3F)
 {
 byte direction = (data & 0x80);
 byte speed = (data & 0x7F);

 byte pwm = speed << 1; // grobe Skalierung 0..255

 if (direction)
  {
  analogWrite(OUT_FWD, pwm);
  analogWrite(OUT_REV, 0);
  }
 else

  {
  analogWrite(OUT_FWD, 0);
  analogWrite(OUT_REV, pwm);
  }
 }
}

Ganz oben haben wir dem Programm beim Hochladen schon die Adresse der jeweiligen Lok 'MY_ADDRESS' mitgegeben. Denn in der Lok wollen wir das Programm nicht einem Arduino, sondern dem viel kleineren ATtiny mitgeben und da heißt es Speicherplatz sparen.

Darunter werden die beiden Ausgangs-Pins für die Vorwärts- und Rückwärtsfahrt bestimmt. Danach hat uns die KI wieder eine Besonderheit eingeschmuggelt. Das sind kleine Erziehungsmaßnahmen, um aus uns richtige Programmierer in C++ zu machen.

Das hat auch mit seinem ständigen Versuch zu tun, die Probleme des Programms mit Interrupts zu lösen. Ist natürlich viel eleganter, aber für Laien auch deutlich schwerer zu verfolgen. Wir haben sie erfolgreich im Programm vermeiden können.

Aber die drei Zeilen unter 'enum State' bedeuten nichts anderes, als dass man immer genau weiß, wo man sich im Programm gerade befindet, bei der Suche nach der Präambel (10x1 plus 0) oder der Interpretation der nachfolgenden Bytes einschließlich der Berücksichtigung des Trennbits (TRUNC_Bit).

Die zwei nächsten Variablen hatten wir schon, nämlich die Zähler für die 10 Einsen und die Einsen insgesamt. currentByte ist das aus dem Datenstrom nach der Präambel jeweils ausgelesene Byte, dass dann bezüglich der Adresse oder der Geschwindigkeit untersucht wird.

Die Variable 'address' speichert die aus dem Datenstrom gewonnene Adresse und 'command' das danach folgende Byte, die dann beide später auch zur Validierung und zum Vergleich mit dem xorByte benutzt werden. Danach wird der genauest mögliche Timer auf Null gesetzt.

Die Flankenerkennung hatten wir schon. Die muss deshalb validiert werden, weil hier eine Problemzone liegt. Sind die vom Optokoppler kommenden Flanken nicht gerade, kann es sein, dass alle 80 Mikrosekunden kein sauberes High- oder Low-Signal erkannt wird.

Wir haben uns auch noch für einen langsameren und damit günstigeren Optokoppler entschieden, werden aber mit unserem Oszilloskop das von den Schienen kommende, über den Optokoppler geleitete Signal noch einmal genau anschauen.

Sehr sauber wird insgesamt also das Programm nicht nur durch die Kommentare in die Teile Suche der Präambel, Lesen und Vergleichen der Bytes und Analyse der Trennbits getrennt. Und die Variable 'phase' führt dann durch die Auswertung der Bytes.

Erst nach erfolgreicher Prüfung der Adresse, der Stimmigkeit aller Trennbits steht 'phase' auf 3 und nach der Übereinstimmung nach Berechnung des XORBytes ist überhaupt der Übergang zum letzten Teil, der Auswertung von Fahrgeschwindigkeit und Fahrtrichtung möglich.

Da die Pins 5 und 6 direkt zur jeweiligen Leistungsstufe für Vor- und Rückwärtsfahrt gehen, werden im Programm schon die Pulsweiten je nach Geschwindigkeit festgelegt. Eine sanftere Beschleunigung oder Bremsung kann auch durch Veränderung der Geschwindigkeit von außen erfolgen.







Sidemap - Technik Impressum E-Mail Datenschutz Sidemap - Hersteller