Skip to content

Commit

Permalink
doc: improve documentation of examples/tos_meets_rust
Browse files Browse the repository at this point in the history
  • Loading branch information
ikey4u committed Jul 26, 2021
1 parent 06291f5 commit e69a572
Show file tree
Hide file tree
Showing 13 changed files with 754 additions and 724 deletions.
1 change: 1 addition & 0 deletions examples/tos_meets_rust/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ __pycache__
xcuserdata
user_config.h
.ccls-cache
tmp
285 changes: 72 additions & 213 deletions examples/tos_meets_rust/README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,4 @@
# TencentOS Tiny meets Rust

## 编译运行

编译前完成如下几步

1. 配置信息

`TOS_CONFIG/_user_config.h` 复制一份为 `TOS_CONFIG/user_config.h`,
写入设备信息和 WiFI 信息.

2. 环境变量

新建系统环境变量 TOS_SRC_ROOT, 其值为 TencentOS Tiny 源码的绝对路径.

3. STLINK 连接板子和电脑

进入本项目根目录, 执行如下命令刷入系统

bash build.sh
# TencentOS Tiny meets Rust (甲醛检测器)

## 目录介绍

Expand All @@ -42,235 +23,113 @@
+-- README.md: 项目概览文档
+-- tosglue.c: Rust 和 TencentOS 的胶水文件

板子启动后会自动执行 BSP/Src/main.c 中的 main 函数,
在 main 函数中会调用用户定义的如下函数
板子启动后会自动执行 BSP/Src/main.c 中的 main 函数, 在 main 函数中会调用用户定义的如下函数

void application_entry_rust();

这个函数原来是 `application_entry`, 集成 Rust 后修改为 `application_entry_rust` 了.

除了入口函数, 需要有底层的中断服务实现, 位于 `BSP/Src/stm32g0xx_it.c` 中.

## Rust 集成

目前十分粗糙的设计架构如下所示

------------------------------------
Rust Application
------------------------------------
Rust Wrapper
------------------------------------
TencentOS API | Third C Libraries API
------------------------------------
TencentOS
------------------------------------

即 TencentOS 对硬件层进行抽象, 对上层提供系统 API 以及可能的第三方 C 库 API,
然后 Rust 对这些 API 通过胶水文件 (Wrapper) 进行封装, 提供给 Rust 应用程序使用.

这里的一个关键问题是如何将 Rust 应用程序编译后和系统源码链接起来生成一个固件.

其基本的解决思想是首先获取 Rust 应用程序编译后的对象文件(.obj), 其次获取系统源码编译后的对象文件,
最后将所有的对象文件链接起来生成系统固件.

具体实践过程中遇到的细节, 会在下面提到.

- 安装工具链

这里默认你已经安装了 rust 工具链, 如果你的 rustc 版本低于 1.47.0 则切换到 nightly 版本,
切换命令如下

rustup default nightly

如果版本大于等于 1.47.0, 则不用切换.

接着安装支持 tos_evb_g0 板子的工具链

rustup target add thumbv6m-none-eabi
sudo apt-get install -y gcc-arm-none-eabi

其他可选工具

sudo apt-get install gdb-arm-none-eabi
sudo apt-get install OpenOCD

- 设置 Rust 插桩文件

`目录介绍` 中的 libs 目录中的文件即为插桩文件, 其结构如下

+-- libs/
+-- rustapp/
+-- stub.c
+-- rustcore/
+-- stub.c

stub.c 中的内容并不重要, 插桩的意思就是占个坑, 它们的真实意图是用来生成两个库文件
librustcore.a 和 librustapp.a 文件, 因此需要在 CMakeLists.txt 中添加如下两行

add_library(rustcore STATIC ${ROOTDIR}/libs/rustcore/stub.c)
add_library(rustapp STATIC ${ROOTDIR}/libs/rustapp/stub.c)

librustapp.a 将会被真实的 rust 应用程序所替代, 那 librustcore.a 是干什么的?
所有的 rust 程序都依赖于 Rust 的核心库即 rust core, 因此这个是必须提供,
Rust Core 的介绍参见 [The Rust Core Library](https://doc.rust-lang.org/core/).

rust core 库和 rust 应用程序库的生成将会在接下来节介绍.

- rust core 库的生成

当添加完 rust 编译嵌入式的工具链时, rust core 库会自动被放到系统中固定的目录下,
其路径的获取方式如下

RUST_THUMBV6M_SYSROOT=$(rustc --print sysroot --target thumbv6m-none-eabi)
RUST_LIBCORE_SRC=$(ls -1 $RUST_THUMBV6M_SYSROOT/lib/rustlib/thumbv6m-none-eabi/lib/libcore-*.rlib)

rlib 文件实际上就是静态库文件, 可以用如下命令查看 rlib 内容

arm-none-eabi-ar t $RUST_LIBCORE_SRC

core-1ba29f225cca71e5.core.1ml6ett9-cgu.0.rcgu.o
lib.rmeta

因此我们直接将该文件复制为 librustcore.a 即可.

- rust app 库的生成

新建的 rust 应用程序目录为 app, Rust 提供了 cargo 工具, 可以快速创建项目,
进入项目根目录执行如下命令即可生成 app 目录(也可以使用 --lib 直接生成库文件目录结构)

cargo new app

- 进入 app/src 目录, 删除 main.rs, 然后新建 lib.rs
- 配置 app/Cargo.toml

配置其内容如下所示

[package]
name = "app"
version = "0.1.0"
authors = ["ikey4u <pwnkeeper@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cty = "0.2.0" # String utilities from cty library: https://crates.io/crates/cty
cstr_core = "0.1.2" # String utilities from cstr_core library: https://crates.io/crates/cstr_core
cortex-m = "0.5.8" # Arm Cortex-M utilities: https://crates.io/crates/cortex-m
[lib]
name = "tosrs"
test = false
bench = false
# Options for `cargo build`
[profile.dev]
panic = "abort" # Disable stack unwinding on panic
# Options for `cargo build --release`
[profile.release]
panic = "abort" # Disable stack unwinding on panic
codegen-units = 1 # Better optimizations
debug = true # Symbols are nice and they don't increase the size on Flash lto = true # Better optimizations
## 编译运行

- 配置腾讯云

到腾讯云物联网开发平台 https://cloud.tencent.com/product/iotexplorer 注册一个新产品.

新建产品后导入模板数据如下

{
"version": "1.0",
"profile": {
"ProductId": "BDDSF87WEA",
"CategoryId": "1"
},
"properties": [
{
"id": "ch20_ppm_value",
"name": "甲醛浓度值",
"desc": "",
"mode": "r",
"define": {
"type": "float",
"min": "0",
"max": "2",
"start": "0",
"step": "0.001",
"unit": "ppm(mg/m3)"
},
"required": false
}
],
"events": [],
"actions": []
}

- 配置 app/.cargo/config 文件
然后新建设备, 新建之后能够得到三个信息: 设备名称, 设备密钥, 产品 ID,
这三个信息需要写入到 TencentOS 固件中.

配置其内容如下所示
配置完成后, 将甲醛传感器与底板连接, 根据板子和底板的 5v 和 GND 接口对应关系, 将底板连接到板子上,
然后根据传感器的 Rx 和 Tx 口将传感器和板子底板连接起来.

[target.thumbv6m-none-eabi]
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = [
# LLD (shipped with the Rust toolchain) is used as the default linker
"-C", "link-arg=-Tlink.x",
]
[build]
target = "thumbv6m-none-eabi"
- 下载源码

注意 tos_evb_g0 对应的 target 是 thumbv6m-none-eabi.
下载 TencentOS 源码

项目中最终的目录如下所示
git clone https://github.com/Tencent/TencentOS-tiny.git

+-- app/
+-- src/
+ bridge.rs
+ lib.rs
+-- Cargo.toml
+-- .cargo/
+-- config

配置完成后, 便可在 app 目录执行如下命令编译
进入 `examples/tos_meets_rust` 目录, 将 `TOS_CONFIG/_user_config.h` 复制一份为 `TOS_CONFIG/user_config.h`,
写入设备信息和 WiFI 信息.

cargo build
新建系统环境变量 TOS_SRC_ROOT, 其值为 TencentOS Tiny 源码的绝对路径.

编译的中间产物位于 `app/target/thumbv6m-none-eabi/debug/deps` 中,
这里面是一系列的 .rlib 文件(静态库文件), 这些库文件包含了应用程序代码及其依赖的库代码.
- 基础依赖安装

我们将这些库包含的所有对象文件提取出来重新打包成一个新的库文件, 即为 librustapp.a 文件.
- ST-LINK 驱动安装, 参考 [stlink](./docs/stlink.md)

其中 bridge.rs 声明了胶水文件中的 API 接口, 比如
- CH34X 驱动安装, 参考 [CH34X](./docs/ch34x.md)

use cty::*;
- kermit 串口工具安装, 参考 [kermit](./docs/kermit.md)

/// These glue functions are from tosglue.c
extern {
pub fn rust_print(msg: *const u8);
}
- 工具链安装

lib.rs 则是应用程序代码, 示例如下
编译需要的 arm-none-eabi 工具连, eabi 的含义是 Embedded Application Binary Interface,
不同发行版安装方式不一样

#![no_std]
- ubuntu

extern crate cortex_m;
sudo apt-get install -y gcc-arm-none-eabi

mod bridge;
- archlinux:

use crate::bridge::*;
use cty::*;
sudo pacman -S arm-none-eabi-gcc arm-none-eabi-newlib

#[no_mangle]
pub extern "C" fn application_entry_rust() -> c_void {
unsafe {
rust_mqtt_daemon();
}
这里默认你已经安装了 rust 工具链, 如果你的 rustc 版本低于 1.47.0 则切换到 nightly 版本,
切换命令如下

loop {
unsafe {
rust_print(b"[+] Welcome to the RUST-WORLD in TencentOS :)".as_ptr());
}
}
}
rustup default nightly

- 胶水文件
如果版本大于等于 1.47.0, 则不用切换, 接着安装支持板子的 rust 工具链

Rust 调用系统 API 或者第三方 C 库的 API 通过胶水文件 tosglue.c 实现,
这里的做法是将 tosglue.c 编译为一个库文件, 但是不参与链接, 而是提取其对象文件,
合并到 librustapp.a 中, 在 CMakeLists.txt 中添加如下行
rustup target add thumbv6m-none-eabi

add_library(tosglue STATIC ${ROOTDIR}/tosglue.c)
- 刷入 WiFi 固件, 参见 [esp](./docs/flash-esp.md)

tosglue.c 中的一个 API 示例如下
- 刷入系统

void rust_print(const char *msg) {
printf("%s\r\n", msg);
}
连接 STLINK 后, 执行如下命令

- 一键编译生成固件
bash build.sh

在编译之前还需要注意修改 CMakeLists.txt, 保证固件链接了 librustcore.a 和 librustapp.a
刷入完成后, 登录 kermit, 然后按下板子上的 reset 按键, 程序就运行了, 串口会输出 WIFI CONNECTED 之类的,
腾讯云上显示设备上线.

target_link_libraries(${PROJECT_NAME} ${LIBS} c nosys rustcore rustapp)
## Rust 集成

这样设置之后, 我们再替换完 librustcore.a 和 librustapp.a 之后,
就可以强制重新生成新的固件.
Rust 集成原理参见 [rust](./docs/rust.md)

最终的编译脚本参见 build.sh.
## 其他

## 参考
1. 如果你在 mac 平台上开发, 你可以参考文档 [setup.mac.md](./docs/setup.mac.md) 来搭建开发环境.

- [Hosting Embedded Rust apps on Apache Mynewt with STM32 Blue Pill](https://medium.com/@ly.lee/hosting-embedded-rust-apps-on-apache-mynewt-with-stm32-blue-pill-c86b119fe5f)
- [STM32L0 Rust Part 1 - Getting Started](https://craigjb.com/2019/12/31/stm32l0-rust/)
- [FreeRTOS meets Rust](http://www.hashmismatch.net/freertos-meets-rust/)
2. 有一份简短的关于本 demo 的介绍 PPT, 位于[这里](./docs/presentation.pdf), 有需要可以打开查看.
Loading

0 comments on commit e69a572

Please sign in to comment.