Embassy 在 Blue Pill 上的点灯案例
Published: 2024-04-05 07:38:59

这篇文章旨在尝试将 Embassy 的 blinky 案例在 Blue Pill (STM32F103C8T6）开发板上运行，它将记录所需要的依赖和常见问题的解决方法

本文参考了 [Jonathan Klimt](https://jonathanklimt.de/electronics/programming/embedded-rust/rust-STM32F103-blink/) 的环境配置

系统环境: Ubuntu 22.04.4 (6.5.0-26-generic)

## 1. 前提
假设已经安装 `rust-up` 和 `build-essential`，有正常的 Rust 编译环境。当前使用的 Rust Toolchian 版本为 1.77.1（2024-03-27），如果出现问题可以尝试使用这个 Toolchain 版本看看问题是否消失。

### 1.1 依赖库安装
```bash
sudo apt install -y pkg-config libudev-dev libusb-1.0 libftdil-debv
```

### 1.2 `probe-rs` Toolkit
我们安装 [`probe-rs`](https://probe.rs/docs/getting-started/installation/)，其中包含了 `probe-rs`，`cargo-flash`，`cargo-embed` 等常用工具

```bash
cargo install probe-rs --features cli
```

### 1.3 ARM Cortex-m toolchain
这是进行交叉编译需要的库
```bash
sudo apt install binutils-arm-none-eabi
```

### 1.4 连接设备
我们需要安装 OpenOCD 来对设备进行快速烧录和调试
```bash
sudo apt-get install openocd
```

安装完成后，我们尝试使用 OpenOCD 对我们的 ST-Link 设备进行探测。我们先查看 ST-Link 连接器是否被时被识别，我们使用 `lsusb | grep ST` 命令对它进行查找

![ST-Link USB Connection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5h2em1qs92eybfoso542.png)

说明我们已经成功连接了，这时我们通过 OpenOCD 来探测设备是否工作正常

```bash
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg
```

![OpenOCD Error Message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vnw378f7s0f80764llgp.png)

我们注意到有错误信息，这是因为这个型号（STM32F103C8T6）的芯片是仿制版，其 CPU tap id 被修改，请见 [这个](https://community.st.com/t5/stm32-mcus-boards-and-hardware/how-to-deal-wirh-could-not-verify-st-device/td-p/234230) 讨论。我们将 OpenOCD 安装目录（通常为 `/usr/share/openocd`）下 `scripts/target` 的 `stm32f1x.cfg` 中的 `0x1ba01477` 改为 `0x2ba01477`。请注意将原始文件保存，以免需要恢复。

我们现在能够成功连接并且识别开发板了


![OpenOCD Successful Message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ta7065ddscgetoo8xj5a.png)

### 1.5 ST-Link Tools
我们安装 [st-link tools](https://github.com/stlink-org/stlink?tab=readme-ov-file) 便于我们对开发板进行快速的格式化和烧录，这样能够使得我们绕开 cargo-embed 工具，能够更清楚地看到错误信息，并且更灵活。

我们下载 [stlink-1.8.0_amd64.deb](https://github.com/stlink-org/stlink/releases) 或任何版本的 Debian 包。接下来在下载位置使用 `sudo apt install ./{file-name.deb}` 进行安装。这样不需要我们编译。

安装完成后使用 `st-info --descr` 进行检查是否正常

![st-info descr command result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lnm70m6k77zvljv9v8h6.png)

我们可以尝试使用 `st-erase` 对设备进行格式化（慎重），但是可能会出现 "Flash Memory is Write Protected" 提示，这是因为 STM32 芯片的写保护措施。如果我们使用 "ST-Link Tools" 直接使用 `cargo embed` 命令，只会在写入 Page 0 时报错，但是不会写出错误原因。

现在我们可以使用 Windows 下的 [ST-Link Utility](https://www.st.com/en/development-tools/stsw-link004.html) 软件对其进行解锁。

首先连接设备，在 ST-Link Utility 中找到 Target -> Connect

![ST-Link Utility Connect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yabvojsfl2r13uir9snd.png)

连接完成后选择 Target -> Option Bytes

![St-Link Utility Option Bytes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4kle7xwtlvsyhgu1me3y.png)

在这里我们关闭 Read Out Protection （如果没有关闭）

![ST-Link Utility Disable Read Out Protection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2c83voipgmkf5rcp5v9s.png)

我们反选所有的 Flash 扇区保护

![ST-Link Utility Unselect All Flash Sector Protections](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cmgwli8d132etwtbvypv.png)

最后点击右下角 Apply 应用设置，现在开发板的读写保护已经被关闭了

## 2. Embassy 案例
我们 clone [Embassy](https://github.com/embassy-rs/embassy) 项目并进入 STM32F1x 的示例下

### 2.1 代码理解
使用 Embassy 框架使得写一个任务变得十分简单。我们使用异步关键词 `async` 修饰这个函数，并且使用 `embassy_executor::main` 来告诉编译器这个是主任务。
我们通过 调用 `embassy_stm32::init` 函数初始化芯片和外围设备，该函数返回可用的外围设备对象（记为 `p`）

```rust
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let p = embassy_stm32::init(Default::default());
}
```

接下来我们可以通过创建`Output`对象获得 `PC13` 这个引脚的控制，最后在一个循环中将这个引脚置高，等待一段时间再置低。
这里简单说明：`Timer::after_millis` 函数返回一个 `Future` 对象，我们在这里使用 `.await` 会放弃CPU使用权并交还给执行器。在时钟过期后，程序将从这里被恢复执行。

```rust
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let p = embassy_stm32::init(Default::default());
    info!("Hello World!");

    let mut led = Output::new(p.PC13, Level::High, Speed::Low);

    loop {
        info!("high");
        led.set_high();
        Timer::after_millis(300).await;

        info!("low");
        led.set_low();
        Timer::after_millis(300).await;
    }
}
```

### 2.2 烧录运行

```rust
git clone https://github.com/embassy-rs/embassy.git
cd embassy/examples/stm32f1
```
现在使用 cargo run 进行编译和写入
``` rust
cargo run --bin blinky --release 
```

在写入完成后 Blue Pill 就能够闪灯了！

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sxbiyixa7cjdu56oa2vm.gif)