为微控制器构建嵌入式应用程序

本指南的源代码可以在 GitHub 上 找到

在本教程中,我们将以 Raspberry Pi Pico 作为嵌入式设备,我们的 Swift 应用程序将在其上运行。如果您没有物理设备,请不要担心!您仍然可以在在线模拟器中运行该应用程序。

安装 Swift

如果您没有安装 Swift,请先安装它。由于 Embedded Swift 是实验性的,并且仅在预览工具链中可用,请确保安装“Development Snapshot”工具链 (main) 而不是发布工具链 (6.0)。如果您使用的是 macOS 机器,您需要确保已安装 Xcode 并通过运行 xcode-select -p 并验证它指向 Xcode 安装路径来选择它。接下来,确保安装的开发快照被选为活动状态,例如通过导出 TOOLCHAINS 环境变量

$ export TOOLCHAINS=org.swift.59202405011a

要测试您是否已安装 Swift,请从您的 shell 或终端应用程序运行 swift --version。它应该显示“6.0-dev”,这意味着您拥有“Development Snapshot”工具链。

安装嵌入式开发依赖项

按照 Pico 入门指南 安装 Raspberry Pi Pico SDK 和 Arm 嵌入式工具链。导出三个环境变量以匹配您的设置和硬件

$ export PICO_BOARD=pico
$ export PICO_SDK_PATH=...       # location to your Pico SDK
$ export PICO_TOOLCHAIN_PATH=... # location to the Arm Embedded Toolchain

如果您拥有启用 Wi-Fi 的 Pico W 板而不是常规 Pico,请注意您将需要 Pico W 示例项目 中描述的略有不同的设置,并且仅指定 PICO_BOARD=pico_w 是行不通的。

安装 CMake 3.29 或更高版本。

要测试您是否安装了所有必需的部件,您可以在终端中运行以下命令

$ swift --version
Apple Swift version 6.0-dev (LLVM b66077aefd3be08, Swift 84d36181a762913)
$ cmake --version
cmake version 3.29.2
$ echo $PICO_BOARD
pico
$ ls $PICO_SDK_PATH                              
CMakeLists.txt          README.md               external/               pico_sdk_version.cmake  tools/
CONTRIBUTING.md         cmake/                  lib/                    src/
LICENSE.TXT             docs/                   pico_sdk_init.cmake     test/
$ ls $PICO_TOOLCHAIN_PATH
13.2.Rel1-darwin-arm64-arm-none-eabi-manifest.txt  include/                                           share/
arm-none-eabi/                                     lib/
bin/                                               libexec/

构建“闪烁”嵌入式应用程序

嵌入式开发中的标准“Hello, World”程序是一个重复闪烁 LED 的程序。让我们构建一个。以下设置也可以在 swift-embedded-examples 中找到,但我们将在下面展示您只需要三个文件。让我们创建一个新的空目录,并为基于 CMake 的项目准备一个简单的结构,该结构可以在 Pico SDK 之上使用

embedded-swift-tutorial
├── BridgingHeader.h
├── CMakeLists.txt
└── Main.swift

Main.swift 和 BridgingHeader.h 文件最初可以具有以下基本内容

// Main.swift
let led = UInt32(PICO_DEFAULT_LED_PIN)
gpio_init(led)
gpio_set_dir(led, /*out*/true)
while true {
  gpio_put(led, true)
  sleep_ms(250)
  gpio_put(led, false)
  sleep_ms(250)
}
// BridgingHeader.h
#include "pico/stdlib.h"

为了在 Pico SDK 的 CMake 支持之上构建,我们需要在 CMakeLists.txt 文件中添加更多 CMake 逻辑

# CMakeLists.txt
cmake_minimum_required(VERSION 3.29)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)

set(CMAKE_Swift_COMPILATION_MODE wholemodule)
set(CMAKE_Swift_COMPILER_WORKS YES)

project(blinky)
pico_sdk_init()
enable_language(Swift)

add_executable(blinky Main.swift)
set_target_properties(blinky PROPERTIES LINKER_LANGUAGE CXX)

# Clear the default COMPILE_OPTIONS which include C specific compiler flags that the Swift compiler will not accept
# Instead, set those options to only apply when compiling C code.
set_target_properties(pico_standard_link PROPERTIES INTERFACE_COMPILE_OPTIONS "")
target_compile_options(pico_standard_link INTERFACE "$<$<COMPILE_LANGUAGE:C>:SHELL: -ffunction-sections -fdata-sections>")

set(SWIFT_INCLUDES)
foreach(dir ${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES})
    string(CONCAT SWIFT_INCLUDES ${SWIFT_INCLUDES} "-Xcc ")
    string(CONCAT SWIFT_INCLUDES ${SWIFT_INCLUDES} "-I${dir} ")
endforeach()

target_compile_options(blinky PUBLIC "$<$<COMPILE_LANGUAGE:Swift>:SHELL:
        -enable-experimental-feature Embedded
        -target armv6m-none-none-eabi -Xcc -mfloat-abi=soft -Xcc -fshort-enums -Xfrontend -function-sections
        -import-bridging-header ${CMAKE_CURRENT_LIST_DIR}/BridgingHeader.h
        ${SWIFT_INCLUDES}
    >")

target_link_libraries(blinky pico_stdlib hardware_uart hardware_gpio)
pico_add_extra_outputs(blinky)

现在我们准备好为 Pico 配置和构建此固件了。运行以下命令

$ cmake -B build -G Ninja .    # configure step
$ cmake --build build          # build step

构建应该成功,并以多种格式(ELF、HEX、UF2)生成固件,包括一些信息转储文件(DIS、ELF.MAP)

$ ls -al build/blinky*
-rwxr-xr-x  1 kuba  staff   8.0K Jan  1 12:00 build/blinky.bin*
-rw-r--r--  1 kuba  staff   145K Jan  1 12:00 build/blinky.dis
-rwxr-xr-x  1 kuba  staff    30K Jan  1 12:00 build/blinky.elf*
-rw-r--r--  1 kuba  staff   222K Jan  1 12:00 build/blinky.elf.map
-rw-r--r--  1 kuba  staff    23K Jan  1 12:00 build/blinky.hex
-rw-r--r--  1 kuba  staff    16K Jan  1 12:00 build/blinky.uf2

在设备上运行固件

如果您有 Raspberry Pi Pico,我们现在将上传构建的固件并运行它。如果您没有,请跳到下一节并在模拟器中运行完全相同的固件文件。

通过 USB 电缆将 Raspberry Pi Pico 板连接到您的 Mac,并确保它处于 USB 大容量存储固件上传模式。如果您从未上传过任何固件,通常情况就是如此 - 如果 Pico 的内存不包含任何有效固件,则 Pico 启动到固件上传模式。一旦上传了有效的固件,设备将在插入后运行该固件。要返回固件上传模式,请在插入板时按住 BOOTSEL 按钮

然后,Pico 应该在 /Volumes 中显示为已挂载的卷(在本例中为 RPI-RP2)

$ ls -al /Volumes
lrwxr-xr-x   1 root  wheel     1B Jan  1 12:00 Macintosh HD@ -> /
drwx------   1 kuba  staff    16K Dec 31  1969 RPI-RP2/

将 UF2 文件复制到此卷

$ cp build/blinky.uf2 /Volumes/RPI-RP2

这将使 Pico 自动安装固件,自行重启并运行固件。

绿色 LED 现在应该反复闪烁。万岁!我们的第一个 Embedded Swift 程序正在嵌入式设备上运行!

在模拟器中运行固件

如果您没有物理 Pico,或者您想快速迭代,Wokwi 是一个免费的在线模拟器,模拟各种嵌入式微控制器,包括 Raspberry Pi Pico。它执行与您通常上传到物理设备的固件二进制文件相同的二进制文件,并一次模拟一条指令。

在 Wokwi 中打开一个 新的 Pico 项目。无需使用代码编辑器编写 C 代码,按 F1 并选择“Upload Firmware and Start Simulation”(上传固件并开始模拟)。然后选择我们构建过程生成的 UF2 文件。

将 UF2 文件上传到 Wokwi 后,模拟将开始,并且 LED 应该开始反复闪烁。万岁!我们的第一个 Embedded Swift 程序正在模拟器中运行!

奖励:使用 Embedded Swift 为您的主机操作系统构建一个简单的程序

虽然 macOS 和 Linux 等桌面操作系统不是 Embedded Swift 的典型目标,但您绝对可以使用 Embedded Swift 模式为它们构建代码。这对于实验、尝试 Embedded Swift 或能够快速迭代某些代码的想法非常有用,这些代码实际上不需要物理设备即可工作。

Embedded Swift 中最简单的程序可以是常规的“Hello, World”程序

// HelloEmbedded.swift
print("Hello, Embedded Swift 😊")

可以通过直接调用 swiftc 编译器将其构建为可执行文件,但我们需要添加标志来启用 Embedded Swift,以及完全模块优化。

$ swiftc HelloEmbedded.swift -o HelloEmbedded -enable-experimental-feature Embedded -wmo

这将生成一个常规的可执行二进制文件,但请注意,它的大小非常小,并且它也实际上不依赖于操作系统中的 Swift 运行时(所有 Embedded Swift 二进制文件都在内部携带其运行时+stdlib 依赖项)

$ ls -al
-rwxr-xr-x    1 kuba  staff    18K May 16 17:19 HelloEmbedded*
-rw-r--r--    1 kuba  staff    59B May 16 17:16 HelloEmbedded.swift
$ otool -L HelloEmbedded
HelloEmbedded:
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1000.0.0)

让我们运行它

$ ./HelloEmbedded 
Hello, Embedded Swift 😊

万岁!我们的第一个主机端 Embedded Swift 程序正在工作!

下一步去哪里


本指南的源代码可以在 GitHub 上 找到