1. 安全启动威胁建模与资产识别
任务目标
编写一个最小Bootloader,在开发板上电后完成必要的硬件初始化,通过串口输出启动信息,并进入主循环等待后续命令。本案例以STM32F103系列MCU为硬件平台,使用标准外设库实现,重点验证芯片上电复位后代码能够正确执行、时钟与串口外设能够正常初始化,以及数据能够通过串口发送至PC终端。
环境与依赖
- 硬件:STM32F103C8T6最小系统板(蓝色药丸板),USB转TTL串口模块
- 软件:Keil MDK-ARM 5.x 或以上版本,STM32标准外设库V3.5
- 连接:PA9(USART1_TX)→ 串口模块RX,PA10(USART1_RX)→ 串口模块TX,GND共地
- 串口终端参数:115200-8-N-1
完整实现代码
#include "stm32f10x.h"
/* 启动信息字符串 */
const char boot_msg[] = "\r\n=== Minimal Bootloader v1.0 ===\r\n"
"System Clock: 72MHz\r\n"
"USART1 Ready. Waiting for command...\r\n";
/* 简单延时函数,基于SysTick或空循环 */
static void delay_ms(uint32_t ms)
{
uint32_t i;
for (i = 0; i < ms * 4000; i++) {
__NOP();
}
}
/* USART1 GPIO与外设初始化 */
static void USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/* 使能GPIOA与USART1时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
/* PA9: TX 推挽复用输出 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* PA10: RX 浮空输入 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* USART1 配置:115200, 8数据位, 1停止位, 无校验 */
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_RX | USART_Mode_TX;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
/* 发送单个字符 */
static void USART1_SendChar(uint8_t ch)
{
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, ch);
}
/* 发送字符串 */
static void USART1_SendString(const char *str)
{
while (*str) {
USART1_SendChar((uint8_t)*str++);
}
}
/* 主函数 */
int main(void)
{
/* 设置中断向量表偏移(若从SRAM启动或Bootloader位于0x08000000则无需设置) */
/* 本案例直接从Flash启动,无需设置VTOR */
/* 系统时钟配置为72MHz(使用外部8MHz晶振) */
SystemInit();
/* 初始化串口 */
USART1_Init();
/* 延时等待电源稳定 */
delay_ms(100);
/* 输出启动信息 */
USART1_SendString(boot_msg);
/* 主循环:等待后续命令(当前为空操作) */
while (1) {
/* Bootloader主循环,可在此添加命令解析 */
}
}
编译与烧录
- 在Keil中新建工程,选择芯片型号STM32F103C8
- 添加上述代码文件,并包含标准外设库头文件路径
- 工程配置中定义
STM32F10X_MD宏,选择USE_STDPERIPH_DRIVER - 编译生成HEX文件,通过ST-Link或串口ISP烧录至开发板
验证方法与通过标准
将开发板PA9/PA10与USB转TTL模块连接,打开PC端串口终端(如PuTTY、SSCOM),设置波特率115200、8数据位、1停止位、无校验。给开发板上电复位,观察串口终端是否收到以下预期输出:
=== Minimal Bootloader v1.0 ===
System Clock: 72MHz
USART1 Ready. Waiting for command...
通过条件:串口终端完整显示上述三行启动信息,且无乱码。若输出乱码,检查波特率设置与晶振频率是否匹配(本代码基于8MHz外部晶振,若使用其他晶振需修改SystemInit配置)。若完全无输出,检查PA9/PA10接线是否交叉连接、串口模块供电是否正常、代码是否成功烧录。
常见问题
- 无输出:检查BOOT0/BOOT1跳线是否从Flash启动,确认烧录地址为0x08000000
- 乱码:确认串口终端波特率与代码一致,检查外部晶振是否为8MHz
- 编译报错:确认已添加标准外设库源文件,并在C/C++选项卡中正确包含头文件路径