1. 搭建最小RTOS工程

任务目标

在Cortex-M处理器(以STM32F103为例)上搭建一个无RTOS的最小工程:包含启动文件、链接脚本和主程序,最终实现板载LED以约1Hz频率闪烁。该工程将作为后续章节移植RTOS的裸机基础。

所需环境与依赖

  • 硬件:STM32F103C8T6最小系统板(蓝 pill),板载LED接PC13(低电平点亮)
  • 工具链:arm-none-eabi-gcc(版本≥10)、OpenOCD、Make
  • 调试器:ST-Link V2

工程文件结构

led_blink/
├── Makefile
├── startup_stm32f103.s
├── stm32f103.ld
└── main.c

关键完整代码

main.c — 主程序与LED控制逻辑:

#include <stdint.h>

#define RCC_APB2ENR   (*(volatile uint32_t*)0x40021018)
#define GPIOC_CRH     (*(volatile uint32_t*)0x40011004)
#define GPIOC_ODR     (*(volatile uint32_t*)0x4001100C)

static void delay_ms(uint32_t ms) {
    for (volatile uint32_t i = 0; i < ms * 4000; i++) {
        __asm volatile("nop");
    }
}

int main(void) {
    // 使能GPIOC时钟(APB2)
    RCC_APB2ENR |= (1 << 4);
    // PC13配置为推挽输出,50MHz(CNF13=00, MODE13=11)
    GPIOC_CRH = (GPIOC_CRH & ~(0xF << 20)) | (0x3 << 20);

    while (1) {
        GPIOC_ODR |= (1 << 13);   // PC13高电平 → LED灭
        delay_ms(500);
        GPIOC_ODR &= ~(1 << 13);  // PC13低电平 → LED亮
        delay_ms(500);
    }
}

void SystemInit(void) {
    // 使用内部8MHz HSI,无需额外初始化
}

startup_stm32f103.s — 最小启动文件(向量表与复位处理):

.syntax unified
.cpu cortex-m3
.thumb

.section .isr_vector
.word _estack
.word Reset_Handler

.section .text
.thumb_func
.global Reset_Handler
Reset_Handler:
    ldr r0, =_sdata
    ldr r1, =_edata
    ldr r2, =_sidata
copy_data:
    cmp r0, r1
    bge zero_bss
    ldr r3, [r2], #4
    str r3, [r0], #4
    b copy_data
zero_bss:
    ldr r0, =_sbss
    ldr r1, =_ebss
    movs r2, #0
bss_loop:
    cmp r0, r1
    bge call_main
    str r2, [r0], #4
    b bss_loop
call_main:
    bl main
    b .

stm32f103.ld — 链接脚本:

MEMORY
{
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
    RAM (rw)   : ORIGIN = 0x20000000, LENGTH = 20K
}

_estack = ORIGIN(RAM) + LENGTH(RAM);

SECTIONS
{
    .isr_vector :
    {
        . = ALIGN(4);
        KEEP(*(.isr_vector))
        . = ALIGN(4);
    } > FLASH

    .text :
    {
        . = ALIGN(4);
        *(.text*)
        *(.rodata*)
        . = ALIGN(4);
        _etext = .;
    } > FLASH

    _sidata = LOADADDR(.data);

    .data :
    {
        . = ALIGN(4);
        _sdata = .;
        *(.data*)
        . = ALIGN(4);
        _edata = .;
    } > RAM AT> FLASH

    .bss :
    {
        . = ALIGN(4);
        _sbss = .;
        *(.bss*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = .;
    } > RAM
}

Makefile — 编译与烧录:

TARGET = led_blink
PREFIX = arm-none-eabi-

CFLAGS = -mcpu=cortex-m3 -mthumb -Wall -O2 -nostdlib -ffreestanding
LDFLAGS = -T stm32f103.ld -nostdlib

all: $(TARGET).bin

$(TARGET).elf: startup_stm32f103.s main.c stm32f103.ld
	$(PREFIX)gcc $(CFLAGS) -o $@ startup_stm32f103.s main.c $(LDFLAGS)

$(TARGET).bin: $(TARGET).elf
	$(PREFIX)objcopy -O binary $< $@

flash: $(TARGET).bin
	openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c "program $< verify reset exit"

clean:
	rm -f $(TARGET).elf $(TARGET).bin

运行与验证

  1. 在工程目录执行 make,生成 led_blink.bin。
  2. 连接ST-Link与开发板,执行 make flash 烧录固件。
  3. 观察板载LED(PC13):应持续以约1Hz频率闪烁(亮500ms,灭500ms)。

通过条件:LED按预期频率闪烁即表示工程搭建成功——启动文件正确完成数据段/零初始化,链接脚本地址映射无误,GPIO寄存器配置生效。

常见问题

  • LED不亮:检查GPIOC时钟使能位(RCC_APB2ENR bit4)是否正确置位;确认PC13配置的位段偏移(CRH寄存器bit20~23)。
  • 编译报错“undefined reference to SystemInit”:main.c中已提供空实现,若使用其他启动文件需保留该符号。
  • 烧录后无反应:确认链接脚本中FLASH起始地址为0x08000000,且向量表首两项(栈顶地址、复位处理函数)正确放置。