教程系列:《Ubuntu 上做 STM32 开发》
🎯 本章目标
✅ 完成 STM32 裸机开发环境的配置(基于 Ubuntu)
✅ 用 VSCode 编写、编译、烧录、调试代码
✅ 点亮 BluePill 板载 LED(PC13)
✅ 全程只使用开源工具
🧰 所需工具
硬件准备:
BluePill(STM32F103C8T6)
ST-Link(调试器)
USB 数据线
软件准备(Ubuntu 系统)
sudo apt update sudo apt install gcc-arm-none-eabi gdb-multiarch openocd build-essentialVSCode 插件:
Cortex-Debug
Serial Monitor
C/C++ Extension Pack
ST-Link udev 规则配置:
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3748", MODE="0666"' | \ 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 = -mcpu=cortex-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 “说话”。🚀