英飞凌TC2xx Bootloader开发全解析:从链接脚本到AB分区
简介:本资源是面向嵌入式开发工程师与汽车电子/工业控制领域技术人员的英飞凌TC2xx系列微控制器Bootloader开发套件,聚焦系统启动流程实现与固件升级机制设计。压缩包共206个文件,含159个头文件(.h)用于寄存器定义与接口声明、30个C源文件(.c)构成完整Bootloader逻辑(涵盖MCU初始化、CAN/UART通信加载、Flash编程、DCM诊断服务及Fls驱动等核心模块),另有2份关键英文用户手册PDF(TC27x与TC21x-TC23x系列)、链接脚本(.ld)、调试配置(.cproject/.prefs)及可执行映像(.elf/.hex),总容量36.71MB。已有3263人学习下载,开发者可直接基于源码理解TC2xx启动时序、定制安全校验与多协议升级功能,并结合手册快速掌握MCU平台特性与外设驱动集成方法。 拿到这个“英飞凌TC2xx_bootloader.zip”工程包的时候,我第一反应是松了口气——终于不是从零开始搭启动代码了。但第二反应也很真实:压缩包解出来那几十个文件,如果没人带着捋一遍,或者没有一两篇把关键路径讲透的文章,光靠自己去翻寄存器手册和链接脚本,确实容易在入口处卡个两三天。尤其是AURIX TC2xx这个系列,无论是TC264、TC275还是TC377,Bootloader的基本套路其实是相通的,但工程上的细节差异又足以让人抓狂。
这篇文章,我会以这个工程包为线索,把TC2xx平台上的Bootloader开发完整拆一遍。从方案选型、编译器选择、链接脚本定位,到启动跳转、Flash擦写、MCAN收发,再到AB分区和回滚状态机,最后落在一堆血泪排查记录上。不管你是要把UDS刷写挪到这个平台,还是只想写一个最简单的CAN升级Bootloader,这篇都能让你少走很多弯路。
1. AURIX TC2xx Bootloader的整体设计与方案选型
1.1 为什么TC2xx需要独立的Bootloader
很多刚接触AURIX的工程师第一反应是:Bootloader这种东西,不就是把收到的固件拿过来写进Flash吗?确实,原理上就这么回事,但放在TC2xx上要复杂得多。TC2xx作为汽车级多核MCU,常见应用是发动机控制器、变速箱控制器、域控制器。这些控制器一旦装上车,不可能再拿个调试器去连JTAG口刷程序,所以必须有一套通过CAN总线或者以太网完成固件更新的机制,这就是Bootloader存在的意义。
另一点经常被忽略的是,TC2xx并没有像某些MCU那样在芯片出厂时就固化一段不可覆盖的ROM Bootloader。AURIX的启动流程虽然内部也有BootROM(也就是启动固件),但它的作用是加载用户配置、执行启动模式选择,最终把控制权交给用户放在Program Flash里的代码。也就是说,你必须在PFLASH的首地址区域自行放置一段可用的引导程序,否则芯片上电后根本不知道该执行什么。这套引导程序就是我们说的Bootloader,它是整个软件栈真正意义上的“第一段代码”,承担着应用跳转和在线升级的双重责任。
我见过不少项目把Bootloader和应用代码放在同一个工程里,依赖链接脚本做了地址偏移,再加一个编译宏切换。这种方案在早期原型验证阶段问题不大,但到量产阶段就很容易出错。原因是升级一次App之后,Bootloader本身也被锁在同一个镜像里,一旦升级过程断电或者App写坏,整颗芯片就可能面临“假砖”。所以成熟的TC2xx项目,一定会把Bootloader和App工程分开管理,Bootloader位于PFLASH起始区域,应用编译后链接到更高的地址,两套代码互不干扰。
1.2 Bootloader方案选型:从裸机CAN到UDS差分
TC2xx平台上的Bootloader实现方案,根据项目需求可以分成几个档次。最简单的一种是“裸CAN接收型”,Bootloader启动后在收到特定ID的报文时进入升级模式,然后按照自定义协议一帧一帧接收固件数据,写完校验跳转。这种方案适合内部工具、单元测试或者早期样件刷写,开发量小,但协议不通用,也没有安全访问和会话管理,正经的OEM项目基本不会采用。
再往上一个档次是“类UDS型”。这类Bootloader仍然基于CAN报文传输,但报文格式和诊断服务参考ISO 14229标准。常见服务有0x10诊断会话控制、0x27安全访问、0x34请求下载、0x36传输数据、0x37请求退出传输,外加一些OEM自定义的例程控制服务。为什么OEM普遍偏好UDS?原因不只是标准,更重要的是诊断仪、产线和售后设备全都基于同一套协议栈,刷写工具链成熟,不用每款ECU单独开发一套上位机。
选择哪种方案,主要看三点:有没有明确的OEM规范要求;项目周期是否允许自己维护一套诊断协议栈;MCU资源是否足够应付后续的A/B分区和回滚逻辑。以我的经验,只要没有OEM规范强约束,就选最简单的方案先跑通硬件链路,再用类UDS协议做优化迭代。千万不要一上来就套完整UDS栈,否则光调试安全算法和会话超时就能吃掉你一周时间。
1.3 一个典型TC2xx Bootloader工程包的组成
打开那个工程包,一般会看到以下几种文件,每个目录都有明确职责。首先是芯片启动相关文件,通常是 c_start.c 、 Startup.c 或者由编译器厂商提供的启动代码,负责初始化栈指针、清除BSS、调用主函数。其次是链接相关文件,Tasking编译器下是 *.lsl 文件,HighTec下是 lcftc2xx.lsl ,这里面定义了芯片的Memory和段的分布,是Bootloader划分地址的核心。然后是硬件驱动层,包括 Mcu 、 Port 、 Flash 、 Can (或者 MCAN )、 Irq 等模块,AURIX生态里很多人会直接用英飞凌的iLLD库,也有一部分资深工程师习惯自己封装寄存器级驱动,两者各有优劣。
功能代码部分比较关键,负责升级状态的机架一般命名为 Fbl 或者 Boot ,里面包含主循环、报文解析、Flash写流程和安全校验。还有一个容易被忽视的目录是 Generated ,里面存放一些由配置工具导出的代码,比如MCAL或者EB tresos生成的配置。建议拿到工程后先看清楚这个目录下的配置是否和芯片型号匹配,因为我遇到过把TC275的工程直接改成TC264,结果外设基地址对不上,程序跑飞在初始化阶段的案例。
2. 开发环境、编译器与链接脚本:不解决这些后面全是坑
2.1 TC264常用编译器:Tasking、HighTec与GreenHills的差异
TC2xx系列的编译器选择,和普通MCU那种“随便用个GCC就行”的情况不太一样。AURIX核是英飞凌TriCore架构,主流的可选编译器包括Tasking、HighTec和GreenHills三家。其中Tasking的配套工具链在AURIX Development Studio里可以直接下载,工程模板齐全,很多从英飞凌参考设计出发的项目都会默认用它。HighTec则是很多国内团队的选择,它对GCC体系友好,支持导入部分Eclipse工程,但在编译器版本和启动文件上偶尔会有匹配问题。
如果一个TC264工程之前是用Tasking写的,你用HighTec重新编译通常会报一堆错误。最常见的错误包括:寄存器访问的内联汇编语法不同、编译器关键字 __attribute__ 的支持差异、链接脚本格式不兼容。这里我建议拿到工程包后先确认它的工程文件是 .project 还是 .cproject ,再确认Makefile或编译器路径指向的是哪个工具链。不要一上来就换编译器,那会让问题从“业务逻辑对不对”变成“环境能不能编过”,排查范围一下子失控。
从性能角度讲,TC264本身的资源对于Bootloader是够用的,不需要在编译优化等级上做太多文章。但有一点要特别留意:Bootloader属于“写一次,用一年”的代码,稳定性优先级永远高于性能。我推荐在Bootloader工程里把优化等级控制在 -O1 或 -O0 ,尤其是Flash擦写和跳转相关的代码,优化级别过高可能触发编译器对延迟函数的裁剪,导致时序错乱。
2.2 链接脚本在哪里找:Lcf和Lsl的定位方法
这是很多人在新手期被卡住的问题,标题里那个热搜词“英飞凌芯片的链接脚本在哪里找”绝非夸张。在Tasking环境下,链接脚本后缀通常是 .lsl ,在HighTec环境下分别是 .lsl 或者 .ld ,但最麻烦的是它们不一定在工程根目录下。有经验的做法是先在工程文件里搜索 Lcf_ 关键字,比如 Lcf_Tasking_Tricore_Tc27x.lsl ,或者直接在项目属性里看Linker选项卡中Script File的路径。
链接脚本的作用说白了就是告诉链接器“哪些段放哪块内存,哪些内存属于CPU0,哪些属于CPU1”。TC2xx是多核芯片,如果链接脚本里没定义CPU1的内存范围,那么CPU1的程序就可能被链接到CPU0不允许访问的区域,运行时直接触发Trap或者Bus Error。所以拿到任何TC2xx工程,第一步应该是打开链接脚本,确认 memory 段里的 pfls0 、 pfls1 、 dfls0 、 lmuram 、 cpu0_dlmu 这些区域起始地址和大小,和你实际用的芯片型号对应。
很多网上流传的TC264工程,它的链接脚本里实际定义的是TC27x的内存,因为原作者是从另一个项目拷过来改的。你要是只编译不加载,可能一切正常;一旦加载进TC264去跑,会发现Flash地址越界或者变量访问异常。这里有个很实用的排查技巧:用编译器的map文件确认代码段实际地址,打开 .map 文件搜索 _start 或 main ,看它们的链接地址是否落在你期望的PFLASH区间内。
2.3 Bootloader与App的内存划分:地址表和分区策略
TC2xx的地址映射里,Program Flash的起始地址通常是 0x80000000 ,Data Flash的起始地址是 0xAF000000 。Bootloader要放在PFlash的最前面,因为芯片复位后BMHD(Boot Mode Header)里配置的启动地址通常指向 0x80000000 。那App放在哪里呢?常见做法是把App固定在 0x80100000 之后,前面预留足够空间给Bootloader存放自己的代码、Calibration常量和Bootloader配置块。
我以一个TC264的项目为例,它的划分可以做成这样:
| 区域 | 起始地址 | 大小 | 说明 |
|---|---|---|---|
| Bootloader代码区 | 0x80000000 | 96KB | 存放Bootloader主体,链接脚本中只读段 |
| Bootloader参数区 | 0x80018000 | 16KB | 存放升级标志、校验值、配置参数 |
| App代码区(Bank A) | 0x80100000 | 1MB | 当前运行的应用程序 |
| App代码区(Bank B) | 0x80200000 | 1MB | 备份或者待写入的新程序 |
| Data Flash区 | 0xAF000000 | 入厂校准数据、DTC、刷写记录 | 绝对地址映射 |
这里有个细节:Bootloader和App之间不是简单“不重叠”就够了,还要考虑中断向量表的位置。TC2xx的中断向量表可以通过寄存器 BIV 设置,所以Bootloader阶段和App阶段需要各自配置 BIV 指向自己的向量表。如果你只在App里改了链接脚本,却忘了在Bootloader跳转前重新设置 BIV ,那App起来后中断基本全部跑飞。这个坑我见过不止一次。
3. Bootloader核心代码拆解:启动、Flash与MCAN
3.1 上电启动流程:从复位到App跳转
TC2xx进入Bootloader后,代码执行路径大概是:复位向量 -> 内核启动函数 -> 初始化时钟和看门狗 -> 初始化CAN和Flash相关外设 -> 进入主循环。主循环里第一步不是立刻做任何升级动作,而是给上位机留一个“升级窗口”。这个窗口一般控制在200毫秒到1秒之间,期间Bootloader持续监听CAN报文,如果收到合法的升级启动请求,就进入升级状态机;如果窗口超时还没收到指令,就校验App区域的有效性,然后跳到App。
跳转的核心是修改 PC 和 PSW 这些内核寄存器,不能简单地用一个函数指针调用。因为编译器生成的函数调用会维护栈帧,而跳转到App意味着整个上下文都要切换。一个常见的做法是先在C代码里读App的复位向量,然后通过内联汇编或者一段独立的汇编代码完成切换。下面给出一段在Tasking环境下经常可以看到的跳转代码:
/* AppInfo 里保存App的栈顶指针和复位向量 */
typedef struct {
uint32 stackPointer;
uint32 resetAddress;
} AppInfo;
void JumpToApp(const AppInfo *info)
{
uint32 sp = info->stackPointer;
uint32 pc = info->resetAddress;
/* 关闭全局中断 */
__disable();
/* 设置新栈指针 */
__mtcr(CPU_SP, sp);
/* 设置新PC,并进入Supervisor模式 */
__mtcr(CPU_PC, pc);
__mtcr(CPU_PSW, 0x0000AF00);
/* 处理器从新PC处取指执行 */
__debug_restore_context();
}
跳转前还有一个极容易被忽略的动作:把Bootloader期间使能的外设中断寄存器全部清掉,尤其是CAN中断和定时器中断。如果跳转前没有关闭这些中断,App还没执行完最基本的初始化,一个残留的CAN中断就能把App的运行环境打乱。更稳妥的做法是在跳转前调用 IfxCpu_disableInterrupts() ,并且把CAN模块的全局中断使能位手动关掉。
3.2 Flash擦写:TC2xx的PF/DF操作细节
TC2xx的Flash控制器和经典单片机不太一样,它没有那种把地址赋个值就自动写入的简单寄存器。Program Flash的擦写需要往命令寄存器里按顺序写入特定的命令序列,然后等待状态寄存器里的忙标志清除。这个过程在iLLD库里被封装成了 IfxFlash_eraseSector() 和 IfxFlash_write() ,而你如果自己手写驱动,就一定要搞清楚命令序列的时序。
擦除操作最需要注意的问题是“擦写期间不可访问同一条Flash总线上的代码”。说白了,如果你从PFlash里的代码执行擦除PFlash某个扇区的操作,而这个代码本身就在PFlash里,那么控制器在擦除期间可能会尝试取指,结果就是总线错误。怎么解决呢?常见办法是把Flash擦写函数放到RAM里执行,或者在擦写期间不产生任何中断和函数调用,只做等待。iLLD库提供的 IfxFlash_eraseSectorWithSegment() 已经考虑了部分场景,但在Bootloader这种“不能出错”的场景下,我仍然建议把Flash驱动函数放在RAM段里保险。
Data Flash的擦写逻辑和PFlash类似,但用途更杂。Bootloader通常会在DFlash里存几个标志字段,比如“App区是否有效”、“是否需要进入编程模式”、“上次升级是否成功”。这些字段的更新频率不高,但对掉电鲁棒性要求很高。建议把每个标志字段做成双备份,写入时先写一个预更新状态,校验通过后再更新主状态。这样即使写DFlash过程中突然断电,重启后也能通过预更新状态判断出上次升级没有完成,从而重新进入Bootloader。
3.3 MCAN中断类型与收发注意点
TC2xx的MCAN模块实际上是Bosch M_CAN的Tricore实现,它支持CAN FD,也支持经典CAN。很多网友搜“英飞凌 mcan 中断类型”,其实想搞清楚的就是MCAN到底有哪些中断需要处理。从功能角度分,MCAN中断主要包括:发送中断、接收中断、错误中断、状态变化中断(比如Bus Off恢复、协议错误)。在Bootloader里,接收中断是最核心的,发送中断可以轮询,错误中断必须开但处理逻辑要尽量简单。
接收中断的配置有几个细节值得讲。第一,MCAN的接收缓冲区是一个Message RAM,里面可以配置多个FIFO,每个FIFO由若干消息对象组成。Bootloader不需要像App那样支持几十个报文ID,一般只配置1到2个FIFO就够了。第二,MCAN的过滤器一定要配置精确。Bootloader的下行报文通常使用一个固定CAN ID,那么过滤器就只放行这个ID,其他报文一律不接收,避免把总线上其他ECU的报文误当成刷写数据。
中断服务函数里的任务要极简。我通常只做两件事:把原始报文拷贝到环形缓冲区,清中断标志,然后置位“收到新帧”的标志。真正的协议解析、地址计算和Flash写入全部放到主循环里。原因很简单,Flash擦写延迟动辄几十毫秒,如果你在中断里做Flash操作,整个CAN中断会被堵住,后续报文全部丢失,升级必然失败。
MCAN位定时的参数也要在这里提一句。TC2xx的MCAN挂载在某个外设总线上,你要先确认 fCAN 的时钟源是多少。比如某个工程里外设时钟是20MHz,目标波特率是500kbps,那么位时间就是40个时钟周期,预分频设置为1,再按同步段1、传播段7、相位段1和相位段2加起来配置。用工具生成当然是捷径,但手算一次能让你在Debug时更快判断出波特率误差来源。
4. 刷写协议、双分区与回滚实战
4.1 从自定义协议到类UDS报文:命令设计要怎么做
上一节解决了“怎么传输字节”的问题,这一节解决“传输的字节代表什么意思”。很多内部Bootloader的协议很简单:第一个字节是命令,第二个字节是数据长度,后面跟着数据,再来一个校验和。这种协议在几十个节点的小规模场合下够用,但到了项目规范化和产线自动化的阶段就不行了。
类UDS协议的好处是服务ID和报文格式标准化。哪怕你没用过CANoe,打开网上随便找的UDS刷写脚本,也能看出0x34和0x36的固定格式。以0x34请求下载为例,它的请求报文包括数据格式标识、地址长度和内存大小等信息,引导Bootloader帮你在指定地址预留空间。0x36传输数据则以块序列号加数据块的形式传输固件内容。0x37传输退出时Bootloader要返回整体校验结果。这一套流程下来,基本能满足产线和售后刷写要求。
命令设计上还有两个容易被忽略的点:第一个是“擦除命令”和“写入命令”要分离。直接在0x36写入时做实时擦除,会让单帧处理时间变长,CAN接收缓冲区容易被填满。我建议在0x31例程控制里定义一个“预擦除”子功能,刷写开始前先擦掉所有目标扇区,然后0x36只管写数据。第二个是安全访问。0x27服务要求Bootloader实现某种种子-密钥算法,哪怕是最简的算法,也要防住“不经授权直接刷写”的情况。
4.2 AB分区设计:为什么双分区能救回一次升级事故
简单Bootloader里的App只有一份,升级流程是“擦除旧App -> 写入新App -> 校验 -> 跳转”。这个流程最致命的问题在第一步和第二步之间:如果擦除之后写入失败,设备上电后没有任何可用的App,只能回到Bootloader重新刷写。只要Bootloader本身还在,就能救回来,怕的是连Bootloader也被一块儿擦掉,那种情况一般只能开盖用调试器。
AB分区方案就是为了把“擦除失败导致设备不可用”的概率降到最低。TC2xx的PFlash容量足够大时,可以划分出A、B两个Bank。Bootloader平时运行在A区的外部跳转到A区App,升级时往B区写入新镜像;全部写完并校验成功后,再更新一个“启动选择标志”字段,让Bootloader下次跳转到B区。这样A区的旧App始终保留着,直到新App确认没有问题,才在后续某次操作中把A区覆盖掉。
有人可能会问:AB分区是不是一定要配合UDS?不一定。AB分区只是存储布局和启动策略,通信协议可以仍然是自定义的。很多地方会把这个概念叫“A/B seamless update”或者“双系统升级”,本质上都是同一件事。对TC2xx这种面向汽车场景的芯片,启动选择标志务必放在Data Flash里独立于App区的固定位置,不要放在PFlash里,因为PFlash的擦写次数有限,而且写标志时如果触发了错误,恢复难度比DFlash大得多。
4.3 带回滚的下载流程状态机:断电也不怕
把AB分区落地到代码层面,就是一套升级状态机。它的核心状态包括: IDLE 、 WAIT_SESSION 、 ERASING 、 DOWNLOADING 、 VERIFYING 、 SET_ACTIVE 、 JUMP_APP 。每次收到有效的升级请求后,状态机从 IDLE 进入 WAIT_SESSION ;上位机请求擦除目标Bank后进入 ERASING ;擦除完成后进入 DOWNLOADING ,开始逐块接收数据;下载结束后做整体CRC校验,校验通过进入 SET_ACTIVE ,更新启动标志;最后复位重启,Bootloader根据标志跳转新App。
这套流程里最核心的设计是“任何一步失败都能回到上一个稳定状态”。掉电恢复逻辑做得好的Bootloader,在每次写入DFlash标志位之前都会先把“当前状态”记录成“上次升级未完成”,写完镜像后再把它改成“升级完成”。这样即使上位机一直不发送数据,或者CAN总线断了,TC2xx重启后仍然能进入Bootloader等待新的升级指令,而不是直接尝试跳转一个残缺的App。
回滚的另一层含义是“新App运行失败了怎么办”。严格意义上,Bootloader本身没法在App运行中判断App是否崩溃,因为App一旦启动,Bootloader的代码就不再执行。通常的做法是App自己跑一个“启动自检”,如果自检发现异常,就写一个“回滚标志”并软复位,让Bootloader在下一次启动时选择旧App分区。Bootloader要做的,只是在跳转前读取这个标志。
5. 台架与量产现场踩坑记录:问题排查速查
5.1 编译链接类:链接脚本不一致引发的“假成功”
第一个要说的坑,是链接脚本里Memory定义和芯片实际不匹配。曾有一次我把一个TC275的模板工程改成TC264,链接和编译全部通过,但程序一加载就卡在启动早期。后来打开map文件才发现,代码被链接到了TC275特有的LMU地址区域,而TC264的LMU地址范围小得多,访问直接落到保留地址上。这个问题如果不用Lauterbach的Trace32去看PC指针,光看日志很难发现。
排查这类问题的思路其实很简单:拿到map文件,确认 _start 或者 main 的地址落在预期范围内。比如Bootloader编译出来, main 应该在0x80000000之后的低地址区;App编译出来, main 应该在0x80100000或者0x80200000区域。如果发现地址不对,就直接查链接脚本的memory段和段放置规则。不需要去怀疑编译器,绝大多数这类问题都是脚本问题。
5.2 跳转与启动类:跳转后HardFault和看门狗超时
跳转后HardFault是Bootloader开发里最高频的问题。我会先检查两件事:跳转时是否关闭了全局中断; AppInfo 里的栈地址是否是一块有效的RAM。以TC264为例,CPU0的本地RAM通常位于 0x70000000 附近,Bootloader的栈和App的栈如果重叠,即使跳转瞬间不崩,等App压栈几次也会踩到异常。解决办法是App工程里重新设置栈指针,不要让App继续沿用Bootloader的栈。
另一个反复出现的坑是看门狗。有些TC2xx工程默认开启了Safety看门狗,Bootloader里一直喂着狗,跳转前也顺手喂了一次,但App起来后如果初始化时间较长,会触发Safety Watchdog超时复位。这个问题的排查方法是看复位原因寄存器,TC2xx里可以通过 SCU_RSTSTAT 判断复位源。如果是看门狗复位,就在App启动头里把喂狗提前到时钟初始化后,这个动作要尽量早。
5.3 Flash与通讯类:擦写中断冲突和掉电恢复
Flash擦除期间被中断打断,是Bootloader从“能跑”到“稳定跑”必须跨过的一道坎。你说中断优先级足够高,但高优先级中断服务函数里的代码如果恰好在PFlash里,它一样会触发总线错误。最简单的规避方法是在擦写前关闭全局中断,擦写完成后再打开。Bootloader阶段不像App那样有严格的实时性要求,这种“粗暴”做法反而最可靠。需要注意的是,关闭中断总时长如果超过MCAN的接收超时,上位机那边会检测到通讯超时,所以建议控制单次擦除的扇区数量。
掉电恢复则是量产现场最让人头疼的问题。写入App的过程中断电,下次上电后怎么处理?很多Bootloader的设计是“优先停留在Bootloader”,也就是说只要检测到App区不完整或者升级标志是“未完成”,就不跳转App,一直等待上位机重新升级。这个策略会导致售后返修时设备“看起来不工作”,但至少不会变成“彻底变砖”,而且可以通过诊断仪在几分钟内完成修复。从工程角度,这个取舍是值得的。
5.4 常见问题速查表
| 现象 | 可能原因 | 处理动作 |
|---|---|---|
| 编译通过,下载后不运行 | 链接脚本地址错误或启动文件缺失 | 查看map文件,确认复位向量和main地址 |
| 跳转App后HardFault | 栈指针无效、中断未关、BIV未设置 | 检查AppInfo结构体数据,跳转前关中断设置BIV |
| CAN收不到报文 | MCAN时钟或位定时配置错误 | 用示波器/逻辑分析仪测CAN_TX波形,核对波特率 |
| 擦写Flash时Bus Error | 代码执行或中断访问了正在擦写的Flash | 将Flash驱动放入RAM,擦写前关闭中断 |
| 升级一半断电后无法启动 | 缺少掉电恢复标志或App区已被部分擦写 | 增加DFlash状态标志,启动时校验App完整性 |
| App启动后周期任务异常 | 多核CPU的变量初始化和同步未做 | 检查Linker脚本中CPU1和CPU2的RAM放置 |
最后再分享一个我个人的习惯。无论Bootloader的方案做得多完善,我都会在开发早期先写一个“最小可刷写验证”:只实现CAN接收、一个固定地址的Flash写入和一个跳转函数,把链路从电脑到总线再到芯片全部打通。这个验证跑通之后,再往里面加协议、加安全访问、加AB分区。不要在一开始就想着把UDS完整栈和所有车规功能都塞进工程里,那只会让调试难度成倍上升。TC2xx的Bootloader说难也难,说简单其实也就是“启动、CAN、Flash、跳转”这四个关键词的组合,把这个组合玩透了,后面再多功能都只是往里填代码的事。
更多推荐


所有评论(0)