基于 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保护位
通信无响应 波特率不匹配 确认双方波特率一致
应用程序无法运行 中断向量表错误 正确重映射中断向量
烧录过程中断 看门狗复位 在烧录期间禁用看门狗
相关推荐
JNX_SEMI7 小时前
EG2031L:220V半桥驱动,1.5A灌流,宽压5V供电
单片机·嵌入式硬件
m0_377108148 小时前
stm32-SPI
stm32·单片机·嵌入式硬件
QiLinkOS8 小时前
从技术到资产的跃迁:企业专利布局的深层逻辑
c语言·数据结构·c++·单片机·嵌入式硬件·算法·开源
夜听莺儿鸣8 小时前
201_002 Zynq7000 SoC PS资源介绍
嵌入式硬件·硬件架构
wohoo_wangzi9 小时前
苏州晟雅泰电子:关于汽车领域会用到的5类存储芯片,容量参数、设计方案和主要应用场景
嵌入式硬件·汽车
踏着七彩祥云的小丑9 小时前
嵌入式测试学习第 22 天:仿真看简易电路,熟悉电路运行逻辑
单片机·嵌入式硬件
czhaii10 小时前
基于51单片机的Modbus从机通信系统
开发语言·单片机
普中科技11 小时前
【普中STM32F1xx开发攻略--标准库版】-- 第 40 章 FSMC-TFTLCD 显示实验
stm32·单片机·嵌入式硬件·fsmc·开发板·tftlcd·普中科技
woohuwan11 小时前
功率线与信号线共模电感的核心区别
嵌入式硬件
LCG元12 小时前
STM32实战:基于STM32F103的智能衣柜(除湿+防霉+照明)
stm32·单片机·嵌入式硬件