最近突然想拿systick做计时时钟,便在MCUXpresso Config Tools的外设框架中开启了systick模块,但开启后,突然发现打印会打印一半后丢失。
具体现象如下:完整的打印是这样:
LPI2C board2board polling example -- Master transfer. Master will send data : 0x 0 0x 1 0x 2 0x 3 0x 4 0x 5 0x 6 0x 7 0x 8 0x 9 0x a 0x b 0x c 0x d 0x e 0x f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f Error occurred in the transfer ! LPI2C board2board polling example -- Master transfer. Master will send data : 0x 0 0x 1 0x 2 0x 3 0x 4 0x 5 0x 6 0x 7 0x 8 0x 9 0x a 0x b 0x c 0x d 0x e 0x f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f Error occurred in the transfer !
但开了systick模块后,打印成了
LPI2C board2board polling example -- Master transfer. Master will send data : 0x 0 0x 1 0x 2 0x 3
排查过程
加断点调试
由于frdm-mcxa156板卡可以在线调试,因此第一步就想到了加断点,看看是不是串口变化了,但加串口后,发现并不是变化了,而是直接程序卡在打印位置了。因此排除串口配置变化,进而查询其他可能原因。
检查systick时钟
按照之前打开lpspi的经验,没报错至少说明时钟配置可用了。因此没去检查这部分,但现在有问题了,那就需要去检查一下时钟是否开启了。
这一检查, 还真发现时钟没有打开。
打开时钟后编译验证,问题依旧
对比rtthread上systick使用
对比rtthread中的mcxa156代码:
1. 发现rtt中直接使用了core_cm33.h中提供的SysTick_Config接口实现,该实现与nxp配置工具生成的实现相比,刚好多了个设置中断优先级的部分。因此直接把实现方法替换成调用SysTick_Config的实现,问题依旧。
2. rtt实现有处理systick中断,因此在代码的main.c中也添加systick中断处理函数,再次编译烧录,发现问题解决。
void SysTick_Handler(void) { }
根本原因确认
找到串口不卡死的办法了,剩下的就是找问题的原因。既然问题的原因是SysTick_Handler函数没有重写导致的卡串口,那就意味着有一个默认的SysTick_Handler函数,通过注释掉自己加的函数,在线调试的方式,定位到了默认的函数,实现如下:
.align 1 .thumb_func .weak SysTick_Handler .type SysTick_Handler, %function SysTick_Handler: ldr r0,=SysTick_Handler bx r0 .size SysTick_Handler, . - SysTick_Handler
从这段代码看,卡死的原因已经确定了。这个的实现就是把SysTick_Handler的地址加载到r0寄存器,之后跳转到r0指向的地址,合着这函数就是在无限的调用自己,没有退出机制。
总结
经过这么些定位,基本确定了A156如果要实现systick,那必须自行实现SysTick_Handler函数了,而且再一次检查中断函数的虚函数实现,会发现大部分涉及到中断的函数的默认实现都是SysTick_Handler这种实现,因此后续调试A153/A156芯片时,遇到开启新中断接口导致系统卡死的情况,可以优先确认对应的中断响应函数是否正确编写。