记录一下关于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消息。
就写这么多,如有不足,忘大神指教。