基于 Freescale S12 单片机的 Bootloader 开发

一、系统架构设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    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 烧录步骤

  1. 烧录Bootloader:使用P&E Multilink将Bootloader烧录到0xE000-0xFFFF区域
  2. 设置保护:配置Flash保护位,防止意外擦除Bootloader
  3. 测试Bootloader:通过串口发送命令,验证Bootloader功能
  4. 烧录应用程序:使用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保护位
通信无响应 波特率不匹配 确认双方波特率一致
应用程序无法运行 中断向量表错误 正确重映射中断向量
烧录过程中断 看门狗复位 在烧录期间禁用看门狗
相关推荐
笨笨小乌龟111 小时前
单片机的半主机模式与 MicroLib 机制(Keil UseMicroLIB)
stm32·单片机·嵌入式硬件
非鱼䲆鱻䲜1 小时前
数模电数控电源(0—9.9v)
嵌入式硬件·multisim·数模电·嘉立创
旧梦吟5 小时前
5.9 电工考试-易错题
stm32·嵌入式硬件
foundbug9995 小时前
STM32 + SHT20 温湿度测试 TFT 显示方案
stm32·单片机·嵌入式硬件
别了,李亚普诺夫6 小时前
MAX30102模块原理及代码实现
单片机·嵌入式
星夜夏空996 小时前
STM32单片机学习(3)——前置知识学习
stm32·单片机·学习
渣渣灰95877 小时前
基于STM32F03ZET6移植FreeRTOS
数据库·stm32·嵌入式硬件
magic_now7 小时前
FAT文件系统:嵌入式设备的极简选择
笔记·嵌入式硬件
星夜夏空997 小时前
STM32单片机学习(5) —— STM32的一些名词解释
stm32·单片机·学习