查看: 4491|回复: 0

[分享] 在KW36上用LIN总线进行固件升级

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

    [LV.8]以坛为家I

    3299

    主题

    6546

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    32024
    最后登录
    2024-4-25
    发表于 2020-5-15 16:07:45 | 显示全部楼层 |阅读模式
    在KW36上用LIN总线进行固件升级
    一、概述


    局域互联网络(Local Interconnect Network,LIN)总线广泛应用于汽车分布式电子系统,是对控制器区域网络(Controller Area Network,CAN)等其它汽车多路网进行补充的一种低成本串行通讯网络。LIN基于UART/SCI格式,采用单主/多从模式,是UART的一种特殊情况。
    KW36是恩智浦推出的一款超低功耗、高集成度的BLE无线MCU,面向汽车、工业等应用,支持BLE5.0和GenFSK通讯。KW36含有丰富的外设资源,如2路SPI接口、2路I2C接口、2路低功耗UART(LPUART)、16位ADC、16位低功耗定时器(LPTMR)、FlexCAN等,其中2路LPUART均支持LIN总线通信。


    一个LIN网络一般含1个LIN主机(Master)和1至多个LIN从机(Slave)。关于固件升级,LIN Master一般从APP、主控计算机等服务端获取固件,完成升级。而LIN Slave通常只与Master单线通讯,不具备与其他服务端连接的条件,这就需要LIN Master先从第三方服务端获取Slave的新固件,再通过LIN总线传输给LIN Slave,从而完成Slave升级。


    本文主要讲述LIN Master如何将固件传送给LIN Slave,让Slave完成切换升级。此方案亦适用于恩智浦新推出的KW38芯片。


    二、LIN总线基础


    2.1 LIN主要特点


    LIN总线基于UART/SCI格式,采用单线传输,应用单主/多从模式,总线电平一般为12V,传输速率最高为20kbps。一个LIN网络最多支持16个节点,即1个主节点和1~15个从节点。从机可以自适应总线时钟源。
    2.2  LIN任务
    LIN总线含两种任务,主任务和从任务。一个LIN网络只含一个主任务,它存在于LIN Master上。每个LIN节点都包含一个从任务。主任务决定何时指定哪帧数据进行传输,从任务负责响应主任务,提供所需要的数据。
    1.png
    图1. LIN总线任务关系(源自《LIN Specification Package V2.1》)
    2.3 LIN报文
    一帧完整报文包含帧头(header,主任务提供)和响应(response,从任务提供)。帧头包含起始段、同步段和受保护命令段,起始段(Break field)为至少11位的显性0电平,同步段(Syncbyte field)为0x55,受保护命令段(Protected identifier field,PID)为一个字节,其中低6位表示ID,高2位为ID的校验位。响应部分包含1~8字节的数据和1字节的校验字节。ID的范围为0~63,其中0~59用于信号帧(Signal-carryframe),60~61用于诊断帧(Diagnostic frame)。常用的无条件信号帧(Unconditionalframe)属于信号帧的最常用的一种,也是本文所采用的帧类型。关于帧类型的描述,详细可参考LIN协议标准。
    2.png
    图2. LIN数据帧结构(源自《LIN Specification Package V2.1》)


    2.4 LIN传输
    每一帧报文都有响应数据发送端和接收端。发送端称为发布者(Publisher),接收端称为订阅者(Subscriber)。不同ID报文间的发布/订阅关系是独立的。所有不同ID的报文都预先定义在调度表(Scheduletable)中,包括他们的帧类型、ID、发布/订阅类型、数据段字节数、帧间延迟等。LIN Master主任务会根据调度表来发送帧头,LIN Master或Slave的从任务根据ID将特定数据放进响应数据段中,然后发送。


    三、软硬件准备


    3.1  运行LIN驱动例程


    首先在恩智浦官网http://mcuxpresso.nxp.com/下载KW36最新SDK,准备2块FRDM-KW36开发板,分别作为LIN Master和LIN Slave,以及12V直流电源和3根杜邦线用于供电和通讯。2根Micro-USB线可用于串口调试。
    在SDK\boards\frdmkw36\driver_examples\lin路径下找到LIN Master和Slave的驱动例程,编译将其分别烧录进2块开发板中,通过串口打印即可看到LIN通讯演示效果。
    3.png
    图3. KW36开发板用于LIN通讯
    3.2 移植LIN驱动
    LIN Slave从Master获取到新固件后,需有一种方式将其切换。SDK中现有方案是增加额外的second bootloader代码,当应用代码获取完新固件,置相关标志位后复位芯片。启动后先执行second bootloader,当second bootloader通过标志位判断到有新固件时,则拷贝升级,完成后恢复标志位再复位,即开始运行新的固件程序。
    按理说,在现有驱动代码中LIN Master增加从固件存储区读取数据、LIN Slave增加往固件存储区写入数据然后置相关标志位并增加现有second bootloader代码即可。


    上文提到,新固件需由LIN Master从第三方服务端获取。而KW36作为一款支持BLE5.0的车规级MCU,可通过BLE方式获取新固件。恩智浦设计了一套较为可靠的BLE空中升级方案(OverThe Air Programming profile,OTAP profile),可方便对BLE设备传输固件数据。SDK\boards\frdmkw36\wireless_examples\bluetooth\otac_att工程实现了从BLE服务端获取新固件、写入存储区、读出存储区、最后完成切换的功能。关于OTAP机制,读者可参考官网《BLE Demo Applications User'sGuide》文档。


    为了简化实现,我们可以直接将LIN Master、LIN Slave驱动分别移植入otac_att工程中,这样LIN Master就具备了从BLE服务端获取数据、读写Flash、LIN发送数据的功能,而LIN Slave具备了LIN接收数据、读写Flash、完成切换的功能。整个流程下来就完成了LIN Slave的固件升级。实际应用中LIN Slave并不需要具备OTAP功能,可裁剪节省Flash空间和升级时间。


    详细移植步骤,可参考《AN12273 UsingMCUXpresso 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)。

    四、LIN总线固件升级机制


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


    关于新固件OTA格式文件,可用从恩智浦官网下载的Connectivity Test Tool来生成,具体步骤为点击菜单OTA Updates -> OTA Bluetooth LE,修改OTA header中的Image ID为LIN Slave固件的自定义ID值(比如0x000A,默认为0x0001)。导入需要升级的bin文件,选择KW36芯片,保存则得到可用于升级的OTA格式文件。
    4.png
    图4. Connectivity Test Tool生成OTA格式固件


    4.2  固件传输
    定义3个帧ID:
    typedef enum
    {
        gID_OtapCmd_c = 0x31,
        gID_OtapGetStatus_c,
        gID_OtapData_c
    } lin_id_t;


    其中gID_OtapCmd_c用于LIN Master指示LIN Slave开始/结束固件传输,gID_OtapGetStatus_c用于LIN Master获取LIN Slave的实时状态,gID_OtapData_c用于LIN Master向LIN Slave传输实际固件数据。从字面上可以理解,LIN Master是gID_OtapCmd_c和gID_OtapData_c这两类数据的Publisher,是gID_OtapGetStatus_c这类数据的Subscriber。gID_OtapCmd_c携带1字节数据,表示命令;gID_OtapGetStatus_c需2个字节,分别表示状态和所需帧序列号(0~255),gID_OtapData_c需8个字节,为实际固件数据。三者均为无条件信号帧。


    定义gID_OtapCmd_c的发送命令枚举:


    typedef enum
    {
        LIN_OTA_CMD_NONE = 0x00,
        LIN_OTA_CMD_START,
        LIN_OTA_CMD_END,
        LIN_OTA_CMD_CONTINUE
    } lin_ota_cmd_c;


    定义gID_OtapGetStatus_c的状态枚举:
    typedef enum
    {
        LIN_OTA_STATUS_IDLE = 0x00,
        LIN_OTA_STATUS_READY,
        LIN_OTA_STATUS_RUNNING,
        LIN_OTA_STATUS_FINISH,
        LIN_OTA_STATUS_ABORT
    }lin_ota_status_t;


    定义1个11(1+2+8)字节数组,依次存储以上3类响应数据。


    1. <font size="3" face="微软雅黑">#defineLIN_FRAME_BUF_SIZE   (11U)
    2. #defineLIN_NUM_OF_FRMS     (3U)

    3. uint8_tg_lin_frame_data_buffer[LIN_FRAME_BUF_SIZE] = {0};
    4. static constlin_frame_struct lin_frame_tbl[LIN_NUM_OF_FRMS] = {
    5.     { LIN_FRM_UNCD, 1U, LIN_RES_PUB, 0U, 0U,1U, 10U, 0U }
    6.     ,{ LIN_FRM_UNCD, 2U, LIN_RES_SUB, 1U, 1U,1U, 4U, 0U }
    7.     ,{ LIN_FRM_UNCD, 8U, LIN_RES_PUB, 3U, 2U,1U, 2U, 0U }};</font>
    复制代码
    其中lin_frame_struct定义如下:
    1. <font size="3" face="微软雅黑">typedef struct
    2. {
    3.     lin_frame_type frm_type;     /*!< Frame information (unconditionalor event triggered..) */
    4.     uint8_t frm_len;              /*!< Length of the frame */
    5.     lin_frame_response frm_response; /*!<Action response when received PID */
    6.     uint8_t frm_offset;           /*!< Frame byte offset in framebuffer */
    7.     uint8_t flag_offset;          /*!< Flag byte offset in flagbuffer */
    8.     uint8_t flag_size;           /*!< Flag size in flag buffer */
    9.     uint32_t delay;             /*!< Frame delay */
    10.     const uint8_t *frame_data_ptr;   /*!< List of Signal to which the frame isassociated and its offset */
    11. } lin_frame_struct;</font>
    复制代码
    当LIN Master通过gID_OtapCmd_c发送开始传输命令后,紧接着用gID_OtapGetStatus_c向LIN Slave获取状态;如果是READY状态,则开始用gID_OtapData_c持续发送数据。发送完成后再通过gID_OtapCmd_c发送结束传输命令。


    当LIN Slave接收到gID_OtapCmd_c携带的开始传输指令后,会检查自身状态,如果可以接收新固件数据,则将状态置为READY,通过gID_OtapGetStatus_c反馈给LIN Master,并做好接受gID_OtapData_c的准备,开始持续接收数据。直到接收到gID_OtapCmd_c的结束指令,LIN Slave开始切换到新固件。以下是Master(M)和Slave(S)的通讯流程:
    1. <font size="3" face="微软雅黑">M->S:  gID_OtapCmd_c [LIN_OTA_CMD_START]
    2. M<-S:  gID_OtapGetStatus_c [LIN_OTA_STATUS_READY,frame_sqn]
    3. M->S:  gID_OtapData_c [data1, data2, …data8]
    4. M->S:  gID_OtapCmd_c [LIN_OTA_CMD_ CONTINUE]
    5. M<-S:  gID_OtapGetStatus_c [LIN_OTA_STATUS_ RUNNING,frame_sqn]
    6. M->S:  gID_OtapData_c [data1, data2, …data8]

    7. M->S:  gID_OtapCmd_c [LIN_OTA_CMD_ END]
    8. M<-S:  gID_OtapGetStatus_c [LIN_OTA_STATUS_FINISH,0]</font>
    复制代码
    在发送gID_OtapData_c数据时,LIN Master每发送一帧前均要先读取Flash指定偏移的8字节数据,再传输给LIN Slave。LIN Slave收到数据后,将其写入Flash指定偏移区域。这种方式实现较简单,但收发每一帧gID_OtapData_c数据都要读写Flash,特别是选用外部Flash作存储,每帧均需SPI读写操作,会导致整体传输速度很慢。


    我们在这里做个优化,按块来进行读写,即LIN Master先读取一块数据放在发送缓存里,比如1K字节,然后再通过多帧gID_OtapData_c依次将缓存里的数据进行传输;LIN Slave接收到每帧数据将其依次接收缓存里,在接收完完整的一块数据后,再将整块数据写入外部Flash。另外考虑到一帧gID_OtapData_c携带数据太少,gID_OtapCmd_c、gID_OtapGetStatus_c可每发送一整块数据执行一次,gID_OtapGetStatus_c第二字节改为表示块的序列号,避免过多冗余。以下是优化后的流程:
    1. <font size="3" face="微软雅黑">M->S:  gID_OtapCmd_c [LIN_OTA_CMD_START]
    2. M<-S:  gID_OtapGetStatus_c [LIN_OTA_STATUS_READY,block_sqn]
    3. M->S:  gID_OtapData_c [data1, data2, …data8]
    4. M->S:  gID_OtapData_c [data1, data2, …data8]

    5. M->S:  gID_OtapCmd_c [LIN_OTA_CMD_ CONTINUE]
    6. M<-S:  gID_OtapGetStatus_c [LIN_OTA_STATUS_ RUNNING,block_sqn]
    7. M->S:  gID_OtapData_c [data1, data2, …data8]
    8. M->S:  gID_OtapData_c [data1, data2, …data8]

    9. M->S:  gID_OtapCmd_c [LIN_OTA_CMD_ END]
    10. M<-S:  gID_OtapGetStatus_c [LIN_OTA_STATUS_FINISH,0]</font>
    复制代码
    4.3  固件切换


    当LIN Slave收到gID_OtapCmd_c携带的LIN_OTA_CMD_END命令时,说明此时固件数据已经传输完成,准备切换动作。按照原先OTAP机制,将新固件大小(Imagelength)、所需扇区(Sector bitmap)写入所存储Flash区域的固定头部位置,写入有效的bootflag,复位后second bootloader即将新固件拷贝到内部Flash指定位置,实现切换。需要提出的是,如果采用内部Flash作升级存储,还需先在新固件存储地址0偏移位置写入开始标签(Startmarker)[0xDE, 0xAD, 0xAC, 0xE5],再写Image length和Sectorbitmap。


    五、后记


    由于LIN总线传输速度限制为20kbps,按照常用的19200bps计算,header部分为13+1+10+10=34bits,response部分为(8+1)*10=90bits,一帧数据实际传输时间需要(34+90)/19200=6.46ms,加上 header和response间最大40%的余量,一帧约需9.04ms。理论上传完200k数据约需4min。在上述方案中,加上每个block间额外的gID_OtapCmd_c、gID_OtapGetStatus_c、帧与帧间的延迟、读写Flash的时间消耗等,会比理论纯数据传输时间要长一些。上述方案实际测试200k固件传输约需6.5min。


    如果1个LIN网络存在多个Slave节点且功能相同,可对挂在同根总线上的多个Slave节点同时进行升级,理论上传输速度与单节点基本一致。




    作者:叶国欣Aaron@NXP     文章出处:恩智浦MCU加油站

    签到签到
    回复

    使用道具 举报

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

    本版积分规则

    关闭

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

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

    GMT+8, 2024-4-26 07:43 , Processed in 0.118858 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

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