查看: 4785|回复: 1

在KW36上用CAN总线进行固件升级

[复制链接]
  • TA的每日心情
    开心
    2024-3-26 15:16
  • 签到天数: 266 天

    [LV.8]以坛为家I

    3302

    主题

    6549

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32045
    最后登录
    2024-4-29
    发表于 2020-6-18 10:57:53 | 显示全部楼层 |阅读模式
    在KW36上用CAN总线进行固件升级

    一、概述


    在上一篇《在KW36上用LIN总线进行固件升级》文章中,我们介绍了在KW36上如何用LIN进行固件升级。今天谈谈如何用CAN总线进行升级。


    控制器区域网络(Controller Area Network,CAN),具有突出的可靠性、实时性和灵活性,广泛地应用于汽车、工业自动化、医疗设备等领域,是ISO国际标准化的串行通讯协议。它是一种多主总线,采用CAN_H、CAN_L两条信号线进行差分传输。CAN最高速率可达1Mbps,而可变速率CAN(CAN with Flexible Data-rate, CAN FD)的传输速率会更快。
    KW36是恩智浦推出的一款超低功耗、高集成度的BLE无线MCU,面向汽车、工业等应用,支持BLE5.0和GenFSK通讯,包含FlexCAN等通讯接口。FlexCAN支持CAN FD,速率最高可达3.2Mbps。
    CAN网络为多主结构,无主从之分。网络中某些CAN节点可能具备空中升级的能力,比如通过BLE方式从APP、主控计算机等服务端获取固件,完成升级。而不具备空中升级能力的CAN节点,就需要通过CAN总线从其他节点获取新固件进行升级。
    本文主要讲述CAN节点如何通过CAN总线将固件传送给其他无空中升级能力的节点,让其完成切换升级。此方案同样适用于恩智浦新推出的KW38芯片。


    二、CAN总线基础


    2.1  CAN主要特点


    多主结构
    CAN为多主结构,每个节点都可成为主机,以报文广播的形式向总线上的其他节点发送数据。节点之间均可进行通信。


    差分传输
    CAN用CAN_H和CAN_L这两根线的电位差来确定总线电平,含显性电平(逻辑“0”)和隐形电平(逻辑“1”)。总线采用线“与”规则,只要有一个单元输出显性电平,总线上即为显性电平;只有所有单元输出隐性电平,总线上才为隐性电平。


    速率灵活
    CAN传输速率最高可到1Mbps,但CAN总线的通讯距离和波特率成反比。当速率为1Mbps时,通讯距离约为40m;当速率为5Kbps时,通讯距离理论上可达10km。同个CAN总线的所有节点速率必须设置一致。


    总线仲裁
    CAN总线采用无破坏性的仲裁机制,即若总线上的多个节点同时发送数据,具有高优先级数据报文的节点仲裁胜出,可以继续发送数据,而其它仲裁失败的节点将退出发送状态而转为接收节点。报文标识符决定了在总线上发送的优先级,标识符越小,优先级越高。


    2.2  CAN报文
    CAN报文用11位字符(标准格式)或29位字符(扩展格式)作为标识符进行标识。发送报文的CAN节点称为发送器,接收报文的CAN节点称为接收器,每个CAN节点均可在发送器和接收器角色之间切换。CAN报文类型有数据帧、远程帧、错误帧和过载帧。


    数据帧:将数据从发送器传输到接收器
    远程帧:总线节点发出远程帧请求其他节点发送具有相同标识符的数据帧


    错误帧:任何节点检测到总线错误就发出错误帧


    过载帧:用于在相邻数据帧或远程帧之间提供附加延时


    数据帧包括帧起始、仲裁域、控制域、数据域等7部分。标准帧和扩展帧的区别主要在仲裁域和控制域。
    1.png
    标准帧里,仲裁域bit0~bit10表示标识符,bit11是RTR位,为0时表示此帧是数据帧,为1时表示此帧是远程帧。控制域bit0是IDE位,置0表示此帧为标准帧;bit1保留,bit2~bit5表示数据域长度。


    扩展帧里,仲裁域bit0~bit10表示高11位标识符,bit11~bit12分别为SRR和IDE位,均置1表示此帧为扩展格式帧。bit13~bit30表示29位标识符中的低18位。
    *注:CAN数据域长度为0~8字节,但CANFD支持扩展长度至64字节。
    远程帧结构与数据帧类似,区别在于不含数据域,且仲裁域的RTR位为1。
    错误帧包含错误标志集合域和错误界定符2部分。
    过载帧包含过载标志集合域和过载界定符2部分。
    关于更多CAN报文帧结构细节,可参考标准《CANSpecification 2.0》。


    三、软硬件准备


    3.1  运行CAN驱动例程
    首先在恩智浦官网http://mcuxpresso.nxp.com/下载KW36最新SDK,准备2块FRDM-KW36开发板用于演示2个CAN节点之间通讯,以及12V直流电源和4根杜邦线用于供电和通讯。2根Micro-USB线可用于串口调试。

    在SDK\boards\frdmkw36\driver_examples\flexcan\interrupt_transfer路径下找到驱动例程,编译将其分别烧录进2块开发板中,通过串口命令设置CAN初始发送节点和接收节点,按照提示操作开发板按键,即可看到CAN收发演示效果。
    2.png
    3.2  移植CAN驱动
    在上一篇文章《在KW36上用LIN总线进行固件升级》提到,LINMaster通过BLE空中升级(Over The Air Programming profile,OTAPprofile)方式获取LIN Slave或它本身的固件;若是LIN Slave固件,则Master通过LIN总线将固件数据传送给Slave。LIN 节点获取到新固件后,应用代码改写bootflag,复位后second bootloader通过boot flag判断到有新固件后拷贝升级,完成后重置bootflag再复位芯片,即开始运行新的固件程序。将LIN Master、LIN Slave驱动分别移植入SDK\boards\frdmkw36\wireless_examples\bluetooth\otac_att工程即可实现固件存储和切换功能。


    CAN为多主结构,没有Master-Slave之分。但在实际应用中,有些CAN节点具备空中升级能力,可自主完成升级;而总线上有些CAN节点不具备空中升级功能,就需要通过其他节点获取固件协助完成升级。为方便讨论,这里将具备空中升级能力、可为其他节点提供固件数据的节点称为Node A,不具备空中升级能力、只能被动接收固件数据的节点称为Node B。


    同理,将SDK\boards\frdmkw36\driver_examples\flexcan\interrupt_transfer驱动移植入SDK\boards\frdmkw36\wireless_examples\bluetooth\otac_att工程中,拷贝成2个工程分别作为NodeA和NodeB。这样NodeA就具备了从BLE服务端获取数据、读写Flash、CAN收发的功能,而NodeB具备了CAN收发、读写Flash、完成切换的功能。整个流程下来就完成了NodeB的固件升级。实际应用中Node B无需具备OTAP功能,可裁剪节省Flash空间和升级时间。详细移植步骤,可参考《AN12273Using MCUXpresso SDK CAN and LIN Drivers to Create a Bluetooth LE-CAN and BluetoothLE-LIN Bridges on KW36/KW35》(http://www.nxp.com/docs/en/application-note/AN12273.pdf)。






    四、CAN总线固件升级机制


    4.1  固件获取
    Node A移植入SDK\boards\frdmkw36\wireless_examples\bluetooth\otac_att后,可从BLE服务端获取NodeB以及它自身的新固件。BLE传输所用固件需为BLE OTA格式文件,即在原有固件头部添加OTAheader和尾部必要信息。OTA header中的Image ID字段可区分固件归属。在获取完固件后,原工程默认对内部Flash的BootFlags段写入有效值,secondbootloader复位后判断到有效值启动拷贝和切换,这适用于Node A它自身的升级。而对于传来的Node B固件,则需对BootFlags写入不同值,防止NodeA复位误升级。

    关于新固件OTA格式文件,可用从恩智浦官网下载的ConnectivityTest Tool来生成,具体步骤为点击菜单OTA Updates -> OTA Bluetooth LE,修改OTA header中的ImageID为NodeB固件的自定义ID值(比如0x000A,默认为0x0001)。导入需要升级的bin文件,选择KW36芯片,保存则得到可用于升级的OTA格式文件。
    3.png
    4.2  固件传输
    定义CAN收发标识符,此处我们按照标准帧格式进行定义,在Node A定义了11位发送标识符,要在NodeB定义相同的11位接收标识符,反之亦然。


    Node A:


    #defineCAN_TX_IDENTIFIER  (0x123)
    #defineCAN_RX_IDENTIFIER  (0x321)
    Node B:


    #defineCAN_TX_IDENTIFIER  (0x321)


    #defineCAN_RX_IDENTIFIER  (0x123)


    空闲情况下,Node A和Node B均设置为接收状态,监听总线上的数据。
    上文提到,CAN节点以报文广播的形式向总线上的其他节点发送数据,比如周期性向网络所有节点更新自己的传感器数据,亦不需要接收节点的反馈。但广播形式发送固件数据,一旦接收失败或者中间数据丢失,会很难监控,最后造成接收节点升级失败。而且接收节点在接收到数据后需要进行存储操作,如果存储未完成又有新一帧数据进来,可能造成存储失败。
    从可靠性考虑,以及CAN本身速率较快,我们这里按照发送应答方式进行传输,即Node A向Node B发送一帧数据后,转为侦听状态,等待NodeB回应;NodeB在收到一帧数据后,校验保存后向Node A发送应答。Node A收到正确应答后,发送下一帧数据,直到所有固件数据发送完成。
    定义固件升级有关命令:
    typedef enum


    {


        CAN_GEN_CMD_NONE = 0x00,


        CAN_GEN_CMD_OTA_CMD,


        CAN_GEN_CMD_OTA_DATA,


        CAN_GEN_CMD_OTA_STATUS


    } can_general_cmd_t;


    其中CAN_GEN_CMD_OTA_CMD用于Node A指示Node B开始/结束固件传输,CAN_GEN_CMD_OTA_STATUS用于Node B向Node A回复当前状态,CAN_GEN_CMD_OTA_DATA用于Node A向Node B传输实际固件数据,及Node B向Node A回复帧接收状态。这些命令均位于数据域的byte0。
    当byte0为CAN_GEN_CMD_OTA_CMD时,byte1为具体的开始/结束子命令。
    定义CAN_GEN_CMD_OTA_CMD的发送命令枚举:
    typedef enum


    {


        CAN_OTA_CMD_NONE = 0x00,


        CAN_OTA_CMD_START,


        CAN_OTA_CMD_END


    } can_ota_cmd_c;


    当byte0为CAN_GEN_CMD_OTA_STATUS时,byte1设置为Node B的当前状态。
    定义CAN_GEN_CMD_OTA_STATUS的状态枚举:
    typedef enum


    {


        CAN_OTA_STATUS_IDLE = 0x00,


        CAN_OTA_STATUS_READY,


        CAN_OTA_STATUS_RUNNING,


        CAN_OTA_STATUS_FINISH,


        CAN_OTA_STATUS_ABORT


    } can_ota_status_t;


    当byte0为CAN_GEN_CMD_OTA_DATA时,byte1设置为当前帧序列号(0~255)。当发送端为Node A时,byte2~byteN为固件数据;当发送端为Node B时,byte2为接收ACK、NAK或其他更多可指示错误信息的状态。前面提到,CAN数据域最多承载8个字节,但CAN FD可扩展至最多64字节。为方便固件数据对齐,这里使用CAN FD,将数据域长度设置为10字节,即1字节命令+1字节序列号+8字节数据。当然,若想要一帧传输更多数据,可将数据域长度适当扩展。
    Node A获取到Node B的新固件后,通过CAN_GEN_CMD_OTA_CMD发送开始传输命令,然后转为监听状态。Node B接受到升级开始命令,通过CAN_GEN_CMD_OTA_STATUS返回当前状态并转为监听。Node A收到后判断到READY状态,则开始用CAN_GEN_CMD_OTA_DATA发送数据。Node B每收到一帧数据后均需应答接收序列号和确认状态。
    发送完成后再通过CAN_GEN_CMD_OTA_CMD发送结束传输命令。
    由于Flash读取和写入需要消耗时间,如果每发一帧(8字节)均读取和写入一次,会影响升级效率。这里采用按块读取和写入的方式,比如Node A先读取1K 字节数据缓存起来,通过多帧数据发送给Node B,Node B每收到一帧数据同样先缓存起来,不执行写入。当Node B接收完1K后再执行写入操作。此时NodeA再读取下一块数据,执行相同操作。

    以下是Node A和Node B的通讯流程示意:
    4.png
    以上流程适用于1对1的升级。如果存在多个NodeB,NodeA在发送一帧数据后,多个NodeB均向NodeA返回ack,Node A很难维护各个Node B的状态。即以上方案无法完成同时对多个Node B同时升级。当然,如果Node A按广播方式给总线上所有Node B发送固件数据,的确可以实现Node B同时升级,但是此种方式并不可靠,固件每一帧数据都很重要,丢失或错误一帧都有可能导致Node B升级失败变成砖头。
    CAN传输速率很快,本文建议采用逐一升级的方式对多个NodeB进行升级,保证所有NodeB都能成功升级。这里为NodeB定义一个deviceid,比如BLEMAC Address的低16位,在开始升级时,Node A先获取各个Node B的device id,保存起来,获取完成后再逐一升级。由于此处各个Node B回复的标识符相同,考虑到Node B回复device id的冲突问题,在各个Node B回复前增加随机延迟,Node A设置超时窗口,在这段时间里持续接收Node B的device id,超时后开始升级。

    以总线上存在2个Node B(B1和B2)为例:
    5.png
    当然,读者亦可考虑将各个Node B的11位标准发送、接收标识符设置成不一致,NodeA获取到各自的标识符后逐一对其升级。
    4.3  固件切换
    当Node B收到CAN_OTA_CMD_END后,说明此时固件数据已经传输完成,准备切换动作。按照原先OTAP机制,将新固件大小(Imagelength)、所需扇区(Sector bitmap)写入所存储Flash区域的固定头部位置,写入有效的bootflag,复位后second bootloader即将新固件拷贝到内部Flash指定位置,实现切换。需要提出的是,如果采用内部Flash作升级存储,还需先在新固件存储地址0偏移位置写入开始标签(Startmarker)[0xDE, 0xAD, 0xAC, 0xE5],再写Image length和Sectorbitmap。

    五、后记



    笔者按照以上方案,测试时将CAN总线传输速度设置为1Mbps,传完200k固件大概需要13s,相比LIN总线固件升级速率要提升很多。如果存在多个NodeB,整个系统升级时间会叠加。


    文章出处:恩智浦MCU加油站

    签到签到
    回复

    使用道具 举报

    该用户从未签到

    0

    主题

    1

    帖子

    0

    新手上路

    Rank: 1

    积分
    6
    最后登录
    2023-7-12
    发表于 2023-7-12 15:37:08 | 显示全部楼层
    大佬,有源码吗?
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-29 09:34 , Processed in 0.117073 second(s), 21 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表