在Ubuntu 20.04上,用QEMU从零搭建STM32虚拟开发板(避坑编译错误)

张开发
2026/5/13 12:18:36 15 分钟阅读

分享文章

在Ubuntu 20.04上,用QEMU从零搭建STM32虚拟开发板(避坑编译错误)
在Ubuntu 20.04上构建STM32虚拟开发环境的完整实践指南对于嵌入式开发者而言硬件设备的限制常常成为学习道路上的绊脚石。想象一下这样的场景深夜灵感迸发想要验证一个STM32的创新想法手边却没有开发板或是作为软件工程师想要了解嵌入式开发却不愿立即投入硬件采购。这时一个可靠的虚拟开发环境就能成为你的救星。本文将带你从零开始在Ubuntu 20.04系统上搭建完整的STM32虚拟开发环境。不同于简单的教程我们会深入每个步骤背后的原理特别关注那些容易导致失败的陷阱确保你能一次性成功构建这个虚拟平台。无论你是嵌入式新手还是经验丰富的开发者想要快速验证代码这个方案都能满足你的需求。1. 环境准备与依赖安装在开始构建STM32虚拟环境之前我们需要确保系统具备所有必要的工具链和依赖项。Ubuntu 20.04作为一个长期支持版本提供了稳定的基础但默认仓库中的软件包版本可能不完全符合我们的需求。首先更新系统软件包列表并升级现有软件sudo apt update sudo apt upgrade -y接下来安装核心开发工具和QEMU的基础依赖sudo apt install -y build-essential git libglib2.0-dev libfdt-dev \ libpixman-1-dev zlib1g-dev ninja-build pkg-config特别注意这些依赖项中libglib2.0-dev和libpixman-1-dev对QEMU的图形和事件处理至关重要而libfdt-dev则负责设备树支持。缺少任何一个都可能导致后续编译失败。对于ARM交叉编译工具链我们推荐使用官方提供的版本sudo apt install -y gcc-arm-none-eabi binutils-arm-none-eabi gdb-arm-none-eabi验证工具链安装是否成功arm-none-eabi-gcc --version如果系统提示找不到命令你可能需要手动将工具链路径添加到环境变量中。典型的安装路径是/usr/bin/arm-none-eabi-*。2. 获取并编译定制版QEMU标准的QEMU虽然功能强大但对STM32的支持有限。我们需要使用专门为STM32修改的qemu_stm32版本。这个定制版本包含了针对STM32微控制器的特殊模拟支持。首先克隆源代码仓库git clone https://github.com/beckus/qemu_stm32.git cd qemu_stm32在编译之前我们需要特别注意一个关键配置选项--disable-werror。这个选项告诉编译器将警告视为警告而非错误避免因严格检查而中断编译过程。配置编译选项./configure --target-listarm-softmmu --disable-werror --enable-debug常见陷阱如果不添加--disable-werror选项现代编译器可能会因为代码中的一些风格问题而报错导致编译失败。这是新手最容易遇到的问题之一。编译过程可能需要一些时间取决于你的系统性能make -j$(nproc)编译完成后建议将生成的QEMU可执行文件安装到系统路径sudo make install验证安装是否成功qemu-system-arm --version如果一切顺利你应该能看到类似qemu-system-arm version 2.8.0 (qemu_stm32)的输出信息。3. 准备STM32固件与演示程序有了模拟器我们还需要能够在其中运行的STM32程序。这里我们使用ST官方提供的示例代码作为起点。首先获取STM32标准外设库Standard Peripheral Librarywget https://www.st.com/content/ccc/resource/technical/software/firmware/group0/5c/5e/c9/6a/1a/3f/42/15/stm32f10x_stdperiph_lib/files/stm32f10x_stdperiph_lib.zip unzip stm32f10x_stdperiph_lib.zip进入示例项目目录cd STM32F10x_StdPeriph_Lib/Project/GPIO/IOToggle修改Makefile中的工具链路径如果需要CROSS_COMPILE arm-none-eabi-编译示例项目make编译成功后你会在build/目录下找到stm32_flash.elf和stm32_flash.bin文件这就是我们要在QEMU中运行的固件。4. 配置与运行STM32虚拟开发环境现在我们已经准备好了模拟器和固件是时候将它们组合起来创建一个完整的虚拟开发环境了。创建一个简单的启动脚本run_qemu.sh#!/bin/bash qemu-system-arm -M stm32-p103 -kernel stm32_flash.elf -serial stdio -gdb tcp::3333 -S这个脚本做了以下几件事-M stm32-p103指定模拟STM32-P103开发板-kernel stm32_flash.elf加载我们编译的固件-serial stdio将串口输出重定向到终端-gdb tcp::3333 -S启用GDB服务器并暂停CPU初始执行给脚本添加执行权限并运行chmod x run_qemu.sh ./run_qemu.sh此时QEMU会启动并等待GDB连接。在另一个终端中启动GDB调试会话arm-none-eabi-gdb build/stm32_flash.elf在GDB中连接到QEMU(gdb) target remote localhost:3333 (gdb) continue如果一切配置正确你应该能在QEMU窗口中看到LED闪烁的模拟效果通过串口输出模拟。5. 调试技巧与性能优化虚拟开发环境的一个巨大优势是强大的调试能力。让我们探索一些高级调试技巧。5.1 使用GDB进行高效调试在QEMU中运行STM32程序时GDB可以提供源代码级别的调试体验。一些有用的GDB命令(gdb) break main.c:45 # 在main.c的第45行设置断点 (gdb) info registers # 查看所有寄存器值 (gdb) x/10xw 0x20000000 # 查看内存内容 (gdb) monitor info mem # 查看内存映射(QEMU特有命令)5.2 QEMU性能调优默认配置下QEMU可能运行较慢。以下是一些优化建议启用加速如果主机支持KVM-enable-kvm -cpu host调整JIT缓存大小-tb-size 256使用多线程翻译-smp 45.3 外设模拟与扩展QEMU的STM32模拟支持有限的外设集。如果需要更多外设可以考虑实现自定义设备// 示例简单的自定义设备实现 static void my_device_init(void) { MemoryRegion *mr g_new(MemoryRegion, 1); memory_region_init_io(mr, NULL, my_device_ops, NULL, my-device, 0x1000); memory_region_add_subregion(get_system_memory(), 0x40000000, mr); }使用现成的QEMU设备模型扩展6. 集成开发环境配置虽然命令行工具足够强大但现代IDE可以提供更高效的开发体验。下面介绍如何将虚拟开发环境与流行的IDE集成。6.1 Eclipse集成安装Eclipse CDT插件创建新的C/C项目配置工具链为arm-none-eabi设置调试配置为远程GDB会话6.2 VS Code配置安装C/C扩展配置launch.json{ name: Debug STM32, type: cppdbg, request: launch, program: ${workspaceFolder}/build/stm32_flash.elf, miDebuggerServerAddress: localhost:3333, debugServerArgs: --interpretermi, MIMode: gdb, miDebuggerPath: arm-none-eabi-gdb }配置tasks.json用于构建6.3 自动化构建系统考虑使用更现代的构建系统如CMakecmake_minimum_required(VERSION 3.10) project(stm32_project C) set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER arm-none-eabi-gcc) add_executable(stm32_flash src/main.c src/stm32f10x_it.c ${STM32_LIB}/src/stm32f10x_gpio.c ${STM32_LIB}/src/stm32f10x_rcc.c ) target_compile_options(stm32_flash PRIVATE -mcpucortex-m3 -mthumb -DSTM32F10X_MD -DUSE_STDPERIPH_DRIVER )7. 常见问题与解决方案在实际搭建过程中你可能会遇到各种问题。以下是经过验证的解决方案7.1 编译错误处理问题error: unknown type name uint32_t解决添加#include stdint.h或确保正确包含STM32头文件问题undefined reference to_sbrk解决实现简单的内存管理函数或链接适当的启动文件7.2 QEMU运行问题问题qemu: fatal: Trying to execute code outside RAM or ROM at 0x00000000解决检查固件是否正确编译和链接特别是链接脚本中的内存布局问题模拟器启动但无任何输出解决确认串口配置正确检查固件是否包含正确的初始化代码尝试在GDB中单步执行查找问题7.3 性能问题问题模拟运行速度极慢解决启用QEMU加速如果硬件支持减少调试输出优化模拟配置如减少不必要的设备模拟问题GDB响应迟缓解决使用-gdb tcp::3333而非默认端口减少断点数量使用硬件断点而非软件断点8. 进阶应用与项目实践掌握了基础环境搭建后让我们探索一些实际应用场景。8.1 模拟外设交互虽然QEMU的STM32模拟有限但我们可以通过以下方式扩展实现自定义设备驱动使用QEMU的插件系统通过Socket与外部程序通信8.2 单元测试框架集成虚拟环境是进行自动化测试的理想平台# 示例使用pytest进行自动化测试 def test_led_blink(): # 启动QEMU qemu subprocess.Popen([./run_qemu.sh], stdoutsubprocess.PIPE) # 等待特定输出 for line in iter(qemu.stdout.readline, b): if bLED ON in line: break # 验证状态 assert bLED ON in line qemu.terminate()8.3 多设备仿真QEMU支持同时模拟多个设备并互联# 启动两个STM32实例并通过虚拟UART连接 qemu-system-arm -M stm32-p103 -kernel firmware1.elf -serial pty qemu-system-arm -M stm32-p103 -kernel firmware2.elf -serial /dev/pts/1这种配置可以用于测试设备间通信协议或分布式算法。

更多文章