Session 3A - Ampelsteuerung mit Schieberegister

/* Programm zum Ansteuern einer T-Kreuzung mit drei Fahrzeugampeln und zweier gekoppelter Fussgaengerampeln.
 * Durch ein Druecken des Tasters werden zunaechst die Fussgaengerampeln auf rot geschaltet. Nach einer vollstaendigen
 * Ampelperiode werden die Fahrzeugampeln auf rot und die Fussgaengerampeln auf gruen geschaltet. Dieser Zustand wird
 * fuer drei Sekunden gehalten, ehe der normale Ablauf wieder startet.
 *
 *  Pinbelegung:
 *  D13:        Schieberegister DS
 *  D11:        Schieberegister SHC
 *  D9:        Schieberegister STC
 *  D3:        Taster
 *
 * Skizze des Layouts:
 *
 *             1 =     =
 *             | =     =  F1
 *             v =     =  v  <-3
 * ===============            ===============
 *                        -
 *                        -
 * ===================================
 *             2->        ^
 *                        F2
 *
 * 1, 2, 3: Ampeln für Fahrzeuge
 * F1, F2: Fussgaengerampeln (werden identisch geschaltet)
 */

int fussgaenger_taster = 2;  // Pin 3 des Arduino wird zum Auslesen des Tasters verwendet; muss an einem Interrupt (INTX)-Pin haengen!

volatile int fussgaengerFlag = 0; // Zeigt an, ob ein Fussgaenger die Strasse ueberqueren will
                                  // volatile bedeutet, dass die Variable ihren Wert zwischen zwei Aufrufen den Wert veraendern kann.
                                  // Damit wird durch den Optimierer kein Aufruf darauf "wegoptimiert".

int latchPin = 8;   // Storage register clock pin
int clockPin = 10;  // Shift register clock pin
int dataPin = 12;   // Datenpin

char zustand = B00000000; // Alle LEDs zu Beginn aus (also '0')
                          // Aufstellung der LEDs (von rechts nach links):
                          // BIT# | LED
                          //  1   | Fussgaengerampeln rot
                          //  2   | Fussgaengerampeln gruen
                          //  3   | Durchgangsampeln rot
                          //  4   | Durchgangsampeln gelb
                          //  5   | Durchgangsampeln gruen
                          //  6   | Seitenampel rot
                          //  7   | Seitenampel gelb
                          //  8   | Seitenampel gruen

// Index in der Zustandsvariable; Achtung: Beginn bei 0 (da realisiert als Offset!)
int fussgaengerampeln_rot = 0;
int fussgaengerampeln_gruen = 1;
int durchgangsampeln_rot = 2;
int durchgangsampeln_gelb = 3;
int durchgangsampeln_gruen = 4;
int seitenampel_rot =5;
int seitenampel_gelb = 6;
int seitenampel_gruen = 7;

void senden(int zustandsOffset, int neuerZustand)
{
  if( neuerZustand == HIGH )
    zustand |= (1 << zustandsOffset);
  else
    zustand &= ~(1 << zustandsOffset);

  digitalWrite(latchPin, LOW); // Schreibzugriffe zulassen
  shiftOut(dataPin, clockPin, MSBFIRST, zustand);
  digitalWrite(latchPin, HIGH); // Schreibzugriffe beenden

}

void fussgaenger()
{               // Die fussgaenger()-Funktion wird bei einem Druck des Tasters aufgerufen
  // Wird bereits ein Fussgaenger ueber die Strasse gelassen?
  if( fussgaengerFlag == 2 )
    return;

  // Schalte beide Fussgaengerampeln auf rot
  senden(fussgaengerampeln_rot, HIGH);
  // Signalisiere der loop()-Funktion, dass ein Fussgaenger die Strasse ueberqueren will
  fussgaengerFlag = 1;
  Serial.println("interrupt");
}


void setup()
{               // Die setup()-Funktion wird einmal durchlaufen und dient der Initialsierung
  // definiere alle Pins fuer die Ampeln als OUTPUT
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  // definiere den Pin fuer den Taster als INPUT
  pinMode(fussgaenger_taster, INPUT);

  // erstelle einen Interrupt fuer den Fussgaengertaster
  attachInterrupt(1, fussgaenger, FALLING);
  Serial.begin(9600);
}

void loop()
{               // Die loop()-Funktion wird wiederholt durchlaufen

  // Test auf Fussgaenger
  if( fussgaengerFlag == 1 )
  {
    Serial.println("Fussgaenger!");
    // Merken, dass derzeit die Fussgaengerampeln auf gruen geschaltet sind, damit bei einem erneuten Druecken
    // die Fussgaengerampeln kein zweites mal aktiviert werden
    fussgaengerFlag = 2;
    
    // Alle Fahrzeugampeln auf rot schalten
    senden(durchgangsampeln_rot, HIGH);
    senden(seitenampel_rot, HIGH);

    // Beide Fussgaengerampeln auf gruen
    senden(fussgaengerampeln_rot, LOW);
    senden(fussgaengerampeln_gruen, HIGH);  
    
    // Zustand fuer drei Sekunden halten
    delay(3000);

    // Fussgaengerampeln auf rot schalten
    senden(fussgaengerampeln_rot, HIGH);
    senden(fussgaengerampeln_gruen, LOW);

    // Rote Lichter der Fahrzeugampeln ausschalten
    senden(durchgangsampeln_rot, LOW);
    senden(seitenampel_rot, LOW);

    // Neuen Fussgaenger zulassen
    fussgaengerFlag = 0;
  }

  // Durchgangsstrasse wird gelb
  senden(durchgangsampeln_rot, HIGH);
  senden(seitenampel_rot, HIGH);
  senden(durchgangsampeln_gelb, HIGH);
  delay(1000);

  // Durchgangsstrasse hat fuer drei Sekunden gruen
  senden(durchgangsampeln_rot, LOW);
  senden(durchgangsampeln_gelb, LOW);
  senden(durchgangsampeln_gruen, HIGH);
  delay(3000);

  // Durchgangsstrasse fuer eine Sekunde auf gelb schalten; Fussgaengerampeln deaktivieren, wenn zuvor einer ruebergelassen wurde
  senden(durchgangsampeln_gruen, LOW);
  
  senden(durchgangsampeln_gelb, HIGH);
  if( fussgaengerFlag == 0 )
  {
    senden(fussgaengerampeln_rot, LOW);
  }
  delay(1000);

  // Durchgangsstrasse hat rot
  senden(durchgangsampeln_rot, HIGH);
  senden(durchgangsampeln_gelb, LOW);    // Berichtigte Stelle (hat gefehlt)
  // Abzweigestrasse auf gelb-rot
  delay(1000);
  senden(seitenampel_gelb, HIGH);
  delay(1000);

  // Abzweigestrasse hat fuer drei Sekunden gruen
  senden(seitenampel_rot, LOW);
  senden(seitenampel_gelb, LOW);
  senden(seitenampel_gruen, HIGH);
  delay(3000);

  // Abzweiugestrasse wird gelb
  senden(seitenampel_gruen, LOW);
  senden(seitenampel_gelb, HIGH);
  delay(1000);

  // Abzweigestrasse wird rot
  senden(seitenampel_gelb, LOW);
  senden(seitenampel_rot, HIGH);
  delay(1000);
}