Einstieg Mikrocontroller mit dem Arduino         

7 Ampelsteuerung         


Elektronik-Labor   Projekte   AVR 


 

1.4.22: Einstieg Mikrocontroller mit Arduino
29.4.22: Einstieg Arduino 1, Portausgaben
6.5.22: Einstieg Arduino 2, Digitale Eingänge
13.5.22: Einstieg Arduino 3, Analoge Messungen
20.5.22: Einstieg Arduino 4, Programmschleifen
28.5.22: Einstieg Arduino 5, Übungen
10.6.22: Einstieg Arduino 6, Sesoren
21.6.22: Einstieg Arduino 7, Ampelsteuerung
24.6.22: Einstieg Arduino 8, Zufall und Spiele

Diesmal haben wir uns ganz auf eine Aufgabe konzentriert. Nina und Carla hatten ein Programm zur Steuerung einer Fußgängerampel entwickelt, das nun weiter ausgebaut werden sollte. Alle Ein- und Ausgänge waren schon festgelegt. Die Ampel für die Autos liegt an D3 bis D5. Die Ampel für die Fußgänger hat zwei LEDs an A4 und A5. Zum ersten Mal in diesem Kurs wurden die analogen Anschlüsse als Ausgänge verwendet. An D2 ist wieder an den Taster angeschlossen und bildet den Anforderungskontakt mit eingeschaltetem internen Pullup.

Erst gab es Bedenken, für jede Ampel nur einen Vorwiderstand zu nehmen. Im Zustand Gelb-Rot werden zwei unterschiedliche LEDs parallel betrieben. Aber ein Versuch hat gezeigt, dass das mit unseren LEDs gerade noch geht.


//Ampel1
void setup() {
 pinMode(3,OUTPUT);
 pinMode(4,OUTPUT);
 pinMode(5,OUTPUT); 

 pinMode(A4,OUTPUT);
 pinMode(A5,OUTPUT); 

 pinMode(2,INPUT);
 pinMode(2,INPUT_PULLUP);
}

 void loop() {
 
digitalWrite(5,0);
  digitalWrite(4,0);
  digitalWrite(3,1);
  digitalWrite(A4,1);
  digitalWrite(A5,0);
  if(digitalRead(2)==0){
    delay(10000);
   
digitalWrite(5,0);
    digitalWrite(4,1);
    digitalWrite(3,0);
    digitalWrite(A4,1);
    digitalWrite(A5,0);
    delay(5000);
    digitalWrite(5,1);
    digitalWrite(4,0);
    digitalWrite(3,0);
    digitalWrite(A4,0);
    digitalWrite(A5,1);
    delay(10000);
 }

Das erste Programm hat schon funktioniert. Im Normalfall haben die Autos grün. Wenn dann ein Fußgänger auf den Anforderungskontakt drückt, muss er noch 10 Sekunden warten. Dann springt die Ampel für die Autos auf gelb und kurz darauf auf rot. Und der Fußgänger bekommt zehn Sekunden lang grün. Danach springt alles wieder auf den Anfang. 

Wir haben dann überlegt, an welchen Stellen es gefährlich wird. Der schnelle Übergang von Fußgänger-Grün auf Auto-Grün würde zu Unfällen führen, weil einige nicht schnell genug auf die andere Seite kommen. Und es fehlte bisher noch eine Rot/Gelb-Phase.

Hier kommt die verbesserte Version. Dabei wurde auch der Vorschlag umgesetzt, den Anschlüssen Namen zu geben, die die Funktion verraten. Das Programm wird dadurch besser lesbar. Außerdem wurden die einzelnen Ampelphasen mit Kommentaren versehen, sodass man sich schneller orientieren kann, wie das alles gemeint ist.
 

//Ampel2
int KFZ_gn =3;
int KFZ_ge =4;
int KFZ_rt =5;
int FG_gn =A5;
int FG_rt =A4;
int FG_taste = 2; 

void setup() {
  pinMode(KFZ_gn,OUTPUT);
  pinMode(KFZ_ge,OUTPUT);
  pinMode(KFZ_rt,OUTPUT);
  pinMode(FG_rt,OUTPUT);
  pinMode(FG_gn,OUTPUT); 

  pinMode(FG_taste,INPUT);
  pinMode(FG_taste,INPUT_PULLUP);

}

 void loop() {
  digitalWrite(KFZ_rt,0);   //Auto grün
  digitalWrite(KFZ_ge,0);
  digitalWrite(KFZ_gn,1);
  digitalWrite(FG_rt,1);
  digitalWrite(FG_gn,0);
  if(digitalRead(FG_taste)==0){ //Anforderungstaste gedrückt?
    delay(10000);               // noch 10 s warten
    digitalWrite(KFZ_rt,0);     //Auto gelb
    digitalWrite(KFZ_ge,1);
    digitalWrite(KFZ_gn,0);
    digitalWrite(FG_rt,1);
    digitalWrite(FG_gn,0);
    delay(1000);
    digitalWrite(KFZ_rt,1);   //beide rot, Sicherheitspause 5 s
    digitalWrite(KFZ_ge,0);
    digitalWrite(KFZ_gn,0);
    digitalWrite(FG_rt,1);
    digitalWrite(FG_gn,0);
    delay(5000);            
    digitalWrite(KFZ_rt,1);   //Fußgänger grün für 10 s
    digitalWrite(KFZ_ge,0);
    digitalWrite(KFZ_gn,0);
    digitalWrite(FG_rt,0);
    digitalWrite(FG_gn,1);
    delay(10000);          
    digitalWrite(KFZ_rt,1);   //beide rot, Sicherheitspause 5 s
    digitalWrite(KFZ_ge,0);
    digitalWrite(KFZ_gn,0);
    digitalWrite(FG_rt,1);
    digitalWrite(FG_gn,0);
    delay(5000);            
    digitalWrite(KFZ_rt,1);  //Auto gelb-rot 2 s
   
digitalWrite(KFZ_ge,1);
    digitalWrite(KFZ_gn,0);
    digitalWrite(FG_rt,1);
    digitalWrite(FG_gn,0);
    delay(2000);
  }                          //Zum Anfang: Auto grün
}

 
Diese Ampel passt zu einer wenig befahrenen Straße mit einem Fußgänger-Übergang. Wenn man eine Ampelkreuzung ohne Anforderungskontakte programmieren will, geht das sehr gut in diesem Stil. Wenn allerdings dann noch Anforderungskontakte dazu kommen, wird es kompliziert, weil ja zu jeder Zeit jemand auf den Knopf drücken kann. Zeitmessung und Beobachtung des Kopfes müssen also gleichzeitig erfolgen. Ein Mittel dazu ist eine Interrupt-Funktion, die automatisch den normalen Ablauf unterbricht, wenn sich der Zustand an einem Eingang ändert. 

Leander ist auch beruflich mit Ampeln beschäftigt. Er hat uns berichtet, dass es im wahren Leben noch viel komplizierter ist. Jede Ampelkreuzung hat zwei Computer, von denen einer nur die Fehlerüberwachung leistet. Er bemerkt, wenn eine Lampe durchbrennt und schaltet alles auf Notbetrieb, also gelbes Blinklicht für alle. Außerdem sind alle Ampeln mit einem zentralen Computer verbunden, der für die grüne Welle oder Anpassungen an den momentanen Verkehr sorgt. Außerdem kann die Straßenbahn oder ein Krankenwagen dort Vorfahrt anfordern. Man sieht, unsere gespielte Ampel ist noch weit davon entfernt.

Zum Schluss soll noch ein anderer Programmierstil gezeigt werden, der Schreibarbeit spart und den Quelltext viel kürzer macht. Statt einzelner Ausgänge werden ganze 8-Bit-Ports in einem Befehl verändert. Die Port-Register heißen PORTD für die Anschlüsse TX1 bis D8, PORTB für D8 bis D13 und PORTC für AD1 bis AD7. Außerdem gibt es die Datenrichtungsregister DDRD, DDRB, und DDRC. Die zugehörigen Bits in diesen Registern müssen auf 1 gesetzt werden, wenn Ausgänge gebraucht werden. Zusätzlich gibt es auch noch eigene Register, um Eingänge zu lesen, z.B. PIND für den Port D.  Genauere Informationen bekommt man im Datenblatt des Mikrocontrollers ATmega328.

//Ampel3

void setup() {
  DDRD = 0b00111001; //Auto rt,ge,gn ... TXD
  DDRC = 0b00110000; //Fußgänger gn,rt
}
 
void loop() {
  PORTD = 0b00001101; //Auto gn, D2 Pullup
 
PORTC = 0b00010000; //Fußgänger rt
  if((PIND & 4)==0){   //Taste an D2 gedrückt?
    delay(5000);
    PORTD = 0b00010101; //Auto ge
    delay(2000);
    PORTD = 0b00100101; //Auto rt
    delay(2000);
   
PORTC = 0b00100000; //Fußgänger gn         
    delay(10000);           
    PORTC = 0b00010000; //Fußgänger rt
    delay(5000); 
    PORTD = 0b00110101; //Auto ge/rt        
    delay(2000);
 
}

Man sieht, der Quelltext ist nur viel kürzer, obwohl der Controller das gleiche tut. Bei der Abfrage des Eingangs D2 gibt es noch eine Besonderheit. Wenn das Datenbit zum Bit 2 in PORTD eingeschaltet ist, während der zugehörige Port ein Eingang ist, wird der Pullup-Widerstand eingeschaltet. Das ist wichtig, damit die Schalterabfrage funktioniert. Wenn man dann den Inhalt von PIND liest, bekommt man den Zustand aller acht Bits. Aber nur das eine Bit interessiert hier, das zu D2 gehört. Mit der UND-Verknüpfung (& 4) werden alle anderen Bits auf null gesetzt. Das Ergebnis ist dann 4, wenn die Taste nicht gedrückt ist und 0, wenn jemand draufdrückt.
 

Download: Arduino7.zip 

Zurück: ArduinoKurs6.htm


Elektronik-Labor   Projekte   AVR