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