基于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
    };
相关推荐
半杯咖啡半行码8 小时前
STM32之ARM通用汇编指令详解:从指令格式到寻址方式
汇编·arm开发·stm32
创界工坊工作室9 小时前
22-基于STM32的汽车防盗系统设计
stm32·单片机·嵌入式硬件·proteus
雨田言炎11 小时前
STM32之RCC系统时钟控制(芯片的心脏控制器)
stm32·单片机·嵌入式硬件
李永奉11 小时前
日常使用的锂电池上,新、旧锂电池的差异分析
单片机·嵌入式硬件·物联网·智能手机·电脑
额额额对了12 小时前
IMX6ULL 杂项设备 LED 驱动
stm32·单片机·嵌入式硬件
长春的KAKA13 小时前
Proxmark3 Iceman分支CLI命令总表
嵌入式硬件
半杯咖啡半行码13 小时前
STM32开发之IIC通信详解:从协议基础到FM24CL04实战
stm32·单片机·嵌入式硬件
硅基手札14 小时前
【I2C 技术系列 05】I2C 多主仲裁与时钟同步
驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机外设
szxinmai主板定制专家14 小时前
面向虚拟电厂协调控制:RK3588+FPGA+AI边缘控制器设计思路与算力评估
人工智能·嵌入式硬件·fpga开发·机器人·jetson·zynq·振动采集
Zw-awa14 小时前
DO 和 AO 到底怎么选:比较器替你决定了什么
stm32