Dienstag, 16. September 2014

Debounce / Entprellen für STM32

Hallo Entwickler,

gestern und heute habe ich mir überlegt, wie man mit dem STM32F10x elegant Tasten entprellen kann. Eine gute Einführung zu diesem Thema liefert http://www.emcu.it/STM32/STM32Discovery-Debounce/STM32Discovery-InputWithDebounce_Output_UART_SPI_SysTick.html . Von dort habe ich auch die grundsätzlich Idee zu meiner Lösung. An drei Stellen habe ich jedoch Verbesserungen vorgenommen:

  • Meine Lösung arbeitet mit bis zu 16 Tastern (also einem gesamten Port)
  • Meine Lösung hat nur halb so viel Speicherplatzverbrauch pro Taster
  • Meine Lösung gibt einen Tastimpuls verzögerungsfrei weiter.
Den letzten Punkt muss ich wohl noch ein wenig detaillierter ausführen. Der emcu-Code stellt einen Pegelwechsel an einem Pin fest und beginnt dann, die entsprechende Pegelvariable hochzuzählen. Erst wenn diese Pegelvariable nach mehreren Durchläufen einen bestimmten Wert erreicht hat, wird der Tastendruck bzw. das Loslassen detektiert. Ich gehe da einen anderen Weg: Sobald ein Pegelwechsel erkannt wird, wird dieser sofort weitergegeben. Danach schließt sich eine "Totzeit" an, in der kein weiterer Pegelwechsel erkannt wird. Jegliches Prellen in dieser Zeit wird also sicher unterdrückt.

So langsam werde ich auch vertraut mit bestimmten Möglichkeiten, dem Compiler und dem Linker in meiner Toolchain Anweisungen im Code zu geben. Sehr interessant für den vorliegenden Anwendungsfall ist beispielsweise das "__attribute__((weak))".

Dieses kommt (per define abgekürzt mit "AW") in dem Modul debounce.c zum Einsatz, um überschreibbare Funktionsdefinitionen für die Ereignishandler meiner Debounce-Routine zu realisieren. Das gleiche Prinzip kommt auch bei den Interrupt-Handlern der Standard Peripherial Libary zum Einsatz. Die Idee ist einfach: Die mit weak attributierte Funktion kann durch eine gleichnamige Funktion in einer beliebigen c-Datei überschrieben werden. Ist keine derartige Überschreibung definiert, wird eben die Weak-Standardimplementierung verwendet. Üblicherweise macht die Standardimplementierung einfach garnichts... Schaut Euch einfach die Funktionen DEBOUNCE_click() und DEBOUNCE_up() in debounce.c und main.c an.


Das Beispiel im SVN ist auf das von mir empfohlene 4€-STM32F103-Board zugeschnitten: Eine LED ist an PA1 angeschlossen; der Taster (in meinem Fall einfach eine Steckbrücke) befindet sich an PB0.

Samstag, 13. September 2014

Microseconds Delay for STM32

Lieber Entwickler,

alle STM32-Derivate bieten für Performance-Messungen einen Cycle-Counter. Nach Aktivierung zählt dieser mit jedem Taktzyklus einfach um Eins nach oben. Damit eignet er sich hervorragend für blockierende Delays im Mikrosekundenbereich. Vorteilhaft an dessen Verwendung ist:

  • Delays mit Timern verbrauchen eine wertvolle und mächtige Timer-Ressource für profanes Zählen
  • Delays mit dem Systick-Timer im Mikrosekundenbereich sorgn dafür, dass der Systick-Interrupt jede Mikrosekunde aufgerufen wird. So kommt der STM32 kaum noch zu seinen eigentlichen Aufgaben ;-)
  • Delays mit __NOP() und Schleifen sind ungenau, weil sie durch Interrupts unterbrochen werden können.
So ist die Verwendung des Cycle-Counters m.E. der absolut zu bevorzugende Weg.


Den Code sowie ein schönes Anwendungsbeispiel findet ihr in meinem SVN-Repository: https://stm32tutor.googlecode.com/svn/trunk/103dht22. Wesentlich dort sind die Dateien timing.c und timing.h. Beachtet, dass das Modul vor seiner erstmaligen Verwendung mit TIMING_Init(); initialisiert werden muss. Diese Routine schaltet intern lediglich den Performance-Couter frei und setzt ihn auf einen Startwert.

Happy Coding

Dienstag, 9. September 2014

DHT22 AM2302 Sensor am STM32F103

Liebe Entwickler,

mein erstes Projekt mit dem STM32F103-Board ist die Ansteuerung eines DHT22-Sensors zur Temperatur- und Luftfeuchtigkeitsmessung. Mein Testaufbau sieht dann so aus:


Die schwarze Box rechts ist ein Logic-Analyzer von Saleae (möglicherweise ein chinesischer Nachbau davon ;-)), der mir enorm beim Debuggen geholfen hat.

Ein halb-englisch-halb-chinesisches Datenblatt vom DHT22 stellt beispielsweise der Händler Adafruit auf der Produktseite zur Verfügung. Ich selbst habe den Sensor nicht dort, sondern wie üblich bei Aliexpress bestellt. Mit Hilfe der ebenfalls auf der Produkseite angebotenen Arduino-Bibliothek ließ sich das Protokoll des DHT22 dann auch entschlüsseln. Insbesondere die Umwandlung der gelesenen Bits in eine Fließkommazahl habe ich von Adafruit geguttenbergt.

Zum Protokoll selbst:
Generell handelt es sich um ein serielles, unidirektionales Verfahren mit LSB-first. Es wird lediglich eine einzige Datenleitung zwischen Host und DHT22 verwendet. Sowohl Host als auch DHT22 können die Datenleitung auf Masse "ziehen" und sie dann wieder "loslassen". Ein PullUp-Widerstand stellt sicher, dass im Ruhezustand die Datenleitung auf dem Niveau der Versorgungsspannung liegt. Empfohlen wird ein 4k7..10k-Widerstand. Der im SMT32 integrierte PullUp hat etwa 40k, weshalb ich mich für einen externen PullUp mit 10k entschieden habe.

Eine Transaktion läuft ausgehend vom Ruhezustand wie folgt ab

  1. Der Host zieht die Datenleitung >500us auf Masse. Der DHT22 interpretiert dies als Startbefehl. Der Host zieht nun bis zum Ende der Übertragung nicht mehr auf Masse, sondern "lauscht" passiv.
  2. Etwa 20-40us nach dem "Loslassen" des Hosts zieht der DHT22 die Datenleitung für 80us auf Masse und zeigt damit seine Präsenz.
  3. Etwa 80us nach dem "Loslassen" beginnt die eigentliche Datenübertragung
  4. Für jedes Bit zieht der DHT22 die Leitung für 50us auf Masse
  5. Die Dauer des darauf folgenden Loslassens bestimmt die Polarität des Bit.
    • Wenn das Loslassen 26-28us dauert, ist dies als 0 zu interpretieren
    • Wenn das Loslassen etwa 70us dauert, ist dies als 1 zu interpretieren
  6. Insgesamt werden so 40bits übertragen

Eine komplette Transaktion sieht im Logic-Analyzer dann so aus:


Mein Ziel in diesem Projekt ist es, bis zu 16 DHT-Module parallel an einen Port des STM32 anzuschließen und diese in einer Interrupt-gesteuerten Schleife immer wieder abzufragen. Ich war der festen überzeugung, dass die Performance eines 72MHz-Prozessors probleeeeeemlos ausreicht, um das notwendige "Bitbanging" und das bisschen Aktualisieren von Datenarrays en passant zu erledigen.
Tja, wie man sich doch täuschen kann!
Tatsächlich wird das Timing in diesem Projekt zur sehr kritischen Komponente. Die Zykluszeiten des DHT22 Signals machen es erforderlich, dass zumindest im 20us-Zyklus abgetastet wird (Shannon-Theorem +  Sicherheitszuschlag ;-) ). In dieser Zeit schafft es der STM32 nicht, über alle 16 Bits des Ports zu iterieren und bei Bedarf ein Datenarray zu aktualisieren. Das Diassembly meines ersten Codeentwurfes zeigt im Abgleich mit der Taktzyklustabelle von ARM, weshalb das so ist (Man beachte den Performance-Counter oben rechts):


So einfache Schleifen oder if-Abfragen verbrauchen letztlich jeweils über 10 Takte. Alleine die 16malige Abfrage, ob für das betreffende Bit etwas zu tun ist, verschlingt über 300 Takte, also grob 5us. Das ist zwar jetzt ein Klagen auf hohem Niveau, aber alleine diese Prüfung macht 25% der mir zur Verfügung stehenden Zeit aus . Im Übrigen half da auch eine testweise aktivierte Compileroptimierung nicht viel.

Ich habe meinen Code weiter optimiert und die Anforderungen reduziert. Wesentliche Punkte waren

  • Der Port-Input-Befehl der SPL ("GPIO_ReadInputDataBits") dauert wegen der im Debug-Mode durchgeführten Assertions schon alleine etwa 3us. Natürlich geht das im Release schneller, aber zur Geschwindigkeitssteigerung dieser spezeillen Funktion beim Debuggen habe ich die SPL-Aufrufe durch direkte Registerzugriffe ersetzt.
  • Reduktion der unterstützten Pins (im Code nur noch 8, kann aber erhöht werden)
  • In einer ersten Version suchte ich sowohl nach der steigenden als auch nach der fallenden Flanke des DHT-Signals. Anhand der Zeitdauer des positiven Signals konnte der Code bestimmen, ob eine 0 oder eine 1 übertragen wurde. Die neue Codeversion wird ausschließlich bei fallenden Flanken aktiv. Die Polarität des Bits wird durch das Intervall zur vorherigen fallenden Flanke bestimmt. Insgesamt wird also weniger Code pro Sensor ausgeführt.
  • Einige Umstellungen im Code brachten einige wenige Taktzyklen.
Der Kerncode "doProtocol" ist so konzipiert, dass er aus Interrupthandlern von Timern und EXTIs aufgerufen werden kann, aber auch für den Aufruf aus einer synchronen pollenden Methode geeignet ist. Im Codebeispiel ist die letzte Möglichkeit umgesetzt.
Die Funktionsweise des Codes ist an sich sehr einfach:
  • Eine Zustandsverwaltung kümmert sich zunächst um das Reset-Signal und das Abwarten der Präsenzmeldungen. Sollte die Präsenzmeldung nicht wie im Datenblatt beschrieben erfolgen, werden Fehler zurückgegeben
  • Sobald die Zustandsverwaltung erkennt, dass die eigentliche Datenübertragung erfolgt, übergibt sie die Kontrolle immer an doBitbang2. Deren Funktionalität ist im Code anhand der Kommentare nachvollziehbar. Besonders hinweisen möchte ich auf zwei Sachverhalte
    • Es findet eine Timeout-Erkennung statt. Wenn der Sensor keine /zu wenige Signale liefert, kommt es nicht zu einem Fehler. Auch zu viele Signale (wie auch immer das passieren kann) werden effizient abgefangen und führen nicht zu einem Überlauf.
    • Die allererste fallende Flanke muss übersprungen werden, weil diese ja noch kein Bit repräsentiert, sondern lediglich den Start des Payloads repräsentiert.
Den kompletten Code findet ihr in Form eines Beispielprojektes unter https://stm32tutor.googlecode.com/svn/trunk/103dht22

Happy Coding - ich freue mich auf Verbesserungsvorschläge




Ein STM32F103 Board für 4€

Liebe Entwickler,

neben meinem STM32F4 Discovery habe ich mir bei Aliexpress.com einige STM32F103C8T6-Boards bestellt und in Betrieb genommen. 5 davon bleiben beim günstigsten Händlerincl. Versandkosten unter der magischen 22€-Zollgrenze. Eine Suche mit "STM32F103C8T6 board" + "Show Price per Piece" + Sortieren nach Preis sollte Euch schnell zum Ziel führen. Ich habe hier bestellt.

Es ist schon genial, was sich mit diesen Boards zum Stückpreis von etwa 4€ anstellen lässt. 32bit, 72MHz, 64kb Flash, 20kB Ram, viele IOs, ADC, RTC, USB und diverse Timer lassen jeden Arduino echt alt aussehen:-) Leider ist der Community-Support noch ungleich schlechter - doch daran möchte ich zusammen mit vielen anderen ja arbeiten.

Traditionell schwierig ist es natürlich, von den Chinesen vernünftige Datenblätter zu bekommen. Einer der unzähligen Händler für dieses Board bietet glücklicherweise einen Link zu einem Datenblatt an. Zur Sicherheit habe ich das Datenblatt hier auf meinen SVN hochgeladen.

Für die STM32F1-Reihe bietet ST noch keine CubeMX-Firmware an. Zu kritisch ist das allerdings nicht - auch die als "Standard Peripherial Library" (SPL) hat genug zu bieten. Die von mir beschriebene Toolchain nutzt das Gnuarmeclipse-Plugin und dieses hat die SPL für den STM32F1 direkt integriert. Die Erstellung eines neuen Projektes ist also komplett menügeführt und ohne händisches Einbindung von Header-Dateien oder das Setzen von DEFINES möglich.

Zum Programmieren und Debuggen dieser Boards bieten die Chinesen Nachbauten des STLinkV2 an. Ich habe diese nicht ausprobiert. Statt dessen verwende ich mein STM32F4-Discovery hierfür. Das klappt in der Regel sehr gut. Manchmal jedoch scheint sich das Konstrukt irgendwie aufzuhängen. In diesen Fällen nutze ich das STM32 ST-Link Utility (siehe meine Toolchain), verbinde mich mit dem Target und führe ein "Erase All" durch. Danach konnte ich immer wieder weiter arbeiten. Die Verkabelung gestaltet sich nicht weiter schwer.

  1. Die beiden Jumper CN3 entfernen ("ST-LINK DISCOVERY")
  2. 4 Leitungen verbinden, nämlich:

STM32F4-Discovery Signal STM32F103-Board
P2.5 oder 6 3V 3V3
CN2.2 SWCLK CLK
CN2.3 GND GND
CN2.4 SWDIO DIO

In Eclipse ist für OpenOCD die folgende Einstellung bei "Config Options" erforderlich:
-f interface/stlink-v2.cfg -f target/stm32f1x_stlink.cfg



Mittwoch, 21. Mai 2014

printf(...) ganz einfach per Semihosting auf dem STM32F4

Lieber Entwickler,

bei einer Konsolenanwendung auf dem PC ist es (schlechte) gängige Praxis, permanent Debug-Ausgaben per System.out.println("") oder Console.Writeln("") oder printf("") auszugeben. Bei einfachen oder experimentellen Anwendungen entwickelt man insbesondere auch in Verbindung mit einem Debugger schnell ein Gefühl dafür, wo im Falle eines Fehlers nochmals Hand anzulegen ist.

Bei Entwicklungen mit dem Microcontroller ist man meist auf LEDs oder einfache LCD-Displays angewiesen, um Text auszugeben. Profis schreiben über über die serielle Schnittstelle raus und hängen sich mit einem Terminal-Programm an den Prozessor. Alle Lösungen haben jedoch Nachteile:

  • LEDs können nur seeehr einfache Informationen ausgeben. Einen komplexen Morsecode will wohl keiner entwickeln...
  • Ein LCD verlangt nach einigem Code, verbraucht rare IOs und kostet Geld
  • Die serielle Schnittstelle verbraucht einen UART und macht eine zusätzliche Kabelverbindung
Mit der Technik "Semihosting" bietet die STM32-Familie glücklicherweise eine sehr einfache Möglichkeit, Informationen vom Microcontroller über die Debug-Verbindung an den Host zu senden. Meine Toolchain ist bereits für Semihosting vorbereitet. Ein Beispielprojekt habe ich in hier abgelegt

Zur Aktivierung sind die folgenden Schritte notwendig.
  • Hole Dir mein Semihosting-Testprojekt
  • Du kannst dieses Projekt 1:1 verwenden. Es misst einfach nur die Spannung am PA1-Eingang und gibt diese jeder Sekunde aus. Bei mir ist dort ein Sharp-Infrarot-Entfernungsmesser angeschlossen. Wenn bei Dir nichts dort angschlossen ist, funktioniert die Anwendung trotzdem. Wesentlich sind die Dateien:
    • Inc/printf.h
    • Inc/semihosting.h
    • Src/printf.c
    • Src/semihosting.c
    • Src/sh_cmd.asm
  • Füge die asm- und die c-Dateien Deinem Src-Verzeichnis hinzu
  • Füge die h- Datei Deinem Inc-Verzeichnis hinzu
  • Aktiviere Semihosting in den Debugger-Settings (siehe Screenshot)
  • Füge #include "printf.h" in den User-Code-Bereich 0 der main.c hinzu
  • Füge in der while-Schleife jetzt einen printf-Befehl und eine Verzögerung hinzu, beispielsweise: "printf("Der Prozessor ist jetzt %u msec gelaufen, HAL_GetTick()); HAL_Delay(1000);"

Ein wenig seltsam mag erscheinen, dass ich hier eine eigene printf-Funktion verwende und nicht auf die integrierte Funktionalität der STM-Firmware-Library ("newlib") zurück greife. Grund hierfür ist, dass die newlib wirklich jedes Feature von printf unterstützt und sowohl viel Speicher im Flash als auch auf dem Heap verbraucht. Meine Implementierung (die ich natürlich 1:1 abkekupfert habe) realisiert eine mehr als ausreichende Teilmenge der Gesamtfunktionalität und bleibt herrlich kompakt.

Happy Coding

Sonntag, 11. Mai 2014

RTOS 8.0.1 auf dem STM32F4 discovery

Liebe Entwickler,
heute versuche ich, das FreeRTOS 8.0.1 auf meinem STM32F4 discovery zum Laufen zu bringen und mein Vorgehen hier zu beschreiben. Über die Installation der Toolchain hatte ich bereits geschrieben. Ziel ist es nun, mit Hilfe des vorbezeichneten OS die LEDs auf dem Board zum Blinken zu bringen.

  • Voraussetzung ist nun, dass ihr die Toolchain und das Basisprojekt funktioniert. Im Staccato:
    • Im CubeMX ein Projekt für das STM32F4 discovery erzeugen
    • --->Achte darauf, dass in der NVIC-Konfiguration 4bin für Preemption-Priorität und 0 Bits für die Subpriorität eingestellt sind<--
    • PINs der LEDs (PD12 - PD15) auf Output Mode PushPull umstellen
    • Code erzeugen und Eclipse C-Projekt im gleichen Verzeichnis erstellen
    • Include-Verzeichnisse hinzufügen
      • ${ProjDirPath}/Drivers/CMSIS/Device/ST/STM32F4xx/Include
      • ${ProjDirPath}/Drivers/CMSIS/Include
      • ${ProjDirPath}/Drivers/STM32F4xx_HAL_Driver/Inc
      • ${ProjDirPath}/Inc
    • Symbole definieren
      • Name="USE_HAL_DRIVER", Value leer lassen
      • Name= "STM32F407xx", Value leer lassen
    • Unnötige Header-Files in Drivers\CMSIS\Device\ST\STM32F4xx\Include ausschließen
    • Datei Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc/startup_stm32f407xx.s umbenennen
    • Target Processor M4; softfp
    • Linker-File einbinden
    • Debugger-Option: -f board/stm32f4discovery.cfg
  • FreeRTOS-Installation
    • Lade Dir nun die FreeRTOS-Distribution herunter (http://www.freertos.org/) und entpacke es in ein Unterverzeichnis Deiner Repository-Verzeichnisses (bei mir c:\stm32ws\repository).
    • Benenne das "Source"-Verzeichis von FreeRTOS in "FreeRTOS" um und ziehe es bei gedrückter rechter Maustaste ins Projekt hinein --> "Link to Files and Folders", "WORKSPACE_LOC".
    • Lösche aus dem Source/Portable-Verzeichnis alle unnötigen Toolchains (!=GCC && !=MemMang) und Prozessortypen (!=ARM_CM4F) raus.
    • Besorge Dir aus der veralteten FreeRTOS-Distribution aus der CubeMX-Firmware (C:\stm32ws\repository\STM32Cube_FW_F4_V1.1.0\Middlewares\Third_Party\FreeRTOS\Source) das Verzeichnis CMSIS_RTOS und kopiere dieses in das FreeRTOS-Verzeicnis Deines Projektes. Verlinke das Verzeichnis nicht, weil wir da gleich eine Datei anpassen.
    • Passe die darin befindliche Datei cmsis_os.c  in der Funktion "osStatus osThreadIsSuspended(osThreadId thread_id)" in Zeile 1098 an :  if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED)...
    • Besorge Dir aus einem FreeRTOS-Demoprojekt der STM32-Firmware (Bsp: C:\stm32ws\repository\STM32Cube_FW_F4_V1.1.0\Projects\STM32F429I-Discovery\Applications\FreeRTOS\FreeRTOS_ThreadCreation\Inc) eine FreeRTOSConfig.h Datei und packe diese in den Inc-Ordner Deines Projektes.
    • Füge das darin befindlich Include Verzeichnis "C:\stm32ws\repository\FreeRTOS\FreeRTOS\include" wie zuvor beschrieben allen Configurations und allen Languages hinzu. Füge außerdem das Verzeichnis "C:\stm32ws\repository\FreeRTOS\FreeRTOS\portable/GCC/ARM_CM4F" hinzu
  • Füge das Board Support Package (BSP) für das STM32F4-Discovery Deinem Projekt hinzu.
    • Ziehe dazu den Ordner "C:\stm32ws\repository\STM32Cube_FW_F4_V1.1.0\Drivers\BSP\STM32F4-Discovery" mit rechter Maustaste in den Ordner "Drivers" Deines Projektes und mache einen Link über "WORKSPACE_LOC". 
    • Füge das darin befindlich Include Verzeichnis "C:\stm32ws\repository\STM32Cube_FW_F4_V1.1.0\Drivers\BSP\STM32F4-Discovery" wie zuvor beschrieben allen Configurations und allen Languages hinzu.
  • Führe ein Exclude from Project der Datei Drivers/CMSIS/RTOS/cmsis_os.h durch. Hierbei handelt es sich nur um ein rudimentäres Template, welches für unsere Zwecke unbrauchbar ist. Jene Datei, die aus diesem Template speziell für FreeRTOS entstehen muss, haben wir und weiter oben schon mit dem Verzeichnis "CMSIS_RTOS" ins Projekt geholt.
Die Vorbereitungen sind damit abgeschlossen. Hoffentlich habe ich bei der Doku keinen Schritt vergessen. Zur Sicherheit gebe ich Euch noch ein Foto der Includes und meiner Projektstruktur.



  • Die main.c Datei muss jetzt mit passenden Tasks und Initialisierungsaufrufen befüllt werden. Von großer Wichtigkeit ist an dieser Stelle jedoch der Aufruf des FreeRTOS-Schedulers nach jeder Millisekunde. Ich empfand es als geschickt, diesen n der Callback-Funktion des SysTick-Timers aufzurufen. Die Funktion xPortSysTickHandler() muss zunächst als "extern" deklariert werden und kann dann aufgerufen werden:
void HAL_SYSTICK_Callback(void){
    xPortSysTickHandler();
}
Das gesamte Projekt findet Ihr auf https://code.google.com/p/stm32tutor/source/browse/#svn%2Ftrunk%2Frtostest%2FSrc

Mittwoch, 7. Mai 2014

STM32F4 Discovery mit OpenSource Toolchain programmieren und debuggen

ACHTUNG: Dieser Beitrag ist mittlerweile veraltet

[2015-12-07] Lieber Entwickelnde,

das Rad der Zeit dreht sich weiter. Mittlerweile ist es nicht mehr erforderlich, sich seine Toolchain "so umständlich" zusammenzubauen, wie hier beschrieben. "ac6" bietet auf http://www.openstm32.org/HomePage eine auch von ST empfohlene freie Toolchain an, die letztlich die gleichen Komponenten einsetzt. Die Dinge sich jedoch fertig vorkonfiguriert und der Start geht einfach und schnell, übrigens auch für die F1-Serie. Aktuell entwickele ich sehr gerne damit!

HTH und happy Coding!


Liebe Entwicklerin, lieber Entwickler,

im Folgenden möchte ich Dir die Installation und Grundkonfiguration einer freien Toolchain für die STM32F4-Prozessorfamilie beschreiben. Diese „Kette der Werkzeuge“ versetzt Dich kostenlos in die Lage, Software mit hohem Komfort zu schreiben, zu compilieren, zu linken, auf den Prozessor zu übertragen, dort zur Ausführung zu bringen und zu debuggen. Dieses Tutorial setzt einen Windows-PC (in meinem Fall Windows 7, 64bit) und natürlich zum Ausprobieren ein Entwicklungsboard mit einem STM32F4 voraus. Ich liebe und empfehle das STM32F4-Discovery für etwa 15Euro bei diversen Online-Händlern. Du brauchst etwa 1 Stunde, um die klassische blinkende Leuchtdiode zu bekommen. Die Toolchain funktioniert mit geringfügigen Änderungen auch mit anderen Mitgliedern der STM32-Familie.