Beiträge von Der Hobbyelektroniker

    Ein ähnliches 'Detektor - Radio' hatte ich auch. Allerdings schon etwas moderner. Anstelle des aufgesteckten Kristalldetektors war eine handelsübliche Germaniumdiode eingebaut. Tagsüber war Radio Beromünster der einzige Sender, der richtig reinkam. Am Abend waren die Ausbreitungsbedingungen für Mittelwelle besser, da konnte man auch andere Stationen empfangen.

    Hallo bobinger,


    start_zeit wird in anzeigen() nicht ausgegeben. Da aktiv ebenfalls auf False gesetzt wird, erfolgt keine neue Berechnung der angezeigten Felder. Es wird auf start gewartet und immer die unveränderten Werte von total_zeit, zwischen_zeit und etappen_zeit angezeigt. Der Wert von start_zeit ist im nicht aktiven Zustand irrelevant.


    Wenn du einen vollen Rese machen möchtest, kannst du die Werte von total_zeit, zwischen_zeit und etappen_zeit auf 0 setzen. start_zeit kannst du lassen wie es ist, diese Variable wird mit start sowieso neu gesetzt.


    Gruss

    René

    Hallo The Ibis,


    leider ist die Thread - Unterstützung in Micropython wesentlich schwächer als im normalen Python. Du könntest eine eigene Thread - Klasse von _thread ableiten und die Verwaltung der Liste innerhalb deiner Klasse vornehmen. Dann könnte eine join() - Methode problemlos implementiert werden. Das Prinzip bleibt aber das gleiche, es ist dann nur etwas 'schöner' formuliert.


    Gruss

    René

    Hallo Olaf,

    es freut mich, dass Dir meine Videos gefallen. Vielen Dank!


    Ich musste auch eine Weile überlegen, bevor ich das Problem eingrenzen konnte. Das hat nichts mit der Zeile text[0] = h zu tun. Es ist tatsächlich egal, wo diese steht. Das Problem liegt beim Aufruf:


    showValues(a, s) funktioniert genauso wie changeValue(s).

    In beiden Fällen wird in s das erste Zeichen ausgetauscht.


    Der Absturz erfolgt in showValues(a, "Hallo);

    Du versuchst da das erste Zeichen des übergebenen Strings "Hallo" auszutauschen. Das ist aber keine Variable.

    Der Versuch changeValue("Hallo") aufzurufen, wird ebenfalls zu einem Absturz führen.


    Wieso es zum Absturz kommt, kann ich auch nicht sagen. Dazu kenne ich das Speichermanagement von C zu wenig gut. Ich nehme an, dass der Compiler für den direkt übergebenen String keinen entsprechenden Speicherplatz reserviert.


    Gruss

    René

    Hallo zusammen


    Auf der einen Seite finde ich die Idee sehr gut. Da könnten Leute miteinander diskutieren, die auf einem ähnlichen Niveau sind. Da jeder seine eigenen Schwerpunkte hat, könnten sie sich trotzdem gut ergänzen. Das erfordert aber genügend Leute, die da mitmachen. Das Forum ist recht klein und ich bin nicht sicher, ob sich da genügend aktive Teilnehmer zusammenfinden.


    Auf der anderen Seite könnte da eigentlich jeder mitmachen. Auch ein Fortgeschrittener ist in einigen Teilgebieten Anfänger. Ich lese hier oft Beiträge, die mir das Gefühl geben, ein blutiger Anfänger zu sein.


    Eigentlich hatte ich immer das Gefühl, dass dieses Forum anfängerfreundlich ist. Bisher ist auf beinahe jede Frage eine freundliche und ausführliche Antwort gegeben worden. Die Diskussionen sind aber oft auf einem sehr hohen Niveau, so dass ich mir schon vorstellen kann, dass Anfänger dadurch abgeschreckt werden. Deshalb werde mir überlegen, wie ich so etwas einrichten kann. Technisch ist das kein Problem, die Frage ist, wie man das präsentiert, dass sich die richtigen Leute angesprochen fühlen.


    Viele Grüsse

    René

    Hallo Kai


    Ja, ich kenne den Heise - Artikel. Er hat mir auch sehr geholfen. Allerdings ist der Sprung von simplen Python - Beispiel zur in C geschriebenen Ampelsteuerung sehr gross. Ich habe inzwischen noch andere Quellen gefunden, allerdings wird man vor lauter Informationen beinahe erschlagen. Trotzdem hat man oft Mühe, genau das Detail zu finden, das man gerade benötigt.


    Hier eine Sammlung der Links, die ich gefunden habe:


    https://www.mathema.de/blog/ra…are-zwischen-cpu-und-fpga

    https://hackspace.raspberrypi.…d-programmable-i-o-part-2

    https://hackspace.raspberrypi.org/articles/pio1


    https://datasheets.raspberrypi…2040/rp2040-datasheet.pdf

    https://datasheets.raspberrypi…spberry-pi-pico-c-sdk.pdf

    https://datasheets.raspberrypi…ry-pi-pico-python-sdk.pdf


    Ich bin gespannt, ob sich die Programmierung der Statemachines in Makerkreisen durchsetzt. Es könnte auch sein, dass alle das einfach ignorieren. Das erinnert mich etwas an die Geschichte mit dem PSOCs von Cypress. Die wären eigentlich hochinteressant. Aber es war für viele Leute absolutes Neuland und die meisten haben sich davon ferngehalten.


    Gruss

    René

    Hallo Hans,


    da hast Du natürlich recht. Allerdings ist das nur ein zusätzliches Feature, das man nicht zwingend benutzen muss. Wenn man mit den normalen Funktionen eines Mikrokontrollers zufrieden ist, erfolgt die Programmierung wie bei einem Arduino oder ESP32.


    Anfänger sollten sich nicht mit PIO - Programmierung beschäftigen. Es ist aber eine gute Idee, mit Micropython einzusteigen. Python ist sehr viel einfacher zu erlernen als C oder C++.


    Gruss

    René

    Teil 4


    Geblinkt wird mit dem set() - Befehl. Das ist einer der 9 Befehle, die die Statemachine kennt. Wie kann man jetzt gleichzeitig mehrere Pins ein- oder ausschalten?


    Code
    1. set(pins, 0b1101)[31] # Pins 2, 4, 5 ein, Pin 3 aus
    2. set(pins, 0b0010)[31] # Pin 3 ein, Pins 2, 4, 5 aus

    Im Kopf der Funktion haben wir mit set_init den Ausgangszustand für 4 Pins festgelegt. Dadurch können wir mit set() 4 Pins ansprechen. Der erste Pin liegt bei set_base (2) und geht bis set_base + 3 (5).

    Mit set() können wir diese Pins ansprechen. Jedes gesetzte Bit setzt den Pin auf HIGH, ein Bitwert von 0 setzt ihn auf LOW. Daher leuchten mit 0b1101 die Pins 2, 4, und 5, während Pin 3 dunkel bleibt.


    Mit set(pins, 0b0010) werden die Pins 2,4,5 auf LOW und Pin 3 auf HIGH geschaltet.


    Jeder set() - Befehl dauert 1 Zyklus. In eckigen Klammern geben wir eine weitere Verzögerung um 31 Zyklen an. Damit benötigt die Instruktionszeile 32 Zyklen.

    Wir arbeiten mit einer Frequenz von 2000 Hz. Somit dauert ein Zyklus 0.5 ms.


    Diese 32 Zyklen reichen nicht, um das Blinken sichtbar zu machen. Aus diesem Grund werden zusätzlich nop() - Operationen eingefügt. Ein nop() [31] braucht 1 + 31 = 32 Zyklen. Die zusätzliche Verzögerung kann maximal 31 Zyklen betragen, daher benötigen wir mehrere nops.


    Code
    1. ...
    2. nop() [31]
    3. nop() [31]
    4. ...

    Wir haben in unserer Funktion folgende Zyklen:

    1 + 31 = 32

    1 + 31 = 32

    1 + 31 = 32

    1 + 3 = 4

    1 + 31 = 32

    1 + 31 = 32

    1 + 31 = 32

    1 + 3 = 4


    Wir haben also ein Total von 200 Zyklen. Das dauert 200 x 0.5 ms = 100 ms.

    Das kann mit dem Oszilloskop verifiziert werden.



    Oszi.jpeg


    Solange die Statemachine aktiv ist, wird die Endlosschleife unabhängig von der CPU ausgeführt. Sie funktioniert auch während eines aktiven sleep() - Befehls oder wenn das Python - Programm beendet wurde.


    Die Statemachine kann aber auch durch einen Befehl deaktiviert werden:


    Code
    1. sm.active(1)
    2. time.sleep(3)
    3. sm.active(0)


    Das waren nur zwei Befehle von 9. Trotzdem lässt sich damit schon einiges machen.

    Wir haben hier nur an der Oberfläche gekratzt, PIO - Programmierung kann sehr anspruchsvoll sein. Ich blicke da auch noch nicht überall durch.

    Wenn es dann soweit ist, erstelle ich vielleicht ein Video dazu.

    Teil 3


    Die Funktion blink() definiert das Assembler - Programm für die Statemachine.


    Bereits die erste Zeile ist sehr speziell. Man nennt dieses Konstruct einen Decorator. Das gehört zur fortgeschrittenen Python - Programmierung und soll hier nicht näher besprochen werden. In unserem Fall sorgt er dafür, dass der nachfolgende Code als Assembler - Programm an die Statemachine übermittelt wird. Dabei initialisieren wir auch die verwendeten Pins. Ich gebe hier mit set_init an, welche Pins ich für den set - Befehl verwenden möchte und wie ihr Anfangszustand ist. Die üblichen Beispiele übergeben hier nur einen Zustand, verwenden also auch nur einen Ausgangspin.

    Ich übergebe hier ein Tupel von 4 identischen Zuständen, daher kann ich 4 Pins ansprechen.


    Wer das noch nicht kennt:

    Code
    1. (PIO.OUT_LOW,)*4) entspricht (PIO.OUT_LOW, PIO.OUT_LOW, PIO.OUT_LOW, PIO.OUT_LOW)

    Ist also ein normales Tupel mit 4 Elementen.


    Innerhalb der Funktion blink() ist eine Endlosschleife definiert. Sobald der Pseudobefehl wrap() erreicht wird, startet die Funktion wieder bei wrap_target(). Das benötigt keinen Taktzyklus.


    Code
    1. def blink():
    2. wrap_target()
    3. ...
    4. wrap()

    Das eigentliche Blinken erfolgt mit dem set() - Befehl. Doch das ist ein Thema für den nächsten Beitrag.

    Teil 2


    Wenn wir das Python - Programm betrachten sticht sofort die Funktion blink() ins Auge. So etwas hast du vielleicht noch nie gesehen. Aber schön der Reihe nach. Wir beginnen mit dem Start des Programms.


    Der unterste Pin ist Pin 2. Den erstellen wir ganz normal.

    Im Modul rp2 findet man die Klasse StateMachine. Davon muss eine Instanz erstellt werden.


    Code
    1. sm = rp2.StateMachine(0, blink, freq=2000, set_base=Pin(2))

    Dabei ist 0 die Statemachine - Nummer (0 .. 7) und blink die Funktion.

    freq ist die Frequenz in Hz. Ohne Angabe wird der Systemtakt (125 MHz) verwendet. Das Minimum beträgt 1908 Hz. Das ist abhängig vom Systemtakt des RP2040. Dieser beträgt beim Pi Pico 125 MHz.

    set_base definiert den ersten Pin, der für set() verwendet wird.


    Wir erstellen also eine Statemachine 0 (möglich wären 0 .. 7). Diese arbeitet mit einer Taktfrequenz von 2000 Hz. Die Funktion blink definiert den Code, den die Statemachine ausführen soll. Die unterste Pinnummer, die wir ansprechen möchten, ist 2. Die Statemachine muss dann nur noch aktiviert werden.


    Code
    1. pin2 = Pin(2)
    2. sm = rp2.StateMachine(0, blink, freq=2000, set_base=pin2)
    3. sm.active(1)


    Das Python - Programm ist damit beendet. Die Statemachine läuft aber unbeirrt weiter.

    Die Funktion blink() betrachten wir im nächsten Beitrag.

    Teil 1


    Momentan wird viel über den neuen Mikrokontroller der Raspberry Pi Foundation geschrieben und gesprochen. Er kann in Micropython, CircuitPython oder C++ programmiert werden. Auf den ersten Blick handelt es sich einfach um noch einen Mikrokontroller. Wenn man aber genauer hinschaut, findet man Begriffe wie 'State Machine' oder 'PIO Programmierung', die bei anderen Kontrollern nicht vorhanden sind.


    Genau damit habe ich mich etwas beschäftigt. Es handelt sich dabei um eigene Hardwarebereiche, die völlig unabhängig von der CPU agieren können. Zuerst sieht alles ganz einfach aus. Die Programmierung erfolgt in einer Art Assembler - Sprache (pioasm), die nur 9 Instruktionen kennt. Ein ganzes Programm kann maximal 32 Instruktionszeile enthalten. Das kann doch nicht so schwierig sein und kann man damit überhaupt etwas Vernünftiges machen?


    Ja, man kann! Aber es ist nicht gerade einfach. Ich möchte in diesem Artikel nur ein ganz einfaches Beispiel zeigen. Es deckt nur einen ganz kleinen Teil der Möglichkeiten ab, verlangt aber schon eine andere Denkweise, als man sich von der normalen Programmierung her gewohnt ist. Ich gehe hier auch nicht auf alle Details ein, es gibt bereits viele Informationen über den Hardwareaufbau der State Machines. Ich nehme das Einstiegsbeispiel aus dem Handbuch, baue es aber um einen entscheidenden Punkt aus.


    Programmiert wird in Micropython. Dieses bietet die Möglichkeit, pioasm - Programme direkt in das Script einzubinden.


    Das Beispiel ist ganz einfach. An den GPIOs 2, 3, 4, 5 ist je eine Led angeschlossen. Die Led an 2, 4, 5 blinken zusammen mit einer Frequenz von 10 Hz. Led 3 leuchtet immer dann, wenn die anderen 3 Leds dunkel sind.


    Das sieht dann so aus:



    Das Video ist um den Faktor 10 verlangsamt, da die tatsächliche Blinkfrequenz 10 Hz beträgt.

    Das ist das dazugehörende Python - Script:


    Da ist doch noch ein gewisser Erklärungsbedarf vorhanden. Also schauen wir uns das im nächsten Beitrag etwas näher an.

    Hallo Hobbying,


    es könnte auch sein, dass dein BME unschuldig ist. Es hat leider auch bei den Heltec - Boards Änderungen gegeben. Neuere Boards benötigen eine etwas andere Ansteuerung. Gehe einmal zur Lektion 11 und lade die dortigen Beispielprogramme herunter. Diese sollten mit beiden Board - Revisions funktionieren.


    Gruss

    René

    Hallo Sigi,


    leider kann ich das auch nicht sagen. C/C++ ist nicht meine 'Muttersprache'.

    Im Hintergrund wird dabei mehr oder weniger derselbe Code erzeugt.


    Die Übergabe als Pointer ist ein typisches C - Konstrukt. Die Funktion weiss dann nur, dass das erste Zeichen ein char ist. Ob das jetzt nur ein einzelnen Zeichen ist oder ein nullterminierter String ist daraus nicht ersichtlich.


    In C++ versucht man Pointer möglichst zu vermeiden. Daher übergibt man eine Referenz (&) auf die Instanz einer Klasse. Die Funktion weiss dann, dass es sich dabei um ein String - Objekt handelt. Wieso es nicht möglich ist, mit String* string einen pointer zu übergeben, weiss ich leider auch nicht.


    Vielleicht weiss einer der im Forum anwesenden C++ - Spezialisten etwas mehr dazu.


    Gruss

    René

    Hallo Hotte25


    Den Fehler sehe ich auch nicht direkt. Es könnte sein, dass dir ein Zahlenüberlauf einen Streich spielt. Aus diesem Grund solltest du im Zusammenhang mit millis() immer den Datentyp unsigned long anstelle von int verwenden. Du kannst einmal versuchen, ob es etwas hilft.


    Code
    1. unsigned long millisSekunde;
    2. unsigned long millisZehntel;
    3. unsigned long minute, sekunde, zehntel;
    4. int x = 0;
    5. unsigned long StartSekunde = 0;
    6. unsigned long StartZehntel = 0;
    7. int Starttaste = 7;
    8. int Stopptaste = 6;


    Für das Zurücksetzen mit beiden Tasten kannst du zum Beispiel diesen Code verwenden:


    Code
    1. if ((digitalRead(Starttaste) || digitalRead(Stopptaste)) == LOW) {
    2. minute = 0, sekunde = 0, zehntel = 0;
    3. x = 0;
    4. delay(500);
    5. return;
    6. }

    Beim Druck auf beide Tasten wird alles zurückgesetzt. Es werden keine anderen Aktionen durchgeführt, bis die Tasten wieder losgelassen werden. Daher das return. Wenn du die Tasten loslässt, wirst du es nicht schaffen, beide absolut gleichzeitig loszulassen. Damit in dieser Zeit keine Aktion gestartet wird, benötigt man eine kleine Pause (delay(500)).


    Gruss

    René

    Hallo Thomas,


    ja, Du brauchst einen Zeittakt, der den nächsten Schritt auslöst. Eine solche Klasse brauch mindestens zwei Funktionen: die Startfunktion wird nur einmal aufgerufen und die Schrittfunktion so oft wie möglich. Das Video, das am kommenden Donnerstag (25.03.2021) erscheint zeigt das anhand einer Klasse mit 2 Leds und einem Taster. Inzwischen solltest Du Dir auch Video 45 anschauen. Dort wird das Prinzip genauer erklärt.


    Glücklicherweise ist die von Dir verwendete getKey() - Funktion nicht blockierend, so dass Du Dich nur noch um die Aktionen kümmern musst.


    Gruss

    René

    Hallo Thomas,


    das Programm ist auch schon in dieser Kurzversion recht komplex. Das Hauptproblem ist aber schon ersichtlich. Du hast zwar sehr schön das blockierende delay() vermieden, die meisten deiner anderen Funktionen sind aber blockierend. Es ist wirklich so, die drei Funktionen in der Funktion AufrufderFunktion werden nacheinander abgearbeitet. Daher darf keine der aufgerufenen Funktionen längere Zeit verbrauchen. Es ist also nicht erlaubt, beim Aufruf von Servobewegung() zu warten, bis der Servo seine Position erreich hat. Eine eigene Klasse für den Servo wäre sinnvoll. Vielleicht helfen dir die Videos 35 bis 37 aus der Reihe 'Der einfache Einstieg in Arduino & Co.' weiter. Dort wird ein Beispiel mit einem Servo gezeigt.


    Ich muss mich aber zuerst noch etwas damit beschäftigen, bevor ich einen brauchbaren Vorschlag machen kann.


    Woher hast du Keypad.h? Die Hauptfrage ist, ob myKepad.getKey() blockierend ist. Ideal wäre, wenn getKey() einfach 0 zurückgeben würde, wenn keine Taste gedrückt ist.


    Gruss

    René