一、系统架构设计
┌─────────────────────────────────────────────────────────────┐
│ S12 Bootloader 系统架构 │
├─────────────────────────────────────────────────────────────┤
│ 引导加载程序 │ 通信接口 │ Flash操作 │ 应用程序 │
│ │ │ │ │
│ • 启动检测 │ • SCI串口 │ • 扇区擦除 │ • 用户程序 │
│ • 模式选择 │ • CAN总线 │ • 字节编程 │ • 中断向量 │
│ • 超时控制 │ • LIN总线 │ • 保护机制 │ • 主函数 │
│ • 状态指示 │ • 以太网 │ • 校验验证 │ • 外设驱动 │
└─────────────────────────────────────────────────────────────┘
二、硬件平台配置
2.1 目标芯片:MC9S12XEP100
主要特性:
• CPU:16位HCS12X内核,50MHz主频
• Flash:1MB,分页管理
• RAM:64KB
• EEPROM:4KB
• 通信接口:SCI×2, SPI×3, I2C, CAN×5
• 定时器:8通道16位定时器
2.2 内存映射
Bootloader区域:0xE000 - 0xFFFF (8KB保护区域)
应用程序区域:0x4000 - 0xDFFF (用户Flash)
EEPROM区域: 0x0000 - 0x0FFF (配置存储)
RAM区域: 0x1000 - 0x3FFF (堆栈和数据)
三、实现
3.1 Bootloader 主程序 (bootloader.c)
c
/**
* @file bootloader.c
* @brief Freescale S12 Bootloader 主程序
* @platform MC9S12XEP100
*/
#include "bootloader.h"
#include "sci.h"
#include "flash.h"
#include "can.h"
#include "timer.h"
#include "watchdog.h"
// Bootloader配置
#define BOOTLOADER_VERSION 0x0100 // V1.0
#define BOOTLOADER_TIMEOUT 5000 // 5秒超时
#define APP_START_ADDRESS 0x4000 // 应用程序起始地址
#define BOOTLOADER_MAGIC 0xAA55 // 启动魔数
// 启动模式枚举
typedef enum {
BOOT_MODE_NORMAL = 0, // 正常启动
BOOT_MODE_FORCE, // 强制进入Bootloader
BOOT_MODE_UPDATE, // 固件更新模式
BOOT_MODE_ERROR // 错误模式
} BootMode_t;
// 通信协议命令
typedef enum {
CMD_GET_VERSION = 0x01, // 获取版本
CMD_ERASE_FLASH = 0x02, // 擦除Flash
CMD_WRITE_FLASH = 0x03, // 写入Flash
CMD_READ_FLASH = 0x04, // 读取Flash
CMD_VERIFY_FLASH = 0x05, // 验证Flash
CMD_JUMP_TO_APP = 0x06, // 跳转到应用
CMD_RESET = 0x07, // 复位系统
CMD_SET_ADDRESS = 0x08, // 设置地址
CMD_GET_STATUS = 0x09 // 获取状态
} BootCommand_t;
// 系统状态结构体
typedef struct {
BootMode_t boot_mode;
uint16_t boot_counter;
uint32_t current_address;
uint32_t bytes_received;
uint8_t update_in_progress;
uint8_t error_code;
} BootStatus_t;
// 全局变量
static BootStatus_t boot_status;
static uint8_t rx_buffer[256];
static uint16_t rx_index = 0;
// 函数声明
static void Bootloader_Init(void);
static void Bootloader_CheckMode(void);
static void Bootloader_ProcessCommand(void);
static void Bootloader_JumpToApplication(void);
static void Bootloader_UpdateStatus(void);
static void Bootloader_ErrorHandler(uint8_t error);
/**
* @brief 主函数
*/
void main(void) {
// 关闭看门狗
Watchdog_Disable();
// 初始化系统时钟
SystemClock_Init();
// 初始化Bootloader
Bootloader_Init();
// 检查启动模式
Bootloader_CheckMode();
// 主循环
while(1) {
// 处理通信命令
Bootloader_ProcessCommand();
// 更新状态
Bootloader_UpdateStatus();
// 检查超时
if(boot_status.boot_counter > BOOTLOADER_TIMEOUT) {
if(boot_status.boot_mode == BOOT_MODE_NORMAL) {
Bootloader_JumpToApplication();
}
}
// 延时
Delay_Ms(10);
}
}
/**
* @brief 初始化Bootloader
*/
static void Bootloader_Init(void) {
// 初始化状态
boot_status.boot_mode = BOOT_MODE_NORMAL;
boot_status.boot_counter = 0;
boot_status.current_address = APP_START_ADDRESS;
boot_status.bytes_received = 0;
boot_status.update_in_progress = 0;
boot_status.error_code = 0;
// 初始化硬件
Timer_Init();
SCI_Init(115200);
CAN_Init(500000);
Flash_Init();
LED_Init();
// 检查EEPROM中的启动标志
uint16_t magic = EEPROM_ReadWord(BOOT_MAGIC_ADDRESS);
if(magic == BOOTLOADER_MAGIC) {
boot_status.boot_mode = BOOT_MODE_FORCE;
EEPROM_WriteWord(BOOT_MAGIC_ADDRESS, 0x0000); // 清除标志
}
// 显示启动信息
SCI_SendString("S12 Bootloader v1.0 Ready\r\n");
SCI_SendString("Press any key within 5 seconds to enter update mode...\r\n");
}
/**
* @brief 检查启动模式
*/
static void Bootloader_CheckMode(void) {
uint8_t key_pressed = 0;
// 检测按键或串口活动
for(uint16_t i = 0; i < BOOTLOADER_TIMEOUT/10; i++) {
if(SCI_DataAvailable()) {
key_pressed = 1;
break;
}
Delay_Ms(10);
boot_status.boot_counter += 10;
// LED闪烁指示
if(i % 100 == 0) {
LED_Toggle();
}
}
if(key_pressed || boot_status.boot_mode == BOOT_MODE_FORCE) {
boot_status.boot_mode = BOOT_MODE_UPDATE;
SCI_SendString("Entering Firmware Update Mode...\r\n");
LED_On();
} else {
boot_status.boot_mode = BOOT_MODE_NORMAL;
SCI_SendString("Booting Application...\r\n");
}
}
/**
* @brief 处理通信命令
*/
static void Bootloader_ProcessCommand(void) {
if(!SCI_DataAvailable()) {
return;
}
uint8_t cmd = SCI_ReadByte();
uint8_t response[4];
switch(cmd) {
case CMD_GET_VERSION:
response[0] = BOOTLOADER_VERSION >> 8;
response[1] = BOOTLOADER_VERSION & 0xFF;
SCI_SendData(response, 2);
break;
case CMD_ERASE_FLASH:
if(Flash_EraseSector(boot_status.current_address)) {
SCI_SendByte(0xAA); // 成功
} else {
SCI_SendByte(0xFF); // 失败
Bootloader_ErrorHandler(0x01);
}
break;
case CMD_WRITE_FLASH:
if(rx_index >= 4) {
uint32_t address = (rx_buffer[0] << 24) | (rx_buffer[1] << 16) |
(rx_buffer[2] << 8) | rx_buffer[3];
uint8_t data = rx_buffer[4];
if(Flash_WriteByte(address, data)) {
SCI_SendByte(0xAA);
boot_status.bytes_received++;
} else {
SCI_SendByte(0xFF);
Bootloader_ErrorHandler(0x02);
}
}
break;
case CMD_SET_ADDRESS:
if(rx_index >= 4) {
boot_status.current_address = (rx_buffer[0] << 24) | (rx_buffer[1] << 16) |
(rx_buffer[2] << 8) | rx_buffer[3];
SCI_SendByte(0xAA);
}
break;
case CMD_JUMP_TO_APP:
Bootloader_JumpToApplication();
break;
case CMD_RESET:
SCI_SendString("System Reset...\r\n");
asm("jmp 0xFFFE"); // 跳转到复位向量
break;
default:
SCI_SendByte(0xFE); // 未知命令
break;
}
rx_index = 0;
}
/**
* @brief 跳转到应用程序
*/
static void Bootloader_JumpToApplication(void) {
SCI_SendString("Jumping to Application...\r\n");
// 禁用中断
asm("sei");
// 关闭外设
SCI_Disable();
CAN_Disable();
Timer_Disable();
LED_Off();
// 设置应用程序栈指针
uint16_t app_sp = *(uint16_t*)APP_START_ADDRESS;
asm("lds %0" :: "m"(app_sp));
// 跳转到应用程序入口点
uint16_t app_entry = *(uint16_t*)(APP_START_ADDRESS + 2);
asm("jmp %0" :: "m"(app_entry));
}
/**
* @brief 更新状态显示
*/
static void Bootloader_UpdateStatus(void) {
if(boot_status.boot_mode == BOOT_MODE_UPDATE) {
static uint8_t led_state = 0;
if(++boot_status.boot_counter % 100 == 0) {
led_state = !led_state;
if(led_state) LED_On();
else LED_Off();
}
}
}
/**
* @brief 错误处理
*/
static void Bootloader_ErrorHandler(uint8_t error) {
boot_status.error_code = error;
SCI_SendString("Bootloader Error!\r\n");
LED_Blink(100); // 快速闪烁表示错误
}
3.2 Flash 操作驱动 (flash.c)
c
/**
* @file flash.c
* @brief S12 Flash 操作驱动
*/
#include "flash.h"
#include "derivative.h"
// Flash命令定义
#define FCMD_ERASE_SECTOR 0x40
#define FCMD_PROGRAM_BYTE 0x20
#define FCMD_PROGRAM_WORD 0x60
#define FCMD_ERASE_ALL 0x41
// Flash状态寄存器位
#define FSTAT_CCIF 0x80 // 命令完成中断标志
#define FSTAT_ACCERR 0x20 // 访问错误
#define FSTAT_PVIOL 0x10 // 保护违例
#define FSTAT_MGSTAT 0x03 // 命令完成状态
// Flash时钟分频
#define FLASH_CLOCK_DIV 16 // 假设总线时钟16MHz,Flash时钟1MHz
static uint8_t Flash_WaitComplete(void) {
uint16_t timeout = 0xFFFF;
while(!(FSTAT & FSTAT_CCIF) && timeout--) {
asm("nop");
}
if(timeout == 0) {
return 0; // 超时
}
// 检查错误标志
if(FSTAT & (FSTAT_ACCERR | FSTAT_PVIOL)) {
FSTAT = (FSTAT_ACCERR | FSTAT_PVIOL); // 清除错误标志
return 0;
}
return 1; // 成功
}
/**
* @brief 初始化Flash模块
*/
void Flash_Init(void) {
// 设置Flash时钟分频
FCLKDIV = FLASH_CLOCK_DIV - 1;
// 等待时钟稳定
while(!(FCLKDIV & 0x80));
// 清除错误标志
FSTAT = (FSTAT_ACCERR | FSTAT_PVIOL);
}
/**
* @brief 擦除Flash扇区
* @param address 扇区起始地址
* @return 成功返回1,失败返回0
*/
uint8_t Flash_EraseSector(uint32_t address) {
// 检查地址对齐
if(address & 0x03FF) {
return 0; // 必须是1KB边界
}
// 检查是否在Bootloader保护区
if(address >= 0xE000) {
return 0; // 不能擦除Bootloader区域
}
// 等待上一条命令完成
if(!Flash_WaitComplete()) {
return 0;
}
// 设置命令地址
FCCOBIX = 0x00;
FCCOB = 0x0000 | (address >> 16); // 页地址
FCCOBIX = 0x01;
FCCOB = (uint16_t)address; // 页内偏移
// 设置擦除命令
FCCOBIX = 0x02;
FCCOB = FCMD_ERASE_SECTOR;
// 执行命令
FSTAT = FSTAT_CCIF; // 启动命令
return Flash_WaitComplete();
}
/**
* @brief 写入单个字节到Flash
* @param address 写入地址
* @param data 写入数据
* @return 成功返回1,失败返回0
*/
uint8_t Flash_WriteByte(uint32_t address, uint8_t data) {
// 检查是否在Bootloader保护区
if(address >= 0xE000) {
return 0; // 不能写入Bootloader区域
}
// 等待上一条命令完成
if(!Flash_WaitComplete()) {
return 0;
}
// 设置命令地址
FCCOBIX = 0x00;
FCCOB = 0x0000 | (address >> 16);
FCCOBIX = 0x01;
FCCOB = (uint16_t)address;
// 设置写入数据
FCCOBIX = 0x02;
FCCOB = FCMD_PROGRAM_BYTE;
FCCOBIX = 0x03;
FCCOB = data;
// 执行命令
FSTAT = FSTAT_CCIF;
return Flash_WaitComplete();
}
/**
* @brief 写入多个字节到Flash
* @param address 起始地址
* @param data 数据缓冲区
* @param length 数据长度
* @return 成功写入的字节数
*/
uint16_t Flash_WriteBuffer(uint32_t address, uint8_t *data, uint16_t length) {
uint16_t written = 0;
for(uint16_t i = 0; i < length; i++) {
if(!Flash_WriteByte(address + i, data[i])) {
break;
}
written++;
}
return written;
}
/**
* @brief 擦除整个应用程序区域
* @return 成功返回1,失败返回0
*/
uint8_t Flash_EraseApplication(void) {
uint32_t address = APP_START_ADDRESS;
uint32_t end_address = 0xDFFF; // 应用程序结束地址
while(address < end_address) {
if(!Flash_EraseSector(address)) {
return 0;
}
address += 1024; // 下一个1KB扇区
}
return 1;
}
/**
* @brief 验证Flash内容
* @param address 起始地址
* @param data 期望数据
* @param length 数据长度
* @return 匹配返回1,不匹配返回0
*/
uint8_t Flash_Verify(uint32_t address, uint8_t *data, uint16_t length) {
for(uint16_t i = 0; i < length; i++) {
uint8_t flash_data = *(uint8_t*)(address + i);
if(flash_data != data[i]) {
return 0;
}
}
return 1;
}
3.3 SCI 串口通信驱动 (sci.c)
c
/**
* @file sci.c
* @brief S12 SCI 串口通信驱动
*/
#include "sci.h"
#include "derivative.h"
// SCI波特率计算
#define SCI_BAUD_DIV(baud) ((16000000UL / 16 / (baud)) - 1)
/**
* @brief 初始化SCI模块
* @param baudrate 波特率
*/
void SCI_Init(uint32_t baudrate) {
uint16_t divisor = SCI_BAUD_DIV(baudrate);
// 禁用SCI
SCICR1 = 0x00;
SCICR2 = 0x00;
// 设置波特率
SCIBDH = (divisor >> 8) & 0xFF;
SCIBDL = divisor & 0xFF;
// 配置SCI参数
SCICR1 = 0x00; // 8位数据,无奇偶校验,1位停止位
// 使能SCI
SCICR2 = 0x2C; // 使能发送、接收、接收中断
}
/**
* @brief 发送单个字节
* @param data 发送的数据
*/
void SCI_SendByte(uint8_t data) {
while(!(SCISR1 & 0x80)); // 等待发送缓冲区空
SCIDRL = data;
}
/**
* @brief 接收单个字节
* @return 接收的数据
*/
uint8_t SCI_ReadByte(void) {
while(!(SCISR1 & 0x20)); // 等待接收数据
return SCIDRL;
}
/**
* @brief 发送字符串
* @param str 字符串指针
*/
void SCI_SendString(char *str) {
while(*str) {
SCI_SendByte(*str++);
}
}
/**
* @brief 发送数据缓冲区
* @param data 数据缓冲区
* @param length 数据长度
*/
void SCI_SendData(uint8_t *data, uint16_t length) {
for(uint16_t i = 0; i < length; i++) {
SCI_SendByte(data[i]);
}
}
/**
* @brief 检查是否有数据可读
* @return 有数据返回1,无数据返回0
*/
uint8_t SCI_DataAvailable(void) {
return (SCISR1 & 0x20) ? 1 : 0;
}
/**
* @brief 禁用SCI
*/
void SCI_Disable(void) {
SCICR2 = 0x00;
}
// SCI接收中断服务函数
interrupt VectorNumber_Vsci0 void SCI0_ISR(void) {
uint8_t status = SCISR1;
if(status & 0x20) { // 接收中断
uint8_t data = SCIDRL;
// 将接收到的数据存入缓冲区
if(rx_index < sizeof(rx_buffer)) {
rx_buffer[rx_index++] = data;
}
}
}
3.4 CAN 总线通信驱动 (can.c)
c
/**
* @file can.c
* @brief S12 CAN 总线通信驱动
*/
#include "can.h"
#include "derivative.h"
// CAN波特率设置(假设总线时钟16MHz)
#define CAN_BAUD_500K 0x1C // 500kbps
#define CAN_BAUD_250K 0x39 // 250kbps
#define CAN_BAUD_125K 0x73 // 125kbps
// CAN消息结构体
typedef struct {
uint32_t id;
uint8_t dlc;
uint8_t data[8];
} CAN_Message_t;
static CAN_Message_t rx_message;
static uint8_t can_initialized = 0;
/**
* @brief 初始化CAN模块
* @param baudrate 波特率
*/
void CAN_Init(uint32_t baudrate) {
uint8_t baud_cfg;
// 选择波特率
switch(baudrate) {
case 500000: baud_cfg = CAN_BAUD_500K; break;
case 250000: baud_cfg = CAN_BAUD_250K; break;
case 125000: baud_cfg = CAN_BAUD_125K; break;
default: baud_cfg = CAN_BAUD_500K; break;
}
// 进入初始化模式
CANCTL0 = 0x01; // INITRQ = 1
while(!(CANCTL1 & 0x01)); // 等待初始化模式确认
// 配置CAN控制器
CANCTL1 = 0x80; // 使能CAN模块
CANBTR0 = 0x01; // 同步跳转宽度=1
CANBTR1 = baud_cfg; // 设置波特率
// 配置验收滤波器(接收所有消息)
CANIDAC = 0x10; // 32位验收滤波器
CANIDAR0 = 0x00; // 验收码寄存器
CANIDAR1 = 0x00;
CANIDAR2 = 0x00;
CANIDAR3 = 0x00;
CANIDMR0 = 0x00; // 验收掩码寄存器(全0表示接收所有)
CANIDMR1 = 0x00;
CANIDMR2 = 0x00;
CANIDMR3 = 0x00;
// 退出初始化模式
CANCTL0 = 0x00; // INITRQ = 0
while(CANCTL1 & 0x01); // 等待退出初始化模式
can_initialized = 1;
}
/**
* @brief 发送CAN消息
* @param msg 消息结构体指针
* @return 成功返回1,失败返回0
*/
uint8_t CAN_SendMessage(CAN_Message_t *msg) {
uint8_t tx_buffer;
if(!can_initialized) {
return 0;
}
// 查找空闲的发送缓冲区
if(CANTFLG & 0x01) {
tx_buffer = 0;
} else if(CANTFLG & 0x02) {
tx_buffer = 1;
} else if(CANTFLG & 0x04) {
tx_buffer = 2;
} else {
return 0; // 没有空闲缓冲区
}
// 设置消息ID
if(msg->id & 0x80000000) {
// 扩展帧
CANTIDR0[tx_buffer] = (msg->id >> 21) & 0xFF;
CANTIDR1[tx_buffer] = ((msg->id >> 13) & 0xFF) | 0x08; // SRR=1
CANTIDR2[tx_buffer] = (msg->id >> 5) & 0xFF;
CANTIDR3[tx_buffer] = (msg->id << 3) & 0xF8;
} else {
// 标准帧
CANTIDR0[tx_buffer] = (msg->id >> 3) & 0xFF;
CANTIDR1[tx_buffer] = (msg->id << 5) & 0xE0;
}
// 设置数据长度
CANTDSR[tx_buffer] = msg->dlc & 0x0F;
// 设置数据
for(uint8_t i = 0; i < msg->dlc && i < 8; i++) {
CANTCDR[tx_buffer][i] = msg->data[i];
}
// 启动发送
CANTFLG = (1 << tx_buffer);
return 1;
}
/**
* @brief 接收CAN消息
* @param msg 消息结构体指针
* @return 成功返回1,失败返回0
*/
uint8_t CAN_ReceiveMessage(CAN_Message_t *msg) {
uint8_t rx_buffer;
if(!can_initialized) {
return 0;
}
// 检查是否有接收消息
if(!(CANRFLG & 0x01)) {
return 0; // 没有消息
}
rx_buffer = 0; // 使用接收缓冲区0
// 读取消息ID
msg->id = ((uint32_t)CANRIDR0[rx_buffer] << 21) |
((uint32_t)(CANRIDR1[rx_buffer] & 0xE0) << 13) |
((uint32_t)(CANRIDR2[rx_buffer]) << 5) |
((uint32_t)(CANRIDR3[rx_buffer] >> 3));
// 读取数据长度
msg->dlc = CANRDSR[rx_buffer] & 0x0F;
// 读取数据
for(uint8_t i = 0; i < msg->dlc && i < 8; i++) {
msg->data[i] = CANRCDR[rx_buffer][i];
}
// 清除接收标志
CANRFLG = 0x01;
return 1;
}
/**
* @brief 禁用CAN
*/
void CAN_Disable(void) {
if(can_initialized) {
CANCTL1 = 0x00; // 禁用CAN模块
can_initialized = 0;
}
}
3.5 应用程序启动代码 (startup.s)
assembly
; startup.s - S12 应用程序启动代码
; 这个文件应该放在应用程序工程中
; 定义常量
APP_STACK_TOP EQU 0x3FFF ; RAM顶部地址
APP_VECTOR_TABLE EQU 0x4000 ; 应用程序中断向量表地址
; 中断向量表重映射
ORG APP_VECTOR_TABLE
; 复位向量
DC.W _Startup
; 其他中断向量...
DC.W SCI0_ISR
DC.W CAN0_ISR
DC.W Timer0_ISR
; ... 更多中断向量
; 启动代码
_Startup:
; 初始化栈指针
LDS #APP_STACK_TOP
; 关闭看门狗
MOVB #0x00, COPCTL
; 初始化时钟
JSR Clock_Init
; 初始化外设
JSR Peripheral_Init
; 跳转到主函数
JSR main
; 无限循环
Forever:
BRA Forever
; 时钟初始化函数
Clock_Init:
; 设置时钟分频
MOVB #0x00, CLKSEL
MOVB #0x00, PLLCTL
; 配置PLL...
RTS
; 外设初始化函数
Peripheral_Init:
; 初始化SCI, CAN, 定时器等
RTS
; 中断服务例程
SCI0_ISR:
; 保存上下文
PSHD
PSHX
PSHY
; 处理SCI中断
; ...
; 恢复上下文
PULY
PULX
PULD
RTI
CAN0_ISR:
; 保存上下文
PSHD
PSHX
PSHY
; 处理CAN中断
; ...
; 恢复上下文
PULY
PULX
PULD
RTI
Timer0_ISR:
; 保存上下文
PSHD
PSHX
PSHY
; 处理定时器中断
; ...
; 恢复上下文
PULY
PULX
PULD
RTI
四、链接脚本配置
4.1 Bootloader 链接脚本 (bootloader.prm)
prm
/* bootloader.prm - Bootloader 链接脚本 */
NAMES END
SECTIONS
ROM_4000 = READ_ONLY 0x4000 TO 0xDFFF; /* 应用程序区域 */
ROM_E000 = READ_ONLY 0xE000 TO 0xFFFF; /* Bootloader区域 */
RAM_1000 = READ_WRITE 0x1000 TO 0x3FFF; /* RAM区域 */
END
PLACEMENT
DEFAULT_ROM INTO ROM_4000;
BOOTLOADER_ROM INTO ROM_E000;
DEFAULT_RAM INTO RAM_1000;
END
STACKSIZE 0x0200 /* 512字节栈 */
4.2 应用程序链接脚本 (application.prm)
prm
/* application.prm - 应用程序链接脚本 */
NAMES END
SECTIONS
ROM_4000 = READ_ONLY 0x4000 TO 0xDFFF; /* 应用程序区域 */
RAM_1000 = READ_WRITE 0x1000 TO 0x3FFF; /* RAM区域 */
END
PLACEMENT
DEFAULT_ROM INTO ROM_4000;
DEFAULT_RAM INTO RAM_1000;
END
STACKSIZE 0x0400 /* 1KB栈 */
参考代码 基于Freescale S12单片机的Bootloader开发源代码 www.youwenfan.com/contentcsu/60566.html
五、编译与烧录
5.1 编译环境
开发工具:CodeWarrior Development Studio for S12(X)
编译器:HIWARE Compiler
调试器:P&E Multilink Universal
5.2 烧录步骤
- 烧录Bootloader:使用P&E Multilink将Bootloader烧录到0xE000-0xFFFF区域
- 设置保护:配置Flash保护位,防止意外擦除Bootloader
- 测试Bootloader:通过串口发送命令,验证Bootloader功能
- 烧录应用程序:使用Bootloader通过SCI或CAN烧录应用程序
5.3 测试命令序列
bash
# 1. 获取版本
发送: 0x01
接收: 0x01 0x00 (V1.0)
# 2. 设置写入地址
发送: 0x08 0x00 0x00 0x40 0x00
接收: 0xAA
# 3. 擦除Flash
发送: 0x02
接收: 0xAA
# 4. 写入数据
发送: 0x03 0x00 0x00 0x40 0x00 0x12
接收: 0xAA
# 5. 跳转到应用程序
发送: 0x06
六、常见问题解决
| 问题 | 原因 | 解决方案 |
|---|---|---|
| Bootloader无法启动 | 时钟配置错误 | 检查晶振和PLL设置 |
| Flash擦除失败 | 保护位设置 | 清除Flash保护位 |
| 通信无响应 | 波特率不匹配 | 确认双方波特率一致 |
| 应用程序无法运行 | 中断向量表错误 | 正确重映射中断向量 |
| 烧录过程中断 | 看门狗复位 | 在烧录期间禁用看门狗 |