Warum einfach, wenn es auch schwierig geht? So dachte ich, als ich versuchte, anhand der offiziellen Dokumentation „Getting started with Raspberry Pi Pico“ der Raspberry Pi Foundation den Pico an meinem Raspberry Pi 400 in Betrieb zu nehmen. Ich verkneife mir weitere Kommentare, sondern beschreibe lieber, wie leicht es ist, den Raspberry Pi Pico unter Thonny mit MicroPython zu programmieren. Dafür verwende ich entweder meinen PC oder meinen Raspberry Pi 400. Beachten Sie auch unseren Pico Quickstart Guide.
Installation
Der Python-Editor Thonny ist schnell auf dem PC installiert, beim Raspberry Pi gehört er zur Basisausstattung des Betriebssystems Raspberry Pi OS (ehemals Raspbian). Die Einstellungen für den jeweiligen Mikrocontroller und damit verbunden MicroPython werden nach dem Start vorgenommen.
Bei thonny.org das Betriebssystem wählen, Download und Installation dauern weniger als fünf Minuten.
Unter Extras\Optionen (engl. Tools\Options) kann man auf der ersten Registerkarte (Allgemeines, engl. General) die Sprache einstellen.
Nach Änderung der Spracheinstellung muss Thonny geschlossen und neu gestartet werden.
Dann machen wir uns an die Einstellungen für den Raspberry Pi Pico. Hier erleben wir den einzigen wirklichen Stolperstein bei der Programmierung des Pico mit MicroPython.
Über das Menü Ausführen\Interpreter auswählen … kommen wir auf die zweite Registerkarte der Optionen. Nachdem wir den Raspberry Pi Pico ausgewählt haben, drücken und halten wir auf dem Pico die einzige Taste BOOTSEL und stecken den Pico dann in die USB-Buchse. Im Boot-Modus meldet sich der Pico vorübergehend als neues Laufwerk am Windows 10 PC an. (Windows 7/8-Nutzer bitte den Blog-Beitrag von Andreas Wolter lesen).
Nach dem Installieren der MicroPython Firmware verschwindet das Laufwerk wieder und wir erhalten in der Kommandozeile (unterer Teil von Thonny, häufig auch shell oder REPL= read–eval–print loop genannt) den Python-typischen Prompt >>>.
Nun können wir entweder im oberen Teil, dem Editor, ein erstes Programm schreiben oder hinter dem Prompt das übliche print(„Hello World“) eingeben und Enter drücken.
Trouble Shooting: Wenn Sie anstelle des obigen Bildes eine Fehlermeldung erhalten, liegt es ggf. daran, dass der COM-Port des Pico nicht automatisch erkannt wurde. Dann bitte auf der Seite Optionen\Interpreter den richtigen Port im Pull-down-Menü wählen.
Wenn das nicht klappt, bitte die Installation wiederholen: USB-Stecker ziehen, BOOTSEL drücken, USB-Stecker einstecken, BOOTSEL loslassen, usw.
Anwendungsprogramme
Wer schon Erfahrung mit Python hat, z.B. beim Raspberry Pi, wird nur wenige Besonderheiten bei MicroPython auf dem Pico finden. Kein Unterschied z.B. beim fünfmaligen Schreiben von „Hello World“ mit Anzeige der Zählvariablen:
Beim Speichern wird gefragt, ob das Programm auf dem Computer oder dem Pico gespeichert werden soll. Empfehlung: Alles auf dem Computer speichern. Später werden wir sehen, was zusätzlich auf dem Pico gespeichert wird.
Erst nach dem Speichern ist das Programm ausführbar, wobei Thonny weitere Änderungen nach Drücken des Startsymbols automatisch abspeichert.
Bereits bei unserem zweiten Programm wollen wir zum „Physical Computing“ übergehen, also die vielen Schnittstellen benutzen, die unser Mikrocontroller bereitstellt. Dafür importieren wir zu Beginn des Programms zwei Programm-Module (Arduino-Nutzer würden Bibliothek, library oder lib sagen). Das Modul time ist ein alter Bekannter für Raspianer, das Modul machine kennen nur diejenigen bereits, die MicroPython auf dem ESP32 ausprobiert haben. Hier jetzt die „Blink-Variante“ in MicroPython:
Aus dem Modul machine wird die Klasse Pin importiert, aus time nur sleep. Wer die Klassen so importiert, kann sie später ohne den Modulnamen aufrufen. Wer das gesamte Modul importiert (z.B. import time), muss den Modulnamen bei allen Anweisungen voranstellen (also time.sleep(1)).
Zum Modul time nennt die Dokumentation neben sleep() auch sleep_ms (Millisekunden) und sleep_us (Mikrosekunden).
import time
time.sleep(1) # sleep for 1 second
time.sleep_ms(500) # sleep for 500 milliseconds
time.sleep_us(10) # sleep for 10 microseconds
start = time.ticks_ms() # get millisecond counter
delta = time.ticks_diff(time.ticks_ms(), start) # compute time difference
Im Programm wird dann das Objekt LED_BUILTIN an Pin(25) als Ausgang mit Wert 0 (ausgeschaltet) instanziiert. In der Endlosschleife wird dann die eingebaute LED im Sekundentakt ein- und ausgeschaltet.
Im dritten Programm wollen wir ebenfalls die Klasse Pin aus dem Modul machine anwenden. Auf Tastendruck soll ein Ampelzyklus ablaufen. Neu ist eigentlich nur die Instanziierung des Tasters als Eingang mit Pull-up Widerstand. Der Taster an Pin 5 wird also gegen Ground geschaltet.
taste = Pin(5, Pin.IN, Pin.PULL_UP)
Um die von mir verwendeten Ein- und Ausgänge zu finden, hier zunächst das offizielle Pinout-Diagramm:
from machine import Pin
from time import sleep
greenLED = Pin(4,Pin.OUT,value=0) # gruen
yellowLED = Pin(3,Pin.OUT,value=0) # gelb
redLED = Pin(2,Pin.OUT,value=0) # rot
LED_BUILTIN = Pin(25,Pin.OUT,value=0)
taste = Pin(5,Pin.IN,Pin.PULL_UP)
def trafficLight():
greenLED.value(1)
sleep(2)
greenLED.value(0)
yellowLED.value(1)
sleep(2)
yellowLED.value(0)
redLED.value(1)
sleep(5)
yellowLED.value(1)
sleep(2)
yellowLED.value(0)
redLED.value(0)
greenLED.value(1)
sleep(5)
greenLED.value(0)
return None
while True:
if taste.value() == 0:
print("button is pressed")
trafficLight()
Soweit ein erster Blick auf die digitalen Ein- und Ausgänge. Nun wenden wir uns den analogen Eingängen (ADC = Analog Digital Converter) zu. Sinnvoll nutzbar sind
- ADC0 an GP26
- ADC1 an GP27
- ADC2 an GP28
Sofern keine Referenzspannung an den physikalischen Pins 35 und 33 angeschlossen ist, beziehen sich die ausgelesenen Werte auf 3,3V. Tatsächlich handelt es sich nur um einen ADC, an dem insgesamt fünf Eingang durch einen „Mux“ (Multiplexer) angeschlossen sind. Den vierten ADC (ADC(3) an GP29) habe ich nicht gefunden, am ADC(4) liegt der interne Temperatursensor. Mehr dazu im Blog von Jörn Weise vom 13.Nov.2021.
Ich schließe meinen selbstgebastelten Spiele-Controller mit dem 10kOhm-Drehpotentiometer an phys. Pin 36 (3,3V), GND und die ADC-Eingänge an. Folgendes kleines Programm, das ich leicht modifiziert habe, stammt aus dem RP2-Kapitel der MicroPython Dokumentation.
Am Pico kennt MicroPython nur die Methode read_u16, also einen 16-Bit-Wert zwischen 0 und 65535. Nachdem ich den einfachen read()-Befehl erfolglos (d.h. mit Fehlermeldung) probiert hatte, habe ich den jeweiligen Wert durch 64 geteilt und erhalte so die bekannte 10-Bit-Auflösung von 0 bis 1023. Alles andere täuscht eine Genauigkeit vor, die nicht gegeben ist, wie man an den Zahlensprüngen bei unveränderter Einstellung erkennt.
Anstelle der hier vorgenommenen Instanziierung in Zeile 13 mit der GPIO-Pinnummer adc=ADC(Pin26)) kann man auch den Index der Liste ADC[0:4] benutzen:
- ADC(0) = Pin(26)
- ADC(1) = Pin(27)
- ADC(3) = Pin(28)
- ADC(3) = GP29 nicht gefunden
- ADC(4) = Temperatursensor
Am Ende des ersten Teils noch die bekannte Alternative zu analogen Ausgängen: die Pulsweitenmodulation (PWM), mit der LEDs gedimmt oder Motoren gedrosselt werden können.
Aus dem Modul machine werden die Klassen Pin und PWM importiert, das Objekt pwm0 instanziiert (an Pin 25 ist die eingebaute LED angeschlossen) und die PWM-Frequenz gesetzt.
In einer Endlosschleife wird der Duty Cycle der PWM herunter geregelt auf 0 (null). Nach einer Sekunde beginnt der Vorgang erneut.
Die try: - except: - Konstruktion bewirkt, dass die Endlosschleife mit Strg-C ohne Fehlermeldung beendet werden kann.
Ausblick
Im zweiten Teil werden wir die bekannten Schnittstellen UART, OneWire, I2C und SPI sowie die Auswertung von Sensoren und Ausgabe am LCD kennenlernen.