www.1-zu-220.de - Startseite

Eine Kehrschleifenschaltung mit Mikrocontroller

von Peter Rachow 2008
Vorgestellt wird eine Kehrschleifenschaltung für 2-Leiter-Modellbahnen, die mit einem Mikrocontroller arbeitet.

Das Grundproblem ist bekannt: Bei einer 2-Leiter-Modellbahn entsteht in der Kehrschleife ein Kurzschluss, weil immer einer der Schienenleiter auf dem "falschen" Pol der Strecke zu liegen kommt: In die Kehrschleife werden daher zwei doppelte Trennstellen eingebaut und die Polarität von Stammstrecke oder Kehrschleifenstrecke wird umgepolt, je nachdem wo sich der Zug gerade befindet.

Anforderungen an die Schaltung:

Vollautomatischer Betrieb: Die Schaltung muss erkennen, wo sich der Zug (in Kehrschleifennähe) gerade befindet, welche Fahrtrichtung er nehmen wird und wie die Polarität von Stamm- und Kehrschleifenstrecke gerade geschaltet ist und welche der Strecken ggf. umgeschaltet werden muss. Diese Aufgabe ist für einen Mikrocontroller geradezu prädestiniert. ;-) Die Wahl fiel auf den ATMega16, weil ich davon noch eine größere Stückzahl in der Bastelkiste liegen hatte.. ;-)

Es werden 3 Lichtschranken eingesetzt: Eine am Stammgleis, eine in Geradeausrichtung und eine am Abzweig,
in der Nähe der Weiche plaziert.
 

Die Schaltung


Kommentar:

Die Schaltung benötigt zum Betrieb 16 bis 20 V DC, diese kann durch einen Gleichrichter aus dem Lichtstrom der Modellbahn generiert werden. Die Stromentnahme ist sehr gering (ca. 4 mA, bzw. 80 mA wenn beide Relais anziehen). Wenn die Weiche geschaltet wird, liegt die Stromstärke allerdings kurzzeitig bei 450 mA (Märklin sei Dank!)!

Gesamtkosten: Ca. 20 EUR.

Funktion

Die Schaltung
ist so ausgelegt, dass jederzeit erkannt wird, wie die Relais zu stehen haben, damit die Polarität des Fahrstromes korrekt an der Stammstrecke und der Schleife liegt. Durchfährt der Zug eine Lichtschranke, wird ermittelt, wohin er fährt. Verlässt er die z. B. Kehre vom Geradeauszweig wird die Polarität in der Stammstrecke so geschaltet wie in der Kehre. Die Polarität in der Kehre bleibt gleich, sonst würde der Zug ja plötzlich rückwärts fahren. Genau so verhält es sich, wenn er vom Geradeauszweig ins Stammgleis einfährt.

Es wird also immer die Strecke umgeschaltet wo der Zug gerade noch nicht fährt.

Fährt der Zug vom Stammgleis in die Kehre wird anhand der Weichenstellung ermittelt, wie die Polarität in der Kehre geschaltet sein muss:

Fall A: Der Zug fährt geradeaus weiter => Die Polarität in der Kehre muss so
geschaltet werden wie im Stammgleis.
Fall B: Der Zug fährt den Abzweig => Die Polarität in der Kehre muss umgekehrt geschaltet werden wie im Stammgleis.

Die Software erwartet immer 2 Lichtschrankendurchfahrten und ist erst dann bereit für den nächsten Zug. Man erkennt dies daran, dass nach 2 Durchfahrten durch die LS alle LEDs wieder verlöschen. Wurde eine LS durchfahren bleibt diese LS für den Rest der jeweiligen Operation außen vor. D. h. auch wenn die Wagenzwischenräume in einem Zug die LS mehrfach ansprechen lassen wird nur das erste Ereignis ausgewertet bis auch die 2. Lichtschranke komplett vom ganzen Zug durchfahren wurde.

Problem: Es gibt nun keine eindeutige Zuordnung mehr zwischen dem Fahrtrichtungsanzeiger im Stellpult und der Fahrtrichtung der Lok. Das ist aber bei allen Kehrschleifenschaltungen so.

Software

Wichtig: Die Fuses beim ATMega16 müssen so gesetzt sein, dass das JTAG-Flag ausgeschaltet ist! Sonst funzt der PORTB des Controllers nicht richtig!

Unten findet man den Quellcode. Wer das Programm nicht selbst compilieren will, hier ist das nötige HEX-File, das auf den Controller zu übertragen ist.

/*****************************************************************/
/*            Kehrschleifensteuerung für Modellbahn-Anlage       */
/*  ************************************************************ */
/*  Mikrocontroller:  ATMEL AVR ATmega16, 8 MHz                  */
/*                                                               */
/*  Compiler:         GCC (GNU AVR C-Compiler)                   */
/*  Autor:            Peter Rachow                               */
/*  Letzte Aenderung: 03-04-08                                   */
/*****************************************************************/
// ATMega16-Belegung der PINs

// AUSGÄNGE

// D6 grüne LED 1 (für Betrieb nicht erforderlich, diente nur zum Testen!)
// D7 rote LED 2 
(für Betrieb nicht erforderlich, diente nur zum Testen!)

// C0 = rote LED-KOntrolle für LDR 1 an Pin B2 (Stammgleis), led(3,x)
// C1 = Treiber für Relais 1
// C2 = rote LED-KOntrolle für LDR 2 an Pin B3 (geradeaus), led(4,x)
// C3 = Treiber für Relais 2
// C4 = rote LED-KOntrolle für LDR 3 an Pin B4 (Abzweig), led(5,x)

// C6 = Treiber für Spule "gerade"
// C7 = Treiber für Spule "rechts"

// EINGÄNGE

// B0 = LDR 1
// B1 = LDR 2
// B2 = LDR 3 (Abzweig)
// B3 = Weichenschalter gerade
// B4 = Weichenschalter abzweigen

// Steckerbelegung Mehrfachstecker 10-pol.

// schwarz = Masse
// braun = Masse
// rot = V+ ein (ungeregelt)
// orange = LDR 1
// gelb = LDR 2
// grün  = LDR 3
// blau = +15V für LEDs
// violett = +5 Vout für LDR-Sensoren

// Mehrfachstecker 5-pol.

// gelb - Weiche auf "gerade"
// orange - Weiche auf "rechts abzweigen"
// rot - Spulenmagnet "gerade"
// braun - Spulenmagnet "rechts abzweigen"
// schwarz - Masse

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>

/*********************/
/* Timer & Interrupt */
/*********************/
#define SCLOCK 8             /* Taktfrequenz im MHz                               */

int main(void);
void led(char, char);
int get_ldr(void);
int get_turnswitch(void);
void set_relay(int, int);
void set_turn(void);
void set_polarity(void);
void ldr_adjust(void);

int ldr_event[2];
int turn_pos = 0;
int polarity[2]; //polarity[0]=Hauptsrecke,polarity[1]=Kehre         
int ldr[3] = {0, 0, 0};

/* Wartezeit in Millisekunden */
void wait_ms(int ms)
{
    int t1, t2;

    for(t1 = 0; t1 < ms; t1++)
        for(t2 = 0; t2 < 137 * SCLOCK; t2++)
            asm volatile ("nop" ::);
}

/* LEDs ein/aus */
void led(char lednum, char status)
{
    switch (lednum)
    {
       //grüne LED an D6
       case 1:  if(status)
                    cbi(PORTD, 6);
       else
                    sbi(PORTD, 6);
       break;           
      
       //rote LED an D7
       case 2:  if(status)
                    cbi(PORTD, 7);
       else
                    sbi(PORTD, 7);
       break;           
      
       case 3:  if(status)
                    cbi(PORTC, 0);
       else
                    sbi(PORTC, 0);
       break;           
      
       case 4:  if(status)
                    cbi(PORTC, 2);
       else
                    sbi(PORTC, 2);
       break;           
      
       case 5:  if(status)
                    cbi(PORTC, 4);
       else
                    sbi(PORTC, 4);
       break;           
    }               
}      

void set_relay(int relay, int status)
{
    switch (relay)
    {
       //Relay 1
       case 0:  if(status)
                    cbi(PORTC, 1);
       else
                    sbi(PORTC, 1);
       break;           
      
       //Relay 2
       case 1:  if(status)
                    cbi(PORTC, 3);
       else
                    sbi(PORTC, 3);
       break;
    }      
      
}

//Weiche stellen
void set_turn(void)
{
  if(turn_pos == 0)
  {
    sbi(PORTC, 6);
  }   
  else
  {
    sbi(PORTC, 7);
  }   
 
  wait_ms(250);
  cbi(PORTC, 6);
  cbi(PORTC, 7);

}     

//Pol-Relais schalten
void set_polarity()
{
  if(polarity[0])
  {
    set_relay(0, 0);
  }   
  else   
  {
    set_relay(0, 1);
  }   
 
  if(polarity[1])
  {
    set_relay(1, 0);
  }   
  else   
  {
    set_relay(1, 1);
  }   


//LDRS justieren
void ldr_adjust()
{
    char res = inp(PINB);
   
    // LDR Stammgleis
    if (!(res & 1))
      led(3 , 1);
    else
      led(3 , 0);
     
    // LDR geradeaus
    if (!(res & 2))
      led(4 , 1);
    else
      led(4 , 0);
   
    // LDR Abzweig
    if (!(res & 4))
      led(5 , 1);
    else
      led(5 , 0);
}   

//LDRs lesen
int get_ldr(void)
{
    char res = inp(PINB);
   
    // LDR Stammgleis
    if (!(res & 1) && !ldr[0])
    {
      if (!ldr_event[0])
      {
        ldr_event[0] = 1;
      }
      else
      {
        if(ldr_event[0] != 1)
        {
            ldr_event[1] = 1;
        } 
      }   
      led(3 , 1);
      ldr[0] = 1;
    }   

    // LDR geradeaus
    if (!(res & 2))
    {
      if (!ldr_event[0] && !ldr[1])
      {
        ldr_event[0] = 2;
      }
      else
      {
        if(ldr_event[0] != 2)
        {
            ldr_event[1] = 2; 
        } 
      }   
      led(4 , 1);
      ldr[1] = 1;
    }   

    // LDR Abzweig
    if (!(res & 4) && !ldr[2])
    {
      if (!ldr_event[0])
      {
        ldr_event[0] = 3;
      }
      else
      {
        if(ldr_event[0] != 3)
        {
            ldr_event[1] = 3; 
        } 
      }   
      led(5 , 1);
      ldr[2] = 1;
    }   
       
    return  res;
}

int get_turnswitch(void)
{
    char res = inp(PINB);
       
    //Wenn beide Taster gleichzeitig gedrückt werden
    // => Endlosschleife zum Lichtschraknken justieren
   
    if(!(res & 8) && !(res & 16))
    {
      while(1)
      {
        led(1, 1);
        ldr_adjust();
        led(1, 0);
      }
    }
   
    // Geradeausfahrt
    if (!(res & 8))
    {
      turn_pos = 0;
      set_turn();
    }   
   
    // Abzweig
    if (!(res & 16))
    {
      turn_pos = 1;
      set_turn();
    }   
   
    return res;
}   

int main()
{
    int t1;
    
    outp(0x00, DDRB); //B auf Input
    outp(0xFF, DDRC); //C auf Output
    outp(0xFF, DDRD); //D auf Output
   
    for (t1 = 1; t1 < 6; t1++)
      led(t1, 0);
   
    outp(0x00, PORTC);
   
    //Alle LEDs aus   
    for(t1 = 1; t1 < 6; t1++)
      led(t1, 0);
   
   
    //Polarität beide Stromkreise normal
    polarity[0] = 0;
    polarity[1] = 0;
    set_polarity();
    wait_ms(500);

    //Weiche gerade

    set_turn();
       
    ldr_event[0] = 0;
    ldr_event[1] = 0;
   
    while(1)
    {
      get_ldr();
     
      if(ldr_event[0] || ldr_event[1]) //!!!
      {
        if(ldr_event[0])
        {
          led(1, 1);
        }   
        if(ldr_event[1])
        {
          led(2, 1);
        }
        wait_ms(500);
        led(1, 0);
        led(2, 0);
      } 
   
      //Fahrtrichtung bestimmen     
     
      if (ldr_event[0] && !ldr_event[1])
      {    // Aus der Kehre kommend
        if(ldr_event[0] == 2) //Gerade
        {
          if (turn_pos == 1)
          {
            turn_pos = 0;
            set_turn();
          }
          polarity[0] = polarity[1];
          set_polarity();
        }   
     
        if(ldr_event[0] == 3) //Abzweig
        {
          if (turn_pos == 0)
          {
            turn_pos = 1;
            set_turn();
          }
          polarity[0] = !polarity[1];
          set_polarity();
        }
   
        if(ldr_event[0] == 1) //Fahrt vom Stammgleis ...
        {
          if (!turn_pos) //...geradeaus
          {
            polarity[1] = polarity[0];
            set_polarity();
          }
          else // ...in den Abzweig
          {
            polarity[1] = !polarity[0];
            set_polarity();
          }
        }
      }     
     
      //2 LS durchfahren
      if(ldr_event[0] && ldr_event[1])
      {
        ldr_event[0] = 0;
        ldr_event[1] = 0;
        //Warten bis alle LDRs wieder frei
        while(!(inp(PINB) & 1) || !(inp(PINB) & 2) || !(inp(PINB) & 4))
        {
          led(1, 1);
          led(2, 0);
          wait_ms(100);
          led(1, 0);
          led(2, 1);
          wait_ms(100);
        }
       
        led(1, 0);
        led(2, 0);
        led(3, 0);
        led(4, 0);
        led(5, 0);
   
        ldr[0] = 0;
        ldr[1] = 0;   
        ldr[2] = 0;
      } 
     
      get_turnswitch(); //Weichenschalter abfragen
    }        

    return 0;
}


(C)
Peter Rachow 2008