教程系列《Ubuntu 上做 STM32 开发》 本章目标✅ 完成 STM32 裸机开发环境的配置基于 Ubuntu✅ 用 VSCode 编写、编译、烧录、调试代码✅ 点亮 BluePill 板载 LEDPC13✅ 全程只使用开源工具 所需工具硬件准备BluePillSTM32F103C8T6ST-Link调试器USB 数据线软件准备Ubuntu 系统sudo apt update sudo apt install gcc-arm-none-eabi gdb-multiarch openocd build-essentialVSCode 插件Cortex-DebugSerial MonitorC/C Extension PackST-Link udev 规则配置echo SUBSYSTEMusb, ATTR{idVendor}0483, ATTR{idProduct}3748, MODE0666 | \ sudo tee /etc/udev/rules.d/49-stlinkv2.rules sudo udevadm control --reload-rules sudo udevadm trigger 项目结构bluepill_blink/ ├── main.c ├── startup.s ├── linker.ld ├── Makefile └── .vscode/ ├── launch.json └── tasks.json main.c点灯代码#define RCC_APB2ENR (*(volatile unsigned int*)0x40021018) #define GPIOC_CRH (*(volatile unsigned int*)0x40011004) #define GPIOC_ODR (*(volatile unsigned int*)0x4001100C) void delay(volatile int t) { while (t--); } int main(void) { RCC_APB2ENR | (1 4); // Enable GPIOC clock GPIOC_CRH ~(0xF 20); // Clear PC13 config GPIOC_CRH | (0x2 20); // Output push-pull, 2MHz while (1) { GPIOC_ODR ^ (1 13); // Toggle PC13 delay(500000); } } startup.s启动文件.syntax unified .cpu cortex-m3 .thumb .global _start .global Reset_Handler .extern _estack .section .isr_vector, a, %progbits .word _estack // Initial Stack Pointer .word Reset_Handler // Reset Handler .section .text .type Reset_Handler, %function Reset_Handler: ldr sp, _estack // 初始化堆栈 bl main // 跳转到 C 入口 b . // 死循环防止退出 .size Reset_Handler, . - Reset_Handler linker.ld链接脚本/* 必须放在开头 */ ENTRY(Reset_Handler) MEMORY { FLASH (rx) : ORIGIN 0x08000000, LENGTH 64K RAM (rwx) : ORIGIN 0x20000000, LENGTH 20K } /* 一定要按照下面顺序 */ SECTIONS { /* 启动向量表 */ .isr_vector : { KEEP(*(.isr_vector)) } FLASH /* 代码段 */ .text : { *(.text*) /* 代码 */ *(.rodata*) /* 只读数据 */ } FLASH /* 初始化数据段 */ .data : AT (ADDR(.text) SIZEOF(.text)) { *(.data*) } RAM /* 零初始化变量 */ .bss : { *(.bss*) *(COMMON) } RAM /* 栈空间 */ .stack (COPY) : { *(.stack) } RAM /* 明确告诉链接器 _estack 是 RAM 的末尾 */ _estack ORIGIN(RAM) LENGTH(RAM); } Makefile构建与烧录TARGET main CPU -mcpucortex-m3 FPU FLOAT-ABI MCU $(CPU) -mthumb $(FPU) $(FLOAT-ABI) C_SOURCES main.c ASM_SOURCES startup_stm32f103c8t6.s OBJS $(C_SOURCES:.c.o) $(ASM_SOURCES:.s.o) LDSCRIPT linker.ld CC arm-none-eabi-gcc AS arm-none-eabi-as LD arm-none-eabi-ld OBJCOPY arm-none-eabi-objcopy OBJDUMP arm-none-eabi-objdump GDB arm-none-eabi-gdb CFLAGS $(MCU) -Wall -O0 -g -nostdlib -nostartfiles LDFLAGS -T $(LDSCRIPT) all: $(TARGET).elf %.o: %.c $(CC) $(CFLAGS) -c $ -o $ %.o: %.s $(CC) $(CFLAGS) -c $ -o $ $(TARGET).elf: $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o $ $(LDFLAGS) $(OBJCOPY) -O ihex $ $(TARGET).hex $(OBJCOPY) -O binary $ $(TARGET).bin $(OBJDUMP) -D $ $(TARGET).lst flash: $(TARGET).elf openocd -f interface/stlink.cfg -f target/stm32f1x.cfg \ -c program $(TARGET).elf verify reset exit clean: rm -f *.o *.elf *.hex *.bin *.lst VSCode 调试配置launch.json{ version: 0.2.0, configurations: [ { name: Debug STM32 (BluePill), type: cortex-debug, request: launch, servertype: openocd, cwd: ${workspaceRoot}, executable: main.elf, device: STM32F103C8, configFiles: [ interface/stlink.cfg, target/stm32f1x.cfg ], runToEntryPoint: main, svdFile: ${workspaceRoot}/STM32F103.svd, preLaunchTask: build } ] }tasks.json{ version: 2.0.0, tasks: [ { label: build, type: shell, command: make, problemMatcher: [$gcc] } ] } 编译 烧录命令示例make # 编译生成 main.elf make flash # 使用 openocd 烧录并复位你应该可以看到板子上PC13连接LED开始闪烁了✅ 本章回顾你已经完成了在 Ubuntu 系统下搭建了完整 STM32 开发链路从零手写裸机程序点亮 PC13 上的 LED使用 VSCode 调试器完成编译、烧录、单步执行你现在真正掌握了裸机开发流程而不是“只会拉图”。下一章我们将加上 UART 输出让 STM32 “说话”。