Go在嵌入式系统中的应用:编写不会导致堆栈溢出的固件

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Go与硬件:为什么这不是玩笑?

\n

当听到“嵌入式开发”这个词时,通常人们会想到C语言、汇编语言以及通过LED进行的痛苦调试。但世界在进步。如今,Go(或Golang)正越来越自信地敲响微控制器世界的大门。它并非毫无来由地敲门,而是携带着goroutine、垃圾回收器和内置接口而来。

\n

在本文中,我们将探讨Go语言嵌入式开发的实际面貌,为假设的微控制器编写真实代码,并判断这是否值得。剧透:是的,如果你厌倦了内存泄漏并喜欢可读性强的代码。

\n\n

为什么选择Go而不是C?三大支柱

\n

经典的嵌入式开发是C语言。但Go有三个杀手级特性,促使工程师们重新审视习惯:

\n
    \n
  • 内存安全:没有可能飞向太空的野指针。Go编译器比C编译器更严格。
  • \n
  • Goroutine:轻量级线程,完美适用于微控制器的事件驱动架构。
  • \n
  • 跨平台性:你在Mac上编写代码,却可以编译到ARM Cortex-M。无需繁琐的配置。
  • \n
\n

当然,有一个问题:原生Go(gc)无法为小型MCU编译。但有一个项目TinyGo——一个编译器,它将Go适配到只有几千字节内存的硬件上。我们将重点关注它。

\n\n

环境配置:TinyGo与你的开发板

\n

在编写代码之前,让我们准备好工作环境。对于Go语言嵌入式开发,你需要:

\n
    \n
  1. 安装TinyGo:brew install tinygo(或从官方网站下载)。
  2. \n
  3. 为你的开发板安装工具链(例如,用于STM32的ARM GCC)。
  4. \n
  5. 通过USB连接开发板。
  6. \n
\n

检查一切是否正常:

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

现在可以为任何支持的开发板编译代码:tinygo build -target=arduino-nano -o firmware.hex main.go。

\n\n

第一个闪烁:Go语言控制LED

\n

经典案例。连接到引脚,控制电平。以下是Go语言微控制器编程的样子:

\n
package main\n\nimport (\n    "machine"\n    "time"\n)\n\nfunc main() {\n    // 内置LED通常在引脚13上(或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

编译并烧录到开发板:

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

瞧!LED闪烁了。请注意:我们使用了machine包——这是TinyGo的硬件抽象层。它隐藏了底层的寄存器操作,但提供了干净的Go接口。

\n\n

GPIO操作与中断

\n

在实际项目中,仅仅闪烁是不够的。需要读取按钮、传感器,并对事件做出响应。在Go用于嵌入式系统中,中断通过通道和goroutine实现。示例:为按钮按下添加一个处理程序。

\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    // 等待按下(pin == LOW,因为上拉至电源)\n    for {\n        if !button.Get() {\n            fmt.Println("按钮被按下!")\n        }\n    }\n}\n
\n

但这是忙等待循环。想要更高效?通过通道使用中断:

\n
func main() {\n    button := machine.D2\n    button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})\n\n    // 用于事件的通道\n    ch := make(chan bool, 1)\n    button.SetInterrupt(machine.PinFalling,                

博客

书籍推荐