郭毅锋 郭世成 黄丽敏 张 栗
(①广西科技大学自动化学院,广西 柳州 545026;②成都大学机械工程学院,四川 成都 610106;③四川省技术转移中心,四川 成都 610095)
DSP 处理器采用哈佛结构和流水线技术,其接口资源丰富,控制精度高,运算速度快,被广泛应用在嵌入式系统的各类领域[1-2]。在DSP 嵌入式系统实际应用中,传统手动编写代码方式灵活但耗时长,而基于MBD 的代码开发方法因其便捷高效的特性被广泛采用,形成了一套标准的开发流程[3]。然而,MBD 代码在配合实现一些特殊功能,如Bootloader 时,其灵活性受到一定限制,不便开发。因此,如何在实际应用中实现MBD 代码的快速便捷下载是一个重要问题。
目前,DSP 芯片最通用的程序下载方式是通过JTAG(joint test action group)接口连接仿真器,并配合使用TI 提供的CCS(code composer studio)集成开发环境实现。这种方式在初期开发和调试阶段非常便利和高效[4-5]。但在实际应用中,嵌入式设备通常需要进行封装保护。在下载程序时必须打开嵌入式设备,这给此过程带来极大不便[6-7]。标准的JTAG 接口需要13 根引脚,不便于从封装系统中引出。即便成功引出JTAG 接口,过长的JTAG线可能会导致信号干扰问题,降低了程序升级的可靠性,同时还需要额外的驱动电路,增加了硬件成本。同样地,通过串口下载也面临着这个问题,需要额外引出接口,十分不便。
在这一背景下,CAN 总线则成为较为理想的下载方式,尤其在汽车电子领域表现出色。CAN总线仅需2 根引脚,具有实时性强、传输距离远、抗电磁干扰等优势。在实际应用中,CAN 总线既可以用于不同设备间的通信又可以兼顾下载的功能,无需额外引出下载接口,是较为理想的下载方式。
本文以TMS320F28335 为例,针对嵌入式系统中在实际应用中MBD 代码的下载需求,开发了一种适用于MBD 代码的基于CAN 通信的Bootloader方案,解决了在实际应用和现场快速调试过程中,快速且便捷更新程序的问题。通过利用CAN 通信技术,实现对MBD 代码的快速、稳定地下载。在方案设计过程中,对MBD 代码的结构进行了深入分析。基于分析结果,设计了合理的Boot 程序和MBD 程序方案和内存划分方案,减少了对MBD 代码的大幅度改动,无需在程序更新时重复修改MBD 代码,确保了程序下载的有效性和稳定性。在具体实现过程中,开发了相应的Boot 程序和上位机程序,详细介绍了Bootloader 的实现流程,并对关键步骤函数进行了深入的分析与解释。通过CAN 通信实现了对MBD 代码的快速下载,实验结果表明该方法稳定可靠且具有实用性。
该方法的整体结构如图1 所示,包括CAN 调试卡、上位机、Boot 程序和MBD 程序。Boot 程序和MBD 程序是两个独立的工程,Boot 程序预先通过JTAG 接口与仿真器连接下载到DSP 的Flash 中,实现与上位机的通信、擦除Flash、烧写程序、引导程序等功能。在Simulink 中搭建程序模型,自动生成代码后,导入CCS 中,修改MBD 代码底层文件,编译转换生成MBD 程序的.bin 文件,将.bin 文件传输到上位机中,准备进行烧写操作。重启设备,上位机调用CAN 调试卡,将MBD 程序通过CAN通信协议烧写到目标设备的指定Flash 区域。传输完成后,Boot 程序执行引导功能,跳转到MBD 程序所在的Flash 区域开始运行。通过这一流程,MBD代码便可成功通过CAN 通信下载到TMS320F28335嵌入式系统中并运行,满足了实际应用中的需求。
图1 方法结构图
将Simulink 中搭建的模型进行自动代码生成后,导入到CCS 中,整个目标程序的结构如图2 所示,主要分为三部分。
图2 MBD 代码结构图
(1).c 类文件中包含了由模型生成的代码以及主程序,还包括使用到的ADC、CAN、eCAP 等功能函数,这些文件负责实现系统的逻辑功能和算法。
(2).asm 文件用于编写底层指令、优化性能、实现硬件接口和处理中断等任务,通过直接控制DSP 处理器,开发人员能够更加灵活地利用硬件资源,实现高效的数字信号处理。.asm 文件提供了对DSP 处理器的底层控制能力,从而满足特定的系统需求。
(3)Include 文件夹中包含了一些必要的库函数以及.cmd 文件,这些文件用于引用所需的库函数和定义内存分配等信息,帮助实现系统功能的完整性和正确性[8-9]。
Bootloader 的核心是把Boot 程序与MBD 程序烧写到不同的Flash 空间中,Boot 程序与MBD 程序应是相互独立的两个功能[10-11]。因此要对Boot 程序与MBD 程序进行合理的功能与内存空间分配,保证两者互不影响,同时减少对MBD 程序的改动。对于手动编写的程序,只需要修改工程中的.cmd 文件即可对内存进行划分,而对于MBD 自动生成代码的程序来讲,根据MBD 代码结构分析,则需要在Includes 目录下的C:ProgramDataMATLABSupport PackagesR2021b oolbox argetsupportpackages ic2000src(不同PC 安装目录不同)目录下找到对应芯片的.cmd 文件。以TMS320F28335 芯片为例,对应的是c28335.cmd 文件,通过修改c28335.cmd 文件对Flash 空间进行分配。
根据c28335.cmd 里的信息,可以得知MBD 自动生成代码后,不同于传统的Flash 分配方式,将0x300000-0x33fff6 划分为A-H 区(图3a),MBD生成的代码将Flash 空间划分成了一个区域(图3b)用于存储应用程序,保证应用程序最大限度利用Flash 空间存放应用程序,并没有考虑其他功能的扩展。0x33fff6-0x33fff8 则保持一致,被分配为应用程序的BEGIN 区。
图3 默认内存划分
图4a 所示为经过修改后的MBD 程序的内存分配情况。与默认分区相比,修改后的分区把0×310000-0×310010 分配为应用程序信息区,用于存储MBD 程序的版本等相关信息,以便上位机读取当前应用程序版本;0×310010-0×310012 为应用程序的BEGIN 区,用于引导程序跳转到应用程序入口;0×310012-0×33ff80 用于存储应用程序。图4b所示为Boot 程序的内存分配情况,MBD 程序未使用的区域0×300000-0×310000 被用来存储Boot 程序;0×33fff6-0×33fff8 分配为Boot 程序的BEGIN 区。通过这样的内存划分方式,Boot 程序与MBD 程序以及它们各自的BEGIN 区完全隔离开来,位于不同的内存区域中,从而实现了两部分程序之间的相互独立,避免了彼此的干扰。
图4 修改后内存划分
CCS 进行编译时,通常会生成.out 文件。.out文件是一个完整的可执行文件,包含了代码、符号表和调试信息,用于在嵌入式系统上执行和调试,但不适用于直接烧写。CCS 可以将.out 文件转换生成.bin 文件,.bin 文件是目标代码的二进制表示,没有附加信息,通常用于直接烧录到目标设备的存储器中[12-13]。根据上述特性,如图5 所示,对于Boot 程序来讲,可直接利用仿真器通过JTAG 口下载.out 文件,对于MBD 程序来讲,通过CAN 通信烧写时则需要CCS 编译程序并转换成.bin 文件。
图5 程序格式
内存分配完成之后,需设计相应的Boot 程序与上位机程序。Boot 程序完成程序跳转、烧写程序等功能。上位机程序要与CAN 通讯设备配套,完成程序解析、指令发送等功能。
实现完整的Bootloader 功能需要Boot 程序和上位机之间的协同配合。程序流程如图6 所示。上电复位后,程序跳转到Boot 程序的BEGIN 区,BEGIN区中存放着codestart 函数。该函数包含一个跳转指令,将程序引导跳转至_c_int00 处,而_c_int00 函数将程序引导跳转至Boot 程序的main 函数。Boot程序完成CAN 通信初始化、系统初始化和中断初始化等功能,然后进入等待状态。定时器开始计时,如果定时器时间超时,则通过jump 函数直接跳转到MBD 程序的BEGIN 区。同样地,利用_c_int00函数将程序引导跳转至MBD 程序的main 函数处。
图6 程序流程
如果定时器时间未超时且接收到上位机的更新命令,则Boot 程序进入Boot 模式,并向上位机返回成功信息。上位机发送擦除MBD 程序区的指令,Boot 程序利用Flash_Erase() 函数擦除MBD 程序所在区域,并返回擦除成功信息。上位机解析MBD程序的bin 文件,并将程序分包发送。Boot 程序接收到程序数据后返回接收成功标志。然后上位机发送CRC 校验码,Boot 程序进行CRC 校验。如果校验成功,Boot 程序将程序写入预分配的MBD 程序Flash 区域,并返回写入完成信息给上位机。上位机判断是否发送完整个程序,如果未发送完毕,则重复执行分包-校验-发送指令的过程。如果发送完毕,则发送跳转指令。Boot 程序利用jump 函数跳转至MBD 程序的BEGIN 区,然后通过调用_c_int00函数进入MBD 程序。
为了确保Bootloader 功能的安全性,程序中对Flash 的操作需放到RAM 中运行。将Flash 操作代码加载到RAM 中可以避免更新操作覆写Boot 程序自身的风险,并提高执行速度和效率。然而,将Flash 操作代码加载到RAM 中会占用一定的RAM空间。因此,在将应用程序写入Flash 存储器时,需要将程序分成多个包进行传输,以适应可用的RAM 大小。
通过以上步骤和安全策略,实现了利用CAN通信烧写MBD 程序并跳转到MBD 程序入口点的功能,完成了板子的固件升级和功能扩展。
在Bootloader 更新过程中,需要进行错误处理。如图7 所示,Boot 程序实现了一系列故障处理流程,以确保安全和可靠的固件更新。
图7 故障处理流程
如果在擦除MBD 程序区时出现失败,Boot 程序会发送一条报文返回上位机擦除失败的信息,并提示相应的故障信息。上位机会接收并显示此信息,表示更新失败。这样可以及时通知上位机发生了擦除错误,避免继续更新过程,以保护设备免受潜在风险。
在接收程序过程中,Boot 程序会进行CRC 校验,以验证写入的程序数据的完整性和准确性。若某段程序的CRC 校验失败,上位机会重新发送相应的数据段。如果连续三次校验失败,则判定数据可能存在严重错误,终止程序更新,并向上位机提示CRC 校验失败。这样可以避免持续尝试更新错误的数据,防止设备损坏或产生不稳定的行为。
如果在写入Flash 的步骤中发生写入错误,Boot 程序会通过相应的错误指令将错误信息返回给上位机。同时,上位机会终止更新程序,确保不会将错误的数据写入设备。这样可以保护设备免受写入错误可能引发的问题。
jump 函数是实现Bootloader 功能中关键的跳转函数,如下所示,声明一个名为jump 的函数,接收一个uint32_t 类型的参数Addr,表示需跳转的地址。
在函数内部,通过将传入的地址强制类型转换为函数指针类型,然后调用该函数指针,实现了跳转到指定地址的功能。通过这样的方式,jump 函数可以实现跳转到指定地址。根据给定的地址,将控制权转移到该地址处的程序代码,实现从引导程序跳转到应用程序。在引导程序中,可以使用此代码来加载并执行应用程序,实现固件升级或切换应用程序的功能。
_c_int00 是一个特殊的函数,它在启动和重置过程中起到重要的作用。_c_int00 函数通常是由芯片厂商提供的汇编代码实现,用于处理启动和重置事件。如图8 所示,在Boot 程序中可利用jump 函数跳转到MBD 程序codestart 所在地址处,调用_c_int00 函数执行一系列系统初始化操作,为系统设置合适的初始状态,并最终跳转到MBD 程序的入口点。
图8 跳转流程
考虑到现场中CAN 总线上一些报文的干扰情况,本文在报文帧定义中进行了一定处理,为了保证通信的可靠性和一致性,在Boot 程序与上位机程序之间的通信中,需要明确指定报文的ID,通过固定报文的ID,Boot 程序和上位机程序可以识别和过滤掉其他无关的报文,避免无效报文的干扰和误解析,确保通信的可靠性和准确性[14-15]。
CAN 通信的8 个字节数据位的划分,基本的指令可以按照表1 进行定义。对于上位机,第一字节代表指令CMD,用于发送更新、清除等命令的指令代码;第二字节数据长度DA_LN 代表后续数据的长度,指示接收方应该读取多少个数据字节;第三到第八字节D1-D6 包含实际的数据信息,根据数据长度来确定字节数。对于Boot 程序,第一字节状态信息Status 返回Boot 程序执行状态的信息,用于指示操作的结果或错误情况;第二字节数据长度与第三到第八字节数据字节与上位机报文定义一样。
表1 报文格式
通过对报文的数据位的划分,可以在CAN 通信中明确定义数据的含义和格式。上位机可以将指令和数据按照预定的格式发送给Boot 程序,而Boot 程序接收到数据后可以根据定义的格式解析和处理数据。
对于这段开关互市的历史,在辽东满族民众的记忆中也有记载和表现。最有代表性的就是在桓仁地区采录的《老杲子》[注]夏秋主编:《满族民间故事·辽东卷》上卷,辽宁民族出版社,2010年,第12页。,其内容如下:
TMS320F28335 中,提供了两个CAN通道口CANA和CANB,选择CANA 通道进行实验,在Boot 程序中配置好通道口、波特率、帧类型、数据长度、发送和接收ID 等,然后按图9 测试环境进行硬件连接。连接完成后,通过JTAG 口预先将Boot 程序下载到TMS320F28335 的Flash 中,之后开始验证Bootloader 功能,操作步骤如下。
图9 测试环境1
(1)如图10 测试程序所示,在Simulink 中搭建任意一段CAN 通信的模型,实验中实现的功能是,将CAN 通信收到的信息重新发送出来。模型搭建完成后,通过编译自动生成代码。
图10 测试程序
(2)将代码导入到CCS 中编译转换生成.bin文件。
(3)将.bin 文件导入到上位机中。
(4)系统板上电,上位机发送更新命令,按照流程把MBD 程序烧写到划分好的Flash 区中,烧写完成后,自动跳转到MBD 程序的起始地址,MBD 程序开始运行。
此时,可测试MBD 程序是否下载成功且运行,如图11 所示,上位机发送ID 为1801D0D0,数据为00 11 22 33 44 55 66 77 的报文,TMS320F28335返回ID 为18F101D0,数据为00 11 22 33 44 55 66 77 的报文(图11)。上位机再次发送数据为20 23 07 20 10 47 04 的报文,TMS320F28335 同样可以返回发送的报文(图12),证明程序通过CAN 通信烧写成功,Bootloader 功能实现。
图11 测试结果1
图12 测试结果2
Boot 程序预先刷写完成后,如图13 所示,MBD 程序的下载也可以在CCS 中选择不擦除Boot程序的Flash 区,即可在保留Boot 程序的情况下将MBD 程序从JTAG 口烧写进去,方便程序开发与在线调试。下载完成后,程序运行结果与CAN 下载后的效果一致。
图13 JTAG 下载设置
在嵌入式系统应用场景中,设备之间的通信普遍是近距离的,通常在数米范围内,而CAN 总线中通常使用的波特率是500 kbps 或250 kbps。因此,如图14 所示,可靠性测试选择用3.3 m 的连接线连接到已装上外壳封装设备上,CAN 卡1 用于下载测试程序,CAN 卡2 用于总线上持续不断地发送不同数量的报文,作为总线上的干扰。以250 kbps和500 kbps 波特率,分别在有无干扰情况下对32 kB和27 kB 的程序.bin 程序进行下载试验测试,可以有效验证大多数嵌入式系统的应用场景。下载时间不计DSP 芯片自身擦除Flash 区的时间,测试结果及测试环境如下。
图14 测试环境2
(1)无干扰情况
为保证测试的准确性,下载时间均为连续实验3 次记录时间后的平均值。根据表2,下载32 kB的程序,在500 kbps 波特率的情况下,成功下载所需的时间约为7.527 s。且在不同情况下,平均下载速度约为4 kB/s,满足了现场调试的实际需求。
表2 无干扰测试
(2)有干扰情况
32 kB 大小的程序在不同干扰情况下进行了下载测试,考虑了不同数量的干扰帧。结果显示,程序在这些情况下都能够成功下载,而与没有干扰情况相比,下载时间只增加了约5%,见表3。即使在CAN 总线上连续发送最多12 个干扰帧的情况下,仍然能够保持一定的下载速度,有效验证了该方案的可靠性。
表3 干扰测试
通过实验得出,本文的方案能够在嵌入式系统中,在一定距离、不同波特率和总线受到干扰的情况下,实现对程序的稳定下载。且下载程序时,无需打开嵌入式设备,实现了在实际应用中的便捷快速下载,提高了现场调试以及实际开发中的效率和可靠性。
本文提出了一种基于CAN 通信的Bootloader 方案,旨在解决DSP 嵌入式系统中MBD 开发方式下代码的便捷下载需求。以TMS320F28335 为例,通过对MBD 代码的结构进行分析,在保证对MBD代码改动较小的情况下,设计了合理的Boot 程序和MBD 程序方案以及内存划分方案,确保程序下载的有效性和稳定性,并且无需在更新MBD 程序时重复修改代码;开发了相应的Boot 程序和上位机程序,详细介绍了Bootloader 的实现流程,并对关键步骤函数进行了深入的分析与解释。通过CAN通信成功实现了对MBD 代码的快速下载,实验结果表明该方法稳定可靠且具有实用性。并且具有一定的通用性,可运用到TI 的其他系列芯片中。