S32K144调试记录(二)

最近在调试一个新板子,我负责板子上通讯模块程序的编写和调试,调试的时候断断续续遇到不少问题,在这里记录下。

调试CAN收发过程中遇到的问题

1 现象

接上篇CAN/LIN的收发程序调通了之后,紧接着整体的路由功能也都实现了,本以为皆大欢喜,后面用板子试了上下电,再进行测试发现,CANoe只能接收几次由CAN2在接收中断里执行的发送任务里发出的报文,而不是稳定的报文。接下来就开启了漫长的调试过程。

2 调试

这路CAN的功能其实很简单,有两个任务,任务一:接收到其他节点发过来的标准CAN报文,触发中断调用Callback函数,在Callback函数里把接收到的报文原封不动的传回去、任务二:每隔100ms就周期性的发几帧报文。以前的项目里这两个功能其实已经实现过了,所以觉得很奇怪为什么板子冷启动之后突然就不行了。
从现象来看,CANoe是可以通过CAN2发出报文,这说明CAN模块是可以正常接收报文,因为如果板子没法接收报文,CANoe会报Not Acknowledge,同时,在CANoe端也可以读到通过CAN2发出的报文,说明板子的CAN模块是可以正常工作的,所以分析直接原因是板子的冷启动导致了CAN2在上电后只能几次进入中断,再往后就无法进入中断调用Callback函数。
开始怀疑是把两路CAN的接收函数放在同一个while(1)里导致的,尝试了把两个接收函数放到了两个任务里,结果仍然进不去中断。
然后怀疑其他模块的任务占用了我的CAN的资源,然后把其他任务注释掉,发现还是不行。
这时候同事提醒我可能不是CAN没有进中断,而是压根CAN没收到报文。那为什么CANoe显示发送成功了呢?因为Transiver或者MCU接收到报文后在硬件层面回复了ACK,但是实际上并没有把收到的报文放到邮箱里,也就不会触发中断调用Callback函数。那么到这里,立马验证到底是没有进中断还是没有进Receive()函数。
这里用到一个小技巧,可以用示波器读MCU输出引脚电平状态的方法,不需要MCU在DEBUG模式下,就可以观测到函数有没有运行到设定的地方。
由于没有直接使用中断函数,而是调用的Callback函数,所以在Callback函数里加入了电平翻转的功能,只要Callback函数被调用了,目标引脚的电平就会不停翻转。代码如下。代码下载进去,上下电后测试PTE3引脚的电平状态,短暂变化几次后保持不变。

void CAN1_TX_RX_Callback(uint8_t instance, flexcan_event_type_t eventType, uint32_t buffIdx, flexcan_state_t *flexcanState)
{
	if(instance == 1)
	{
		PINS_DRV_SetPinDirection(PTE, 3, 1);
					PINS_DRV_TogglePins(PTE, 1<<3);
	}
	else if(instance == 0)
	{
		//if(Msg_CanRxbuffer.msgId || Msg_CanRxbuffer.data[0])
		{
			PINS_DRV_SetPinDirection(PTE, 9, 1);
			PINS_DRV_TogglePins(PTE, 1<<9);
		}
	}
	if(eventType == FLEXCAN_EVENT_RX_COMPLETE)			//receive completed
	{
		//CAN1_HandleRequest();

	}
}

再往上查,FLEXCAN_IRQHandlerRxMB这个函数里调用了Callback函数,把电平翻转的代码插到这个函数里。

	if(instance == 1)
	{
		PINS_DRV_SetPinDirection(PTE, 3, 1);
		PINS_DRV_TogglePins(PTE, 1<<3);
	}
	else if(instance == 0)
	{
		//if(Msg_CanRxbuffer.msgId || Msg_CanRxbuffer.data[0])
		{
			PINS_DRV_SetPinDirection(PTE, 9, 1);
			PINS_DRV_TogglePins(PTE, 1<<9);
		}
	}
	 /* Invoke callback */
	 if (state->callback != NULL)
	 {
		 state->callback(instance, FLEXCAN_EVENT_RX_COMPLETE, mb_idx, state);
	 }

再次测试,现象与刚才的一致。再往上查,发现FLEXCAN_IRQHandler这个函数调用了FLEXCAN_IRQHandlerRxMB这个函数,于是插入调试代码。

     /* Check mailbox completed reception */
     if (state->mbs[mb_idx].state == FLEXCAN_MB_RX_BUSY)
     {
     	if(instance == 1)
     	{
     		PINS_DRV_SetPinDirection(PTE, 3, 1);
     		PINS_DRV_TogglePins(PTE, 1<<3);
     	}
     	else if(instance == 0)
     	{
     		//if(Msg_CanRxbuffer.msgId || Msg_CanRxbuffer.data[0])
     		{
     			PINS_DRV_SetPinDirection(PTE, 9, 1);
     			PINS_DRV_TogglePins(PTE, 1<<9);
     		}
     	}
     	FLEXCAN_IRQHandlerRxMB(instance, mb_idx);
     }

再次测试,现象与刚才的一致。于是把测试代码拿出这个if语句,判断是否与邮箱的状态有关。

    if(instance == 1)
   	{
   		PINS_DRV_SetPinDirection(PTE, 3, 1);
   		PINS_DRV_TogglePins(PTE, 1<<3);
   	}
   	else if(instance == 0)
   	{
   		//if(Msg_CanRxbuffer.msgId || Msg_CanRxbuffer.data[0])
   		{
   			PINS_DRV_SetPinDirection(PTE, 9, 1);
   			PINS_DRV_TogglePins(PTE, 1<<9);
   		}
   	}
       /* Check mailbox completed reception */
   if (state->mbs[mb_idx].state == FLEXCAN_MB_RX_BUSY)
    {
        FLEXCAN_IRQHandlerRxMB(instance, mb_idx);
    }

经过测试,终于发现了异常。当把测试代码放到判断邮箱状态上一行后,此时PTE9引脚的输出电平会一直变化。也就是说每次代码执行到这里时邮箱都是FLEXCAN_MB_RX_IDLE的状态,然后通过查看CAN模块的FLEXCAN_DRV_Receive函数

    /* Start receiving mailbox */
    if(state->mbs[mb_idx].state != FLEXCAN_MB_IDLE)
    {
        return STATUS_BUSY;
    }
    state->mbs[mb_idx].state = FLEXCAN_MB_RX_BUSY;
    state->mbs[mb_idx].mb_message = data;
    state->mbs[mb_idx].isBlocking = isBlocking;

    /* Enable MB interrupt*/

    result = FLEXCAN_SetMsgBuffIntCmd(base, mb_idx, true);

    if (result != STATUS_SUCCESS)
    {
        state->mbs[mb_idx].state = FLEXCAN_MB_IDLE;
    }

只有当程序判断当前邮箱是FLEXCAN_MB_IDLE的状态,才会开启邮箱的接收过程,同时更改邮箱状态为FLEXCAN_MB_RX_BUSY。
这说明,之前所有的测试例子中,由于某种原因,邮箱是FLEXCAN_MB_RX_BUSY的状态,导致无法将data里的数据放入到邮箱中,邮箱无法启动接收过程。但是由于上一次接收成功的过程中已经打开了中断使能,所以后面无论接收过程是否被启动,只要MCU的硬件完成了接收过程,就会触发中断进入到中断服务程序。
定位到问题后,再用CAN0_ReceiveData函数验证一下是否正确。

status_t CAN0_ReceiveData(void)
{
	status_t result;

	result = FLEXCAN_DRV_Receive(INST_CANCOM1, RX_MB_64BYTES, &Msg_CanRxbuffer);
    if(result == STATUS_SUCCESS)
    {
		PINS_DRV_SetPinDirection(PTE, 3, 1);
		PINS_DRV_TogglePins(PTE, 1<<3);
    }
	return result;
}

经过测试,发现PTE3输出电平不会翻转,验证了判断是正确的。
到这一步,问题排查就很清晰了。用测试代码一层一层往上测试,发现负责通讯的任务都挂掉了。再测试其他任务,发现都是挂掉的状态。
经过与硬件工程师讨论,板子上负责芯片供电的VDD本来有一颗电解电容用于稳压的,由于时间原因没有焊上去,导致输出电压不稳定,也间接让程序里多个用软件计时的任务挂掉了。而用硬件计时的定时器任务完好无损,这也是为什么定时器中断函数能够稳定的执行,并稳定的输出100ms的CAN报文。
VDD输出波形见下图。
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值