Arduino IDE - Programmieren für Einsteiger - [Teil 4]

Teil 4

In diesem Beitrag werden wir zeigen, wie man die Pins des Arduinos als Ein- und Ausgänge nutzen kann. Wir werden dafür Stück für Stück ein Programm schreiben, sowie einen Schaltkreis mit LED und Potentiometer aufbauen.

Was wird benötigt?

Anzahl Bauteil Anmerkung
Computer (Windows, Linux, MacOS)
1 Arduino Nano Mikrocontroller
1 Mini-USB Kabel
1 Steckbrett
LED
1 Widerstand 220 Ohm
1 Potentiometer 10 KOhm
1 Verbindungskabel

    Digitale und analoge Ein- und Ausgänge

    Für die Nutzung von Peripherie am Arduino stehen Ein- und Ausgänge zur Verfügung. Der Arduino Nano besitzt 8 analoge sowie 14 digitale Pins, die je nach Konfiguration als Ein- oder als Ausgang genutzt werden können. Zusätzlich können die analogen Anschlüsse auch als Digitalpins genutzt werden. Im ersten Beispiel hatten wir die Onboard-LED zum Blinken gebracht.

    Wir werden nun ein Potentiometer anschließen und damit eine externe LED dimmen. Außerdem werden wir die Blinkgeschwindigkeit regulieren. Wir nutzen einen analogen Eingang für das Potentiometer, sowie einen digitalen Ausgang für die LED. Außerdem werden wir verschiedene Presets erzeugen, die wir mit einem Taster durchschalten werden. Dafür verwenden wir einen Digitalpin als Eingang. Das alles soll unterbrechungsfrei funktionieren. Das heißt, wir möchten Änderungen ohne Verzögerung vornehmen können.

    Tipp: Die nun angeschlossene LED wird hier über die Pins des Arduinos mit Strom versorgt. Für anspruchsvollere Projekte sollte eine separate Stromversorgung genutzt werden. Das kann mit einer Transistorschaltung realisiert werden.

    Auf dem folgenden Bild sieht man die Bezeichnungen der Anschlüsse des Arduino Nanos. Die Pins mit den Bezeichnungen Pxy sind für Signalein- und Ausgabe. Der ATMega328p hat drei Ports, die mit PB, PC und PD gekennzeichnet sind und jeweils 8 Pins besitzen. Von Port B und C kann man jeweils nur 6 Pins nutzen. Von Port D stehen alle 8 Pins zur Verfügung.

    Man muss dabei nur darauf achten, dass die ersten beiden Pins 0 und 1 von Port D für die serielle Schnittstelle verwendet werden. Das heißt, dass es zu Beeinträchtigungen beim Upload auf den Arduino oder bei der Benutzung des seriellen Monitors kommt.

    Abbildung 16: Arduino Nano V3.0 Pinout

    Tipp: Nutzt man verschiedene Peripherie, ist es ratsam, erst eine Komponente anzuschließen und den Quellcode dafür zu schreiben. Danach dann die nächste Komponente und immer so weiter.

    Externe LED

    Wir bringen zuerst die externe LED zum Leuchten. Dann lassen wir sie blinken. Anschließend werden wir sie dimmen. Wir untersuchen dann, wie wir die Helligkeit sowie die Blinkgeschwindigkeit manipulieren können. Das können wir dann mit dem Potentiometer. Das schließen wir als Nächstes an. Wir analysieren danach, welche Daten wir vom Poti bekommen und wie wir sie nutzen können.

    Als Drittes schließen wir einen Taster an. Der muss bauartbedingt entprellt werden. Das werden wir mit Software umsetzen. Erst wenn die drei Komponenten für sich ordnungsgemäß funktionieren, werden wir unser eigentliches Programm schreiben.

    Beginnen wir mit der externen LED. Da wir bereits mit dem Blink-Beispiel ein Programm haben, mit dem wir die Onboard-LED nutzen können, werden wir darauf aufbauen. Wir ändern lediglich die Pinnummer. Im folgenden Bild sieht man, wie alles angeschlossen wird.

    Abbildung 17: Schaltplan Arduino Nano mit LED

    Wir laden dann nochmal das Blink-Beispiel in die Arduino IDE. Dort fügen wir vor dem setup() folgende Zeile ein:

    int led_pin = 3;

    Damit schaffen wir uns eine Variable, hinter der sich der Pin D3 verbirgt. Wir werden sie im Quellcode öfter brauchen. Sollten wir den Pin ändern wollen, müssen wir das dann nur an dieser Stelle tun. Nicht an allen anderen Stellen im Quellcode. Wir ändern nun folgende Zeile im setup():

    pinMode(LED_BUILTIN, OUTPUT);

    in:

    pinMode(led_pin, OUTPUT);

    Damit legen wir fest, dass Pin D3 ein Ausgang sein wird. Außerdem müssen wir nun noch weiter unten im loop() ebenfalls unseren LED-Pin angeben. Wir ersetzen daher überall LED_BUILTIN durch unsere Variable led_pin.

    Tipp: Man kann in der Arduino IDE unter dem Menü „Bearbeiten“ oder mit STRG+F die Suche nutzen und mit der Ersetzen-Funktion alles auf einmal ändern.

    Im Schaltplan kann man sehen, dass ein Widerstand benötigt wird. LEDs haben eine Flussspannung von ca. 1,5 bis 3 V. Oberhalb dieser Spannung sind sie (fast) ohne Widerstand leitfähig. Da die Ausgangsspannung an den Pins des Arduinos 5 V beträgt, muss man mit einem Widerstand den Strom begrenzen.

    Hintergrundinformation: Damit die LED leuchtet, muss die Spannung mindestens Flussspannung (engl. forward voltage) sein. Diese ist in Abhängigkeit der Farbe zwischen 1,5 und 3 Volt. Damit bleibt VCC=5V minus Flussspannung = ungefähr 2 bis 3 V. Diese Spannung könnte ungehindert zu einem unzulässig hohen Strom führen. Für den Arduino gilt:  Summe der Strömflüsse aus ALLEN Ein-/Ausgangspins soll  200mA nicht übersteigen! Also brauchen wir einen Widerstand von 220 - 330 Ohm. Beispiel: 3V / 300 Ohm = 10 mA, also okay.

    Das Ausgangssignal auf den digitalen Pins wird durch die Funktion digitalWrite() beeinflusst. Mit den beiden Parametern in den Klammern gibt man an, welcher Pin wie verändert werden soll. Möglich sind HIGH oder LOW bzw. 1 oder 0. Im Programmcode wird unser LED-Pin 3 zuerst auf HIGH gesetzt, also in dem Fall eingeschaltet. Dann wird mit delay() pausiert und anschließend der gleiche Pin auf LOW gesetzt, gefolgt von einer weiteren Pause. Das ganz wiederholt sich dann endlos.

    Wir laden das geänderte Blink-Beispiel auf den Arduino. Nun sollte statt der Onboard-LED die externe LED blinken.

    Blinken ohne Unterbrechung

    In dem Beispiel wird das Blinken mit der delay()-Funktion umgesetzt. Der Zahlenwert in den Klammern wird in Millisekunden angegeben. Der Nachteil hierbei ist, dass die Ausführung der Programmschleife jedes Mal ausgebremst wird. Mit Blick auf unser zukünftiges Programm wünschen wir uns aber ein nichtblockierendes Blinken. Es gibt auch dafür einen Beispielsketch. Dieser ist unter „02.Digital“ mit dem Namen „BlinkWithoutDelay“ zu finden. Dort tragen wir in der Zeile mit dem Inhalt: 

    const int ledPin =  LED_BUILTIN;

    statt LED_BUILTIN unsere 3 für Pin 3 ein und laden das Beispiel auf den Arduino. Die LED sollte jetzt genauso blinken wie vorher. Es ist äußerlich kein Unterschied zu sehen. Allerdings ist der Ablauf des Programmcodes nun ein anderer.

    Es wird zu Beginn das gewünschte Blinkintervall als konstante Variable definiert. Die Hauptschleife wird dann bei jedem Durchlauf die aktuelle Zeit in Millisekunden speichern und mit einem zuvor gespeicherten Wert vergleichen. Wurde das Zeitintervall noch nicht überschritten, passiert nichts. Ist die vergangene Zeit dann bei einem Durchlauf größer als das angegebene Intervall, wird der Zustand der LED umgeschaltet und auf den LED-Pin gegeben.

    Außerdem muss dann noch die aktuelle Zeit als alte Zeit gespeichert werden. Mit dieser wird dann ab dem nächsten Schleifenumlauf neu verglichen.

    Vereinfacht kann man sich das so vorstellen:

    Jemand ist für das Ein- und Ausschalten einer Lampe in einem Raum zuständig. Der Wechsel zwischen An und Aus soll nach einer bestimmten Zeit erfolgen. Der Lampenbeauftragte hat keine Uhr, mit der er die aktuelle Zeit sehen kann.

    Allerdings besitzt er eine andauernd laufende Stoppuhr, die er nicht anhalten kann. Beginnt der gesamte Ablauf, notiert er sich die aktuelle Zeit. In unregelmäßigen Abständen schaut er auf die notierte alte Zeit und subtrahiert sie von der aktuellen Zeit. Wie oft er raufschaut, wird variieren. Ist sein Zwischenergebnis größer, als seine Zeitvorgabe, betritt er den Raum und schaltet die Lampe ein bzw. aus. Er notiert sich die aktuelle Zeit wieder.

    Er schaut wieder nach der notierten Zeit und subtrahiert jedes Mal die aktuelle Zeit vom letzten aufgeschriebenen Zeitwert. Schaut er nicht auf die Uhr, kann er in der Zwischenzeit andere Dinge erledigen, z.B. Lampen in anderen Räumen umschalten. Mit der delay()-Funktion könnte der Lampenbeauftragte keine weiteren Aufgaben übernehmen. Er würde vor dem Raum mit der Lampe warten, bis er sie umschalten kann.

    Das BlinkWithoutDelay-Beispiel kann man an dieser Stelle schon etwas optimieren. Zuerst einmal sollten Variablen nicht ständig neu deklariert werden. Es würde sonst mit jedem Schleifendurchlauf neuer Speicher reserviert werden. Somit sollte diese Zeile an den Anfang des Quellcodes, allerdings ohne die Zuweisung aus der millis()-Funktion: 

    unsigned long currentMillis = 0;

    Außerdem kann die Variable ledState als Boolean deklariert und definiert werden, denn es gibt nur zwei Zustände. Im ursprünglichen Beispiel wird sie als Integer deklariert, aber mit Boolean-Werten definiert. Nämlich LOW und HIGH. Das ändern wir, in dem wir die Variable als bool deklarieren. An der Stelle in der Hauptschleife, an der der LED-Status umgeschaltet wird, kann man dann aus fünf Zeilen eine Zeile machen:

    if (ledState == LOW) {
        ledState = HIGH;
    } else {
       ledState = LOW;
    }
    

    wird geändert in:

    
    
    ledState = !ledState;

    Statt mit if-Anweisungen abzufragen, ändern wir einfach nur den alten Zustand in den jeweils anderen. Das Ausrufezeichen steht für ein logisches NICHT. Also findet eine Negierung statt. NICHT LOW ist ein HIGH und NICHT HIGH ist ein LOW.

    Der optimierte Quellcode sieht dann so aus:

    const int ledPin =  3;
    bool ledState = LOW;
    unsigned long previousMillis = 0;
    const long interval = 1000;
    unsigned long currentMillis = 0;
    
    void setup() {
        pinMode(ledPin, OUTPUT);
    }
    
    void loop() {
        currentMillis = millis();
      
        if (currentMillis - previousMillis >= interval) {
            previousMillis = currentMillis;
            ledState = !ledState;
            digitalWrite(ledPin, ledState);
        }
    }

    PWM - Pulsweitenmodulaton

    Wir möchten nun die LED nicht nur blinken lassen, sondern auch ihre Helligkeit verändern. Eine LED kann nicht einfach wie eine Glühbirne mit variierender Spannung heller oder dunkler eingestellt werden. Allerdings kann man sich mit einem Trick aushelfen. Das Prinzip ist einfach. Schaltet man eine LED nur sehr kurz ein und gleich wieder aus, kann sie nicht ihre volle Helligkeit erreichen. Je kürzer die Zeit, desto dunkler bleibt die LED.

    Da es sich dabei um Zeiten im Millisekundenbereich handelt, muss das eine elektronische Schaltung übernehmen. In dem Bild des Arduino Nanos mit seinen Anschlüssen sieht man die Bezeichnung PWM an einigen der Pins.

    An diesen Anschlüssen ist es möglich, die eingebaute Pulsweitenmodulation zu verwenden. Dafür steht dann die Funktion analogWrite() zur Verfügung. Sie bekommt in den Klammern zwei Parameter übergeben. Zum einen den Pin, an dem die LED angeschlossen ist. Zum anderen einen Wert, der für die Einschaltdauer steht. Allerdings ist es nicht einfach eine Zeitangabe. Um das näher zu erläutern, brauchen wir ein Zeitdiagramm von einer Pulsweitenmodulation. In der Arduino Reference unter folgende Link ist die PWM genauer erklärt: https://www.arduino.cc/en/Tutorial/PWM. Von dort stammt folgende Grafik:

    Abbildung 18: Pulsweitenmodulation         Quelle: Timothy Hirzel, www.arduino.cc


    Die Funktion analogWrite() erzeugt ein Rechtecksignal. Deren Periodendauer ist u.a. abhängig vom Prozessortakt. Die Dauer des High-Pegels wird dabei prozentual bestimmt. Diese nennt sich „Duty Cycle“. Je kleiner der Duty Cycle ist, desto dunkler ist die LED.

    Tipp: Man kann damit außerdem auch die Geschwindigkeit von Gleichstrommotoren steuern.

    Der zweite Parameter der analogWrite()-Funktion entspricht dem Duty Cycle. Allerdings gibt man ihn nicht von 0% bis 100% an, sondern von 0 bis 255. Also entspricht der Wert 255 dem Maximum 100%. Die Zwischenwerte kann man aus der letzten Grafik ablesen.

    Tipp: Es ist möglich, eine PWM auch direkt über die Register der CPU zu steuern. Einen Artikel dazu gibt es auch in der Arduino Reference unter folgendem Link: https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM.
    Dadurch kann man mehr Einfluss auf die Taktrate und den Duty Cycle nehmen.

    In den Beispielen der Arduino IDE ist in den Basics auch ein „Fade“-Projekt zu finden, das eine PWM für eine LED erzeugt, die dann Pulsiert. Wir schauen uns ein wenig davon ab. Die Schaltung belassen wir so wie im letzten Beispiel.

    Wir erzeugen einen neuen Sketch und tragen wieder unseren LED-Pin 3 ein. Im setup() initialisieren wir Pin 3 als Ausgang. Nun kommen wir zur loop()-Funktion. Da es sich hierbei bereits um eine Schleife handelt, können wir für jeden Durchlauf die Helligkeit verändern. Dafür brauchen wir eine Variable, deren Wert sich stetig verändert. Wir nennen diesen Zähler helligkeit. Da der zweite Parameter der analogWrite()-Funktion nur Werte von 0 bis 255 zulässt, müssen wir den Bereich eingrenzen. Das regeln wir mit einer if-Abfrage und einer Multiplikation mit 1 oder -1. Der Quellcode sieht dann folgendermaßen aus:

    const int ledPin =  3;
    int helligkeit = 0;
    int richtung = 1;
    
    void setup() {
        Serial.begin(115200);
        pinMode(ledPin, OUTPUT);
    }
    
    void loop() {
        Serial.println(helligkeit);
        analogWrite(ledPin, helligkeit);
        helligkeit = helligkeit + richtung;
        if (helligkeit > 254 || helligkeit < 1) {
            richtung = richtung * -1;
        }
        delay(10);
    }
    
    

    Für die Zählrichtung nutzen wir eine weitere Variable namens richtung. Sie wird entweder den Wert 1 oder -1 annehmen. Als zusätzliche Hilfe nutzen wir den seriellen Monitor. Im setup() wird die serielle Schnittstelle dafür initialisiert.

    Mit Richtung ist gemeint, ob der Zähler von 0 bis 255, oder von 255 bis 0 zählt. Wir geben im loop() dann die Werte für die Helligkeit aus, die wir auf den Ausgang des LED-Pins geben. Mit analogWrite(ledPin, helligkeit) bekommt die LED den aktuellen Wert für den Duty Cycle. Da wir nicht immer nur eine Helligkeit sehen wollen, sondern eine pulsierende LED, muss sich dieser Wert ändern. Dafür addieren wir in jedem Schleifendurchlauf zur Helligkeit den Wert der Richtung hinzu.

    Zu Beginn erhöht sich der Wert also immer um 1, da wir die Variable mit 1 deklariert und definiert haben. Das geschieht mit helligkeit = helligkeit + richtung. Die Variable helligkeit wird mit richtung addiert. Das Ergebnis wird wieder in die Variable helligkeit geschrieben. Der Wert würde sich endlos erhöhen. Die darauffolgende if-Anweisung grenzt die Werte von 0 bis 255 ein. In der Bedingung steht verallgemeinert:

    wenn die Helligkeit größer ist als 254 oder die Helligkeit kleiner ist als 1

    Ist eine der beiden Bedingungen erfüllt, ändern sich die Variable richtung entweder von 1 in -1 oder von -1 in 1. Denn 1 * -1 = -1 und -1 * -1 = 1.

    Um nun die Zeit des Pulsierens zu beeinflussen, setzen wir nach jedem Durchlauf eine kurze Pause. Damit kann man zusätzlich zum CPU-Takt die Periodendauer des Rechtecksignals beeinflussen. Hier nutzen wir vorerst die delay()-Funktion. Später müssen wir das ändern, damit der Ablauf nicht blockiert (siehe oben).

    Laden wir das Programm auf den Arduino, sollte die LED an Pin 3 pulsieren und im seriellen Monitor der Zähler zwischen 0 und 255 in 1er-Schritten pendeln.

    Potentiometer als analoger Sensoreingang

    Wir können nun eine LED blinken oder pulsieren lassen. Als nächstes möchten wir die Helligkeit der blinkenden LED verändern. Dafür nutzen wir ein Drehpotentiometer. Dabei handelt es sich um einen veränderbaren Widerstand. Die Spannung, die am analogen Eingang des Arduinos über diesem Widerstand abfällt, kann als Ganzzahl gemessen werden. Dafür werden die eingebauten Analog-Digital-Wandler genutzt.

    Der Wertebereich für die Spannung reicht von 0V bis 5V. Der Wandler des Arduino Nanos hat eine Bitbreite von 10 Bit. Somit sind Werte von 0 bis 1023 möglich, wobei 1023 der Maximalspannung von 5V entspricht.  Wir ergänzen unsere vorherige Schaltung nun um das Potentiometer. In meinem Fall mit einem Widerstand von 10 KOhm. Die folgende Abbildung zeigt den Aufbau.

     Abbildung 19: Schaltplan Arduino Nano mit LED und Poti


    Die LED ignorieren wir für den Moment. Wir lesen nur den Wert des Potentiometers ein. Analoge Pins müssen wir im setup() nicht initialisieren. Wir brauchen nur die serielle Schnittstelle, um uns den Eingangswert des Potentiometers im Monitor anzusehen.

    Der Quellcode sieht dann vorerst folgendermaßen aus:

    void setup() {
        Serial.begin(115200);
    }
    
    void loop() {
        Serial.println(analogRead(A0));
    }
    


    Mit der Funktion analogRead(A0) wird der Wert am angegebenen Analogeingang eingelesen. Wir sparen uns hier die Deklaration von Variablen. Wir schreiben den Aufruf analogRead(A0) direkt in die Ausgabe von println() für die serielle Schnittstelle. Laden wir das Programm auf den Arduino und öffnen den seriellen Monitor, können wir die Zahlenwerte von 0 bis 1023 verändern.

    Tipp: Die analogen Pins können auch als digitale Ein- oder Ausgänge genutzt werden. 

    LED dimmen

    Wir werden nun das Potentiometer nutzen, um die Helligkeit der LED zu verändern. Dabei wird uns als erstes auffallen, dass der Duty Cycle der PWM Werte von 0 bis 255 zulässt, der analoge Eingang aber einen Wertebereich von 0 bis 1023 liefert. Darum kümmern wir uns später. Zuerst bringen wir die Quellcodes für die LED und das Potentiometer zusammen.  Das sieht dann wie folgt aus:

    const int ledPin =  3;
    
    void setup() {
        pinMode(ledPin, OUTPUT);
    }
    
    void loop() {
        analogWrite(ledPin, analogRead(A0));
    }
    

    Statt der Ausgabe auf dem seriellen Monitor nutzen wir nun die LED. Wir setzen die Funktion analogRead(A0) direkt als zweiten Parameter in analogWrite() ein. Somit wird der Wert am analogen Eingang A0 direkt für die PWM am LED-Pin 3 benutzt. Laden wir das Programm auf den Arduino, stellen wir fest, dass beim Drehen des Potentiometers die LED dunkler wird und irgendwann wieder auf die volle Helligkeit springt. Oder sie wird heller und springt dann auf die geringsten Helligkeit.

    Das liegt an den unterschiedlichen Wertebereichen. 0 bis 255 entsprechen 8 Bit. Wird der Wertebereich überschritten, beginnt er bei 0. Oder wird er unterschritten, beginnt er bei 255. Für solch einen Fall bietet die Arduinobibliothek eine Funktion namens map().  Damit können wir festlegen, dass ein Wertebereich einem anderen Wertebereich entspricht. Die Umsetzung sieht dafür eine Quelle vor und die Angaben der Wertebereiche. Die Funktion gibt dann einen entsprechenden Wert zurück. 

    Wir ändern den Quellcode wie folgt und laden ihn auf den Arduino hoch:

    const int ledPin =  3;
    int input = 0;
    
    void setup() {
        pinMode(ledPin, OUTPUT);
    }
    
    void loop() {
        input = analogRead(A0);
        input = map(input, 0, 1023, 0, 255);
        analogWrite(ledPin, input);
    } 

    Für die bessere Lesbarkeit wurde nun eine weitere Variable namens input eingefügt. In der Hauptschleife lesen wir nun zuerst den Wert am analogen Eingang A0 ein. Anschließend passen wir den Wertebereich an. Dafür nutzen wir die gleiche Variable als ersten Parameter, also als Quelle. 0 bis 1023 ist der Quellwertebereich und 0 bis 255 der Zielwertebereich für die PWM.

    Wir überschreiben dann mit dem Ergebnis wieder die gleiche Variable und geben sie im letzten Schritt an die PWM als Helligkeitswert. Laden wir das Programm auf den Arduino und drehen das Potentiometer, ändert sich die Helligkeit ohne Sprünge.

    Um den Quelltext zu verkürzen, kann man Variablen auch weglassen. Darunter leidet die Lesbarkeit, aber es sind weniger Zeilen und es wird weniger Speicher verbraucht. Der Code sieht dann wie folgt aus: 

    void setup() {
        pinMode(3, OUTPUT);
    }
    
    void loop() {
        analogWrite(3, map(analogRead(A0), 0, 1023, 0, 255));
    }
    

     

    Auf vielfachen Wunsch hier der Link zum Download als pdf (gültig bis 31.07.2020)

    https://www.dropbox.com/s/qop8aubv7b2dkjo/Arduino%20IDE_Teil4.pdf?dl=0

     

    1 Kommentar

    oliviero lodi

    oliviero lodi

    complimenti per la completa indicazione come eseguire i collegamenti e ile variabile per l’utilizzo di arduino.
    distinti saluti Oliviero

    Einen Kommentar hinterlassen

    Alle Kommentare werden vor der Veröffentlichung moderiert