基于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
    };
相关推荐
Flamingˢ30 分钟前
YNQ + OV5640 视频系统开发(二):OV5640_Data IP 核源码解析
arm开发·嵌入式硬件·网络协议·tcp/ip·fpga开发·vim·音视频
Flamingˢ37 分钟前
ZYNQ + OV5640 视频系统开发(三):AXI VDMA 帧缓存原理
arm开发·嵌入式硬件·fpga开发·vim·音视频
xiangw@GZ39 分钟前
功耗测量:基于INA226的功耗测量原理深度解析
嵌入式硬件
Zevalin爱灰灰1 小时前
基于STM32实现OTA&BootLoader 第五章——OTA功能开发【下】
stm32·单片机·物联网·mqtt·嵌入式·esp8266
红叶落水1 小时前
GD32H737 1Mbps 数字通信链路实现
单片机
Strange_Head1 小时前
快速入门 MQTT:从 Broker、发布订阅到双机通信
嵌入式硬件
LCG元2 小时前
STM32实战:基于STM32F103的MQTT协议通信(EMQ X Broker)
stm32·单片机·嵌入式硬件
zmj3203242 小时前
51单片机
单片机·嵌入式硬件·51单片机
zmj3203242 小时前
MCS-51单片机
单片机·嵌入式硬件·51单片机
深念Y2 小时前
从CH341A编程器、SPI Flash到Linux+STM32理解
linux·stm32·flash·bios·固件·编程器·闪存