Raspberry Pi Pico Kurs – #6 – PWM, ADC und Kommunikation mit dem Computer

Lesezeit 19 min.

Im vorherigen Abschnitt haben wir grundlegende Variablen, bedingte Anweisungen und Schleifen kennengelernt. Außerdem habe ich Ihnen ein wenig über die Fehler und die einfache Art, sie zu lösen, erzählt. Im heutigen Material werfen wir einen genaueren Blick auf einige der Funktionen des RP2040 Mikrocontrollers. Dazu gehören PWM, USB und ADC. Natürlich werden Sie neben der theoretischen Beschreibung auch etwas über die praktische Anwendung dieser Funktionen erfahren, also lassen Sie uns loslegen.

Kaufen Sie ein Set, um das Programmieren mit dem Raspberry Pi Pico W zu erlernen, und nutzen Sie die Vorteile des Kurses, der im Botland Blog verfügbar ist!

Im Set enthalten: Raspberry Pi Pico W-Modul, Kontaktplatte, Leitungen, LEDs, Widerstände, Tasten, Fotowiderstände, digitale Licht-, Temperatur-, Feuchtigkeits- und Drucksensoren, OLED-Display und ein USB-microUSB-Kabel.

Vor dem Start sollte das Team zusammengestellt werden

Ein Set von Komponenten für den Raspberry Pi Pico Kurs.

Wer das Programmieren anhand von realen Projekten erlernen möchte, braucht natürlich die richtige Ausrüstung, aber keine Sorge – Sie müssen jetzt nicht von einem Artikel zum nächsten springen und eine Liste der benötigten elektronischen Komponenten erstellen. Im Botland-Shop ist ein fertiger Bausatz erhältlich, der alle notwendigen Komponenten enthält, um die in der Tutorial-Reihe beschriebenen Projekte mit dem Raspberry Pi Pico durchzuführen.

In dem fertigen Set von Elementen finden Sie:

  • Raspberry Pi Pico W,
  • MicroUSB-Kabel,
  • Kontaktplatte,
  • Ein Set von Anschlusskabeln in drei Ausführungen,
  • Ein Set von LEDs in drei Farben,
  • Ein Set der in der Elektronik am häufigsten verwendeten Widerstände,
  • Tact Switch-Tasten,
  • Fotowiderstände,
  • Digitaler Lichtsensor,
  • Digitaler Feuchtigkeits-, Temperatur- und Drucksensor,
  • OLED-Display.

Impulsbreitenmodulation - PWM

Bisher haben die vom Raspberry Pi Pico W gesteuerten LEDs entweder geleuchtet oder nicht, und es gab keine Möglichkeit, dieses Element auf eine etwas “flüssigere” Weise zu steuern. Die Gewinnung eines analogen Signals aus digitalen Zuständen ist gar nicht so einfach; diese Art von Funktionalität kann nur mit Digital-Analog-Wandlern (DACs) erreicht werden. Es gibt jedoch eine Möglichkeit, wie wir ein analoges Signal erzeugen und damit die Helligkeit der LED steuern können.

Beispiele für PWM-Signalformen mit 50%, 75% und 25% Füllung. (https://en.wikipedia.org/wiki/Pulse-width_modulation)

Pulsweitenmodulation (PWM) ist ein Begriff, der sich auf eine Funktion bezieht, die in den meisten modernen Mikrocontrollern implementiert ist. Wie der Name schon sagt, können wir damit die Breite eines einzelnen vom RP2040 erzeugten Impulses manipulieren. In den Beispielen, die wir durchgearbeitet haben, kam es vor, dass die LED jede halbe Sekunde synchron aktiviert wurde. In diesem Fall kann man sagen, dass der gesamte Impuls eine Sekunde dauerte und eine Füllrate von 50% hatte, da die Diode nur die Hälfte der verfügbaren Zeit arbeitete. Für einen Mikrocontroller ist eine Sekunde jedoch fast eine Ewigkeit und er kann erfolgreich Impulse erzeugen, deren Zeit viel kürzer ist. Zu diesem Zweck werden spezielle Befehle verwendet, die von den internen Modulen des Prozessors unterstützt werden, damit wir das System nicht immer wieder anhalten müssen.

Wir werden die Füllung eines solchen Impulses kontrollieren, d.h. die Zeit, während ein hoher Zustand am Ausgang aufrechterhalten wird. Indem wir die Füllung ändern, erhalten wir eine Art Imitation eines analogen Signals. Die Imitation ist hier die korrekteste Aussage, da es sich immer noch um ein digitales Signal handelt, nur dass es dank der Trägheit des menschlichen Auges so aussieht, als ob wir die Helligkeit der Diode steuern, als ob wir ihre Versorgungsspannung ändern. Darüber hinaus werden PWM-Signale auch in anderen Schaltungen und Systemen erfolgreich eingesetzt. Es gibt Sensoren, bei denen der gemessene Wert, z.B. der Sauerstoffgehalt in der Luft, genau durch das PWM-Signal am Ausgang des Moduls dargestellt wird. Darüber hinaus werden Signale, deren Impulsbreite verändert wird, zur Steuerung von Servomechanismen in der Robotik verwendet.

				
					#include "pico/stdlib.h"
#include "hardware/pwm.h"

#define GREEN_LED 0 //assigning names of sspecyfic values

uint8_t fill = 0;
uint8_t changes = 1;

int main() {
    gpio_init(GREEN_LED); //initialization and setting of pin mode
    gpio_set_function(GREEN_LED, GPIO_FUNC_PWM);

    uint8_t slice_num = pwm_gpio_to_slice_num(GREEN_LED); //assignment of values to variables slice_num and channel
    uint8_t channel = pwm_gpio_to_channel(GREEN_LED);
    
    pwm_set_wrap(slice_num, 255); //set the maximum PWM range (255=100%)

    pwm_set_enabled(slice_num, true); //PWM signal activation
    
    while (true)
    {
        pwm_set_chan_level(slice_num, channel, fill); //setting the signal filling to the value stored in fill

        if (fill < 255) //if fill <255 assign value + change
        {
            fill = fill + changes;
        }
        Else //incompatible condition set fill to 0
        {
            fill = 0;
        }

        sleep_ms(10);

    }
    
}

				
			

Kommen wir nun direkt zum Code für die praktische Nutzung des PWM-Signals zur Steuerung der LED. Um diese Funktionalität zu testen, habe ich ein neues Projekt erstellt, das ich pwm_led genannt habe. Die Schaltung selbst, die auf der Kontaktplatte aufgebaut ist, bleibt dagegen unverändert, obwohl wir in diesem Beispiel nur eine LED verwenden werden.

Das vorbereitete Programm ermöglicht es uns, die LED mit einem PWM-Signal zu steuern, d. h. wir werden die LED schrittweise aufhellen, indem wir die Impulsfüllung erhöhen. Die Zeit, während der hohe Zustand auf der Leitung aufrechterhalten wird, wird durch eine Zahl bestimmt, wobei 0 für 0% (die LED leuchtet nicht) und 255 für 100% (die LED leuchtet während der gesamten Dauer des Impulses) steht.

Der Start des Programms sieht vertraut aus, obwohl es einige neue Funktionen gibt. Wie Sie sehen können, habe ich zum ersten Mal eine zusätzliche Bibliothek hardware/pwm.h eingebunden. Sie wird benötigt, weil wir die Hardware-Unterstützung des Mikrocontrollers bei der Erzeugung des PWM-Signals nutzen werden und die Funktionen, die diesen Prozess beschreiben, in dieser Bibliothek beschrieben sind. Wir müssen auch ein zusätzliches Anweisungspaket in der Datei CMakeLists.txt hinzufügen, aber darum kümmern wir uns später. Vor der Hauptfunktion main gab es die bekannte Definition und Deklaration von zwei Variablen vom Typ uint8_t – fill und changes.

Angesichts dieser Variablen müssen wir uns einen Moment zurückhalten. In den vorherigen Beispielen wurden die Variablen innerhalb der Hauptfunktion deklariert, normalerweise um die Befehle herum, die sie verwendeten. Dies waren lokale Variablen, die wir nur innerhalb der Funktionen verwenden konnten, in denen sie erzeugt wurden. Variablen, deren Deklarationen oberhalb der Hauptfunktion platziert sind, können als globale Variablen bezeichnet werden, was bedeutet, dass sie im gesamten Programm verwendet werden können. Natürlich könnten die Variablen fill und changes, die nacheinander die Füllung und die Änderung der Füllung bei jedem Programmzyklus angeben, weiter unten im Code erstellt werden, aber um das Konzept der lokalen und globalen Variablen zu erklären, habe ich sie hier platziert. Die Zahlen 0 und 1 neben den Namen dieser Variablen müssen ebenfalls erklärt werden. Mit diesen können wir den Standardwert festlegen, den die Variablen annehmen, sobald das Programm gestartet wird.

Wir starten die Funktion main mit den bekannten Befehlen, wobei dieses Mal dank GPIO_FUNC_PWM der Pin, der GREEN_LED zugewiesen ist, als Pin fungiert, der die PWM-Funktionalität unterstützt. Als nächstes deklarieren wir zwei Variablen slice_num und channel, denen wir sofort die Werte zuweisen, die von den Befehlen pwm_gpio_to_slice_num und pwm_gpio_to_channel gelesen werden, deren Argument eine konkrete Anschlussnummer des RP2040 ist. Wir müssen nicht darüber nachdenken, was in diesen Variablen gespeichert ist. Sie werden benötigt, weil das PWM-Signal, obwohl es einer bestimmten Leitung zugewiesen ist, tatsächlich durch Slice, Channel und Fill bestimmt wird, die wir bereits deklariert haben.

Mit dem nächsten Befehl, pwm_set_wrap, legen wir den numerischen Füllbereich des PWM-Signals fest. In die Argumente der Funktion geben wir den zuvor gelesenen Slice und einen Wert ein, der die 100%ige Füllung eines einzelnen Impulses beschreibt. Im Beispiel ist es 255, aber jeder beliebige Wert kann dort erfolgreich eingefügt werden. Beachten Sie jedoch, dass er sich auf die Variable fill bezieht, deren Typ uint8_t mit einem begrenzten Bereich ist. Für größere Werte sollten Sie Variablentypen mit größeren Bereichen in Betracht ziehen, wie z.B. uint16_t oder uint32_t. Mit dem nächsten Befehl pwm_set_enable aktivieren wir das PWM-Signal auf der Leitung, deren Slice-Wert slice_num zugewiesen ist.

Der Hauptteil des Programms bestand aus einer unendlichen while-Schleife, innerhalb derer wir im ersten Schritt den Füllwert des PWM-Signals an dem durch slice_num und channel beschriebenen Pin festlegen. Der Füllwert selbst wird in fill gespeichert, dessen Standardwert Null ist, so dass die Diode beim ersten Start ausgeschaltet wird. Dann wurde eine bedingte Funktion platziert, um den Füllwert zu überprüfen. Wenn er kleiner als 255 ist, wird der Füllwert in sich selbst geändert und der Wert unter der Variable changes hinzugefügt. Mit anderen Worten: Jedes Mal, wenn die bedingte Funktion ausgeführt wird, erhöht sich der Füllstand um eins, bis die Bedingung fill < 255 nicht mehr erfüllt ist. Die Füllung wird dann wieder auf Null reduziert. Am Ende des Programms gab es eine leichte Verzögerung, die das Programm für 10 ms pausierte. In einer kurzen Zusammenfassung der Schleife, die vom Raspberry Pi Pico W ausgeführt wird, bis der Strom abgeschaltet wird, kann man also sagen, dass die Füllung des PWM-Signals alle 10ms um eins erhöht wird, bis sie 255 überschreitet und dann die Diode ausgeschaltet wird.

CMakeLists.txt für das Projekt pwm_led.

Wie ich am Anfang erwähnt habe, müssen wir, wenn wir die Hardware-Unterstützung für PWM-Signale nutzen wollen, die Bibliothek hardware/pwm.h in die Datei CMakeLists.txt aufnehmen. Ihre Deklaration wird innerhalb des Befehls target_link_libraries platziert, wie in der obigen Grafik zu sehen ist. In Zukunft werden wir aufeinanderfolgende Bibliotheken immer mit Leerzeichen trennen.

Das laufende Programm pwm_led sollte sich identisch zu dem im Video verhalten. Die LED wird allmählich heller und wenn sie ihre volle Leistung erreicht hat, erlischt sie und der ganze Zyklus beginnt von vorne. Wie ich bereits erwähnt habe, ist die Steuerung der Diode jedoch nicht gleichmäßig, sie blinkt mit einer sehr hohen Frequenz, so dass es für das menschliche Auge nicht wahrnehmbar ist. Mit jeder Programmschleife erhöht sich auch die Zeitspanne, in der ein einzelner Impuls im hohen Zustand ist, so dass es den Anschein hat, dass die LED immer heller leuchtet. Ich möchte Sie ermutigen, zu experimentieren und den Wert der Variable changes und die Zeit, für die das Programm angehalten wird, zu ändern. Außerdem können Sie versuchen, den numerischen Bereich der Füllung zu ändern und dabei den Typ der Variablen fill zu berücksichtigen.

Kommunikation mit dem Computer, d.h. USB

Bis jetzt haben wir nur den Mikrocontroller selbst und die mit ihm verbundenen elektronischen Komponenten verwendet. Es ist an der Zeit, das zu ändern und zum ersten Mal mit einem größeren Gerät wie einem Computer zu kommunizieren.

In Tutorials, die auf kleinen Embedded-Plattformen basieren, ist die Kommunikation mit dem Computer in der Regel eng mit einer Diskussion über die UART-Schnittstelle verbunden. Ich habe den Eindruck, dass dies hauptsächlich aus Artikeln über den Arduino stammt, bei dem die Schnittstelle eine Hardware ist, die über einen bestimmten Chip mit dem USB-Anschluss des Boards verbunden ist. Die Entwickler des Raspberry Pi Pico und des RP2040 Mikrocontrollers selbst haben sich jedoch für eine etwas anspruchsvollere Lösung entschieden. Natürlich verfügt der Prozessor über Hardware-UART-Unterstützung, aber die Kommunikation mit dem Computer kann auch einfacher über USB hergestellt werden. Die auf dem Laminat des RPI sichtbare microUSB-Buchse wird nicht nur für die Stromversorgung und die Programmierung verwendet, sondern über sie können auch Daten mit dem Computer ausgetauscht werden. Wir werden auf den hier erwähnten UART, der eine der einfachsten seriellen Schnittstellen ist, in einem der letzten Artikel dieser Serie zurückkommen.

MicroUSB-Anschluss auf der RPI Pico W-Platine.

Bevor wir jedoch zum Programm übergehen, möchte ich Ihnen ein wenig über USB selbst erzählen. Die erste Assoziation mit USB ist in erster Linie die Art des Anschlusses, den wir in den meisten heute hergestellten Geräten finden können. Diese Anschlüsse gibt es in verschiedenen Standards und Typen, die sich in den physischen Abmessungen, der Anzahl der Anschlüsse und den elektrischen Parametern unterscheiden. Das Wichtigste für uns ist jedoch USB (Universal Serial Bus), ein universeller serieller Bus ist, mit dem Daten zwischen zwei Geräten übertragen werden können. Zu diesem Zweck verwendet die Grundkonfiguration ein Paar D+ und D- Signale, die sich zusätzlich zu den Stromkabeln im Standard-USB-Kabel befinden. Auf der von uns verwendeten Platine sind die USB-Steckerkabel direkt mit dem Mikrocontroller verbunden. Wenn Sie also das Kabel an den Computer anschließen, stellen Sie eine Verbindung zum Prozessor her, ohne dass etwas dazwischen ist. Der RP2040 unterstützt dieses Protokoll hardwareseitig, so dass ich Ihnen gleich einige Beispiele zeigen werde, bei denen wir mit einem einfachen Befehl Nachrichten senden können, die auf dem Computerbildschirm erscheinen. Eine solche Lösung ist für die Programmierung und das Debugging äußerst nützlich. Bis jetzt waren unsere Programme einfach und es bestand keine Notwendigkeit, darin so genannte ‘Flaggen’ zu erstellen, aber gleich werden wir zu komplizierteren Konstruktionen übergehen, in denen wir entsprechende Nachrichten platzieren werden. Diese ermöglichen es uns, in Echtzeit zu erfahren, welcher Teil des Codes vom Mikrocontroller ausgeführt wird.

Die Schaltung, die wir vorhin gebaut haben, kann gleich bleiben. Zunächst werden wir nur die Platine und das USB-Kabel verwenden, aber in späteren Codes werden wir versuchen, auch die LED zu steuern.

Senden von Nachrichten an den Computer

				
					#include "pico/stdlib.h"

int main() {
    stdio_init_all(); //initialization of the stdio.h library
    
    while(true) {
        printf("RPI Pico W sendet die Nachricht\n");
        sleep_ms(1000);
    }
}

				
			

Für die folgenden Beispiele habe ich ein Projekt namens usb_communication vorbereitet, das ich in diesem und den folgenden Programmen verwenden werde.

Nehmen wir als ersten Schritt einen extrem einfachen Code, dessen Aufgabe es ist, jede Sekunde den Text ‘RPI Pico W sendet eine Nachricht’ an den Computer zu senden. Wie Sie sehen können, wird diese Aufgabe von der Funktion printf übernommen, die zu den grundlegenden Befehlen der Sprache C gehört. Sie ermöglicht es uns, Nachrichten zu erstellen, die nur aus Text oder aus Text zusammen mit Variablen bestehen, wie ich später noch erläutern werde. Die Nachricht, die wir an den Computer senden wollen, steht in doppelten Anführungszeichen, und am Ende steht ein mysteriöses ‘\n’. Dies ist das Zeilenende-Zeichen. Es ist nicht sichtbar, aber es teilt dem Computer mit, dass der nächste empfangene Text in einer neuen Zeile angezeigt werden soll.

Damit der printf-Befehl korrekt kompiliert werden kann, müssen wir die Bibliothek <stdio.h> einbinden, die eine ganze Reihe grundlegender C-Sprachfunktionen enthält. Außerdem muss diese Bibliothek initialisiert werden; dafür wird das Konstrukt stdio_init_all() verwendet.

CMakeLists.txt für usb_communication.

Wenn wir wollen, dass das Projekt korrekt kompiliert wird, müssen wir außerdem drei Befehle in die Datei CMakeLists.txt aufnehmen, von denen einer ein Kommentar ist.

				
					# enable usb output, disable uart output
pico_enable_stdio_usb(usb_communication 1) 
pico_enable_stdio_uart(usb_communication 0)

				
			

Damit aktivieren wir den so genannten Output Stream am USB-Port und deaktivieren gleichzeitig den UART-Ausgang.

Im Monitor der seriellen Schnittstelle sichtbare Meldungen.

Nachdem Sie das Projekt erstellt und die Datei .uf2 in den RPI-Speicher hochgeladen haben, meldet sich das Board als serielles USB-Gerät unter einem der COM-Ports. Details können Sie im Gerätemanager einsehen. Wenn Sie die vom Mikrocontroller gesendeten Nachrichten überprüfen möchten, können Sie ein beliebiges Programm verwenden, mit dem Sie die seriellen Schnittstellen verwalten können, oder Sie nutzen die in Visual Studio Code integrierte Funktionalität für diesen Zweck. Unten finden Sie den SERIAL MONITOR, den Sie öffnen können, um verschiedene Optionen zu sehen. Die wichtigste davon ist Port. Hier wird der RPI unter dem Namen USB Serial Device angezeigt, in meinem Fall dem COM3-Port zugewiesen. Da es sich hierbei um einen virtuellen seriellen Anschluss handelt, brauchen wir die anderen Optionen nicht zu beachten. Wenn Sie auf die Schaltfläche Überwachung starten klicken, wird der Anschluss geöffnet und die vom Prozessor gesendete Nachricht “RPI Pico W sendet Nachricht” sollte auf dem Bildschirm des Computers erscheinen. Dank der Verwendung eines Zeilenendezeichens (\n) beginnt jede Nachricht in einer neuen Zeile.

Numerische Nachrichten

				
					#include "pico/stdlib.h"

uint8_t counter = 0;
float pi = 3.1415926;

int main() {
    stdio_init_all(); //initialization of the stdio.h library
    
    while(true) {
        printf("RPI Pico W sendet die Nachricht\n");
        printf("counter value = %d\n", counter); //%d = counter variable
        printf("pi = %f\n", pi); //%f = pi variable
        counter++;
        sleep_ms(1000);
    }
}

				
			

Neben den eigentlichen Textnachrichten können auch Variablen, genauer gesagt die in ihnen gespeicherten Werte, übermittelt werden. Zu diesem Zweck können wir auch den Befehl printf verwenden, allerdings in einer leicht veränderten Struktur. Oben sehen Sie eine erweiterte Version des Codes aus dem vorherigen Beispiel. Ich habe zwei Variablen hinzugefügt: eine Ganzzahl – counter, deren Startwert Null ist, und eine Fließkommazahl – pi, die den ungefähren Wert der Zahl pi speichert. Ich habe zwei zusätzliche printf-Funktionen in den Hauptteil des Programms eingefügt, in denen Sie die etwas mysteriösen Notationen %d und %f bemerken werden. Sie fügt Variablen in die zu erstellende Nachricht ein, die weiter unten nach dem Dezimalpunkt stehen. Mit anderen Worten: %d wird durch den Wert counter ersetzt und pi wird an die Stelle von %f gesetzt. Ganz am Ende habe ich auch eine Erhöhung des Zählerwerts um eins eingefügt, so dass bei jedem Durchlauf der Schleife ein anderer Zustand angezeigt wird.

Im Monitor der seriellen Schnittstelle sichtbare Meldungen.

Nachdem wir den Code ausgeführt und den Monitor der seriellen Schnittstelle geöffnet haben, sehen wir die aufeinanderfolgenden Nachrichten, die vom Raspberry Pi Pico W gesendet werden. Zusätzlich zu dem Text, der bereits aus dem früheren Programm bekannt ist, sehen wir die aufeinanderfolgenden Zustände der Variablen counter, die bei jedem Durchlauf des Programms um eins erhöht wird, und den Wert, der unter dem Fließkomma-Pi gespeichert ist.

Konstruktionen wie %d und %f sind in der Programmierung äußerst nützlich, da sie es uns ermöglichen, die unter einer bestimmten Variablen gespeicherten Daten aufzudecken. Abgesehen davon können wir auch %x (für Ganzzahlen im Hex-Format), %c (ASCII-Zeichen, für Variablen vom Typ char) oder %s (sogenannter String, d.h. Zeichenketten aus ASCII-Zeichen) verwenden. Sie fragen sich vielleicht, wie Sie das Prozentzeichen oder den Backslash im Monitor der seriellen Schnittstelle anzeigen können. Die Antwort ist einfach: Verwenden Sie es zweimal, d.h. indem Sie den Befehl printf(“%% \n”) erstellen; Sie werden ‘% \n’ im VSC-Fenster sehen.

Ich frage mich, ob Sie bemerkt haben, dass ich in den letzten beiden Beispielen Deutsch verwendet habe. Was ich hier meine, ist “RPI Pico sendet eine Nachricht”. Wenn Sie dieses Detail erkannt haben, ist es sehr gut. Ich habe diese Nachricht absichtlich geschrieben, damit ich Sie jetzt an das gute Prinzip des Gebrauchs der englischen Sprache erinnern kann. Deutsch ist akzeptabel, aber es ist eine gute Angewohnheit, alle Nachrichten und Kommentare auf Englisch zu schreiben.

LED-Steuerung über einen Computer

				
					#include "pico/stdlib.h"

#define RED_LED 2
uint8_t led_state = 0;
char user_input;

int main() {
    stdio_init_all(); //initialization of the stdio.h library

    gpio_init(RED_LED);
    gpio_set_dir(RED_LED, GPIO_OUT);
    
    while(true) {
        
        printf("Enter 0 or 1 to control the LED\n");
        user_input = getchar(); //writing the first received character to a variable

        if(user_input == '1'){ //condition checking the received sign
            gpio_put(RED_LED, 1);
            printf("LED activated\n");
        }
        else if(user_input == '0'){
            gpio_put(RED_LED, 0);
            printf("LED off\n");
        }
        else
        {
            printf("Incorrect character entered\n");
        }
        
    }
}

				
			

Das letzte Programm, das wir untersuchen werden, um die USB-Schnittstelle besser kennenzulernen, ist die bidirektionale Kommunikation. Mit dem oben gezeigten Programm können wir die LED-Leuchte über den seriellen Monitor steuern. Der RPI Pico W fängt das erste vom Computer gesendete Zeichen ab und entscheidet anhand dieses Zeichens, ob die LED aktiviert oder deaktiviert werden soll.

Das Programm verwendet zwei Variablen led_state, die den Zustand der LED speichern, d.h. Null – die LED leuchtet nicht und Eins – der Halbleiter emittiert Photonen. Die zweite Variable ist user_input, in der wir das über die Computertastatur eingegebene und an den RP2040 gesendete Zeichen speichern.

Wir können den Hauptteil des Programms in zwei Teile unterteilen. Im ersten Teil wird eine Meldung angezeigt, in der Sie aufgefordert werden, eine Null oder eine Eins einzugeben und auf die vom Computer gesendeten Daten zu reagieren. Mit der Funktion getchar() speichern wir das erste vom Mikrocontroller empfangene Zeichen in der Variablen user_input. Die Betonung, dass es sich um das erste Zeichen handelt, ist hier äußerst wichtig, denn der Computer kann eine Reihe von Zeichen senden, aber eine solche Situation werde ich am Ende dieses Unterabschnitts beschreiben. Der zweite Teil des Programms umfasste eine kaskadierte bedingte Anweisung. Bis jetzt haben wir ein einziges ifa in Verbindung mit dem Befehl else verwendet. Diesmal werden wir auch so etwas wie das else if verwenden, ein notwendiges Verfahren, wenn wir zwei Bedingungen überprüfen, die sich auf dieselbe Variable beziehen. Das Programm in der bedingten Funktion prüft den Wert von user_input. Wenn es den Wert 1 speichert, d.h. wenn der Computer ein Zeichen an den Mikrocontroller gesendet hat, dann wird die LED aktiviert. Analog dazu wird die Diode ausgeschaltet, wenn in user_input eine Null gespeichert ist. Der letzte Punkt betrifft die Situation, in der keine der Bedingungen erfüllt ist. In diesem Fall können wir davon ausgehen, dass ein ungültiges Zeichen über die Computertastatur eingegeben wurde und diese Meldung angezeigt wird.

Auch die Verwendung der Kaskadenfunktion selbst bedarf der Klärung. Kann der Befehl else if durch ein einfaches ifa ersetzt werden? Sie können sich wahrscheinlich denken, dass das nicht der Fall ist, und das ist auch die richtige Antwort. Stellen Sie sich jedoch vor, dass dort ein if-Befehl platziert wurde. Wir senden das Zeichen eins an den Mikrocontroller, die erste Bedingung wird geprüft und die Diode beginnt zu leuchten. Wir prüfen die nächste Bedingung, sie ist ungültig, und weiter stößt der Prozessor auf den Befehl else, den er ausführen muss, weil die vorherige Bedingung nicht erfüllt war. In einer solchen Situation wird die Diode aktiviert und gleichzeitig wird eine Meldung über ein falsches Zeichen angezeigt. Genau diese Situationen werden durch die bedingte Kaskadenanweisung verhindert. Wir überprüfen in Ihr zwei Bedingungen unabhängig voneinander, und es genügt, wenn nur eine der Bedingungen erfüllt ist und der im else-Block geschriebene Code nicht ausgeführt wird. Es wird nur dann eine Meldung über ungültige Zeichen angezeigt, wenn beide Bedingungen nicht erfüllt sind.

LED-Steuerung aus dem Monitor der seriellen Schnittstelle.

Sobald die Datei .uf2 in den RPI-Speicher hochgeladen wurde, können wir den seriellen Monitor starten und versuchen, die LED anzusteuern. Wichtig ist, dass Sie beim ersten Mal, wenn Sie den Code ausführen, wahrscheinlich keine Meldung sehen werden, in der Sie aufgefordert werden, eine Null oder eine Eins einzugeben. Der Grund dafür ist, dass die Meldung fast sofort nach der Ausführung des Codes gesendet wird. In einer solchen Situation, wenn Sie während des Prototypings möchten, dass das Programm nicht sofort startet, können Sie eine primitive Lösung wie die Funktion sleep_ms verwenden. Wenn Sie diese Funktion z.B. vor dem Beginn einer unendlichen while-Schleife platzieren und 10s in ihr Argument aufnehmen, haben wir genug Zeit, um den Monitor der seriellen Schnittstelle zu starten, bevor der Hauptteil des Codes beginnt. Sie können versuchen, eine solche Änderung zu implementieren.

Dank der unteren Leiste, die in der Entwicklungsumgebung sichtbar ist, können wir einzelne Zeichen oder einen größeren Text senden. Wenn Sie eine 1 eingeben und auf die Eingabetaste klicken, leuchtet die LED auf. Wenn Sie eine 0 senden, wird die LED ausgeschaltet. Darüber hinaus sehen wir auf dem Computerbildschirm die entsprechenden Meldungen „LED activated” oder „LED off”. Wenn wir ein anderes Zeichen senden, informiert uns der Mikrocontroller, dass es ungültig ist.

Vorhin habe ich erwähnt, dass der Prozessor immer das erste empfangene Zeichen in die Variable user_input schreibt. Aber was passiert, wenn wir versuchen, ihm eine Zeichenfolge zu senden? Senden Sie jetzt zum Beispiel etwas wie: “501”. Nach dem Senden leuchtet die Diode auf und Sie sehen etwas Seltsames auf dem Computerbildschirm, nämlich eine Meldung, dass das Zeichen nicht korrekt ist und dass die Diode ausgeschaltet und eingeschaltet wird. Lassen Sie uns diesen Fall jedoch genauer analysieren. RP2040 empfängt die Zeichenfolge ‘501’, die in dem entsprechenden Puffer gespeichert wird. Das ausführende Programm liest die Fünf, die per Definition ein ungültiges Zeichen ist, und eine solche Meldung wird angezeigt. Die Schleife kehrt zum Anfang zurück, aber es befinden sich noch mehr Daten im Puffer. Die fünf wurden bereits vorher entfernt, aber die nächste ist Null, also schaltet das Programm die LED aus. Bei der nächsten Ausführung der Schleife werden einige Daten in den Puffer eingegeben, so dass der Mikrocontroller denkt, der Benutzer habe weitere Zeichen gesendet. Dies ist eine Eins, was dazu führt, dass die Diode eingeschaltet wird. Erst bei der nächsten Ausführung der Schleife ist der Puffer leer und das Programm wartet auf Tastatureingaben. Wenn Sie also einen größeren Text an den Mikrocontroller senden, wird er dort in seiner Gesamtheit verarbeitet und die Schaltung reagiert auf jedes einzelne Zeichen.

ADC-Wandler

ADC Wandler im RP2040. (https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf)

Mit dem PWM-Signal haben wir versucht, das analoge Signal auf unsere Weise zu imitieren, so dass es uns so vorkam, als würde die LED auf sanfte Weise gesteuert. Dies war jedoch nur eine Imitation, denn wie Sie bereits wissen, handelte es sich in Wirklichkeit immer noch um ein 100-prozentig digitales Signal. In diesem Unterabschnitt befassen wir uns mit dem echten analogen Signal, aber wir werden es nicht erzeugen, sondern nur auslesen.

In modernen Mikrocontrollern finden wir so etwas wie einen ADC. Genauer gesagt handelt es sich um einen ADC (eng. Analog-digital converter), der die Umwandlung eines analogen Signals in eine digitale Form ermöglicht. Es funktioniert nämlich so, dass die Schaltung der an der externen Leitung abgelesenen Spannung einen bestimmten Zahlenwert zuordnet. Wie hoch dieser Wert sein wird, hängt in erster Linie von der Auflösung des Konverters ab. Der Prozessor auf dem RPI Pico W Board ist mit einem 12-Bit-Wandler ausgestattet, so dass das Analogsignal einen Wert von 0 bis 4095 annehmen kann. Es gibt auch 8- oder 16-Bit-Wandler. Die Faustregel lautet: Je höher die Auflösung, desto genauer die Messung. Bei 8-Bit-Designs liegt der Lesebereich beispielsweise zwischen 0 und 255, wodurch er kleiner und die Messung weniger genau ist. Sie müssen auch bedenken, dass das zu messende Signal spannungsverträglich mit dem Mikrocontroller sein muss. Der RP2040 arbeitet mit einer Logikspannung von 3,3 V. Das gemessene Signal sollte daher diesen Wert niemals überschreiten, da sonst die Schaltung beschädigt werden könnte.

Der Raspberry Pi Pico W verfügt über fünf Eingänge, an die wir ein analoges Signal anschließen können. Einer davon ist für den internen Temperatursensor reserviert, der sich direkt im Silizium befindet. Die verbleibenden Eingänge sind mit den externen Leitungen mit den Nummern 26, 27, 28 und 29 verbunden. Es ist auch erwähnenswert, dass der RP2040 tatsächlich nur einen ADC besitzt, um alle möglichen analogen Signalquellen zu verarbeiten. Diese werden durch einen speziellen Multiplexer geschaltet und erst dann wird das Signal umgewandelt. Diese Lösung wird recht häufig verwendet, vor allem weil die Messwandler recht komplex und teuer in der Herstellung sind.

Fotowiderstand-Testschaltung.

Wir werden den Betrieb des ADC unter realen Bedingungen testen, und zwar mit Hilfe eines Fotowiderstands, der an den RPI angeschlossen ist und seinen Widerstandswert unter dem Einfluss von Licht, das auf seine Oberfläche fällt, ändert. Er wird jedoch nicht einfach so angeschlossen – um ein analoges Signal zu erzeugen, müssen wir einen Spannungsteiler bauen, der aus dem oben erwähnten Element und einem 1,2kΩ-Widerstand besteht. Wir schließen eine Leitung des Fotowiderstands an die 3,3 V Spannung an, die an Leitung Nummer 36 anliegt. Ich persönlich habe diese Spannung an den rot markierten Bus auf der Kontaktplatte geleitet. Die zweite Leitung muss mit dem ersten Widerstandsbeinchen verbunden werden, während das zweite Widerstandsende mit GND verbunden wird. Wir lesen das Signal am Anschlusspunkt des Widerstands und des Fotowiderstands aus und verbinden sie per Leitung mit Pin GP26, der auf der Platine mit ADC0 bezeichnet ist.

Die Lösung mit dem Spannungsteiler ist notwendig, denn wenn wir den Fotowiderstand einfach so an den RPI anschließen würden, bekämen wir eine Spannung nahe 3,3V, unabhängig von dem Lichtstrahl, der auf das Element fällt. Der Widerstand und der fließende Strom würden sich ändern, aber die Spannung bliebe immer die gleiche. Mit einem zusätzlichen Widerstand können wir jedoch die an den Elementen anliegende Spannung in zwei Teile aufteilen, die von dem Widerstand abhängen. Mehr Licht, das auf den Fotowiderstand fällt, bedeutet eine Verringerung seines Widerstands, einen Anstieg des Stroms und eine Verringerung der auf ihm abgelagerten Spannung. Der größte Teil der Spannung wird dann von dem Widerstand abgefangen, an dem die Messung tatsächlich vorgenommen wird. Beachten Sie, dass wir die Spannung in Bezug auf Masse ablesen. Wir messen also tatsächlich den Widerstand und nicht den Fotowiderstand. Wenn weniger Licht vorhanden ist, erhöht sich der Widerstand des Fotowiderstands und es wird eine höhere Spannung am Widerstand angelegt, wodurch gleichzeitig die Potenzialdifferenz über dem Widerstand sinkt. Vielleicht fragt sich jemand, warum ich gerade eine solche Schaltung gebaut habe, und ob man den Widerstand und den Fotowiderstand austauschen kann? Natürlich können diese Elemente auch ausgetauscht werden, aber dann würden wir die Funktionsweise der Schaltung umkehren. Bei meiner Lösung steigt die angezeigte Spannung mit der Anzahl der Photonen, die auf den Fotowiderstand fallen. Nach dem Vertauschen der Elemente würde es umgekehrt funktionieren, was mir weniger intuitiv erscheint.

Nach dieser etwas längeren Einführung können wir mit der Programmierung fortfahren. Zu Testzwecken habe ich ein Projekt namens photoresistor_adc vorbereitet. Seine Aufgabe wird es sein, das Signal vom GP26-Pin zu lesen und den numerischen Wert über USB an den Computer zu senden, damit wir ihn auf dem seriellen Monitor sehen können.

				
					#include "pico/stdlib.h"
#include "hardware/adc.h"

int main() {
    stdio_init_all();

    adc_init(); //ADC initialization

    adc_gpio_init(26); //assignment to ADC pin 26 (ADC0)
    adc_select_input(0);

    while(true) {
        
        uint16_t result = adc_read(); //ADC value reading
        float voltage_value = result * (3.3/4095); //ADC to voltage conversion
        printf("Read ADC: value = %d, voltage = %f V\n", result, voltage_value);
        
        sleep_ms(500);
    }
}

				
			

Das Programm, das wir gemeinsam besprechen werden, ist recht kurz, aber es enthält einige Neuerungen, die Sie gleich kennenlernen werden. Da wir eine weitere eingebaute Funktion des Mikrocontrollers verwenden werden, müssen wir die entsprechende Bibliothek einbinden, und das ist hardware/adc.h. Vergessen Sie nicht, sie in die Datei CMakeLists.txt einzutragen. Außerdem verwendet das Projekt auch einen USB-Ausgabe-Stream und auch dieser sollte in dieser Datei aktiviert werden, wie in den vorherigen Beispielen.

Im ersten Teil des Programms initialisieren wir den ADC mit Hilfe des Befehls adc_init() und wählen den verwendeten Port aus. Die erste Funktion wählt die entsprechende GPxx-Nummer aus, während adc_select_input für die Auswahl des ADC-Signals verantwortlich ist, das über den bereits erwähnten Multiplexer an den Konverter weitergeleitet wird.

Im Hauptteil des Programms erstellen wir eine Variable result, die dank der Verwendung der Funktion adc_read() den numerischen Wert des vom GP26-Pin gelesenen ADC-Signals speichern wird. Im nächsten Schritt generieren wir den Fließkomma voltage_value, denn neben dem numerischen Wert des ADC wäre es auch nützlich, die spezifische Spannung zu kennen, die am Ausgang des RPI Pico W erscheint. Wir können die Potentialdifferenz anhand einer einfachen Beziehung berechnen:

4095 = 3,3V

1 = xV

4095x = 3,3V

x = 3,3V/4095 = 0,000805V

Damit wissen wir, dass jede Erhöhung des ADC-Werts um eins tatsächlich einer Spannungserhöhung von etwa 0,000805V entspricht. Die Sprache C kann erfolgreich alle Arten von mathematischen Operationen erstellen. Damit der RP2040 uns den entsprechenden Wert zur weiteren Anzeige sendet, werden wir eine einfache mathematische Berechnung vorbereiten. Um den Spannungswert zu erhalten, der dem ADC entspricht, multiplizieren wir ihn einfach mit dem soeben berechneten Wert. Dies wird durch den Sternchen-Operator (*) ermöglicht, der den Wert der Variablen result mit (3,3/4095) multipliziert. Natürlich könnten Sie hier auch einfach 0,000805 eingeben, aber nichts hindert den Mikrocontroller daran, den entsprechenden Wert selbst zu bestimmen. Dank des Operators (/) dividieren wir also 3,3 durch 4095. Beachten Sie, dass die Operation in Klammern steht. Würden die Klammern fehlen, würde das System zuerst die Multiplikation und erst dann die Division durchführen, was zu einem falschen Ergebnis führen würde. Es ist wissenswert, dass C die aus der Mathematik bekannte und durch Klammern realisierte Reihenfolge der Operationen unterstützt.

Im letzten Punkt senden wir dank der bekannten printf-Funktion die entsprechende Nachricht mit der Variablen result und voltage_value an den Computer.

ADC-Werte und Spannungen, die über den RPI übertragen werden.

Nach dem Start des Programms und dem Öffnen des seriellen Monitors können wir die sich ändernde numerische Darstellung des ADC und der Spannung auf dem Bildschirm unseres Computers beobachten. Diese Werte steigen mit der Menge des auf den Fotowiderstands einfallenden Lichts.

Beschädigter Fotowiderstand RPP130 aus den 1980er Jahren (http://www.cemi.cba.pl/fotorezystor.html)
Moderner Fotowiderstand.

Zum Schluss möchte ich Ihnen noch zwei interessante Fotos zum Thema Fotowiderstand zeigen. Diese optoelektronischen Komponenten bestehen aus dünnen Halbleiterbahnen, die auf ein dielektrisches Substrat aufgebracht sind. Das verwendete Halbleitermaterial hängt von der spezifischen Art des Fotowiderstands und seinen spektralen Eigenschaften ab. Übliche Materialien sind Cadmiumsulfid (CdS), Bleisulfid (PbS), Bleiselenid (PbSe) oder Indiumantimon (InSb). Jeder Fotowiderstand muss, wie andere elektronische Komponenten auch, unter sterilen Bedingungen hergestellt werden, aber Fehler können immer passieren. Auf dem Foto rechts sehen Sie einen gewöhnlichen, korrekt hergestellten Fotowiderstand. Das linke Foto zeigt jedoch ein während der Produktion beschädigtes Element mit der Kennzeichnung RPP130 aus der inzwischen stillgelegten Unitra TOMI Fabrik in Toruń. Die Ursache für das Versagen war wahrscheinlich Wasserdampf, der sich auf der Fotowiderstandstruktur abgesetzt hatte, bevor er in das hermetische Gehäuse gelegt wurde.

Beschädigter Fotowiderstand

Diese Art des Versagens ist jedoch nicht nur eine Domäne der uralten Fotowiderstände aus den 1980er Jahren. Auf dem Foto oben sehen Sie einen interessanten Fall eines modernen Fotowiderstands mit einem ähnlichen Defekt. Dank seiner ungewöhnlichen Struktur hat er einen viel höheren Widerstand als die im Katalog angegebenen 15kΩ.

Ein paar Worte zum Schluss...

Mehr Raspberry Pi Pico W Material ist hinter uns. Diesmal haben wir etwas über die Pulsweitenmodulation (PWM) gelernt und diese Funktionalität zur Steuerung einer LED verwendet. Außerdem habe ich Ihnen erklärt, wie Sie die Kommunikation mit dem Computer herstellen, so dass wir in zukünftigen Programmen die vom RP2040 gesendeten Informationen einfach auf dem Bildschirm anzeigen können. Darüber hinaus haben wir einen Fotowiderstand mit einem ADC betrieben. Im nächsten Artikel werden wir die bisher gewonnenen Erkenntnisse nutzen und gemeinsam ein etwas größeres Programm erstellen.

Quellen:

  • https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
  • https://datasheets.raspberrypi.com/picow/pico-w-datasheet.pdf
  • https://www.raspberrypi.com/products/rp2040/
  • https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html
  • https://en.wikipedia.org/wiki/Pulse-width_modulation
  • http://www.cemi.cba.pl/fotorezystor.html

Wie hilfreich war dieser Beitrag?

Klicke auf die Sterne um zu bewerten!

Durchschnittliche Bewertung 5 / 5. Stimmenzahl: 12

Bisher keine Bewertungen! Sei der Erste, der diesen Beitrag bewertet.

Teilen:

Picture of Rafał Bartoszak

Rafał Bartoszak

Ein mit Botland kooperierender Elektroniker, der sein Wissen im Internet teilt. Enthusiast für digitale Systeme, programmierbare Schaltungen und Mikroelektronik. Leidenschaftlich für Geschichte, mit besonderem Schwerpunkt auf dem 20. Jahrhundert.

Siehe mehr:

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Aus Sicherheitsgründen ist die Verwendung des reCAPTCHA-Dienstes von Google erforderlich, der den Google-Datenschutzbestimmungen und Nutzungsbedingungen unterliegt..