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, §or_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
使用说明
-
编译Bootloader:
- 设置FLASH起始地址为0x08000000,大小为0x10000
- 编译并烧录到芯片
-
编译应用程序:
- 设置FLASH起始地址为0x08010000
- 在应用程序中添加中断向量表重映射代码
- 添加升级触发功能
-
升级工具开发:
- 开发PC端升级工具,通过CAN适配器与设备通信
- 实现固件分包发送、应答重传、校验等功能
-
升级流程:
- 设备上电进入Bootloader
- 等待5秒,如果没有收到升级命令则跳转到应用程序
- PC端发送升级请求命令
- Bootloader应答,开始接收固件信息
- 擦除Flash,接收固件数据
- 固件接收完成,校验通过后跳转到新固件
注意事项
- 电源稳定性:升级过程中必须保证电源稳定
- 看门狗:在Bootloader中适当喂狗或禁用看门狗
- 超时处理:添加超时机制,防止卡死在升级状态
- 断点续传:可添加断点续传功能,提高升级可靠性
- 加密签名:可根据需要添加固件加密和签名验证