基于CAN总线的STM32F103 BootLoader实现方案

一、硬件配置方案

c 复制代码
// CAN初始化关键代码(CubeMX生成优化版)
void MX_CAN1_Init(void)
{
    CAN_FilterTypeDef sFilterConfig = {0};
    
    // 使能CAN和GPIO时钟
    __HAL_RCC_CAN1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    // 配置CAN引脚(PA11-RX, PA12-TX)
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    // CAN基本配置
    hcan1.Instance = CAN1;
    hcan1.Init.Prescaler = 9;          // 72MHz/9=8MHz → 500kbps
    hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
    hcan1.Init.TimeSeg1 = CAN_BS1_8TQ;
    hcan1.Init.TimeSeg2 = CAN_BS2_3TQ;
    hcan1.Init.TimeTriggeredMode = DISABLE;
    hcan1.Init.AutoBusOff = ENABLE;
    hcan1.Init.AutoWakeUp = ENABLE;
    hcan1.Init.AutoRetransmission = ENABLE;
    hcan1.Init.ReceiveFifoLocked = DISABLE;
    hcan1.Init.TransmitFifoPriority = DISABLE;
    HAL_CAN_Init(&hcan1);

    // 过滤器配置(接收所有消息)
    sFilterConfig.FilterBank = 0;
    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
    sFilterConfig.FilterIdHigh = 0x0000;
    sFilterConfig.FilterIdLow = 0x0000;
    sFilterConfig.FilterMaskIdHigh = 0x0000;
    sFilterConfig.FilterMaskIdLow = 0x0000;
    HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig);
}

二、CAN通信协议设计

1. 消息格式定义
c 复制代码
typedef struct {
    uint32_t magic;      // 魔数0xA5A5A5A5
    uint8_t  cmd;        // 命令码:0x01=擦除,0x02=写入,0x03=跳转
    uint16_t addr;       // 目标地址
    uint16_t len;        // 数据长度
    uint8_t  data[256];  // 数据负载
    uint16_t crc;        // CRC16校验
} can_frame_t;
2. 关键通信流程
c 复制代码
sequenceDiagram
    participant Bootloader
    participant Host
    participant App

    Host->>Bootloader: 发送擦除命令(0x01)
    Bootloader->>Flash: 擦除APP区域(0x08008000-0x0801FFFF)
    Bootloader-->>Host: 返回ACK(0x06)

    Host->>Bootloader: 分块发送数据(0x02)
    Bootloader->>Flash: 写入数据(每包≤256字节)
    Bootloader-->>Host: 返回ACK(0x06)或NACK(0x15)

    Host->>Bootloader: 发送跳转命令(0x03)
    Bootloader->>Bootloader: 验证APP校验和
    Bootloader->>App: 设置VTOR寄存器
    Bootloader->>App: 跳转执行

三、BootLoader工程实现

1. 启动流程优化
c 复制代码
void Bootloader_Main(void)
{
    // 1. 禁用中断
    __disable_irq();

    // 2. 检查升级标志
    if(READ_REG(FLASH->OPTCR) & 0x01) {
        Jump_To_App(APP_ADDRESS);
    }

    // 3. 初始化CAN
    MX_CAN1_Init();
    CAN_Filter_Config();

    // 4. 进入接收循环
    while(1) {
        if(CAN_Message_Received()) {
            Process_CAN_Frame();
        }
    }
}
2. 安全校验机制
c 复制代码
// CRC16校验实现
uint16_t Calculate_CRC(uint8_t *data, uint16_t len) {
    uint16_t crc = 0xFFFF;
    for(uint16_t i=0; i<len; i++) {
        crc ^= (uint16_t)data[i] << 8;
        for(uint8_t j=0; j<8; j++) {
            crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : crc << 1;
        }
    }
    return crc;
}

// 安全校验流程
if(Verify_CRC(frame.data, frame.len) != frame.crc) {
    Send_Error(0x15); // CRC错误
    return;
}

四、App工程适配

1. 中断向量表重定向
c 复制代码
// 在system_stm32f1xx.c中修改
#define VECT_TAB_OFFSET  0x8000  // Bootloader占用前32KB

// 在App启动文件中添加
extern uint32_t _estack_app;
__attribute__((section(".isr_vector")))
uint32_t *isr_vector[] = {
    &_estack_app,
    (uint32_t*)APP_Reset_Handler,
    // 其他中断向量...
};
2. 双工程编译配置
c 复制代码
# Bootloader Makefile片段
BOOTLOADER_SRCS = startup_stm32f103xb.s can_driver.c flash_ops.c
BOOTLOADER_OBJS = $(BOOTLOADER_SRCS:.c=.o)

# App工程Makefile片段
APP_SRCS = main.c app_logic.c
APP_OBJS = $(APP_SRCS:.c=.o)

五、上位机程序设计(Qt示例)

c 复制代码
// CANBootloader.pro配置
QT += core gui serialbus

// 通信线程实现
void CanWorker::run() {
    QCanBusDevice *device = QCanBus::instance()->createDevice("can0", this);
    if(!device) return;

    connect(device, &QCanBusDevice::framesReceived, [=](quint32 frameId) {
        QCanBusFrame frame = device->receive();
        processFrame(frame);
    });
}

// 数据发送逻辑
void MainWindow::on_sendButton_clicked() {
    QByteArray data = ui->dataEdit->toPlainText().toUtf8();
    QCanBusFrame frame(0x18E00001); // 标准ID
    frame.setPayload(data);
    canDevice->transmit(frame);
}

参考代码 基于CAN总线的 STM32F103 的BootLoader,上位机+下位机程序 www.youwenfan.com/contentcss/69866.html

六、量产注意事项

  1. Flash保护策略

    c 复制代码
    // 启用写保护
    FLASH_OB_WRP(ENABLE, 0x000000FF); // 保护前1KB
  2. 版本回滚机制

    c 复制代码
    #define MAX_RETRIES 3
    if(flash_erase_failed > MAX_RETRIES) {
        Jump_To_Backup_App(); // 跳转到备份区
    }
  3. OTA升级流程

    CAN命令
    确认


    完成
    主机
    Bootloader
    校验通过?
    写入Flash
    请求重传
    跳转App


七、调试与验证

  1. 逻辑分析仪捕获

    c 复制代码
    01 03 00 80 00 08 00 00 00 00 00 00 00 00 00 00  // 擦除命令
    02 03 00 80 00 08 00 00 00 00 00 00 00 00 00 00  // 写入命令
    03 03 00 80 00 08 00 00 00 00 00 00 00 00 00 00  // 跳转命令
  2. 错误处理测试

    • 模拟总线断开:触发CAN错误中断,自动重启

    • 数据篡改测试:修改接收数据后验证CRC失败


八、扩展功能建议

  1. 安全增强

    c 复制代码
    // AES加密实现
    void Encrypt_Data(uint8_t *data, uint16_t len) {
        AES_KEY enc_key;
        AES_set_encrypt_key(key, 128, &enc_key);
        AES_cbc_encrypt(data, data, len, &enc_key, iv, AES_ENCRYPT);
    }
  2. 多节点管理

    c 复制代码
    // 节点地址分配表
    const uint8_t node_table[32] = {
        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
        0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
    };
相关推荐
qq_401700413 小时前
STM32使用SPI接口AD7175芯片ADC采集的驱动
stm32·单片机·嵌入式硬件
爱倒腾的老唐4 小时前
02、STM32——嵌入式芯片
linux·stm32·嵌入式硬件
DLGXY4 小时前
STM32(二十二)——时间戳、BKP备份寄存器、RTC实时时钟
stm32·嵌入式硬件·实时音视频
小白_史蒂夫5 小时前
【使用记录】(二)华为Atlas 200 DK 板卡修改板卡IP
单片机
学嵌入式的小杨同学5 小时前
STM32 进阶封神之路(八):外部中断 EXTI 实战 —— 按键检测从轮询到中断(库函数 + 寄存器双版本)
linux·stm32·单片机·嵌入式硬件·mcu·架构·硬件架构
Python小老六13 小时前
冯诺依曼架构 vs 哈佛架构 对比
stm32·单片机·嵌入式硬件·架构
TEC_INO14 小时前
Hal库的使用
单片机·hal库
羽获飞14 小时前
从零开始学嵌入式之STM32——13.使用STM32自带硬件模块实现IIC协议通讯
单片机·嵌入式硬件
单片机设计星球14 小时前
51单片机的【智能婴儿床】仿真设计
单片机·嵌入式硬件·51单片机