Go in Embedded: Eine Firmware in einer Sprache schreiben, die den Stack nicht sprengt

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Go und Hardware: Warum das kein Scherz ist?

\n

Wenn man den Begriff „Embedded-Entwicklung“ hört, stellt man sich normalerweise C, Assembler und mühsames Debuggen über eine LED vor. Aber die Welt steht nicht still. Heute klopft Go (oder Golang) immer selbstbewusster an die Tür der Mikrocontroller-Welt. Und es klopft nicht ohne Grund, sondern mit Goroutinen, Garbage Collector und eingebauten Interfaces.

\n

In diesem Artikel schauen wir uns an, wie Embedded-Entwicklung mit Go aussieht, schreiben echten Code für einen hypothetischen Mikrocontroller und verstehen, ob sich der Aufwand lohnt. Spoiler: Ja, wenn Sie Speicherlecks satt haben und lesbaren Code lieben.

\n\n

Warum Go und nicht C? Drei Säulen

\n

Klassisches Embedded ist C. Aber Go hat drei Killer-Features, die Ingenieure dazu bringen, ihre Gewohnheiten zu überdenken:

\n
    \n
  • Speichersicherheit: Keine wilden Zeiger, die ins All abheben können. Der Go-Compiler ist strenger als ein C-Compiler.
  • \n
  • Goroutinen: Leichtgewichtige Threads, die sich perfekt in die ereignisgesteuerte Architektur von Mikrocontrollern einfügen.
  • \n
  • Plattformunabhängigkeit: Sie schreiben Code auf einem Mac und kompilieren für ARM Cortex-M. Ohne großen Aufwand.
  • \n
\n

Natürlich gibt es einen Haken: Natives Go (gc) kann nicht für kleine MCUs kompilieren. Dafür gibt es das Projekt TinyGo – einen Compiler, der Go für Hardware mit Kilobyte-Speicher anpasst. Darauf konzentrieren wir uns.

\n\n

Einrichtung der Umgebung: TinyGo und Ihr Board

\n

Bevor wir Code schreiben, bereiten wir den Arbeitsplatz vor. Für die Embedded-Entwicklung mit Go benötigen Sie:

\n
    \n
  1. TinyGo installieren: brew install tinygo (oder von der offiziellen Website herunterladen).
  2. \n
  3. Das Toolchain für Ihr Board installieren (z. B. ARM GCC für STM32).
  4. \n
  5. Das Board über USB anschließen.
  6. \n
\n

Überprüfen, ob alles funktioniert:

\n
tinygo version\ntinygo: 0.31.0 (linux/amd64)\n
\n

Jetzt können Sie Code für jedes unterstützte Board kompilieren: tinygo build -target=arduino-nano -o firmware.hex main.go.

\n\n

Erster Blink: Eine LED mit Go

\n

Der Klassiker. Wir verbinden uns mit einem Pin und lassen ihn blinken. So sieht Mikrocontroller-Programmierung mit Go aus:

\n
package main\n\nimport (\n    "machine"\n    "time"\n)\n\nfunc main() {\n    // Die eingebaute LED ist normalerweise an Pin 13 (oder LED_BUILTIN)\n    led := machine.LED\n    led.Configure(machine.PinConfig{Mode: machine.PinOutput})\n\n    for {\n        led.High()\n        time.Sleep(500 * time.Millisecond)\n        led.Low()\n        time.Sleep(500 * time.Millisecond)\n    }\n}\n
\n

Kompilieren und auf das Board flashen:

\n
tinygo flash -target=arduino-nano -port=/dev/ttyUSB0 main.go\n
\n

Voilà! Die LED blinkt. Beachten Sie: Wir verwenden das Paket machine – die Hardware-Abstraktion von TinyGo. Sie verbirgt die Arbeit mit Registern, bietet aber eine saubere Go-Schnittstelle.

\n\n

Arbeit mit GPIO und Interrupts

\n

In echten Projekten reicht bloßes Blinken nicht. Man muss Taster, Sensoren lesen und auf Ereignisse reagieren. In Go für eingebettete Systeme werden Interrupts über Kanäle und Goroutinen realisiert. Beispiel: Wir hängen einen Handler für einen Tastendruck an.

\n
package main\n\nimport (\n    "machine"\n    "fmt"\n)\n\nfunc main() {\n    button := machine.D2\n    button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})\n\n    // Warten auf Tastendruck (pin == LOW, da Pullup zur Versorgung)\n    for {\n        if !button.Get() {\n            fmt.Println("Taste gedrückt!")\n        }\n    }\n}\n
\n

Das ist aber eine Busy-Loop. Wollen wir effizienter sein? Verwenden wir einen Interrupt über einen Kanal:

\n
func main() {\n    button := machine.D2\n    button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})\n\n    // Kanal für Ereignisse\n    ch := make(chan bool, 1)\n    button.SetInterrupt(machine.PinFalling,                

Blogs

Buchempfehlungen