STM32F407IGT6
,需要和几个电机控制器进行通讯,有很多参数需要进行监控。CAN
的接收中断,接收不到数据,问题卡了很久,下面简单分享一下解决的过程和思路。背景
CAN总线
CAN控制器
CAN收发器
调试过程
硬件排查
CAN分析仪
芯片CAN控制器调试
总结
秀!靠这篇我竟然2天理解了CAN协议!
秒懂CAN总线,你学会了吗?
CAN总线不加终端电阻,会出现什么后果?
为什么CAN总线最高速度为1Mbps?
从今天起,你就是CAN专家了。
3个原因告诉你,CAN为什么比RS-485更好?
详解CAN总线协议
...更多CAN总线可以关注下方公众号在历史消息中搜索
CAN总线的硬件通常由以下几个部分组成:
STM32F407IGT6
中带有两路的CAN控制器,分别为CAN1
和 CAN2
,具体如下图所示;
SN65HVD230
,这是TI公司的一款性能强大且具体低功耗功能的CAN收发器,具体的典型应用威廉希尔官方网站
如下所示;
将CAN分析仪的CAN_H
和CAN_L
分别并联到CAN收发器的CAN_H
和CAN_L
上,然后打开CAN分析仪厂家提供的PC软件,就可以对CAN总线的数据进行监听;
STM32F407
无法接收到连续的数据,可以接收到一次数据,后面便无法再进入中断。这时候,只能再芯片端进行Debug
了。V1.7.10
版本的,如下图所示;当时我把项目升级到最新的HAL库,发现CAN部分的驱动改动比较大,另外,下文都是基于
V1.7.10
版本的HAL库。CAN控制器的初始化代码如下所示;
void MX_CAN_Init(void)
{
CAN_FilterConfTypeDef sFilterConfig;
/*CAN单元初始化*/
hCAN.Instance = CANx; /* CAN外设 */
hCAN.pTxMsg = &TxMessage;
hCAN.pRxMsg = &RxMessage;
hCAN.Init.Prescaler = 6; /* BTR-BRP 波特率分频器 定义了时间单元的时间长度 42/(1+6+7)/6 = 500Kbps */
hCAN.Init.Mode = CAN_MODE_NORMAL; /* 正常工作模式 */
hCAN.Init.SJW = CAN_SJW_1TQ; /* BTR-SJW 重新同步跳跃宽度 1个时间单元 */
hCAN.Init.BS1 = CAN_BS1_6TQ; /* BTR-TS1 时间段1 占用了6个时间单元 */
hCAN.Init.BS2 = CAN_BS2_7TQ; /* BTR-TS1 时间段2 占用了7个时间单元 */
hCAN.Init.TTCM = DISABLE; /* MCR-TTCM 关闭时间触发通信模式使能 */
hCAN.Init.ABOM = ENABLE; /* MCR-ABOM 自动离线管理 */
hCAN.Init.AWUM = ENABLE; /* MCR-AWUM 使用自动唤醒模式 */
hCAN.Init.NART = DISABLE; /* MCR-NART 禁止报文自动重传 DISABLE-自动重传 */
hCAN.Init.RFLM = DISABLE; /* MCR-RFLM 接收FIFO 锁定模式 DISABLE-溢出时新报文会覆盖原有报文 */
hCAN.Init.TXFP = DISABLE; /* MCR-TXFP 发送FIFO优先级 DISABLE-优先级取决于报文标示符 */
HAL_CAN_Init(&hCAN);
/*CAN过滤器初始化*/
sFilterConfig.FilterNumber = 0; /* 过滤器组0 */
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; /* 工作在标识符屏蔽位模式 */
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; /* 过滤器位宽为单个32位。*/
/* 使能报文标示符过滤器按照标示符的内容进行比对过滤,扩展ID不是如下的就抛弃掉,是的话,会存入FIFO0。*/
sFilterConfig.FilterIdHigh = 0x0000; //(((uint32_t)0x1314<<3)&0xFFFF0000)>>16; /* 要过滤的ID高位 */
sFilterConfig.FilterIdLow = 0x0000; //(((uint32_t)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; /* 要过滤的ID低位 */
sFilterConfig.FilterMaskIdHigh = 0x0000; /* 过滤器高16位每位必须匹配 */
sFilterConfig.FilterMaskIdLow = 0x0000; /* 过滤器低16位每位必须匹配 */
sFilterConfig.FilterFIFOAssignment = 0; /* 过滤器被关联到FIFO 0 */
sFilterConfig.FilterActivation = ENABLE; /* 使能过滤器 */
sFilterConfig.BankNumber = 14;
HAL_CAN_ConfigFilter(&hCAN, &sFilterConfig);
}
根据注释,可以大概看懂,另外再简单分析一下关键的几点;不难发现,
CAN1
的FIFO0
产生接收中断需要满足三个条件中的任意一个;FMPIE0
置1
且 FMP0
置1
;FIFO不为空会产生中断FFIE0
置1
且 FULL
置1
;FIFO满,会产生中断FOVIE0
置1
且 FOVR0
置1
;FIFO溢出,会产生中断使用仿真器对芯片进行调试,设置断点,发现
FMPIE0
被清空了,具体如下图所示;
FMPIE0
这一位是FIFO0中有挂起的消息会产生中断的中断使能标志位;所以到这里,问题有点明朗了,为什么无法进入中断?是中断使能位被清空了。那么下面就是检查代码,看看是哪里把中断给
disable
了。继续调试,发现在ESR
寄存器中,TEC
的值一直增加,然后EWGF
被值1
了;具体如下所示;
TEC
和REC
分别是发送错误计数器和接收错误计数器;如 CAN 协议所述,错误管理完全由硬件通过发送错误计数器( CAN_ESR 寄存器中的 TEC 值)和接收错误计数器( CAN_ESR 寄存器中的 REC 值)来处理,这两个计数器根据错误 状况进行递增或递减。有关 TEC 和 REC 管理的详细信息,请参见 CAN 标准。两者均可由软件读取,用以确定网络的稳定性。此外, CAN 硬件还将在 CAN_ESR 寄存器中 提供当前错误状态的详细信息。通过 CAN_IER 寄存器( ERRIE 位等),软件可以非常灵活 地配置在检测到错误时生成的中断。当
TEC
大于96的时候,硬件会将EWGF
置1
(错误警告标志位);在代码中找到了相应的宏定义;这下问题越来越清晰了。全文搜索这个宏定义,在
HAL_CAN_IRQHandler
中找到了__HAL_CAN_DISABLE_IT(CAN_IT_FMP0)
,关闭了FIFO0
的消息挂起中断, 整体代码如下;
/**
* @brief Handles CAN interrupt request
* @param hcan: pointer to a CAN_HandleTypeDef structure that contains
* the configuration information for the specified CAN.
* @retval None
*/
void HAL_CAN_IRQHandler(CAN_HandleTypeDef* hcan)
{
uint32_t tmp1 = 0U, tmp2 = 0U, tmp3 = 0U;
uint32_t errorcode = HAL_CAN_ERROR_NONE;
/* Check Overrun flag for FIFO0 */
tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_FOV0);
tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FOV0);
if(tmp1 && tmp2)
{
/* Set CAN error code to FOV0 error */
errorcode |= HAL_CAN_ERROR_FOV0;
/* Clear FIFO0 Overrun Flag */
__HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_FOV0);
}
/* Check Overrun flag for FIFO1 */
tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_FOV1);
tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FOV1);
if(tmp1 && tmp2)
{
/* Set CAN error code to FOV1 error */
errorcode |= HAL_CAN_ERROR_FOV1;
/* Clear FIFO1 Overrun Flag */
__HAL_CAN_CLEAR_FLAG(hcan, CAN_FLAG_FOV1);
}
/* Check End of transmission flag */
if(__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_TME))
{
tmp1 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_0);
tmp2 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_1);
tmp3 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_2);
if(tmp1 || tmp2 || tmp3)
{
tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK0);
tmp2 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK1);
tmp3 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK2);
/* Check Transmit success */
if(tmp1 || tmp2 || tmp3)
{
/* Call transmit function */
CAN_Transmit_IT(hcan);
}
else /* Transmit failure */
{
/* Set CAN error code to TXFAIL error */
errorcode |= HAL_CAN_ERROR_TXFAIL;
}
/* Clear transmission status flags (RQCPx and TXOKx) */
SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2 |
CAN_FLAG_TXOK0 | CAN_FLAG_TXOK1 | CAN_FLAG_TXOK2);
}
}
tmp1 = __HAL_CAN_MSG_PENDING(hcan, CAN_FIFO0);
tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FMP0);
/* Check End of reception flag for FIFO0 */
if((tmp1 != 0U) && tmp2)
{
/* Call receive function */
CAN_Receive_IT(hcan, CAN_FIFO0);
}
tmp1 = __HAL_CAN_MSG_PENDING(hcan, CAN_FIFO1);
tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_FMP1);
/* Check End of reception flag for FIFO1 */
if((tmp1 != 0U) && tmp2)
{
/* Call receive function */
CAN_Receive_IT(hcan, CAN_FIFO1);
}
/* Set error code in handle */
hcan->ErrorCode |= errorcode;
tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_EWG);
tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_EWG);
tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR);
/* Check Error Warning Flag */
if(tmp1 && tmp2 && tmp3)
{
/* Set CAN error code to EWG error */
hcan->ErrorCode |= HAL_CAN_ERROR_EWG;
}
tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_EPV);
tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_EPV);
tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR);
/* Check Error Passive Flag */
if(tmp1 && tmp2 && tmp3)
{
/* Set CAN error code to EPV error */
hcan->ErrorCode |= HAL_CAN_ERROR_EPV;
}
tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_BOF);
tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_BOF);
tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR);
/* Check Bus-Off Flag */
if(tmp1 && tmp2 && tmp3)
{
/* Set CAN error code to BOF error */
hcan->ErrorCode |= HAL_CAN_ERROR_BOF;
}
tmp1 = HAL_IS_BIT_CLR(hcan->Instance->ESR, CAN_ESR_LEC);
tmp2 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_LEC);
tmp3 = __HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_ERR);
/* Check Last error code Flag */
if((!tmp1) && tmp2 && tmp3)
{
tmp1 = (hcan->Instance->ESR) & CAN_ESR_LEC;
switch(tmp1)
{
case(CAN_ESR_LEC_0):
/* Set CAN error code to STF error */
hcan->ErrorCode |= HAL_CAN_ERROR_STF;
break;
case(CAN_ESR_LEC_1):
/* Set CAN error code to FOR error */
hcan->ErrorCode |= HAL_CAN_ERROR_FOR;
break;
case(CAN_ESR_LEC_1 | CAN_ESR_LEC_0):
/* Set CAN error code to ACK error */
hcan->ErrorCode |= HAL_CAN_ERROR_ACK;
break;
case(CAN_ESR_LEC_2):
/* Set CAN error code to BR error */
hcan->ErrorCode |= HAL_CAN_ERROR_BR;
break;
case(CAN_ESR_LEC_2 | CAN_ESR_LEC_0):
/* Set CAN error code to BD error */
hcan->ErrorCode |= HAL_CAN_ERROR_BD;
break;
case(CAN_ESR_LEC_2 | CAN_ESR_LEC_1):
/* Set CAN error code to CRC error */
hcan->ErrorCode |= HAL_CAN_ERROR_CRC;
break;
default:
break;
}
/* Clear Last error code Flag */
hcan->Instance->ESR &= ~(CAN_ESR_LEC);
}
/* Call the Error call Back in case of Errors */
if(hcan->ErrorCode != HAL_CAN_ERROR_NONE)
{
/* Clear ERRI Flag */
hcan->Instance->MSR = CAN_MSR_ERRI;
/* Set the CAN state ready to be able to start again the process */
hcan->State = HAL_CAN_STATE_READY;
/* Disable interrupts: */
/* - Disable Error warning Interrupt */
/* - Disable Error passive Interrupt */
/* - Disable Bus-off Interrupt */
/* - Disable Last error code Interrupt */
/* - Disable Error Interrupt */
/* - Disable FIFO 0 message pending Interrupt */
/* - Disable FIFO 0 Overrun Interrupt */
/* - Disable FIFO 1 message pending Interrupt */
/* - Disable FIFO 1 Overrun Interrupt */
/* - Disable Transmit mailbox empty Interrupt */
__HAL_CAN_DISABLE_IT(hcan, CAN_IT_EWG |
CAN_IT_EPV |
CAN_IT_BOF |
CAN_IT_LEC |
CAN_IT_ERR |
CAN_IT_FMP0|
CAN_IT_FOV0|
CAN_IT_FMP1|
CAN_IT_FOV1|
CAN_IT_TME);
/* Call Error callback function */
HAL_CAN_ErrorCallback(hcan);
}
}
最后,找到无法进入接收中断的原因,是CAN总线出现发送错误的情况,从而触发了错误警告标志位EWGF
,进而将关闭了消息挂起中断。全部0条评论
快来发表一下你的评论吧 !