2. 串口输出启动信息

任务目标

在Bootloader启动早期初始化UART外设,通过串口输出格式化的启动日志,为后续阶段提供调试输出通道。本案例以STM32F407平台为例,实现一个最小可用的串口日志输出模块。

环境依赖

  • 硬件:STM32F407开发板(USART2,PA2=TX,PA3=RX),USB转串口模块
  • 工具链:arm-none-eabi-gcc + Makefile,或STM32CubeIDE
  • 串口终端:115200-8-N-1

关键完整代码

// uart_log.h
#ifndef UART_LOG_H
#define UART_LOG_H

#include <stdint.h>
#include <stdarg.h>

void uart_init(void);
void uart_send_char(char c);
void log_printf(const char *fmt, ...);

#endif

// uart_log.c
#include "uart_log.h"
#include "stm32f4xx.h"

void uart_init(void) {
    // 使能GPIOA和USART2时钟
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;

    // PA2=TX(AF7), PA3=RX(AF7)
    GPIOA->MODER &= ~(GPIO_MODER_MODER2_Msk | GPIO_MODER_MODER3_Msk);
    GPIOA->MODER |= (GPIO_MODER_MODER2_1 | GPIO_MODER_MODER3_1);
    GPIOA->AFR[0] |= (7 << GPIO_AFRL_AFSEL2_Pos) | (7 << GPIO_AFRL_AFSEL3_Pos);
    GPIOA->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR2_Msk | GPIO_OSPEEDER_OSPEEDR3_Msk);

    // USART2: 115200, 8N1
    USART2->BRR = 84000000 / 115200;  // APB1=84MHz
    USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}

void uart_send_char(char c) {
    while (!(USART2->SR & USART_SR_TXE));
    USART2->DR = c;
}

static void uart_send_str(const char *s) {
    while (*s) {
        uart_send_char(*s++);
    }
}

void log_printf(const char *fmt, ...) {
    char buf[128];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);
    uart_send_str(buf);
}

// main.c(Bootloader入口)
#include "uart_log.h"

void bootloader_main(void) {
    uart_init();
    log_printf("[BOOT] UART initialized\r\n");
    log_printf("[BOOT] System clock: %d MHz\r\n", 168);
    log_printf("[BOOT] Jumping to app...\r\n");

    while (1) {
        // Bootloader主循环
    }
}

编译与运行

# 使用Makefile构建
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -O2 \
    -I. uart_log.c -o uart_log.o
arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -O2 \
    -I. main.c -o main.o
arm-none-eabi-ld -T bootloader.ld uart_log.o main.o -o bootloader.elf
arm-none-eabi-objcopy -O binary bootloader.elf bootloader.bin

# 烧录(以ST-Link为例)
st-flash write bootloader.bin 0x08000000

验证方法

  1. 将USB转串口模块的RX连接开发板PA2(TX),GND相连
  2. 打开串口终端,配置115200-8-N-1
  3. 复位开发板,观察串口输出

通过条件:串口终端依次显示以下三行日志,且无乱码:

[BOOT] UART initialized
[BOOT] System clock: 168 MHz
[BOOT] Jumping to app...

常见问题

  • 输出乱码:检查BRR分频值是否与APB1时钟匹配(F407默认APB1=84MHz),以及串口终端波特率是否设置为115200
  • 无输出:确认PA2/PA3复用功能配置正确(AF7),且USART2时钟已使能
  • vsnprintf依赖:若使用newlib-nano,需在链接时添加--specs=nano.specs以减小代码体积