一、硬件配置方案
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
六、量产注意事项
-
Flash保护策略
c// 启用写保护 FLASH_OB_WRP(ENABLE, 0x000000FF); // 保护前1KB -
版本回滚机制
c#define MAX_RETRIES 3 if(flash_erase_failed > MAX_RETRIES) { Jump_To_Backup_App(); // 跳转到备份区 } -
OTA升级流程
CAN命令
确认
是
否
完成
主机
Bootloader
校验通过?
写入Flash
请求重传
跳转App
七、调试与验证
-
逻辑分析仪捕获
c01 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 // 跳转命令 -
错误处理测试
-
模拟总线断开:触发CAN错误中断,自动重启
-
数据篡改测试:修改接收数据后验证CRC失败
-
八、扩展功能建议
-
安全增强
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); } -
多节点管理
c// 节点地址分配表 const uint8_t node_table[32] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 };