记录一下关于MPC5744P CAN总线学习

记录一下关于MPC5744P CAN总线学习

主要是针对开发出BUSOFF管理,NXP感觉都是一个套路,无论powerpc,还是arm_m

status_t FLEXCAN_DRV_Init(uint8_t instance,flexcan_state_t *state,const flexcan_user_config_t *data)

先从这个函数起

 if(FLEXCAN_IsEnabled(base))
    {
        /* To enter Disable Mode requires FreezMode first */
        FLEXCAN_EnterFreezeMode(base);
        FLEXCAN_Disable(base);
    }

首先是判断当前CAN是否ENABLE ,如果是ENABLE,比如你是从error_handle调用这个初始化函数,那么首先进入FLEXCAN_EnterFreezeMode(base);下面分析下这个函数

 base->MCR = (base->MCR & ~CAN_MCR_FRZ_MASK) | CAN_MCR_FRZ(1U);
 base->MCR = (base->MCR & ~CAN_MCR_HALT_MASK) | CAN_MCR_HALT(1U);
 首先将MCR reg中的FRZ HALT bit 清除 然后置位 
 if (((base->MCR & CAN_MCR_MDIS_MASK) >> CAN_MCR_MDIS_SHIFT) == 0U)
	{
		enabled = true;
	}
else
	{
		base->MCR &= ~CAN_MCR_MDIS_MASK;
	}

如果enable==true,就不用开启和关闭时钟,如果enable==false,则先开启,后面又关闭时钟

/* Check Low-Power Mode Acknowledge Cleared */
while (((base->MCR & CAN_MCR_LPMACK_MASK) >> CAN_MCR_LPMACK_SHIFT) == 1U) {}//Freeze mode cannot be entered while FlexCAN is in a low power mode.

如果是从低功耗模式唤醒的话,就等待LPMACK清零,否则该位直接为0,跳过

if ((((base->ESR1 & CAN_ESR1_FLTCONF_MASK) >> CAN_ESR1_FLTCONF_SHIFT) & 2U) != 0U)//ESR1 reg Fault_Confinement_State(第26-27位),01代表主动错误状态,11代表busoff,这里只要处于这两种状态之一,就进入以下流程
    {
    	/* Save registers before Soft Reset */
    	uint32_t tempIMSK[2],tempMCR;
    	tempIMSK[0] = base->IMASK1;//保存MB 中断寄存器状态
    	tempIMSK[1] = base->IMASK2;
    	tempMCR = base->MCR;//将整个MCR保存
    	/* Soft Reset FlexCan */
    	base->MCR |= CAN_MCR_SOFTRST(1U);//软重启一次
    	while (((base->MCR & CAN_MCR_SOFTRST_MASK) >> CAN_MCR_SOFTRST_SHIFT) != 0U) {}//等待软重启完毕
    	/* Restore registers after Soft Reset */
    	base->IMASK1 = tempIMSK[0];
    	base->IMASK2 = tempIMSK[1];
    	base->MCR = tempMCR;
    	}
    	else
	    {
	    	base->MCR = (base->MCR & ~CAN_MCR_HALT_MASK) | CAN_MCR_HALT(1U);//不知道为啥再次置位,注意这句 No reception or transmission is performed by FlexCAN before
this bit is cleared.
	    }
    while (((base->MCR & CAN_MCR_FRZACK_MASK) >> CAN_MCR_FRZACK_SHIFT) == 0U) {}//轮询该位等待确切进入frz mode
    if (false == enabled)
    {
    	base->MCR |= CAN_MCR_MDIS_MASK;
    	/* Wait until disable mode acknowledged */
        while (((base->MCR & CAN_MCR_LPMACK_MASK) >> CAN_MCR_LPMACK_SHIFT) == 0U) {}
    }

然后调用void FLEXCAN_Disable(CAN_Type * base)。关闭时钟。。。。
感觉和闹着玩一样,总之BUSOFF是从err_handle 进入的,肯定是FLEXCAN_IsEnabled(base)==true
下面梳理一下这个流程,感觉只要在err_handle中调用FLEXCAN_DRV_Init,然后在这个基础上修改,比如增加定时器,增加恢复次数计数,就可以进行BUSOFF快慢恢了。

中间一些设置波特率,运行模式之类的就不说了,无非就是一会开一会关反复横跳,说一下调用到FLEXCAN_Init 函数,这个函数里面会清除所有err_flag

/* Clear all error interrupt flags */
    (base->ESR1) = FLEXCAN_ALL_INT;

好了,不接着上面分析了,这两天简单搞了下BUSOFF机制,直接上代码

void Xcp_Can_Init(void)
{
	FLEXCAN_DRV_Init(INST_CANCOM1, &canCom1_State, &canCom1_InitConfig0);
	for(uint8_t i=0;i<16;i++)
	{
		XcpCan_Fliter_Config[i].isRemoteFrame=false;
		XcpCan_Fliter_Config[i].isExtendedFrame=false;
		XcpCan_Fliter_Config[i].id=XcpCan_Fliter_Table[i];
	}
	FLEXCAN_DRV_ConfigRxFifo(INST_CANCOM1,FLEXCAN_RX_FIFO_ID_FORMAT_A,XcpCan_Fliter_Config);
	CAN_2->CTRL1 |=CAN_CTRL1_BOFFREC_MASK;
	FLEXCAN_DRV_SetRxMaskType(INST_CANCOM1,FLEXCAN_RX_MASK_INDIVIDUAL);
	FLEXCAN_DRV_InstallEventCallback(INST_CANCOM1,XcpRxCallback,NULL);
	FLEXCAN_DRV_InstallErrorCallback(INST_CANCOM1, Xcp_err_callback, NULL);
	FLEXCAN_DRV_RxFifo(INST_CANCOM1,&XcpCan_RxFrame);
}

初始化CAN的时候要注册FLEXCAN_DRV_InstallErrorCallback(INST_CANCOM1, Xcp_err_callback, NULL);SDK里面ERR_INT,RXWARN,TXWARN,BUSOFF中断都是默认打开的,这里如果要使用这些中断,就要注册回调函数,所有这些中断的回调函数都是同一个函数,即FLEXCAN_DRV_InstallErrorCallback注册的函数,函数名由你自己定义,反正函数名不就是个地址嘛。这里推荐一下程序小拜的blog,里面也有讲解

void Xcp_err_callback(uint8_t instance, flexcan_event_type_t eventType,flexcan_state_t *flexcanState)
{
	uint32_t err_count=(uint32_t)(CAN_2->ECR);
	uint32_t rx_err_count=((err_count&0xffffffff00)>>8)&0xff;

	if(rx_err_count>=120)
	{
		rx_err_times++;
		if(pit_ch0_count >= 60)
		{
			can_bus_breakdown=1;
			PIT_DRV_StopChannel(INST_PIT1,0);
		}
		else
		{
			if(((PIT_0->TIMER[0].TCTRL)&0x1)==0)
			{
				PIT_DRV_StartChannel(INST_PIT1,0);
			}
		}
	}

	if(((FLEXCAN_DRV_GetErrorStatus(INST_CANCOM1)&CAN_ESR1_FLTCONF_MASK)>>4)==0x3)
	{
		busoff_count++;
		if(fast_recovery_times <5)
		{
			if(((PIT_0->TIMER[1].TCTRL)&0x1)==0)
			{
			  pit1_ChnConfig1.period=(uint32_t)fast_recovery_delay;
				PIT_DRV_InitChannel(INST_PIT1, &pit1_ChnConfig1);
				INT_SYS_SetPriority(PIT_Ch1_IRQn, 1U);
				PIT_DRV_StartChannel(INST_PIT1,1);
				fast_recovery_times++;
			}
		}
		else
		{
			PIT_DRV_StopChannel(INST_PIT1,1);
			pit1_ChnConfig1.period=(uint32_t)slow_recovery_delay;
			PIT_DRV_InitChannel(INST_PIT1, &pit1_ChnConfig1);
			INT_SYS_SetPriority(PIT_Ch1_IRQn, 1U);
			PIT_DRV_StartChannel(INST_PIT1,1);
			slow_recovery_times++;
		}
	}
};
  • 在err_callback里面if(((FLEXCAN_DRV_GetErrorStatus(INST_CANCOM1)&CAN_ESR1_FLTCONF_MASK)>>4)==0x3)代表ESR1寄存器FLTCONF位为0b11,表示从CAN控制器从Active_ERR状态转换为了BUSOFF状态,当然你也可以屏蔽ERR_INT,RXWARN,TXWARN这些中断,这样就只有当BUSOFF发生时才进入中断处理函数,进而进入回调函数,进入BUSOFF的原因只能是tx_err_cnt>255,也就是发送错误计数超过255,而rx_err_count最大只能增加到127,到127时使CAN控制器从Pasive_ERR进入Active_ERR,但绝不会再增加使其进入BUSOFF,这个时候就算你吧CAN_RX拔了,只要再插上,然后CAN控制器接收到一帧数据,tx_err_count就会清零,控制器又恢复到正常状态(Pasive_ERR)。

  • 只要FLTCONF位为0b11,CAN控制器就不能收发了,相当于死机了,然后这个时候你就要关闭CAN,重新初始化,FLTCONF位直接赋值是不能清除的,即SDK中的

 /* Clear all error interrupt flags */
    (base->ESR1) = FLEXCAN_ALL_INT;
  • 清除该位后CAN控制器才能从新恢复功能,然后去和总线同步,恢复通信,清除的办法就是
 * Reset the FLEXCAN */
    base->MCR = (base->MCR & ~CAN_MCR_SOFTRST_MASK) | CAN_MCR_SOFTRST(1U);
  • 让CAN控制器软重启,为了不出错,所以就直接从新走一遍初始化流程,在void Xcp_Can_Init(void)中的FLEXCAN_DRV_Init这个函数中如果判断if(FLEXCAN_IsEnabled(base))(表示当前是从CAN控制器使能状态),就会有以下操作
if ((((base->ESR1 & CAN_ESR1_FLTCONF_MASK) >> CAN_ESR1_FLTCONF_SHIFT) & 2U) != 0U)
    {
    	/* Save registers before Soft Reset */
    	uint32_t tempIMSK[2],tempMCR;
    	tempIMSK[0] = base->IMASK1;
    	tempIMSK[1] = base->IMASK2;
    	tempMCR = base->MCR;
    	/* Soft Reset FlexCan */
    	base->MCR |= CAN_MCR_SOFTRST(1U);
    	while (((base->MCR & CAN_MCR_SOFTRST_MASK) >> CAN_MCR_SOFTRST_SHIFT) != 0U) {}
    	/* Restore registers after Soft Reset */
    	base->IMASK1 = tempIMSK[0];
    	base->IMASK2 = tempIMSK[1];
    	base->MCR = tempMCR;
#if FEATURE_CAN_HAS_MEM_ERR_DET
    /* Disable the Protection again because is enabled by soft reset */
    FLEXCAN_DisableMemErrorDetection(base);
#endif
    }
  • 保存寄存器,软重启,也就清除了BUSOFF,CAN逐渐恢复正常。其实SDK中已经考虑得比较全面了,我们只要确定好调用时机就好,要做到快慢恢复,我们需要增加一个定时器,这里得定时器就用MPC5744P得PIT
    CH1。
#define fast_recovery_delay 100000
#define slow_recovery_delay 1000000
uint8_t fast_recovery_times=0;
uint8_t slow_recovery_times=0;
  • 定义快慢恢复时间和记录快慢恢复次数,当
if(fast_recovery_times <5)
		{
			if(((PIT_0->TIMER[1].TCTRL)&0x1)==0)
			{
			  pit1_ChnConfig1.period=(uint32_t)fast_recovery_delay;
				PIT_DRV_InitChannel(INST_PIT1, &pit1_ChnConfig1);
				INT_SYS_SetPriority(PIT_Ch1_IRQn, 1U);
				PIT_DRV_StartChannel(INST_PIT1,1);
				fast_recovery_times++;
			}
		}
		else
		{
			PIT_DRV_StopChannel(INST_PIT1,1);
			pit1_ChnConfig1.period=(uint32_t)slow_recovery_delay;
			PIT_DRV_InitChannel(INST_PIT1, &pit1_ChnConfig1);
			INT_SYS_SetPriority(PIT_Ch1_IRQn, 1U);
			PIT_DRV_StartChannel(INST_PIT1,1);
			slow_recovery_times++;
		}
  • 快恢复次数大于5就进入慢恢复,而恢复是在PIT得中断处理函数中进行的(计时时间到),相当于增加了一个延迟。
void PIT_Ch1_IRQHandler (void)
{
	pit_ch1_count++;
	PIT_DRV_StopChannel(INST_PIT1,1);
	PIT_DRV_DisableChannelInterrupt(INST_PIT1,1);
	Xcp_Can_Init();
  PIT_DRV_ClearStatusFlags(INST_PIT1, 1U);
}
  • 直接调用Xcp_Can_Init就好,记得在中断处理函数中停掉PIT,不然会一直中断,也就一直调用CAN初始化,而再次开启PIT取决于下次进入BUSOFF的时机(如果前一个BUSOFF没有被处理,即FLTCONF没有被清除,这个时候就算短接CAN_H,CAN_L也不会再次进入err_callback,相当于CAN死机了,在你下一次重启电脑之前,你对他继续破坏他也不会做出反应)
  • 当PIT中断调用Xcp_Can_Init,CAN控制器重启后,只要同步完成(自动的,128连续11个隐性位),并成功发出去一帧数据后,说明BUSOFF恢复成功,故障清除,否则就会再次进入BUSOFF(你可以拔掉CAN_TX试试),又进入快慢恢复流程。
  • 发出去数据后,只要使fast_recovery_times=0;就可以重设快慢恢复节奏,具体如下
void XcpRxCallback(uint8_t instance, flexcan_event_type_t eventType,uint32_t buffIdx, flexcan_state_t *flexcanState)
{
	switch(eventType)
  {
    case FLEXCAN_EVENT_TX_COMPLETE:
    	  can_tx_count++;
    	  fast_recovery_times=0;
        FLEXCAN_DRV_RxFifo(instance,&XcpCan_RxFrame);
        break;
    case FLEXCAN_EVENT_RXFIFO_COMPLETE:
    	  can_rx_count++;
    	  if((XcpCan_RxFrame.msgId == Xcp_RXID) && (XcpCan_RxFrame.data[0]==0XF4))
				{
    	  	if(((PIT_0->TIMER[0].TCTRL)&0x1)!=0)
					{
						PIT_DRV_StopChannel(INST_PIT1,0);
						PIT_DRV_InitChannel(INST_PIT1, &pit1_ChnConfig0);
					}
				}
        FLEXCAN_DRV_RxFifo(instance,&XcpCan_RxFrame);
        break;
    default:
        break;
  }
};

这个函数是由FLEXCAN_DRV_InstallEventCallback(INST_CANCOM1,XcpRxCallback,NULL);注册的,就是CAN的发送和接收完成中断回调,在这个函数里判断event,就可以处理CAN消息。
就写这么多,如有不足,忘大神指教。

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值