STM32F105 CAN远程升级Bootloader设计

STM32F105 CAN远程升级Bootloader设计

1. Flash分区规划

/* flash地址定义 */
#define FLASH_BASE_ADDR      0x08000000
#define BOOTLOADER_ADDR      0x08000000
#define BOOTLOADER_SIZE      0x10000      // 64KB
#define APPLICATION_ADDR     0x08010000
#define APPLICATION_MAX_SIZE 0x70000      // 448KB
#define UPDATE_FLAG_ADDR     0x0800FC00    // 升级标志存储地址
#define APP_INFO_ADDR        0x0800FE00    // 应用信息存储地址

/* 升级标志定义 */
typedef enum {
    APP_VALID = 0x5A5AA5A5,    // 应用程序有效
    APP_INVALID = 0xFFFFFFFF,  // 应用程序无效
    UPDATE_REQUEST = 0xA5A55A5A, // 请求升级
} APP_FLAG;

2. Bootloader主程序

#include "stm32f10x.h"
#include "stm32f10x_flash.h"
#include "stm32f10x_can.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "string.h"
#include "stdint.h"

/* 升级相关数据结构 */
#pragma pack(push, 1)
typedef struct {
    uint32_t total_size;      // 固件总大小
    uint32_t packet_count;    // 总包数
    uint32_t packet_size;     // 每包大小
    uint32_t crc32;          // CRC校验值
    uint8_t  version[16];     // 版本号
} APP_INFO_TypeDef;

typedef struct {
    uint32_t sequence;        // 包序号
    uint8_t  data[8];         // 数据
} UPDATE_PACKET_TypeDef;
#pragma pack(pop)

/* 全局变量 */
APP_INFO_TypeDef app_info;
uint32_t update_flag = APP_INVALID;
uint8_t update_buffer[1024];  // 接收缓冲区
uint32_t write_addr = APPLICATION_ADDR;
uint32_t received_size = 0;
uint8_t  update_state = 0;

/* 函数声明 */
void System_Init(void);
void CAN_Init(void);
void Flash_Erase(uint32_t addr, uint32_t size);
uint8_t Flash_Write(uint32_t addr, uint8_t *data, uint32_t size);
void Jump_To_App(void);
uint32_t Calculate_CRC32(uint8_t *data, uint32_t length);
void CAN_Send_Ack(uint8_t ack_code);
void Process_Update_Packet(CAN_RxHeaderTypeDef *rx_header, uint8_t *data);

int main(void)
{
    /* 系统初始化 */
    System_Init();
    
    /* 读取升级标志 */
    update_flag = *(uint32_t *)UPDATE_FLAG_ADDR;
    
    /* 检查应用程序是否有效 */
    if(update_flag == APP_VALID)
    {
        /* 检查栈指针是否有效 */
        if(((*(volatile uint32_t*)APPLICATION_ADDR) & 0x2FFE0000) == 0x20000000)
        {
            /* 延时500ms,等待可能的升级命令 */
            for(volatile int i = 0; i < 500000; i++);
            
            /* 如果没有收到升级命令,跳转到应用程序 */
            if(update_state == 0)
            {
                Jump_To_App();
            }
        }
    }
    
    /* 进入Bootloader主循环 */
    while(1)
    {
        /* 等待CAN消息 */
        if(CAN_CheckEvent())
        {
            Process_CAN_Message();
        }
        
        /* 处理超时 */
        if(update_state == 1)
        {
            // 处理升级超时
        }
    }
}

3. CAN升级协议实现

/* CAN消息ID定义 */
#define CAN_ID_UPDATE_REQ    0x100   // 升级请求
#define CAN_ID_UPDATE_INFO   0x101   // 升级信息
#define CAN_ID_UPDATE_DATA   0x102   // 升级数据
#define CAN_ID_UPDATE_ACK    0x103   // 应答
#define CAN_ID_UPDATE_END    0x104   // 升级结束
#define CAN_ID_APP_VER       0x105   // 版本查询

/* 应答码定义 */
#define ACK_OK               0x00
#define ACK_ERR              0x01
#define ACK_BUSY             0x02
#define ACK_TIMEOUT          0x03
#define ACK_CRC_ERR          0x04

/* 处理CAN消息 */
void Process_CAN_Message(void)
{
    CAN_RxHeaderTypeDef rx_header;
    uint8_t rx_data[8];
    
    if(HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK)
    {
        switch(rx_header.StdId)
        {
            case CAN_ID_UPDATE_REQ:
                Process_Update_Request(rx_data);
                break;
                
            case CAN_ID_UPDATE_INFO:
                Process_Update_Info(rx_data);
                break;
                
            case CAN_ID_UPDATE_DATA:
                Process_Update_Data(&rx_header, rx_data);
                break;
                
            case CAN_ID_UPDATE_END:
                Process_Update_End(rx_data);
                break;
                
            case CAN_ID_APP_VER:
                Send_App_Version();
                break;
                
            default:
                break;
        }
    }
}

/* 处理升级请求 */
void Process_Update_Request(uint8_t *data)
{
    if(update_state != 0)
    {
        CAN_Send_Ack(ACK_BUSY);
        return;
    }
    
    /* 设置升级标志 */
    update_flag = UPDATE_REQUEST;
    Flash_Write_Word(UPDATE_FLAG_ADDR, update_flag);
    
    update_state = 1;  // 进入接收信息状态
    received_size = 0;
    write_addr = APPLICATION_ADDR;
    
    CAN_Send_Ack(ACK_OK);
}

/* 处理升级信息 */
void Process_Update_Info(uint8_t *data)
{
    if(update_state != 1)
    {
        CAN_Send_Ack(ACK_ERR);
        return;
    }
    
    /* 解析固件信息 */
    memcpy(&app_info, data, sizeof(APP_INFO_TypeDef));
    
    /* 擦除应用程序区域 */
    Flash_Erase(APPLICATION_ADDR, app_info.total_size);
    
    update_state = 2;  // 进入接收数据状态
    
    CAN_Send_Ack(ACK_OK);
}

/* 处理升级数据 */
void Process_Update_Data(CAN_RxHeaderTypeDef *rx_header, uint8_t *data)
{
    static uint32_t packet_seq = 0;
    static uint32_t buffer_index = 0;
    
    if(update_state != 2)
    {
        CAN_Send_Ack(ACK_ERR);
        return;
    }
    
    UPDATE_PACKET_TypeDef packet;
    packet.sequence = rx_header->ExtId;  // 使用扩展ID存储包序号
    memcpy(packet.data, data, 8);
    
    /* 检查包序号 */
    if(packet_seq != packet.sequence)
    {
        CAN_Send_Ack(ACK_ERR);
        return;
    }
    
    packet_seq++;
    
    /* 存储到缓冲区 */
    memcpy(&update_buffer[buffer_index], packet.data, 8);
    buffer_index += 8;
    
    /* 缓冲区满,写入Flash */
    if(buffer_index >= 1024)
    {
        Flash_Write(write_addr, update_buffer, buffer_index);
        write_addr += buffer_index;
        received_size += buffer_index;
        buffer_index = 0;
    }
    
    /* 发送应答 */
    uint8_t ack_data[8];
    ack_data[0] = ACK_OK;
    memcpy(&ack_data[1], &received_size, 4);
    CAN_Send_Msg(ack_data, 5, CAN_ID_UPDATE_ACK);
}

/* 处理升级结束 */
void Process_Update_End(uint8_t *data)
{
    uint8_t result = data[0];
    
    if(result == 1)  // 升级完成
    {
        /* 写入剩余数据 */
        if(received_size < app_info.total_size)
        {
            uint32_t remain = app_info.total_size - received_size;
            Flash_Write(write_addr, update_buffer, remain);
        }
        
        /* 计算CRC并验证 */
        uint32_t calc_crc = Calculate_CRC32((uint8_t *)APPLICATION_ADDR, app_info.total_size);
        if(calc_crc != app_info.crc32)
        {
            CAN_Send_Ack(ACK_CRC_ERR);
            update_state = 0;
            return;
        }
        
        /* 更新应用信息 */
        app_info.total_size = app_info.total_size;
        Flash_Write_Word(APP_INFO_ADDR, (uint32_t)&app_info);
        
        /* 设置应用程序有效标志 */
        update_flag = APP_VALID;
        Flash_Write_Word(UPDATE_FLAG_ADDR, update_flag);
        
        update_state = 0;
        
        CAN_Send_Ack(ACK_OK);
        
        /* 延时后跳转到应用程序 */
        Delay(1000);
        Jump_To_App();
    }
    else
    {
        /* 升级失败,清除标志 */
        update_flag = APP_INVALID;
        Flash_Write_Word(UPDATE_FLAG_ADDR, update_flag);
        update_state = 0;
        
        CAN_Send_Ack(ACK_ERR);
    }
}

4. Flash操作函数

/* 擦除Flash扇区 */
void Flash_Erase(uint32_t addr, uint32_t size)
{
    uint32_t sector_error = 0;
    FLASH_EraseInitTypeDef erase_init;
    
    /* 解锁Flash */
    HAL_FLASH_Unlock();
    
    /* 计算需要擦除的扇区 */
    uint32_t start_sector = Get_Sector_Number(addr);
    uint32_t end_sector = Get_Sector_Number(addr + size - 1);
    
    erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;
    erase_init.Banks = FLASH_BANK_1;
    erase_init.Sector = start_sector;
    erase_init.NbSectors = end_sector - start_sector + 1;
    erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3;
    
    HAL_FLASHEx_Erase(&erase_init, &sector_error);
    
    /* 锁定Flash */
    HAL_FLASH_Lock();
}

/* 写入Flash */
uint8_t Flash_Write(uint32_t addr, uint8_t *data, uint32_t size)
{
    uint32_t *p_data = (uint32_t *)data;
    uint32_t word_count = (size + 3) / 4;  // 转换为字(4字节)
    
    HAL_FLASH_Unlock();
    
    for(uint32_t i = 0; i < word_count; i++)
    {
        if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, 
                             addr + i * 4, 
                             p_data[i]) != HAL_OK)
        {
            HAL_FLASH_Lock();
            return 0;
        }
    }
    
    HAL_FLASH_Lock();
    return 1;
}

/* 写入单个字 */
void Flash_Write_Word(uint32_t addr, uint32_t data)
{
    HAL_FLASH_Unlock();
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, data);
    HAL_FLASH_Lock();
}

/* 获取扇区号 */
uint32_t Get_Sector_Number(uint32_t addr)
{
    if(addr < 0x08004000) return FLASH_SECTOR_0;
    if(addr < 0x08008000) return FLASH_SECTOR_1;
    if(addr < 0x0800C000) return FLASH_SECTOR_2;
    if(addr < 0x08010000) return FLASH_SECTOR_3;
    if(addr < 0x08020000) return FLASH_SECTOR_4;
    if(addr < 0x08040000) return FLASH_SECTOR_5;
    if(addr < 0x08060000) return FLASH_SECTOR_6;
    if(addr < 0x08080000) return FLASH_SECTOR_7;
    if(addr < 0x080A0000) return FLASH_SECTOR_8;
    if(addr < 0x080C0000) return FLASH_SECTOR_9;
    if(addr < 0x080E0000) return FLASH_SECTOR_10;
    return FLASH_SECTOR_11;
}

5. 跳转到应用程序

/* 跳转到应用程序 */
void Jump_To_App(void)
{
    typedef void (*pFunction)(void);
    pFunction Jump_To_Application;
    uint32_t JumpAddress;
    
    /* 检查应用程序地址是否有效 */
    if(((*(volatile uint32_t*)APPLICATION_ADDR) & 0x2FFE0000) != 0x20000000)
    {
        return;  // 无效的栈指针
    }
    
    /* 关闭所有中断 */
    __disable_irq();
    
    /* 设置主堆栈指针 */
    __set_MSP(*(volatile uint32_t*)APPLICATION_ADDR);
    
    /* 获取复位向量地址 */
    JumpAddress = *(volatile uint32_t*)(APPLICATION_ADDR + 4);
    Jump_To_Application = (pFunction)JumpAddress;
    
    /* 初始化应用程序的堆栈指针 */
    __set_MSP(*(volatile uint32_t*)APPLICATION_ADDR);
    
    /* 跳转到应用程序 */
    Jump_To_Application();
}

6. 中断向量表重映射

/* 在应用程序中需要重映射中断向量表 */
void Remap_Interrupt_Vector(void)
{
    /* 在应用程序的main函数开始处调用 */
    SCB->VTOR = APPLICATION_ADDR;  // 设置中断向量表偏移
}

7. 应用程序中的升级支持

/* 在应用程序中添加升级触发功能 */
typedef struct {
    void (*JumpToBootloader)(void);
    uint32_t MagicKey;
} JUMP_TO_BOOT_TypeDef;

#define JUMP_MAGIC_KEY 0xDEADBEEF

/* 跳转到Bootloader的函数 */
__attribute__((section(".jumptoboot"))) JUMP_TO_BOOT_TypeDef JumpStruct = {
    .JumpToBootloader = 0,
    .MagicKey = 0
};

void Enter_Bootloader(void)
{
    /* 设置跳转结构 */
    JumpStruct.JumpToBootloader = (void (*)(void))0x08000000;
    JumpStruct.MagicKey = JUMP_MAGIC_KEY;
    
    /* 设置升级标志 */
    Flash_Write_Word(UPDATE_FLAG_ADDR, UPDATE_REQUEST);
    
    /* 复位系统 */
    NVIC_SystemReset();
}

/* 在main函数开始处检查是否需要跳转到Bootloader */
if(*(uint32_t *)UPDATE_FLAG_ADDR == UPDATE_REQUEST)
{
    /* 清除标志 */
    Flash_Write_Word(UPDATE_FLAG_ADDR, APP_INVALID);
    
    /* 跳转到Bootloader */
    if(JumpStruct.MagicKey == JUMP_MAGIC_KEY && 
       JumpStruct.JumpToBootloader != NULL)
    {
        JumpStruct.JumpToBootloader();
    }
    else
    {
        /* 硬件复位到Bootloader */
        __set_FAULTMASK(1);
        NVIC_SystemReset();
    }
}

8. CAN初始化优化

void CAN_Init_For_Bootloader(void)
{
    hcan1.Instance = CAN1;
    hcan1.Init.Prescaler = 6;
    hcan1.Init.Mode = CAN_MODE_NORMAL;
    hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
    hcan1.Init.TimeSeg1 = CAN_BS1_9TQ;
    hcan1.Init.TimeSeg2 = CAN_BS2_4TQ;
    hcan1.Init.TimeTriggeredMode = DISABLE;
    hcan1.Init.AutoBusOff = DISABLE;
    hcan1.Init.AutoWakeUp = DISABLE;
    hcan1.Init.AutoRetransmission = ENABLE;
    hcan1.Init.ReceiveFifoLocked = DISABLE;
    hcan1.Init.TransmitFifoPriority = DISABLE;
    
    HAL_CAN_Init(&hcan1);
    
    /* 配置过滤器,接收所有消息 */
    CAN_FilterTypeDef filter_config;
    filter_config.FilterBank = 0;
    filter_config.FilterMode = CAN_FILTERMODE_IDMASK;
    filter_config.FilterScale = CAN_FILTERSCALE_32BIT;
    filter_config.FilterIdHigh = 0x0000;
    filter_config.FilterIdLow = 0x0000;
    filter_config.FilterMaskIdHigh = 0x0000;
    filter_config.FilterMaskIdLow = 0x0000;
    filter_config.FilterFIFOAssignment = CAN_RX_FIFO0;
    filter_config.FilterActivation = ENABLE;
    HAL_CAN_ConfigFilter(&hcan1, &filter_config);
    
    /* 启动CAN */
    HAL_CAN_Start(&hcan1);
    HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
}

9. CRC校验函数

/* CRC32计算 */
uint32_t Calculate_CRC32(uint8_t *data, uint32_t length)
{
    uint32_t crc = 0xFFFFFFFF;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
    
    CRC_ResetDR();
    
    for(uint32_t i = 0; i < length; i += 4)
    {
        uint32_t word = *(uint32_t *)&data[i];
        crc = CRC_CalcBlockCRC(&word, 1);
    }
    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, DISABLE);
    return crc;
}

10. 链接脚本修改

/* STM32F105xC链接脚本片段 */
MEMORY
{
    RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K
    FLASH (rx)     : ORIGIN = 0x08000000, LENGTH = 512K
}

SECTIONS
{
    /* Bootloader部分 */
    .bootloader :
    {
        . = ALIGN(4);
        KEEP(*(.isr_vector_boot))
        *(.bootloader*)
        . = ALIGN(4);
    } >FLASH AT> FLASH
    
    /* 应用程序部分 */
    .application ORIGIN(FLASH) + 0x10000 :
    {
        . = ALIGN(4);
        KEEP(*(.isr_vector_app))
        *(.text*)
        *(.rodata*)
        *(.data*)
        *(.bss*)
        *(COMMON)
        . = ALIGN(4);
    } >FLASH AT> FLASH
    
    /* 跳转结构特殊存储 */
    .jumptoboot 0x0800FB00 :
    {
        KEEP(*(.jumptoboot))
    } >FLASH
}

参考代码 实现stm32f105程序代码的远程升级的boot www.youwenfan.com/contentcnv/70981.html

使用说明

  1. 编译Bootloader

    • 设置FLASH起始地址为0x08000000,大小为0x10000
    • 编译并烧录到芯片
  2. 编译应用程序

    • 设置FLASH起始地址为0x08010000
    • 在应用程序中添加中断向量表重映射代码
    • 添加升级触发功能
  3. 升级工具开发

    • 开发PC端升级工具,通过CAN适配器与设备通信
    • 实现固件分包发送、应答重传、校验等功能
  4. 升级流程

    1. 设备上电进入Bootloader
    2. 等待5秒,如果没有收到升级命令则跳转到应用程序
    3. PC端发送升级请求命令
    4. Bootloader应答,开始接收固件信息
    5. 擦除Flash,接收固件数据
    6. 固件接收完成,校验通过后跳转到新固件

注意事项

  1. 电源稳定性:升级过程中必须保证电源稳定
  2. 看门狗:在Bootloader中适当喂狗或禁用看门狗
  3. 超时处理:添加超时机制,防止卡死在升级状态
  4. 断点续传:可添加断点续传功能,提高升级可靠性
  5. 加密签名:可根据需要添加固件加密和签名验证

专注于matlab/simulink,电子电路,编程