文章目录
-
- 一、前言
-
- [1.1 技术背景](#1.1 技术背景)
- [1.2 本文目标](#1.2 本文目标)
- [1.3 技术栈](#1.3 技术栈)
- 二、环境准备
-
- [2.1 硬件准备](#2.1 硬件准备)
- [2.2 Flash分区规划](#2.2 Flash分区规划)
- 三、Bootloader设计
-
- [3.1 启动流程](#3.1 启动流程)
- [3.2 工程配置](#3.2 工程配置)
- 四、核心实现
-
- [4.1 Flash操作驱动](#4.1 Flash操作驱动)
- [4.2 YMODEM协议实现](#4.2 YMODEM协议实现)
- [4.3 应用程序跳转](#4.3 应用程序跳转)
- [4.4 Bootloader主程序](#4.4 Bootloader主程序)
- [4.5 应用程序配置](#4.5 应用程序配置)
- 五、编译与下载
-
- [5.1 编译Bootloader](#5.1 编译Bootloader)
- [5.2 编译应用程序](#5.2 编译应用程序)
- [5.3 使用SecureCRT升级](#5.3 使用SecureCRT升级)
- 六、故障排查与问题解决
-
- [6.1 跳转失败](#6.1 跳转失败)
- [6.2 Flash写入失败](#6.2 Flash写入失败)
- 七、总结
-
- [7.1 核心知识点回顾](#7.1 核心知识点回顾)
- [7.2 扩展学习方向](#7.2 扩展学习方向)
- [7.3 学习资源](#7.3 学习资源)
一、前言
1.1 技术背景
在嵌入式产品开发中,固件更新是一个重要的维护手段。传统的烧录方式需要连接调试器,对于已经部署到现场的设备来说非常不便。IAP(In-Application Programming)技术允许设备在运行过程中接收新固件并更新自身,实现远程升级功能。
Bootloader是位于Flash起始位置的引导程序,负责初始化硬件、检查升级条件、加载应用程序或执行固件更新。
1.2 本文目标
通过本教程,你将学会:
- Bootloader的设计原理
- STM32启动流程和向量表重映射
- Flash读写操作
- YMODEM协议实现
- 固件升级流程设计
1.3 技术栈
硬件平台:
- STM32F103C8T6(主控芯片)
- ST-Link V2(调试器)
- USB转TTL模块(串口通信)
软件环境:
- STM32CubeIDE v1.10.0+
- HAL库
- YMODEM协议
二、环境准备
2.1 硬件准备
| 设备 | 数量 | 说明 |
|---|---|---|
| STM32F103C8T6最小系统板 | 1块 | 主控芯片 |
| ST-Link V2 | 1个 | 程序下载 |
| USB转TTL模块 | 1个 | 串口升级 |
2.2 Flash分区规划
STM32F103C8T6有64KB Flash,分区如下:
Flash地址空间(0x08000000 - 0x08010000)
0x08000000 - 0x08004000 Bootloader (16KB)
0x08004000 - 0x0800F000 Application (44KB)
0x0800F000 - 0x08010000 Configuration (4KB)
三、Bootloader设计
3.1 启动流程
按键按下
无升级请求
有效
无效
成功
失败
复位
硬件初始化
检查升级条件
进入升级模式
检查应用程序
跳转到应用程序
YMODEM接收
写入Flash
校验
错误处理
应用程序运行
3.2 工程配置
创建Bootloader工程:
- 工程名称:Bootloader
- 芯片型号:STM32F103C8T6
- 配置串口USART1用于升级通信
Flash配置:
ROM起始地址:0x08000000
ROM大小:0x4000 (16KB)
四、核心实现
4.1 Flash操作驱动
📄 创建文件:
Core/Src/flash_driver.c
c
/*
* flash_driver.c - Flash操作驱动
*
* 功能:
* - Flash解锁/锁定
* - Flash页擦除
* - Flash编程
* - Flash读取
*/
#include "flash_driver.h"
#include "stm32f1xx_hal.h"
/* Flash页大小 */
#define FLASH_PAGE_SIZE 1024
/* Flash起始地址 */
#define FLASH_BASE_ADDR 0x08000000
/* 应用程序起始地址 */
#define APP_START_ADDR 0x08004000
/* Flash解锁 */
void flash_unlock(void)
{
HAL_FLASH_Unlock();
}
/* Flash锁定 */
void flash_lock(void)
{
HAL_FLASH_Lock();
}
/* 擦除Flash页 */
uint8_t flash_erase_page(uint32_t page_addr)
{
FLASH_EraseInitTypeDef erase_init;
uint32_t page_error;
HAL_StatusTypeDef status;
/* 计算页号 */
uint32_t page = (page_addr - FLASH_BASE_ADDR) / FLASH_PAGE_SIZE;
erase_init.TypeErase = FLASH_TYPEERASE_PAGES;
erase_init.PageAddress = page_addr;
erase_init.NbPages = 1;
status = HAL_FLASHEx_Erase(&erase_init, &page_error);
return (status == HAL_OK) ? 0 : 1;
}
/* 擦除应用程序区域 */
uint8_t flash_erase_app(void)
{
uint32_t addr;
for (addr = APP_START_ADDR; addr < 0x08010000; addr += FLASH_PAGE_SIZE) {
if (flash_erase_page(addr) != 0) {
return 1;
}
}
return 0;
}
/* 写入Flash(半字) */
uint8_t flash_write_halfword(uint32_t addr, uint16_t data)
{
HAL_StatusTypeDef status;
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr, data);
return (status == HAL_OK) ? 0 : 1;
}
/* 写入Flash(字) */
uint8_t flash_write_word(uint32_t addr, uint32_t data)
{
HAL_StatusTypeDef status;
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, data);
return (status == HAL_OK) ? 0 : 1;
}
/* 写入Flash缓冲区 */
uint8_t flash_write_buffer(uint32_t addr, uint8_t *buf, uint32_t len)
{
uint32_t i;
uint16_t halfword;
for (i = 0; i < len; i += 2) {
halfword = buf[i] | (buf[i + 1] << 8);
if (flash_write_halfword(addr + i, halfword) != 0) {
return 1;
}
}
return 0;
}
/* 读取Flash */
void flash_read(uint32_t addr, uint8_t *buf, uint32_t len)
{
uint32_t i;
for (i = 0; i < len; i++) {
buf[i] = *(__IO uint8_t *)(addr + i);
}
}
/* 获取应用程序大小 */
uint32_t flash_get_app_size(void)
{
/* 从配置区读取 */
return *(__IO uint32_t *)0x0800F000;
}
/* 设置应用程序大小 */
void flash_set_app_size(uint32_t size)
{
flash_unlock();
flash_write_word(0x0800F000, size);
flash_lock();
}
/* 校验应用程序 */
uint8_t flash_verify_app(uint32_t crc)
{
/* 从配置区读取CRC */
uint32_t stored_crc = *(__IO uint32_t *)0x0800F004;
return (stored_crc == crc) ? 0 : 1;
}
📄 创建文件:
Core/Inc/flash_driver.h
c
/*
* flash_driver.h - Flash驱动头文件
*/
#ifndef __FLASH_DRIVER_H__
#define __FLASH_DRIVER_H__
#include <stdint.h>
/* Flash解锁 */
void flash_unlock(void);
/* Flash锁定 */
void flash_lock(void);
/* 擦除Flash页 */
uint8_t flash_erase_page(uint32_t page_addr);
/* 擦除应用程序区域 */
uint8_t flash_erase_app(void);
/* 写入Flash半字 */
uint8_t flash_write_halfword(uint32_t addr, uint16_t data);
/* 写入Flash字 */
uint8_t flash_write_word(uint32_t addr, uint32_t data);
/* 写入Flash缓冲区 */
uint8_t flash_write_buffer(uint32_t addr, uint8_t *buf, uint32_t len);
/* 读取Flash */
void flash_read(uint32_t addr, uint8_t *buf, uint32_t len);
/* 获取应用程序大小 */
uint32_t flash_get_app_size(void);
/* 设置应用程序大小 */
void flash_set_app_size(uint32_t size);
/* 校验应用程序 */
uint8_t flash_verify_app(uint32_t crc);
#endif /* __FLASH_DRIVER_H__ */
4.2 YMODEM协议实现
📄 创建文件:
Core/Src/ymodem.c
c
/*
* ymodem.c - YMODEM协议实现
*
* 功能:
* - 文件接收
* - CRC校验
* - 超时重传
*/
#include "ymodem.h"
#include "flash_driver.h"
#include <string.h>
#include <stdio.h>
/* YMODEM控制字符 */
#define SOH 0x01 /* 128字节数据包 */
#define STX 0x02 /* 1024字节数据包 */
#define EOT 0x04 /* 传输结束 */
#define ACK 0x06 /* 确认 */
#define NAK 0x15 /* 否认 */
#define CAN 0x18 /* 取消 */
#define CRC16 0x43 /* 'C' - CRC校验请求 */
/* 包大小 */
#define PACKET_SIZE_128 128
#define PACKET_SIZE_1024 1024
#define PACKET_HEADER_SIZE 3
#define PACKET_TRAILER_SIZE 2
#define PACKET_OVERHEAD (PACKET_HEADER_SIZE + PACKET_TRAILER_SIZE)
/* 超时时间 */
#define TIMEOUT_MS 1000
#define MAX_ERRORS 10
/* 串口句柄 */
extern UART_HandleTypeDef huart1;
/* 缓冲区 */
static uint8_t packet_data[PACKET_SIZE_1024 + PACKET_OVERHEAD];
static uint8_t file_name[64];
static uint32_t file_size;
/* 函数声明 */
static uint16_t crc16(const uint8_t *data, uint16_t len);
static uint8_t receive_byte(uint32_t timeout);
static uint8_t receive_packet(uint8_t *data, uint16_t *len, uint32_t timeout);
static void send_byte(uint8_t byte);
/* CRC16计算 */
static uint16_t crc16(const uint8_t *data, uint16_t len)
{
uint16_t crc = 0;
uint16_t i, j;
for (i = 0; i < len; i++) {
crc ^= (uint16_t)data[i] << 8;
for (j = 0; j < 8; j++) {
if (crc & 0x8000) {
crc = (crc << 1) ^ 0x1021;
} else {
crc <<= 1;
}
}
}
return crc;
}
/* 接收一个字节 */
static uint8_t receive_byte(uint32_t timeout)
{
uint8_t byte;
HAL_UART_Receive(&huart1, &byte, 1, timeout);
return byte;
}
/* 发送一个字节 */
static void send_byte(uint8_t byte)
{
HAL_UART_Transmit(&huart1, &byte, 1, HAL_MAX_DELAY);
}
/* 接收数据包 */
static uint8_t receive_packet(uint8_t *data, uint16_t *len, uint32_t timeout)
{
uint8_t ch;
uint16_t packet_size;
uint16_t crc, rx_crc;
uint8_t packet_num, packet_num_comp;
/* 等待包头 */
do {
ch = receive_byte(timeout);
if (ch == SOH) {
packet_size = PACKET_SIZE_128;
break;
} else if (ch == STX) {
packet_size = PACKET_SIZE_1024;
break;
} else if (ch == EOT) {
return 2; /* 传输结束 */
} else if (ch == CAN) {
return 3; /* 取消传输 */
}
} while (1);
/* 接收包序号 */
packet_num = receive_byte(timeout);
packet_num_comp = receive_byte(timeout);
if (packet_num != (uint8_t)(~packet_num_comp)) {
return 1; /* 包序号错误 */
}
/* 接收数据 */
for (uint16_t i = 0; i < packet_size; i++) {
data[i] = receive_byte(timeout);
}
/* 接收CRC */
rx_crc = receive_byte(timeout) << 8;
rx_crc |= receive_byte(timeout);
/* 计算CRC */
crc = crc16(data, packet_size);
if (crc != rx_crc) {
return 1; /* CRC错误 */
}
*len = packet_size;
return 0; /* 成功 */
}
/* YMODEM接收 */
uint8_t ymodem_receive(void)
{
uint8_t packet_data[PACKET_SIZE_1024];
uint16_t packet_len;
uint8_t packet_num = 0;
uint8_t errors = 0;
uint8_t result;
uint32_t flash_addr = 0x08004000;
uint32_t total_received = 0;
file_name[0] = '\\0';
file_size = 0;
printf("\\r\\nWaiting for file...\\r\\n");
/* 发送'C'请求CRC校验 */
for (int i = 0; i < 3; i++) {
send_byte(CRC16);
HAL_Delay(1000);
}
/* 接收第一个包(文件名包) */
while (1) {
result = receive_packet(packet_data, &packet_len, TIMEOUT_MS);
if (result == 0) {
/* 解析文件名 */
if (packet_num == 0) {
/* 提取文件名 */
uint8_t *p = packet_data;
uint8_t i = 0;
while (*p && i < sizeof(file_name) - 1) {
file_name[i++] = *p++;
}
file_name[i] = '\\0';
/* 提取文件大小 */
while (*p) p++;
p++;
file_size = 0;
while (*p >= '0' && *p <= '9') {
file_size = file_size * 10 + (*p++ - '0');
}
printf("File: %s, Size: %lu bytes\\r\\n", file_name, file_size);
/* 擦除应用程序区域 */
printf("Erasing Flash...\\r\\n");
flash_unlock();
if (flash_erase_app() != 0) {
printf("Erase failed!\\r\\n");
flash_lock();
return 1;
}
printf("Erase complete\\r\\n");
send_byte(ACK);
send_byte(CRC16);
packet_num = 1;
} else {
/* 数据包 */
if (flash_write_buffer(flash_addr, packet_data, packet_len) != 0) {
printf("Flash write error!\\r\\n");
flash_lock();
return 1;
}
flash_addr += packet_len;
total_received += packet_len;
/* 显示进度 */
if (file_size > 0) {
uint8_t percent = (total_received * 100) / file_size;
printf("Progress: %3d%%\\r", percent);
}
send_byte(ACK);
packet_num++;
}
errors = 0;
} else if (result == 2) {
/* 传输结束 */
send_byte(ACK);
printf("\\r\\nTransfer complete, received %lu bytes\\r\\n", total_received);
flash_lock();
return 0;
} else if (result == 3) {
/* 取消传输 */
printf("\\r\\nTransfer cancelled\\r\\n");
flash_lock();
return 1;
} else {
/* 错误 */
errors++;
if (errors > MAX_ERRORS) {
printf("\\r\\nToo many errors, aborting\\r\\n");
send_byte(CAN);
send_byte(CAN);
flash_lock();
return 1;
}
send_byte(NAK);
}
}
}
/* 获取文件名 */
char* ymodem_get_filename(void)
{
return (char *)file_name;
}
/* 获取文件大小 */
uint32_t ymodem_get_filesize(void)
{
return file_size;
}
📄 创建文件:
Core/Inc/ymodem.h
c
/*
* ymodem.h - YMODEM头文件
*/
#ifndef __YMODEM_H__
#define __YMODEM_H__
#include <stdint.h>
/* YMODEM接收 */
uint8_t ymodem_receive(void);
/* 获取文件名 */
char* ymodem_get_filename(void);
/* 获取文件大小 */
uint32_t ymodem_get_filesize(void);
#endif /* __YMODEM_H__ */
4.3 应用程序跳转
📄 创建文件:
Core/Src/app_jump.c
c
/*
* app_jump.c - 应用程序跳转
*/
#include "app_jump.h"
#include "stm32f1xx_hal.h"
/* 应用程序起始地址 */
#define APP_START_ADDR 0x08004000
/* 检查应用程序有效性 */
uint8_t check_app_valid(void)
{
/* 检查栈顶地址是否合法 */
uint32_t stack_top = *(__IO uint32_t *)APP_START_ADDR;
/* 栈顶应在SRAM范围内(0x20000000 - 0x20005000) */
if ((stack_top & 0x2FFF0000) != 0x20000000) {
return 0;
}
/* 检查复位向量 */
uint32_t reset_vector = *(__IO uint32_t *)(APP_START_ADDR + 4);
if ((reset_vector & 0xFF000000) != 0x08000000) {
return 0;
}
return 1;
}
/* 跳转到应用程序 */
void jump_to_app(void)
{
uint32_t app_stack;
uint32_t app_reset_vector;
void (*app_reset_handler)(void);
printf("Jumping to application...\\r\\n");
/* 获取应用程序栈顶地址 */
app_stack = *(__IO uint32_t *)APP_START_ADDR;
/* 获取应用程序复位向量 */
app_reset_vector = *(__IO uint32_t *)(APP_START_ADDR + 4);
/* 创建函数指针 */
app_reset_handler = (void (*)(void))app_reset_vector;
/* 关闭中断 */
__disable_irq();
/* 关闭外设 */
HAL_DeInit();
/* 设置主堆栈指针 */
__set_MSP(app_stack);
/* 设置向量表偏移 */
SCB->VTOR = APP_START_ADDR;
/* 跳转到应用程序 */
app_reset_handler();
}
📄 创建文件:
Core/Inc/app_jump.h
c
/*
* app_jump.h - 应用程序跳转头文件
*/
#ifndef __APP_JUMP_H__
#define __APP_JUMP_H__
#include <stdint.h>
/* 检查应用程序有效性 */
uint8_t check_app_valid(void);
/* 跳转到应用程序 */
void jump_to_app(void);
#endif /* __APP_JUMP_H__ */
4.4 Bootloader主程序
📄 创建文件:
Core/Src/bootloader_main.c
c
/*
* bootloader_main.c - Bootloader主程序
*/
#include "main.h"
#include "flash_driver.h"
#include "ymodem.h"
#include "app_jump.h"
#include <stdio.h>
#include <string.h>
/* 版本信息 */
#define BOOTLOADER_VERSION "1.0.0"
/* 升级按键引脚 */
#define UPDATE_KEY_PORT GPIOA
#define UPDATE_KEY_PIN GPIO_PIN_0
/* 串口句柄 */
UART_HandleTypeDef huart1;
/* 重定向printf */
int _write(int file, char *ptr, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
return len;
}
/* 检查升级按键 */
static uint8_t check_update_key(void)
{
/* 读取按键状态 */
if (HAL_GPIO_ReadPin(UPDATE_KEY_PORT, UPDATE_KEY_PIN) == GPIO_PIN_RESET) {
HAL_Delay(50); /* 消抖 */
if (HAL_GPIO_ReadPin(UPDATE_KEY_PORT, UPDATE_KEY_PIN) == GPIO_PIN_RESET) {
return 1;
}
}
return 0;
}
/* 显示菜单 */
static void show_menu(void)
{
printf("\\r\\n");
printf("========================================\\r\\n");
printf(" STM32 Bootloader v%s\\r\\n", BOOTLOADER_VERSION);
printf("========================================\\r\\n");
printf(" 1. Update firmware (YMODEM)\\r\\n");
printf(" 2. Jump to application\\r\\n");
printf(" 3. Erase application\\r\\n");
printf(" 4. System info\\r\\n");
printf(" 5. Reboot\\r\\n");
printf("========================================\\r\\n");
printf("Select option: ");
}
/* 系统信息 */
static void show_info(void)
{
printf("\\r\\nSystem Information:\\r\\n");
printf(" MCU: STM32F103C8T6\\r\\n");
printf(" Flash: 64KB\\r\\n");
printf(" RAM: 20KB\\r\\n");
printf(" Bootloader: v%s\\r\\n", BOOTLOADER_VERSION);
printf(" App valid: %s\\r\\n", check_app_valid() ? "Yes" : "No");
/* 读取设备ID */
uint32_t id[3];
id[0] = HAL_GetUIDw0();
id[1] = HAL_GetUIDw1();
id[2] = HAL_GetUIDw2();
printf(" Device ID: %08X%08X%08X\\r\\n", id[0], id[1], id[2]);
}
/* 升级固件 */
static void update_firmware(void)
{
printf("\\r\\nStarting firmware update...\\r\\n");
if (ymodem_receive() == 0) {
printf("\\r\\nUpdate successful!\\r\\n");
/* 等待用户确认 */
printf("Press any key to jump to application...\\r\\n");
uint8_t ch;
HAL_UART_Receive(&huart1, &ch, 1, HAL_MAX_DELAY);
/* 检查并跳转 */
if (check_app_valid()) {
jump_to_app();
} else {
printf("Application invalid!\\r\\n");
}
} else {
printf("\\r\\nUpdate failed!\\r\\n");
}
}
/* 擦除应用程序 */
static void erase_application(void)
{
printf("\\r\\nErasing application...\\r\\n");
flash_unlock();
if (flash_erase_app() == 0) {
printf("Erase complete\\r\\n");
} else {
printf("Erase failed!\\r\\n");
}
flash_lock();
}
/* 主函数 */
int main(void)
{
uint8_t option;
/* 初始化HAL库 */
HAL_Init();
SystemClock_Config();
/* 初始化GPIO */
MX_GPIO_Init();
/* 初始化串口 */
MX_USART1_UART_Init();
printf("\\r\\n");
printf("Bootloader started\\r\\n");
/* 检查升级按键 */
if (!check_update_key()) {
/* 检查应用程序是否有效 */
if (check_app_valid()) {
printf("Application valid, jumping...\\r\\n");
HAL_Delay(500);
jump_to_app();
}
}
/* 进入升级模式 */
printf("Entering update mode\\r\\n");
while (1) {
show_menu();
/* 接收选项 */
HAL_UART_Receive(&huart1, &option, 1, HAL_MAX_DELAY);
printf("%c\\r\\n", option);
switch (option) {
case '1':
update_firmware();
break;
case '2':
if (check_app_valid()) {
jump_to_app();
} else {
printf("\\r\\nNo valid application!\\r\\n");
}
break;
case '3':
erase_application();
break;
case '4':
show_info();
break;
case '5':
printf("\\r\\nRebooting...\\r\\n");
HAL_NVIC_SystemReset();
break;
default:
printf("\\r\\nInvalid option!\\r\\n");
break;
}
}
}
/* GPIO初始化 */
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/* 配置升级按键 */
GPIO_InitStruct.Pin = UPDATE_KEY_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(UPDATE_KEY_PORT, &GPIO_InitStruct);
/* 配置LED */
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
/* 串口初始化 */
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1);
}
4.5 应用程序配置
应用程序需要配置中断向量表偏移:
📄 在应用程序的
system_stm32f1xx.c中修改:
c
/* 向量表偏移 */
#define VECT_TAB_OFFSET 0x4000 /* 16KB偏移 */
或在 main.c 中添加:
c
/* 设置向量表偏移 */
SCB->VTOR = 0x08004000;
五、编译与下载
5.1 编译Bootloader
- 配置Flash起始地址为0x08000000,大小16KB
- 编译工程
5.2 编译应用程序
- 创建新工程,配置Flash起始地址为0x08004000
- 添加向量表偏移配置
- 编译生成bin文件
5.3 使用SecureCRT升级
- 连接串口(115200波特率)
- 按住升级按键上电
- 选择菜单选项1
- 在SecureCRT中选择
Transfer -> Send YMODEM - 选择应用程序bin文件
- 等待传输完成
六、故障排查与问题解决
6.1 跳转失败
原因分析:
- 向量表未正确设置
- 中断未关闭
- 外设未复位
解决方案:
c
/* 确保正确关闭中断和复位外设 */
__disable_irq();
HAL_DeInit();
__set_MSP(app_stack);
SCB->VTOR = APP_START_ADDR;
6.2 Flash写入失败
原因分析:
- Flash未解锁
- 页未擦除
- 写入地址不对齐
解决方案:
c
/* 确保解锁和擦除 */
flash_unlock();
flash_erase_page(addr);
flash_write_halfword(addr, data);
flash_lock();
七、总结
7.1 核心知识点回顾
- Bootloader设计:理解启动流程和分区规划
- Flash操作:掌握Flash的擦除和编程
- YMODEM协议:实现可靠的文件传输
- 程序跳转:理解向量表和中断处理
7.2 扩展学习方向
- 加密升级:实现固件加密和签名验证
- 双备份机制:实现A/B分区升级
- 网络升级:通过以太网或WiFi升级
- USB升级:通过USB Mass Storage升级
7.3 学习资源
官方文档: