在嵌入式MCU软件开发过程中,通常我们需要先通过调试器(Debugger,带程序下载(Flash/EEPROM擦除、编程和校验)功能)或者仿真器(Simulator,不带目标MCU编程功能)对所写代码的功能进行在线调试(online-debug),验证确认其功能符号设计需求后,在发布编程文件--S19/HEX/BIN给生成部门通过离线编程器(Offline/Stand-aloneFlashProgrammer)进行批量编程。批量(MassProduction,简称MP)的产品正常工作时是不需要/也不能连接调试器或者仿真器的(一方面,调试器和仿真器价格高昂,成本不允许,另一方面,无法满足系统设计的产品重量和空间要求),因此,我们开发的嵌入式MCU应用软件/程序必须保证其在拔掉调试器和编程器或仿真器后依然能够功能运行正常,即离线工作(Offline,也称作脱机工作)正常。
在实际工作中,大家常常会遇到,开发阶段在线调试工作正常的代码,离线工作时却无法正常工作,这是为什么呢?
本文就基于NXP的汽车级MCU(S32K1xx/KEA,S12(X),MaginiVS12Z和QorivvaMPC56/57xx系列)为例,给大家介绍这类问题的可能原因和具体解决办法,希望对大家有所帮助。
1.应用工程的编译目标为debug_RAM,将代码编译链接到SARM而非Flash
在嵌入式MCU的软件开发集成环境(IDE)中创建应用工程时,通常都会创建至少两个编译目标(buildtarget)和调试目标(debugtarget),使用不同的连接文件,将编译结果分别编译链接到SRAM和Flash中。
比如,在NXP的S32DSIDE中,创建S32K1xx系列MCU的应用工程时,就会自动创建Debug、Debug_RAM和Release三个编译目标,其差异和用途如下:
Tips:a.由于S32DS是基于GNU的工具链,编译器(gcc)优化这一块目前还不是很完善,建议量产时还是使用Debug的编译目标,以保证编译结果能够正常运行;
b.编译目标中工具链的调试信息等级(debuglevel)配置,只影响elf文件中包含调试信息的多少,不会影响最终生成的编程文件(S19/HEX/BIN)大小,因为编程文件中不包含任何调试信息;
c.一个S32DS工程可以有多个编译目标,用户可以通过工程属性创建、配置不同的编译目标实现应用工程的个性化编译;
因此,若选择使用Debug_RAM作为在线调试时的编译目标,并通过Debug_RAM调试目标下载,则编译结果未被下载到Flash中,因此,拔掉调试器,无法正常工作。
解决办法:切换应用工程的编译目标和调试目标为debug/release,将应用程序编译链接并下载到掉电不丢失的Flash中。
2.启动代码(startupcode)未对使用的SRAM进行ECC初始化
为了保证嵌入式MCU应用程序的正常运行,提高系统的抗干扰能力,越来越多的MCU在其存储器中加入了单比特自动纠错,多比特检错的ECC功能。
对于此类MCU,由于调试器的下载脚本(在线调试时会被自动调用和执行)会对其SRAM进行初始化,若应用工程的启动代码(startupcode)未对其SRAM进行就去访问(读)SRAM,则会导致ECC错误,从而进入内核异常:
Tips:通常若MCU的SRAM带有ECC功能,在新建应用工程的启动代码中都会添加相应的ECC初始化代码,比如S32K1xx系列MCU的startup启动流程如下,在startup的第③步即对SRAM进行了初始化:
Notes:需要注意的是,对RAM初始化时使用的起始地址和长度信息来自应用工程的链接文件定义,若在SRAMsize小的MCUpartnumber(比如S32K144,SRAM为60KB,起始地址为:0x1FFF_8000~0x2000_6FFF)的应用工程上移植SRAMsize大的MCUpartnumber(比如S32K146,SRAM为120KB,起始地址为:0x1FFF_0000~0x2000_EFFF)应用程序,若不替换使用相应partnumberMCU的链接文件,则会出现SRAMECC未初始化即使的情况。
解决办法:Doublecheck应用工程的启动代码(startupcodes),确保CPU内核在访问(读取)SRAM之前有对其使用的全部地址空间进行了ECC初始化。
3.FlexNVM(EEE--EmulatedEEPROM)分区失败
在NXP的S12XE系列MCU和S32K1xx系列MCU中都使用了EEE(EmulatedEEPROM,硬件状态机模拟EEPROM),在访问之前,必须对其进行分区(partion),而能够成功分区的前提是EEE所使用的D-Flash/FlexNVM和分区信息储存区域(FIR)为擦除状态。在CodeWarrior和S32DSIDE的下载脚本中,通常都会有相应的擦除命令(eraseallblock或者masserase)完成这些操作,从而在线调试时,其分区一定能够成功,而量产时,没有调试器连接下载的过程,若在应用程序中未做相应的容错处理,在EEE分区失败的情况下,继续使用EEE,则会到MCU工作异常。
4.RAM初始化时未关闭看门狗,导致看门狗超时复位
离线工作时,这些下载脚本和Flash算法将不会被执行,若应用工程的启动代码(startupcode)在进行RAM初始化(.data段和.bss段初始化)之前未关闭片上看门狗,而且应用工程中使用的全局变量又比较多的情况下,就极容易出现看门狗超时复位,从而无法正常执行应用程序。
解决办法:Doublecheck应用工程的启动代码(startupcodes),确保在进行RAM初始化之前,MCU的片上看门狗已经被关闭(disabled)。
5.内部参考时钟(IRC)没有校准(trim),导致系统和外设时钟误差过大
通常为了降低系统成本和提高时钟的稳定性,在嵌入式MCU中都集成了一定数量的内部参考时钟(IRC-InternalReferenceClock)。但是,有工艺设计的限制,这些IRC时钟源的频率误差都比较大,必须经过校准(trim)才能够提高MCUdatasheet中所规范的时钟精度。
在MCU的调试器下载脚本或者Flash算法中,往往会提供MCU片内IRCtrim的功能,从而保证,在线调试时,MCU能够使用比较稳定的IRC参考时钟。若在MCU的启动代码中缺少IRCtrim的功能代码,则会导致脱机运行时,依赖IRC时钟的内核和外设模块工作异常。
解决办法:若使用IRC时钟作为参考时钟的外设模块对时钟精度要求比较高,请务必在应用工程的启动代码中添加IRC时钟的trim代码。
6.bootloader跳转导致APP程序启动或者时钟、外设初始化失败
在带有bootloader功能的嵌入式MCU产品(比如汽车ECU)开发中,bootloader也是一个完整的应用工程,它也会对目标MCU的系统时钟和外设模块(至少bootloader获取更新firmware需要的通信外设模块(比如CAN或者UAR)T和定时器(比如看门狗或者用于通信超时的timeout硬件定时器模块)以及GPIO模块)进行初始化,这些时钟资源和外设模块,若在跳转在应用程序(APP)之前未复位,则会影响APP功能代码再次初始化系统时钟和外设模块,从而导致产品功能异常。
APP工程单独下载在线调试时,不存在bootloader的影响,因此工作正常,而加入bootloader后,则必须考虑以上影响。
解决办法:在bootloader初始化MCU系统时钟和外设模块之前(越早越好,可能的话在bootloader的复位函数中通过汇编指令实现最好),就读取并判断APP更新状态,跳转到APP。若有APP更新需求,在运行bootloader功能代码完成APPfirmware更新之后,通过看门狗或者软件复位重新运行bootloader再跳转,而不是更新完成后立即跳转到APP。
7.使用调试器的半主机(semi-host)模式重定向printf()到debugconsole输出调试信息
通过调试器的半主机(semi-host)模式重定向printf()到debugconsole输出调试信息,从而可以调用printf()函数格式化打印调试信息,将在线调试时关心的寄存器和变量数据等输出到debugconsole,从而提高调试效率。
8.外设模块冻结模式配置影响其功能配置失败
除了以上列出的可能原因,为了在线调试时的减少CPU内核的中断或者方便量产时产生生产,嵌入式MCU的很多外设模块和存储器控制器有冻结模式(Freezemode,也就是调试模式)控制位,从而可能会导致外设模块在调试模式和离线工作的正常模式下功能有所差异。
总结
本文以NXP的汽车级MCU(S32K1xx/KEA,S12(X),MaginiVS12Z和QorivvaMPC56/57xx系列)为例,给大家介绍MCU在线调试正常,但离线工作异常这类问题的可能原因和相应的解决办法。