打印
[APM32F4]

使用APM32F4xx实现CANopen的固件更新(LSS协议)

[复制链接]
25|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
a976209770|  楼主 | 2024-12-2 13:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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)。
    • 数据帧格式:[0x11]
  • 从站行为:切换到编程模式,准备接收固件数据。

3.2. 波特率切换
  • 主站发送:LSS请求帧(命令代码0x22 + 波特率值)。
    • 数据帧格式:[0x22, BaudRate_H, BaudRate_M, BaudRate_L, Reserved, Reserved, Reserved, Reserved]
    • 波特率值为32位,例如0x0004E200表示125 kbit/s。
  • 从站响应:ACK(命令代码0x22)。
    • 数据帧格式:[0x22]
  • 从站行为:更新波特率并重启CAN通信。

3.3. 接收固件数据
  • 主站发送:固件数据帧。
    • 数据帧格式:[0x01, Data_1, Data_2, ..., Data_N]
    • 每帧数据长度为8字节(CAN协议限制)。
  • 从站响应:ACK(命令代码0x01)。
    • 数据帧格式:[0x01]
  • 从站行为
    • 将数据写入Flash存储。
    • 准备接收下一帧。
3.4. 结束固件发送
  • 主站发送:结束命令(命令代码0xEE)。
    • 数据帧格式:[0xEE]
  • 从站行为:校验接收的固件完整性。
3.5. 校验结果
  • 校验成功
    • 从站响应:ACK(命令代码0x02)。
      • 数据帧格式:[0x02]
    • 从站行为:触发系统重启,运行新固件。
  • 校验失败
    • 从站响应:NACK(命令代码0xFF)。
      • 数据帧格式:[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服务的处理、固件接收与存储、以及最终的固件校验和重启操作。代码注释详细且可直接用于项目中。









使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

24

主题

27

帖子

0

粉丝