当前位置: 首页 > news >正文

rust嵌入式开发零基础入门教程(三)


欢迎来到 Rust 嵌入式开发零基础入门教程的第三部分!在前面的教程中,我们已经搭建了开发环境,理解了 Rust 的核心概念,并准备好了真实的开发板。现在,是时候让你的 Rust 代码在硬件上跑起来,点亮一个 LED 了!

本节教程,我们将:

  1. 了解嵌入式项目的 Cargo.toml 配置。

  2. 编写一个简单的 LED 闪烁程序。

  3. 烧录并运行程序到你的 STM32 开发板。

  4. 进行基本的调试。


6. 理解嵌入式项目的 Cargo.toml

在第二部分中,我们通过 cortex-m-quickstart 模板创建了一个 Rust 嵌入式项目。现在让我们深入了解一下这个项目中的 Cargo.toml 文件。它是 Rust 项目的核心配置文件,定义了项目的元数据、依赖项、构建配置等。

打开你项目根目录下的 Cargo.toml 文件(例如 hello-cortex-m/Cargo.toml)。它看起来可能像这样:

Ini, TOML

# hello-cortex-m/Cargo.toml[package]
name = "hello-cortex-m"
version = "0.1.0"
authors = ["Your Name <your@email.com>"]
edition = "2021" # Rust 版本,推荐 2021[dependencies]
# 嵌入式 Rust 的核心运行时
cortex-m-rt = "0.7.0"
# 用于在调试时打印信息(通过半主机模式)
cortex-m-semihosting = "0.5.0"
# 用于处理 panic,这里选择在 panic 时停止 CPU
panic-halt = "0.2.0"# 针对你的具体芯片的 HAL (Hardware Abstraction Layer) 库
# 以 STM32F401RE 为例,你需要选择对应的 HAL 库
# 例如:stm32f4xx-hal = { version = "0.15.0", features = ["stm32f401", "rt"] }
# 请根据你的开发板芯片型号替换这里的 HAL 库
# 例如,如果是 STM32F411RE,可能需要 "stm32f411" feature
# 如果是 STM32F103C8T6 (俗称"蓝 pill"),可能需要 "stm32f103"
# 这里我们先注释掉,在后面点灯时再启用# [features]
# default = ["stm32f4xx-hal/stm32f401"] # 如果你希望默认使用某个芯片型号[profile.dev]
# 优化级别,0 表示不优化,方便调试
opt-level = 0[profile.release]
# 发布版本优化级别,s 表示代码大小优化
opt-level = "s"# 其他一些配置,例如二进制大小报告等
# [build-dependencies]
# cargo-binutils = "0.3.0" # 如果你想使用 binutils (objcopy, objdump)

关键部分解释:

  • [package]: 定义了项目的基本信息,如名称、版本、作者和 Rust 版本 (edition)。

  • [dependencies]: 核心部分,列出了项目所依赖的外部库 (crates)。

    • cortex-m-rt: 提供了 Cortex-M 微控制器的运行时启动代码,处理 CPU 初始化、中断向量表等。

    • cortex-m-semihosting: 实现了通过调试器将程序输出发送到主机的功能(我们在第一部分用它打印 "Hello World!")。

    • panic-halt: 定义了当 Rust 程序遇到不可恢复的错误(panic)时如何处理,这里是让 CPU 停止。在嵌入式环境中,你通常不会有完整的操作系统来打印错误信息,所以停止 CPU 或复位是常见的处理方式。

    • HAL (Hardware Abstraction Layer) 库: 这是最重要的部分。针对你的具体微控制器型号,你需要引入对应的 HAL 库。这些库封装了底层寄存器操作,提供了更高层次、更安全的 API 来控制外设(GPIO、定时器、串口等)。

      • 示例:stm32f4xx-hal: 这是一个常见的 STM32F4 系列的 HAL 库。features = ["stm32f401", "rt"] 中的 stm32f401 是指明你使用的具体芯片型号(例如 STM32F401RET6),rt 表示启用运行时支持。

  • [profile.dev][profile.release]: 定义了不同构建模式下的编译选项。

    • dev (开发模式): 优化级别通常较低 (opt-level = 0),以便快速编译和调试。

    • release (发布模式): 优化级别较高 (opt-level = "s""z"),以减小程序体积和提高运行速度。"s" 表示大小优化,"z" 表示更激进的大小优化。


7. 编写你的第一个 "Hello, LED!" 闪烁程序

现在,我们来编写点亮 LED 的程序。这个程序将循环点亮和熄灭开发板上的一个板载 LED。

你需要做什么:

  1. 确定你的开发板上的 LED 连接到哪个 GPIO 引脚。

    • STM32 Nucleo-64 系列 (如 F401RE/F411RE): 板载绿色 LED 通常连接到 PA5 引脚。

    • 其他板子: 请查阅你的开发板原理图或用户手册。

  2. 修改 Cargo.toml,引入正确的 HAL 库。

7.1 修改 Cargo.toml

STM32F401RE Nucleo-64 开发板为例,需要在 [dependencies] 下添加 stm32f4xx-hal 库。

Ini, TOML

# hello-cortex-m/Cargo.toml[package]
name = "hello-cortex-m"
version = "0.1.0"
authors = ["Your Name <your@email.com>"]
edition = "2021"[dependencies]
cortex-m-rt = "0.7.0"
cortex-m-semihosting = "0.5.0" # 暂时保留,调试时有用
panic-halt = "0.2.0"# --- 新增/修改部分开始 ---
# 添加 STM32F4xx HAL 库,并启用对应的芯片型号特性
# 根据你的板子型号选择正确的 feature,例如 "stm32f401"
stm32f4xx-hal = { version = "0.15.0", features = ["stm32f401", "rt"] } # 如果是F411,则改成"stm32f411"
# --- 新增/修改部分结束 ---[profile.dev]
opt-level = 0[profile.release]
opt-level = "s"

重要: 请务必根据你自己的 STM32 芯片型号来选择 features 中的正确值。如果你不确定,可以查看 stm32f4xx-hal crate 的文档或 GitHub 仓库,它会列出所有支持的芯片型号。

7.2 编写 src/main.rs

现在,打开 src/main.rs 文件,并将其内容替换为以下代码:

Rust

#![no_std] // 不使用 Rust 标准库
#![no_main] // 不使用 Rust 的默认 main 函数// 导入必要的库和宏
use panic_halt as _; // panic 时停止 CPU
use cortex_m_rt::entry; // Cortex-M 运行时入口
use cortex_m::delay::Delay; // 延时功能// 导入你选择的 HAL 库
// 例如:stm32f4xx_hal 库
use stm32f4xx_hal::{pac, prelude::*}; // pac: 外设访问层, prelude: 常用 trait#[entry]
fn main() -> ! {// 获取对外设的访问权限let dp = pac::Peripherals::take().unwrap();let cp = cortex_m::Peripherals::take().unwrap(); // Cortex-M 内核外设// 配置 RCC (复位和时钟控制)let rcc = dp.RCC.constrain();// 设置时钟频率,例如 HSE (外部高速晶振) 为 8MHz,系统时钟为 84MHz (F401RE 的默认最高频率)// 根据你的板子外部晶振和需求调整时钟配置let clocks = rcc.cfgr.use_hse(8.MHz()).sysclk(84.MHz()).freeze();// 初始化延时结构体let mut delay = Delay::new(cp.SYST, clocks);// 获取 GPIOA 外设(通常 LED 连接到 GPIOA)let gpioa = dp.GPIOA.split();// 配置 PA5 引脚为推挽输出模式// `into_push_pull_output` 是 HAL 库提供的方法let mut led = gpioa.pa5.into_push_pull_output();// 无限循环,闪烁 LEDloop {led.set_high(); // 点亮 LED (输出高电平)delay.delay_ms(500_u32); // 延时 500 毫秒led.set_low();  // 熄灭 LED (输出低电平)delay.delay_ms(500_u32); // 延时 500 毫秒}
}

代码解释:

  1. #![no_std]#![no_main]: 再次强调,这是裸机嵌入式程序的标志。

  2. use panic_halt as _;use cortex_m_rt::entry;: 同第一部分。

  3. use cortex_m::delay::Delay;: 导入 Cortex-M 提供的基本延时功能。

  4. use stm32f4xx_hal::{pac, prelude::*};:

    • pac: 外设访问层 (Peripheral Access Crate)。它提供了对芯片所有寄存器的底层、不安全的直接访问。HAL 库通常会建立在 PAC 之上。

    • prelude::*: 导入 HAL 库中常用的 trait,如 constrain()into_push_pull_output() 等,让代码更简洁。

  5. let dp = pac::Peripherals::take().unwrap();: 获取对芯片所有外设 (Peripherals) 的唯一访问句柄。unwrap() 是因为 take() 返回一个 Option 类型,它只能被获取一次。

  6. let cp = cortex_m::Peripherals::take().unwrap();: 获取对 Cortex-M 内核外设(如系统定时器 SYST)的唯一访问句柄。

  7. 时钟配置 (rcc, clocks): 微控制器需要精确的时钟来运行。这里我们配置了 RCC(复位和时钟控制)外设,设置系统时钟频率。这是嵌入式开发中非常重要的一步,因为所有外设的时序都依赖于正确的时钟配置。

  8. let mut delay = Delay::new(cp.SYST, clocks);: 创建一个延时实例,它使用内核的系统定时器 (SYST) 和我们配置的时钟来提供精确的延时。

  9. let gpioa = dp.GPIOA.split();: 获取对 GPIOA 外设的访问句柄,并使用 split() 方法将其分解为独立的引脚。这是 HAL 库的一个常见模式,它允许你单独配置每个引脚。

  10. let mut led = gpioa.pa5.into_push_pull_output();:

    • gpioa.pa5: 获取 GPIOA 端口的第 5 号引脚 (PA5)。

    • into_push_pull_output(): 将该引脚配置为推挽输出模式。这是最常见的输出模式,用于控制 LED、继电器等。mut 关键字表示 led 是一个可变变量,因为我们需要改变它的状态(高电平/低电平)。

  11. loop { ... }: 一个无限循环,这是嵌入式程序的主循环,它会不断执行里面的代码。

  12. led.set_high();led.set_low();: 这是 HAL 库提供的方法,用于设置 GPIO 引脚的电平。

    • set_high(): 将引脚设置为高电平 (通常是 3.3V 或 5V),点亮 LED。

    • set_low(): 将引脚设置为低电平 (0V),熄灭 LED。

  13. delay.delay_ms(500_u32);: 使用之前创建的延时实例,延时 500 毫秒。_u32 是一种类型后缀,明确表示 500 是一个 u32 类型。

7.3 构建项目

保存 Cargo.tomlsrc/main.rs 文件后,在项目根目录(hello-cortex-m)下,运行构建命令:

Bash

cargo build --release

如果一切顺利,编译将成功。生成的 .elf 文件(你的固件)将位于 target/thumbv7em-none-eabihf/release/hello-cortex-m


8. 烧录并运行程序到开发板

现在,我们把程序烧录到实际的开发板上。

  1. 确保你的开发板已通过 USB 线连接到电脑。

  2. 确保你已经正确安装了 ST-Link 驱动、OpenOCD 和 probe-run

  3. 验证 .cargo/config.toml 中的 runner 配置与你的芯片型号匹配。

在项目根目录下,运行 probe-run 命令来烧录和运行:

Bash

cargo run --release

cargo run --release 的背后: cargo run 会首先编译你的项目(如果需要),然后查找 .cargo/config.toml 中为你的目标配置的 runner。在这里,runner = "probe-run --chip STM32F401RETx" 会被执行。 probe-run 会:

  • 连接到你的调试探针 (例如 ST-Link)。

  • 使用探针找到并连接到你的微控制器。

  • 将编译好的 .elf 固件烧录到微控制器的 Flash 内存中。

  • 启动程序执行。

  • 如果你的代码使用了 hprintln!,它会捕获半主机输出并显示在你的终端。


9. 进行基本的调试 (可选但推荐)

在嵌入式开发中,调试是不可或缺的。VS Code 配合 cortex-debug 插件可以提供强大的调试能力。

  1. 安装 VS Code 插件:

    • 打开 VS Code,前往扩展市场(Ctrl+Shift+X)。

    • 搜索并安装 "Cortex-Debug" 插件。

  2. 配置调试器 (launch.json): 如果你使用了 cortex-m-quickstart 模板,你的项目根目录下的 .vscode/launch.json 文件应该已经包含了一个基本的调试配置。它可能看起来像这样:

    JSON

    // .vscode/launch.json
    {"version": "0.2.0","configurations": [{"name": "Cortex-M Debug","cwd": "${workspaceRoot}","executable": "./target/thumbv7em-none-eabihf/debug/hello-cortex-m", // 注意这里是 debug 版本"request": "launch","type": "cortex-debug","servertype": "openocd",// 请根据你的调试探针选择正确的 OpenOCD 接口配置// 例如:stlink-v2.cfg 或 stlink-v3.cfg"interface": "swd","device": "STM32F401RE", // 你的芯片型号,用于 GDB"configFiles": ["interface/stlink-v2.cfg", // 你的调试器配置文件"target/stm32f4x.cfg"     // 你的芯片配置文件],"svdFile": "path/to/your/STM32F401.svd", // 可选:用于寄存器查看"runToMain": true,"preLaunchTask": "Cargo Build (debug)" // 确保先构建 debug 版本}]
    }
    
    • 重要调整:

      • executable: 确保路径正确,指向你的 debug 构建版本(因为调试通常在 debug 模式下进行)。

      • interface: 根据你的调试器类型选择,例如 swd (SWD 接口)。

      • configFiles: 这是最容易出错的地方。 你需要根据你的 ST-Link 版本芯片系列 来选择正确的 OpenOCD 配置文件。

        • ST-Link 接口文件:通常是 interface/stlink.cfg 或更具体的 interface/stlink-v2.cfginterface/stlink-v3.cfg

        • 目标芯片文件:例如 target/stm32f4x.cfg (针对 STM32F4 系列)。你可以在 OpenOCD 的安装目录下找到这些文件(通常在 scripts/interfacescripts/target 文件夹中)。

      • device: 你的具体芯片型号,用于 GDB。

      • svdFile: 强烈推荐! SVD 文件描述了芯片内部的寄存器布局。下载你芯片对应的 SVD 文件(例如 STM32F401.svd),并在 launch.json 中配置路径。这将允许你在调试时直接查看和修改寄存器值。

  3. 启动调试:

    • 在 VS Code 中打开 src/main.rs

    • 点击左侧的“运行与调试”图标(或按 Ctrl+Shift+D)。

    • 在顶部的下拉菜单中选择 "Cortex-M Debug" 配置。

    • 点击绿色的“开始调试”按钮(或按 F5)。

如果配置正确,VS Code 将连接到你的开发板,并将程序加载到微控制器中。你可以在代码中设置断点,单步执行,查看变量,甚至观察寄存器状态。


恭喜你!

你已经成功迈出了 Rust 嵌入式开发最重要的几步:理解项目结构、编写代码、烧录到真实硬件,并进行了初步的调试!

点亮 LED 是嵌入式开发的 "Hello, World!"。从这里开始,你可以尝试:

  • 改变 LED 闪烁的速度。

  • 尝试控制其他 GPIO 引脚(如果你的板子上有其他可控的 LED 或跳线)。

  • 阅读 stm32f4xx-hal 或你所用 HAL 库的文档,了解如何控制更多外设(如按钮、串口等)。

在接下来的教程中,我们可能会探讨更复杂的传感器读取、通信协议(如 I2C/SPI)、或者中断处理等主题。

http://www.lryc.cn/news/597352.html

相关文章:

  • C++day1
  • Android网络请求,Retrofit,OKHttp学习
  • Python爬虫--Xpath的应用
  • rust嵌入式开发零基础入门教程(五)
  • 使用阿里云 ESA 边缘函数转发代理 docker registry
  • OpenLayers 快速入门(十)常用 API 补充
  • 量化金融简介(附电子书资料)
  • 分布式系统中的缓存设计与应用
  • 三步实现Android系统级集成:预装Google TTS + 默认引擎设置 + 语音包预缓存方案
  • AI助力,轻松实现人声分离伴奏提取
  • 【金融机器学习】第五章:最优投资组合——Bryan Kelly, 修大成(中文翻译)
  • 手机开启16k Page Size
  • 大模型【进阶】(四)QWen模型架构的解读
  • SpringBoot07-数据层的解决方案:SQL
  • FireFox一些设置
  • latex中既控制列内容位置又控制列宽,使用>{\centering\arraybackslash}p{0.85cm}
  • OpenLayers 快速入门(二)Layer 对象
  • 深入掌握 Python 面向对象的灵魂——魔法函数(Magic / Dunder Methods)全景指南
  • CAN的终端电阻
  • 设计模式代码总结
  • 用 PyTorch 实现全连接网络识别 MNIST 手写数字
  • Android插件化实现方案深度分析
  • window下c++共享内存,进程互斥锁。
  • macOS配置maven及报错处理:zsh: permission denied: mvn
  • 大厂总结常用分析问题方法之CMMI-IDEAL模型
  • VRRP技术-设备备份技术
  • Modbus TCP转Devicenet:水泥厂PLC与多类仪表的自动化通信实践
  • 学习 Flutter(五):玩安卓项目实战 - 下
  • 2025年7月一区SCI-投影迭代优化算法Projection Iterative Methods-附Matlab免费代码
  • Flutter学习笔记(四)---基础Widget