1. 背景介绍CANopen协议中的LSS(Layer Setting Services)是一种用于动态配置设备通信参数(如节点ID和波特率)的服务。在固件更新场景中,LSS服务的扩展可以用于让设备进入固件更新模式并接收新的程序代码。
在本技术文档中,我们将使用APM32F4xx的CAN外设实现一个支持LSS协议的固件更新功能。主要功能包括:
- 设备通过LSS命令进入编程模式。
- 接收主机发送的固件数据并存储到Flash中。
- 验证固件数据的完整性,执行系统重启,运行新固件。
2. 功能实现流程
2.1 初始化CAN外设- 配置CAN波特率、过滤器及中断处理。
- 配置为标准CAN通信模式。
2.2 实现LSS服务- 解析并处理LSS请求帧。
- 支持以下LSS命令:
- 进入编程模式:命令代码0x11。
- 波特率切换:命令代码0x22。
2.3 接收固件数据并更新- 主站分片发送固件数据,接收后写入Flash存储。
- 完成后校验数据完整性。
2.4 执行更新
3. 详细步骤说明
通信流程图:
3.1. 进入编程模式
- 主站发送:LSS请求帧(命令代码0x11)。
- 数据帧格式:[0x11, Reserved, Reserved, Reserved, Reserved, Reserved, Reserved, Reserved]
- 从站响应:ACK(命令代码0x11)。
- 从站行为:切换到编程模式,准备接收固件数据。
3.2. 波特率切换
- 主站发送:LSS请求帧(命令代码0x22 + 波特率值)。
- 数据帧格式:[0x22, BaudRate_H, BaudRate_M, BaudRate_L, Reserved, Reserved, Reserved, Reserved]
- 波特率值为32位,例如0x0004E200表示125 kbit/s。
- 从站响应:ACK(命令代码0x22)。
- 从站行为:更新波特率并重启CAN通信。
3.3. 接收固件数据
- 主站发送:固件数据帧。
- 数据帧格式:[0x01, Data_1, Data_2, ..., Data_N]
- 每帧数据长度为8字节(CAN协议限制)。
- 从站响应:ACK(命令代码0x01)。
- 从站行为:
3.4. 结束固件发送- 主站发送:结束命令(命令代码0xEE)。
- 从站行为:校验接收的固件完整性。
3.5. 校验结果- 校验成功:
- 从站响应:ACK(命令代码0x02)。
- 从站行为:触发系统重启,运行新固件。
- 校验失败:
- 从站响应:NACK(命令代码0xFF)。
- 从站行为:通知主站失败原因,等待进一步指令。
4. 代码实现
4.1 主程序和初始化
#include "apm32f4xx_can.h"
#include "apm32f4xx_rcm.h"
#include "flash.h" // 假设包含Flash操作的头文件
#include <string.h>
#define LSS_REQUEST_ID 0x7E5 // LSS请求帧ID
#define LSS_RESPONSE_ID 0x7E4 // LSS响应帧ID
#define FLASH_BASE_ADDRESS 0x08020000 // 固件更新存储地址
// 全局变量
CAN_RxMessage_T rxMessage;
uint8_t firmwareData[256]; // 固件数据缓冲区
uint32_t firmwareSize = 0;
// 函数声明
void CAN_Init(void);
void LSS_ProcessMessage(CAN_RxMessage_T* rxMessage);
void EnterProgrammingMode(void);
void ChangeBaudRate(uint32_t baudrate);
uint8_t ReceiveFirmwareChunk(void);
uint8_t VerifyFirmware(void);
void SystemReset(void);
int main(void)
{
// 初始化系统时钟
SystemInit();
// 初始化CAN外设
CAN_Init();
// 主循环
while (1)
{
// 检查是否有CAN消息接收
if (CAN_MessagePending(CAN1, CAN_RX_FIFO_0) > 0)
{
CAN_ReceiveMessage(CAN1, CAN_RX_FIFO_0, &rxMessage);
LSS_ProcessMessage(&rxMessage); // 处理LSS协议消息
}
}
}
4.2 CAN初始化
void CAN_Init(void)
{
CAN_Config_T canConfig;
CAN_FilterConfig_T filterConfig;
// 使能CAN1时钟
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_CAN1);
// 配置CAN参数
CAN_ConfigStructInit(&canConfig);
canConfig.autoBusOffManage = ENABLE;
canConfig.autoWakeUpMode = ENABLE;
canConfig.nonAutoRetran = DISABLE;
canConfig.rxFIFOLockMode = DISABLE;
canConfig.txFIFOPriority = ENABLE;
canConfig.mode = CAN_MODE_NORMAL;
canConfig.syncJumpWidth = CAN_SJW_1;
canConfig.timeSegment1 = CAN_TIME_SEGMENT1_12;
canConfig.timeSegment2 = CAN_TIME_SEGMENT2_4;
canConfig.prescaler = 6; // 设置波特率
if (CAN_Config(CAN1, &canConfig) == ERROR)
{
// 初始化错误,进入死循环
while (1);
}
// 配置CAN过滤器,接收LSS相关的帧
filterConfig.filterNumber = 0;
filterConfig.filterMode = CAN_FILTER_MODE_IDMASK;
filterConfig.filterScale = CAN_FILTER_SCALE_32BIT;
filterConfig.filterIdHigh = (LSS_REQUEST_ID << 5) & 0xFFFF;
filterConfig.filterIdLow = 0;
filterConfig.filterMaskIdHigh = 0xFFFF;
filterConfig.filterMaskIdLow = 0xFFFF;
filterConfig.filterFIFO = CAN_FILTER_FIFO_0;
filterConfig.filterActivation = ENABLE;
CAN_ConfigFilter(&filterConfig);
}
4.3 LSS协议消息处理
void LSS_ProcessMessage(CAN_RxMessage_T* rxMessage)
{
if (rxMessage->id == LSS_REQUEST_ID)
{
uint8_t command = rxMessage->data[0];
switch (command)
{
case 0x11: // 进入编程模式
EnterProgrammingMode();
break;
case 0x22: // 更改波特率
{
uint32_t baudrate = (rxMessage->data[1] << 24) |
(rxMessage->data[2] << 16) |
(rxMessage->data[3] << 8) |
rxMessage->data[4];
ChangeBaudRate(baudrate);
break;
}
default:
// 其他命令不处理
break;
}
}
}
4.4 编程模式与固件接收
void EnterProgrammingMode(void)
{
// 初始化Flash模块
Flash_Init();
CAN_TxMessage_T txMessage;
txMessage.id = LSS_RESPONSE_ID;
txMessage.rtr = CAN_RTXR_DATA;
txMessage.dlc = 1;
txMessage.data[0] = 0x11; // 应答ACK
CAN_TxMessage(CAN1, &txMessage);
// 开始接收固件数据
while (ReceiveFirmwareChunk() == SUCCESS)
{
Flash_Write(FLASH_BASE_ADDRESS + firmwareSize, firmwareData, sizeof(firmwareData));
firmwareSize += sizeof(firmwareData);
}
// 校验固件
if (VerifyFirmware() == SUCCESS)
{
SystemReset(); // 重启运行新固件
}
else
{
// 固件错误处理逻辑
}
}
4.5 辅助函数
void ChangeBaudRate(uint32_t baudrate)
{
// 停止CAN外设
CAN_Disable(CAN1);
// 配置新的波特率
CAN_InitTypeDef canConfig;
canConfig.prescaler = SystemCoreClock / baudrate;
CAN_Config(CAN1, &canConfig);
// 重启CAN外设
CAN_Enable(CAN1);
}
uint8_t ReceiveFirmwareChunk(void)
{
// 模拟从CAN接收固件数据
memset(firmwareData, 0xFF, sizeof(firmwareData)); // 假设成功接收数据
return SUCCESS;
}
uint8_t VerifyFirmware(void)
{
// 校验固件内容的完整性
return SUCCESS; // 假设校验成功
}
void SystemReset(void)
{
NVIC_SystemReset(); // 触发系统复位
}
4. 总结
以上代码完整实现了基于APM32F4xx的CANopen LSS协议,用于实现固件更新功能,内容包括CAN初始化、LSS服务的处理、固件接收与存储、以及最终的固件校验和重启操作。代码注释详细且可直接用于项目中。
|