嵌入式 Rust 之书---第二章 入门(QEMU)

本文详细介绍使用Rust语言为Cortex-M3微控制器编写、编译、调试嵌入式程序的过程,涵盖QEMU仿真、交叉编译及GDB远程调试技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

2.1 QEMU

2.1.1 创建非标准的Rust程序

2.1.2 代码简介 

2.1.3 交叉编译 

2.1.4 二进制文件分析工具

2.1.5 运行 

2.1.6 调试


在本节中,我们将引导您完成编写,构建,刷新和调试嵌入式程序的过程。您将能够在没有任何特殊硬件的情况下尝试大多数示例,因为我们将向您展示使用QEMU(一种流行的开源硬件仿真器)的基础知识。唯一需要硬件的部分自然就是硬件部分,我们使用OpenOCD对自己的开发板进行编程。

2.1 QEMU

我们将开始为LM3S6965编写一个程序,这是一个Cortex-M3微控制器。我们选择这个作为我们的初始目标,因为它可以使用QEMU进行仿真,因此您不需要在本节中使用硬件,我们可以专注于工具和开发过程。

重要 我们将在本教程中使用名称“app”作为项目名称。每当您看到“app”一词时,您应该将其替换为您为项目选择的名称。或者,您也可以将项目命名为“app”并避免替换。

2.1.1 创建非标准的Rust程序

我们将从cortex-m-quickstart项目模板中生成一个新项目。 

使用cargo-generate 

首先安装cargo-generate

cargo install cargo-generate

然后生成新项目

cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart

 Project Name: app
 Creating project called `app`...
 Done! New project created /tmp/app

cd app

使用git 

克隆repository 

git clone https://github.com/rust-embedded/cortex-m-quickstart app
cd app

然后在Cargo.toml文件中填写占位符

[package]
authors = ["{{authors}}"] # "{{authors}}" -> "John Smith"
edition = "2018"
name = "{{project-name}}" # "{{project-name}}" -> "awesome-app"
version = "0.1.0"

# ..

[[bin]]
name = "{{project-name}}" # "{{project-name}}" -> "awesome-app"
test = false
bench = false

 以上两者都不用

获取cortex-m-quickstart模板的最新快照并将其解压缩。

curl -LO https://github.com/rust-embedded/cortex-m-quickstart/archive/master.zip
unzip master.zip
mv cortex-m-quickstart-master app
cd app 

或者您可以浏览到cortex-m-quickstart,单击绿色的“克隆或下载”按钮,然后单击“下载ZIP”。然后在“使用git”版本的第二部分中填写Cargo.toml文件中的占位符。 

2.1.2 代码简介 

为方便起见,这里是src / main.rs中源代码最重要的部分:

#![no_std]
#![no_main]

extern crate panic_halt;

use cortex_m_rt::entry;

#[entry]
fn main() -> ! {
    loop {
        // your code goes here
    }
}

这个程序与标准的Rust程序有点不同,所以让我们仔细看看。

  • #![no_std]表示该程序不会链接到标准包,std。相反,它将链接到其子集:core crate。
  • #![no_main]表示该程序不会使用大多数Rust程序使用的标准main接口。 使用no_main的主要(没有双关语)原因是在no_std上下文中使用main接口需要nightly。
  • extern crate panic_halt; 这个crate 提供了一个panic_handler,用于定义程序的恐慌行为(the panicking behavior)。我们将在本书的Panicking章节中更详细地介绍这一点。
  • #[entry]是由cortex-m-rt crate提供的属性,用于标记程序的入口点。由于我们没有使用标准main接口,我们需要另一种方式来指示程序的入口点,它就是#[entry]。 
  • fn main() -> !。我们的程序将是目标硬件上运行的唯一进程,因此我们不希望它结束!我们使用一个 divergent function(函数签名中的 - >!位)来确保在编译时就是这种情况。

2.1.3 交叉编译 

下一步是交叉编译Cortex-M3架构的程序。这就像运行cargo build --target $TRIPLE一样简单 - 如果您知道编译目标($triple)应该是什么。幸运的是,模板中的.cargo / config有答案:

[build]
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi"    # Cortex-M0 and Cortex-M0+
target = "thumbv7m-none-eabi"    # Cortex-M3
# target = "thumbv7em-none-eabi"   # Cortex-M4 and Cortex-M7 (no FPU)
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)

要为Cortex-M3架构进行交叉编译,我们必须使用thumbv7m-none-eabi。此编译目标已设置为默认值,因此下面的两个命令执行相同的操作:

cargo build --target thumbv7m-none-eabi
cargo build

2.1.4 二进制文件分析工具

现在我们在target / thumbv7m-none-eabi / debug / app中有一个非本机ELF二进制文件。我们可以使用cargo-binutils进行检查。使用cargo-readobj,我们可以打印ELF头以确认这是一个ARM二进制文件。

注意:

  • --bin app,是用于在target/$triple/debug/app检查二进制文件的sugar
  • --bin app,如有必要,还将重新编译二进制文件 

cargo-size可以打印二进制文件的链接器部分的大小。

注:该输出假设rust-embedded/cortex-m-rt#111已合并。 

cargo size --bin app --release -- -A

我们使用--release检查优化版本

 

关于ELF链接器部分的扩展:

  • .text    包含程序指令
  • .rodata    包含字符串等常量值
  • .data    包含初始值不为零的静态分配变量
  • .bss    包含初始值为零的静态分配变量 
  • .vector_table    是我们用来存储向量(中断)表的非标准部分
  • .ARM.attributes和.debug_*    包含元数据(metadata),并且在flashing 二进制文件时不会加载到目标上。

重要:ELF文件包含调试信息之类的元数据,因此它们在磁盘上的大小无法准确反映程序拷贝到设备时占用的空间。始终使用cargo-size来检查二进制文件的实际大小。 

cargo-objdump可用于反汇编二进制文件。

cargo objdump --bin app --release -- -disassemble -no-show-raw-insn -print-imm-hex

注意此输出可能因系统而异。新版本的rustc,LLVM和库可以生成不同的汇编语言。 我们截取了一些指令以使代码片段保持较小。

2.1.5 运行 

接下来,让我们看看如何在QEMU上运行嵌入式程序!这一次我们将使用Hello示例,它实际上可以做一些事情。为了方便起见,这里是examples/hello.rs的源代码:

//! Prints "Hello, world!" on the host console using semihosting

#![no_main]
#![no_std]

extern crate panic_halt;

use cortex_m_rt::entry;
use cortex_m_semihosting::{debug, hprintln};

#[entry]
fn main() -> ! {
    hprintln!("Hello, world!").unwrap();

    // exit QEMU
    // NOTE do not run this on hardware; it can corrupt OpenOCD state
    debug::exit(debug::EXIT_SUCCESS);

    loop {}
}

 该程序使用称为semihosting的东西将文本打印到主机控制台。当使用真正的硬件时,这需要一个调试会话,但当使用QEMU时,这才有效。让我们从编译示例开始:

cargo build --example hello

输出二进制文件将位于target/thumbv7m-none-eabi/debug/examples/hello。要在QEMU上运行此二进制文件,请运行以下命令:

qemu-system-arm \
  -cpu cortex-m3 \
  -machine lm3s6965evb \
  -nographic \
  -semihosting-config enable=on,target=native \
  -kernel target/thumbv7m-none-eabi/debug/examples/hello

 

 打印文本后,该命令应成功退出(退出代码= 0)。在*nix上,您可以使用以下命令检查:

echo $?

让我们分解一下QEMU命令:

  • qemu-system-arm    这是QEMU模拟器。这些QEMU二进制文件有一些变体; 这个名称是ARM机器的完整系统仿真。
  • -cpu cortex-m3    这告诉QEMU模拟Cortex-M3 CPU。指定CPU模型可以捕获一些错误的编译错误:例如,运行为Cortex-M4F编译的程序(具有硬件FPU)将在执行期间发生QEMU错误。
  • -machine lm3s6965evb    这告诉QEMU模拟LM3S6965EVB,这是一个包含LM3S6965微控制器的评估板。
  • -nographic    这告诉QEMU不要启动它的GUI。
  • -semihosting-config (..)    这告诉QEMU启用半主机机制(semihosting)。半主机允许模拟设备使用主机stdout,stderr和stdin以及在主机上创建文件。 
  • -kernel $file    这告诉QEMU在模拟机器上加载和运行哪个二进制文件。

输入这么长的qemu命令太费事了!我们可以设置一个自定义的运行程序来简化这个过程。.cargo/config有一个引用qemu的注释掉的运行程序;让我们取消注释:

[target.thumbv7m-none-eabi]
# 取消注释,'cargo run'使程序在QEMU上执行
runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"

此运行程序仅适用于thumbv7m-none-eabi目标,这是我们的默认编译目标。 cargo run将编译程序并在qemu上运行:

cargo run --example hello --release

2.1.6 调试

调试对嵌入式开发至关重要。让我们看看是怎么做到的。

调试嵌入式设备涉及远程调试,因为我们要调试的程序不会在运行调试程序(gdb或lldb)的计算机上运行。

远程调试涉及客户机和服务器。在QEMU设置中,客户机将是一个gdb(或lldb)进程,服务器将是运行嵌入式程序的QEMU进程。在本节中,我们将使用已经编译的hello示例。第一个调试步骤是在调试模式下启动QEMU:

qemu-system-arm \
  -cpu cortex-m3 \
  -machine lm3s6965evb \
  -nographic \
  -semihosting-config enable=on,target=native \
  -gdb tcp::3333 \
  -S \
  -kernel target/thumbv7m-none-eabi/debug/examples/hello 

此命令不会将任何内容打印到控制台,并且会阻止终端。这次我们又传递了两个标志:

  • -gdb tcp::3333    这告诉QEMU等待TCP端口3333上的gdb连接。
  • -S    这告诉QEMU在启动时冻结机器。如果没有这个,在我们有机会启动调试器之前,程序就已经到了main的末尾! 

接下来,我们在另一个终端启动GDB并告诉它加载示例的调试符号: 

gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello 

注意:您可能需要另一个版本的gdb而不是gdb multiarch,这取决于您在安装章节中安装了哪个版本。这也可以是 arm-none-eabi-gdb或只是gdb。然后在GDB shell中我们连接到QEMU,它正在等待TCP端口3333上的连接。

target remote :3333 

 

 您将看到进程停止,程序计数器指向一个名为reset的函数。这是重置处理程序:启动时Cortex-M核心执行的操作。这个重置处理程序最终将调用我们的主函数。让我们使用断点和continue命令一路跳过:

 

我们现在已经接近打印“Hello, world!”的代码了。 让我们使用next命令继续前进:

到这里,你应该看到“Hello, world!”打印在运行qemu-system-arm的终端上。再次调用Next将终止QEMU进程。

现在可以退出gdb会话。

 

<think>我们正在讨论物联网工程中的嵌入式计算。根据用户需求,需要涵盖核心概念、应用场景和技术要求。同时,参考了提供的引用内容,特别是引用[1][2][3][4]中关于嵌入式系统与物联网的关系、培养目标、物联网定义以及实践项目等内容。 核心概念部分需要结合嵌入式系统在物联网中的作用;应用场景要具体;技术要求要包括硬件、软件、开发工具等。 注意:引用标记要自然添加在相关内容的末尾,如[^1]。数学公式要使用正确的LaTeX格式。 结构建议: 1. 核心概念:定义嵌入式计算在物联网中的角色,结合四层架构说明 2. 应用场景:列举典型场景,并说明嵌入式系统的作用 3. 技术要求:从硬件、软件、开发技能等方面展开 4. 学习路径与资源:参考引用[2][4]中的实践项目 5. 相关问题 同时,注意引用[1]中嵌入式系统的定义,引用[2]中培养目标提到的技能要求,引用[3]中物联网的四层架构,引用[4]中的实践示例。</think>### 物联网工程中的嵌入式计算 嵌入式计算是物联网(IoT)的核心支撑技术,通过将专用计算系统嵌入物理设备,实现数据采集、实时控制和智能决策。以下是其核心概念、应用场景和技术要求: --- ### 一、核心概念 1. **硬件-软件协同设计** - **微处理器架构**:采用ARM Cortex-M/R系列(如STM32)、RISC-V等低功耗处理器,支持实时任务调度[^1] - **资源约束优化**:在有限内存(通常<1MB RAM)和功耗(如电池供电设备<100mW)下实现高效计算[^2] - **异构接口集成**:统一管理传感器(I²C/SPI)、通信模块(Wi-Fi/BLE/ZigBee)和执行器(PWM/GPIO)[^1] 2. **物联网架构中的角色** ```mermaid graph TB A[感知层-嵌入式传感器] -->|采集数据| B[网络层-通信模块] B -->|传输| C[平台层-边缘计算节点] C -->|分析| D[应用层-云服务] ``` 嵌入式系统主要承担**感知层数据采集**和**边缘层实时处理**(如过滤噪声数据)[^3] --- ### 二、典型应用场景 #### 1. **工业预测性维护** - **技术实现**: - 振动传感器+FFT分析算法→嵌入式故障诊断 - 要求实时响应延迟≤10ms,计算精度>95%[^2] - **案例**:数控机床轴承磨损监测,减少停机时间30% #### 2. **智慧农业** - **嵌入式系统功能**: ```python # 伪代码:自动灌溉决策 if soil_moisture < 30% and weather_prediction != "rain": activate_water_pump(duration=120s) # 通过继电器控制水泵 ``` - 使用ESP32+土壤湿度传感器,功耗<5mA[^4] #### 3. **医疗可穿戴设备** - **技术要求**: - 心电信号采样率≥200Hz(STM32F4系列ADC实现) - 本地滤波算法(如卡尔曼滤波)降低云端传输负载[^1] --- ### 三、关键技术要求 #### 1. **硬件能力** | **指标** | 工业级要求 | 消费级要求 | |----------------|-------------------|-----------------| | 处理器主频 | ≥200MHz(Cortex-M7)| ≥80MHz(Cortex-M4)| | 内存容量 | ≥512KB RAM | ≥256KB RAM | | 通信接口 | 双模:以太网+LoRa | Wi-Fi/BLE 5.0 | #### 2. **软件栈** - **实时操作系统(RTOS)**: - FreeRTOS/Zephyr:支持任务优先级抢占,上下文切换时间<1μs - 开发语言:C/C++(占比90%)、Rust(新兴安全语言)[^2] - **低功耗优化**: $$ P_{\text{总}} = P_{\text{动态}} + P_{\text{静态}} = CV^2f + I_{\text{leak}}V $$ 通过休眠模式(如STM32的Stop Mode)将功耗降至μA级 #### 3. **开发工具链** | **工具类型** | 工业标准方案 | 开源方案 | |---------------|---------------------|-----------------| | 编译器 | IAR Embedded Workbench | GCC Arm Toolchain | | 调试器 | J-Link Probe | OpenOCD | | 仿真环境 | Keil μVision | QEMU for ARM | --- ### 四、学习路径与资源 #### 1. **实践项目** - **入门级**:ESP32温湿度监测器(Wi-Fi上传+手机显示)[^4] ```c // 示例代码片段(Arduino框架) void loop() { float temp = dht.readTemperature(); if (isnan(temp)) return; mqttClient.publish("sensor/temp", String(temp).c_str()); delay(5000); // 5秒采样周期 } ``` - **进阶级**:基于Zephyr RTOS的工业网关(支持Modbus转MQTT) #### 2. **核心教材** -嵌入式系统软硬件协同设计实战》(机械工业出版社) - 重点:第4章(低功耗设计)、第7章(RTOS内核剖析) - 《ARM Cortex-M权威指南》(C语言版) #### 3. **认证体系** - Arm Certified Embedded Engineer - STMicroelectronics STM32认证开发者 --- ### 相关问题 1. **如何选择RTOS与裸机编程的适用场景?** 2. **嵌入式开发中如何平衡实时性与功耗的关系?** 3. **在资源受限设备上实现机器学习推理的技术方案?** 4. **工业物联网嵌入式系统如何通过功能安全认证(如IEC 61508)?** > **实践提示**:使用**PlatformIO+VSCode**开发环境可统一管理多平台(ESP32/STM32/Nordic)项目,显著提升开发效率[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值