Rust单片机编程:用一块STM32板发送http请求
目录
记录从0开始在STM32F407上编写Rust实现HTTP GET。 本文多处参考了The Embedded Rust Book,建议对Rust嵌入式开发尚不熟悉的同学反复观看这本书。 在一切开始之前,首先使用合适的Type-C线将服务器的任一USB口与板子的CMSIS-DAP接口连接。 基本的编译、烧写和调试(仿真等)环境可以参照这一章节配置,此处仅列举需要修改的步骤和有必要补充的信息: 注意:这一小节与The Embedded Rust Book出入非常大 我们希望OpenOCD等在不需要root权限的情况下就可以自由与开发板通讯,因此需要自定义 这里注意行末设备名为 有了 写好规则后使用下述命令应用(无需拔掉设备重新插上): The Embedded Rust Book中的指引为使用包管理器直接安装OpenOCD。 但对于某些情况,比如我们的Ubuntu 22.04.1,通过 这一版对STM32F4x系列的支持还比较糟糕,因此墙裂建议手动编译一下最新的OpenOCD以防止犯病: 为检验工具和驱动是否已被正确配置,我们需要验证OpenOCD能否正常工作(与板上自带的ST-Link调试硬件通信)。在项目根目录下创建 此时,运行 并且程序阻塞住了控制台。 至此环境配置完毕。 在开始仿真调试前,建议先用 仿真大致操作与The Embedded Rust Book无异,但由于我们的板子是STM32F4x,故QEMU的参数要做一定调整。 比如, 然后就可以正常仿真了(会隐式地以上述参数调用QEMU): 如果需要用GDB调试,只需要将启动QEMU的参数如上述 这部分最重要的就是搞清楚板子的内存布局,要确定Flash和SRAM的起始点和长度以确保编译和运行时的行为正确。 主要参照 ST 的 RM0090 文档(Rev 19)。 在项目根目录的memory.x文件中,写入: 详细依据见下文。 架构图在第73页: 我们可以看到,从interface到memory是一条总线连接的,即主内存在使用上是不分区的,即Flash的长度应为所有Sector的大小之和。为了提升性能,对指令码和数据码各自有对应的cache,但不影响主内存的分布。 因此参照表3: 但上述其实不过是对最高配置的介绍,并非所有的STM32F40x都有高达 由于我们的板子的详细型号是STM32F407VET6,因此Flash只有 参照第68页的描述: 由于我们的板子不存在SRAM3,所以SRAM的起始地址应为 但在编译时,还应结合板子的整体架构考虑: 在文字描述中高达 因此实际在编译时可以声明的内存大小只有 待续。 STM32CubeIDE 也许是不错的一体化集成开发环境。 可能接错到USB-Slave接口上了。可以用 如果内存布局设置错误或有严重的编程失误导致内存错误,可能会在OpenOCD输出中看到类似如下报错: 同时GDB也会反馈类似内容: 可能板子被锁住: 出现这种情况时,请首先确认内存布局正确。 注意,更改 另外,建议使用最新版本的OpenOCD,可能在重新开启OpenOCD进程或烧写程序(GDB的 如果仍被锁住,则可按照如下步骤处理: 如果顺利的话,会出现如下提示: 然后重复第4步。 无论当前项目是否有子crates、目录如何组织, 确实有一种特殊情况可以不提供 这里只看到 这里文件名不是必需为 头几个sector的大小比较小是因为许多板子并未达到 https://github.com/rust-lang/cargo/issues/9537 ↩ https://docs.rust-embedded.org/book/start/registers.html#memory-mapped-registers ↩
由于例子使用的开发板为STM32F3DISCOVERY,与我们的有一定区别,因此各种操作与书中提及多有出入,以本文为准。实验材料
环境配置
自定义
udev规则udev的规则。运行lsusb命令列出USB设备信息,我们可能获得类似如下的内容:Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 002: ID 174c:3074 ASMedia Technology Inc. ASM1074 SuperSpeed hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 007: ID c251:f001 Keil Software, Inc. CMSIS-DAP
Bus 001 Device 002: ID 174c:2074 ASMedia Technology Inc. ASM1074 High-Speed hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hubCMSIS-DAP的设备即为我们的板子1。我们以这行为例,解读一下lsusb返回内容的含义:001号总线007号设备idVendor(设备产商),值为c251;第二部分为idProduct(设备类型编号),值为f001idVendor和idProduct,接下来我们需要在/etc/udev/rules.d/70-st-link.rules文件里追加一行(若无文件则新建)2:# OWNER的值请填写开发者自己使用的用户名
ATTRS{idVendor}=="c251", ATTRS{idProduct}=="f001", OWNER="ysun"# 这里的action参数至关重要
# 如果不指定它的值为change,内核会通知udev将所有设备都认作新设备
# 这可能有非常恶劣的影响
sudo udevadm control --reload-rules && sudo udevadm trigger --action=change编译安装OpenOCD
apt安装的OpenOCD为v0.11.0版本。# 安装(编译时)必需依赖
sudo apt install libtool libhidapi-dev libusb-1.0-0-dev
git clone https://github.com/openocd-org/openocd.git && cd openocd
./configure --enable-cmsis-dap
make
sudo make install环境验证
openocd.cfg文件,并写入:# 设备接口配置文件
source [find interface/cmsis-dap.cfg]
# 理论上udev规则配置好可以自动获取到,若不能则可以手动指定:
# cmsis_dap_vid_pid 0xc251 0xf001
# 这是一个历史遗留问题,其实是CMSIS-DAP v2 bulk interface自动检测的bug
# 对于一些v1的设备会误被判做是v2,导致通信失败,详见这个commit:
# https://github.com/openocd-org/openocd/commit/3da0c2504c14ea0736b973fa0b5085c42a9c6ce8
# 该bug在v0.12.0被修复,对之前的版本可以加上下面这行
# cmsis_dap_backend hid
# 目标芯片配置文件
source [find target/stm32f4x.cfg]openocd,应该会有类似输出:Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : CMSIS-DAP: SWD Supported
Info : CMSIS-DAP: FW Version = ħŮ�Ƽ�
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 0 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : clock speed 2000 kHz
Info : SWD DPIDR 0x2ba01477
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f4x.cpu on 3333
Info : Listening on port 3333 for gdb connections仿真
cargo-generate从模板仓库开始初始化一个实验项目。.cargo/config.toml中需要增加这些内容:[target.thumbv7em-none-eabihf]
runner = "qemu-system-arm -cpu cortex-m4 -machine netduinoplus2 -nographic -semihosting-config enable=on,target=native -kernel"
[build]
target = "thumbv7em-none-eabihf"cargo run --example hello --release.cargo/config.toml文件中替换即可。上硬件
内存布局
太长不看
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
/* TODO Adjust these memory regions to match your device memory layout */
/* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */
FLASH : ORIGIN = 0x08000000, LENGTH = 512K
RAM : ORIGIN = 0x20000000, LENGTH = 128K
}Flash布局


Flash应为从0x08000000开始,到0x080FFFFF结束。用末尾减去开头,刚好是1024K。1M的实际Flash大小,具体仍要参照型号。512K。SRAM布局

0x20000000。Backup SRAM只在特殊的电压条件下才会被调用,因此SRAM的长度应标注为。192K
192K的“系统”SRAM当中其实还包含了只能经由D-bus主线直接与MCU通信的、大小为64K的CCM内存。192 - 64 = 128K。ESP8266篇
其他选择
常见问题
0. 为什么已经插在USB上了,但
lsusb看不到板子?sudo dmesg查看详细日志: 
1. 使用GDB调试时出现内存错误
Error: Failed to read memory at 0x2002ffd00x080013aa in cortex_m_rt::HardFault_ (ef=0x0) at src/lib.rs:563
563 atomic::compiler_fence(Ordering::SeqCst);
memory.x文件后需要重新cargo build,这点在The Embedded Rust Book中也有提及。load指令)时自动解锁。openocd进程与板子连接telnet与openocd通讯:telnet localhost 4444stm32f4x unlock 0stm32f2x unlocked.
INFO: a reset or power cycle is required for the new settings to take effect.shutdown,然后将板子断电再通电。stm32f4x mass_erase 02.
cargo build报错:cannot find linker script memory.xmemory.x必须位于项目的根目录下(和最外层的Cargo.toml同级)4。memory.x文件:项目代码中直接引入了BSC (Board Support Crate),比如stm32f3-discovery。除此之外,使用属于其他抽象级别的crates都必须为cortex-m-rt的编译提供memory.x5。
CMSIS-DAP,没有看到STM32相关的信息。sudo dmesg可以在日志中看到细节: ↩70-st-link.rules,但建议前缀的数字要大于50,否则有可能覆写操作系统的一些默认规则。 有关udev规则语法,可参照udev(7)手册。 ↩1M的Flash大小,因此只有头几个sector,后面较大的则被舍弃了。
这样的设计使得不同型号的板之间的sector布局尽量保持一致,程序移植的兼容性比较好。 ↩