3. 读取按键控制启动模式
任务说明
本案例实现一个按键控制的启动模式选择器:系统上电后检测指定GPIO引脚上的按键状态,若按键被按下(低电平)则进入下载模式(等待主机通过串口发送固件),否则跳转到应用模式(执行用户应用程序)。该逻辑是Bootloader中最基础的分支决策部分。
硬件连接
- 按键接PA0引脚,另一端接地(按下为低电平)
- 板载LED接PC13,用于指示当前所处模式
- 串口1(PA9/PA10)用于下载模式通信
完整代码
// 文件: boot_selector.c
// 依赖: STM32F103标准外设库, 系统时钟已初始化为72MHz
#include "stm32f10x.h"
#include <string.h>
#define APP_ADDRESS 0x08003000 // 应用程序起始地址(跳过Bootloader占用的12KB)
#define KEY_GPIO_CLK RCC_APB2Periph_GPIOA
#define KEY_GPIO GPIOA
#define KEY_PIN GPIO_Pin_0
#define LED_GPIO_CLK RCC_APB2Periph_GPIOC
#define LED_GPIO GPIOC
#define LED_PIN GPIO_Pin_13
// 按键消抖延时(简单软件延时)
static void delay_ms(volatile uint32_t ms) {
while (ms--) {
for (volatile uint32_t i = 0; i < 7200; i++);
}
}
// GPIO初始化
static void gpio_init(void) {
GPIO_InitTypeDef gpio;
RCC_APB2PeriphClockCmd(KEY_GPIO_CLK | LED_GPIO_CLK, ENABLE);
// PA0上拉输入(按键接地,按下为低)
gpio.GPIO_Pin = KEY_PIN;
gpio.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(KEY_GPIO, &gpio);
// PC13推挽输出(LED低电平点亮)
gpio.GPIO_Pin = LED_PIN;
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(LED_GPIO, &gpio);
}
// 检测按键是否按下(带消抖)
static uint8_t key_pressed(void) {
if (GPIO_ReadInputDataBit(KEY_GPIO, KEY_PIN) == Bit_RESET) {
delay_ms(20); // 消抖
if (GPIO_ReadInputDataBit(KEY_GPIO, KEY_PIN) == Bit_RESET) {
return 1;
}
}
return 0;
}
// 跳转到应用程序
static void jump_to_app(void) {
typedef void (*pFunction)(void);
uint32_t app_sp = *(volatile uint32_t*)APP_ADDRESS; // 取应用栈顶地址
uint32_t app_pc = *(volatile uint32_t*)(APP_ADDRESS + 4); // 取应用复位向量
if ((app_sp & 0xFFF00000) != 0x20000000) {
return; // 栈顶地址非法,停留在Bootloader
}
pFunction jump = (pFunction)app_pc;
__set_MSP(app_sp); // 设置主栈指针
jump(); // 跳转执行
}
// 下载模式处理(此处仅演示模式进入,实际接收逻辑由主机工具配合)
static void download_mode(void) {
GPIO_SetBits(LED_GPIO, LED_PIN); // LED灭,指示下载模式
// 实际项目中在此处初始化串口并等待接收固件数据
// 本案例仅演示模式选择,串口接收逻辑省略
while (1); // 停留在下载模式
}
int main(void) {
gpio_init();
// 上电延时,等待电源稳定
delay_ms(100);
if (key_pressed()) {
// 按键按下 → 下载模式
GPIO_ResetBits(LED_GPIO, LED_PIN); // LED亮
download_mode();
} else {
// 按键未按下 → 应用模式
GPIO_SetBits(LED_GPIO, LED_PIN); // LED灭
jump_to_app();
}
// 若跳转失败(应用地址无效),则停留在此处
while (1);
}
编译与烧录
# 使用arm-none-eabi工具链编译(以Makefile方式为例)
arm-none-eabi-gcc -c -mcpu=cortex-m3 -mthumb boot_selector.c -o boot_selector.o
arm-none-eabi-ld -T linker.ld boot_selector.o -o boot_selector.elf
arm-none-eabi-objcopy -O ihex boot_selector.elf boot_selector.hex
# 通过ST-Link烧录到0x08000000地址
st-flash write boot_selector.hex 0x08000000
链接脚本中需设置FLASH起始地址为0x08000000,长度12KB(0x3000),应用程序烧录到0x08003000之后。
验证方法
- 下载模式验证:按住PA0按键不放,同时给板上电(或按复位键)。观察PC13 LED点亮,程序停留在下载模式(可用调试器查看PC指针停在
download_mode的while循环内)。 - 应用模式验证:不按按键,直接上电或复位。若
0x08003000处已烧录有效应用程序,则LED熄灭且程序跳转执行应用代码;若该地址无有效程序(全0xFF),则程序不会跳转,最终停在while(1),LED保持熄灭。 - 判定标准:两种操作下LED状态与程序停留位置符合上述描述,即表示按键控制启动模式功能正常。
关键说明
- 跳转前必须检查应用栈顶地址是否落在SRAM区间(
0x20000000起始),防止非法地址导致硬件错误。 - 按键消抖采用20ms软件延时,实际产品中可改用定时器实现非阻塞消抖。
- 下载模式中的串口接收逻辑不属于本案例范围,实际项目中在
download_mode()内初始化USART并实现YModem或自定义协议即可。