前言
最近負責的一個項目用的主控芯片是STM32F407IGT6
,需要和幾個電機控制器進行通訊,有很多參數需要進行監控。
有一個問題一直無法解決。在開啟CAN
的接收中斷,接收不到數據,問題卡了很久,下面簡單分享一下解決的過程和思路。
目錄
CAN總線
CAN總線是一種串行通信協議,用于在微控制器和其他設備之間傳輸數據。CAN總線通常用于汽車、工業自動化和機器人等領域。
CAN總線的硬件通常由以下幾個部分組成:
CAN總線的控制器區域通常包括CAN控制器和CAN收發器。
- CAN控制器負責處理CAN總線上的數據傳輸,包括數據發送和接收、錯誤檢測和糾正等;
- CAN收發器則負責將CAN控制器的信號轉換為總線上的電信號,并將總線上的電信號轉換為CAN控制器可以理解的信號。
CAN控制器
主板上的芯片STM32F407IGT6
中帶有兩路的CAN控制器,分別為CAN1
和 CAN2
,具體如下圖所示;
- 檢查CAN控制器和CAN收發器之間是否正常;
- 檢查CAN收發器的差分信號是否正常,這里可能要了解一下CAN總線電平的顯性電平和隱性電平的特點,以及CAN底層協議的細節,會比較復雜;
個人比較推薦使用上述步驟檢查硬件鏈路是否存在問題,那如何對數據進行分析呢?當然可以對著示波器的波形一點一點進行分析,但是這樣是很低效的,這里我建議使用CAN分析儀進行數據抓包,下面我們繼續進行介紹。
CAN分析儀
至于數據傳輸是否正確,可以使用CAN盒進行數據監聽,下面是我使用的一款CAN分析儀,如圖;
將CAN分析儀的CAN_H
和CAN_L
分別并聯到CAN收發器的CAN_H
和CAN_L
上,然后打開CAN分析儀廠家提供的PC軟件,就可以對CAN總線的數據進行監聽;
-
進行到這里,我在項目中遇到的問題是,發送正常,但是
STM32F407
無法接收到連續的數據,可以接收到一次數據,后面便無法再進入中斷。這時候,只能再芯片端進行Debug
了。
芯片CAN控制器調試
這里的代碼用的HAL庫,庫版本相對來說比較老,是V1.7.10
版本的,如下圖所示;
當時我把項目升級到最新的HAL庫,發現CAN部分的驅動改動比較大,另外,下文都是基于V1.7.10
版本的HAL庫。
CAN控制器的初始化代碼如下所示;
voidMX_CAN_Init(void)
{
CAN_FilterConfTypeDefsFilterConfig;
/*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;/*過濾器被關聯到FIFO0*/
sFilterConfig.FilterActivation=ENABLE;/*使能過濾器*/
sFilterConfig.BankNumber=14;
HAL_CAN_ConfigFilter(&hCAN,&sFilterConfig);
}
根據注釋,可以大概看懂,另外再簡單分析一下關鍵的幾點;
- 波特率設置為 500Kbps;
- 對報文不進行過濾,可以接收任何擴展ID的數據;
雖然不進行任何過濾,但是還是無法接收到CAN回傳的數據,無法進入的接收中斷;
從STM32F407的編程手冊里了解到;
不難發現,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
的消息掛起中斷,整體代碼如下;
/**
*@briefHandlesCANinterruptrequest
*@paramhcan:pointertoaCAN_HandleTypeDefstructurethatcontains
*theconfigurationinformationforthespecifiedCAN.
*@retvalNone
*/
voidHAL_CAN_IRQHandler(CAN_HandleTypeDef*hcan)
{
uint32_ttmp1=0U,tmp2=0U,tmp3=0U;
uint32_terrorcode=HAL_CAN_ERROR_NONE;
/*CheckOverrunflagforFIFO0*/
tmp1=__HAL_CAN_GET_FLAG(hcan,CAN_FLAG_FOV0);
tmp2=__HAL_CAN_GET_IT_SOURCE(hcan,CAN_IT_FOV0);
if(tmp1&&tmp2)
{
/*SetCANerrorcodetoFOV0error*/
errorcode|=HAL_CAN_ERROR_FOV0;
/*ClearFIFO0OverrunFlag*/
__HAL_CAN_CLEAR_FLAG(hcan,CAN_FLAG_FOV0);
}
/*CheckOverrunflagforFIFO1*/
tmp1=__HAL_CAN_GET_FLAG(hcan,CAN_FLAG_FOV1);
tmp2=__HAL_CAN_GET_IT_SOURCE(hcan,CAN_IT_FOV1);
if(tmp1&&tmp2)
{
/*SetCANerrorcodetoFOV1error*/
errorcode|=HAL_CAN_ERROR_FOV1;
/*ClearFIFO1OverrunFlag*/
__HAL_CAN_CLEAR_FLAG(hcan,CAN_FLAG_FOV1);
}
/*CheckEndoftransmissionflag*/
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);
/*CheckTransmitsuccess*/
if(tmp1||tmp2||tmp3)
{
/*Calltransmitfunction*/
CAN_Transmit_IT(hcan);
}
else/*Transmitfailure*/
{
/*SetCANerrorcodetoTXFAILerror*/
errorcode|=HAL_CAN_ERROR_TXFAIL;
}
/*Cleartransmissionstatusflags(RQCPxandTXOKx)*/
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);
/*CheckEndofreceptionflagforFIFO0*/
if((tmp1!=0U)&&tmp2)
{
/*Callreceivefunction*/
CAN_Receive_IT(hcan,CAN_FIFO0);
}
tmp1=__HAL_CAN_MSG_PENDING(hcan,CAN_FIFO1);
tmp2=__HAL_CAN_GET_IT_SOURCE(hcan,CAN_IT_FMP1);
/*CheckEndofreceptionflagforFIFO1*/
if((tmp1!=0U)&&tmp2)
{
/*Callreceivefunction*/
CAN_Receive_IT(hcan,CAN_FIFO1);
}
/*Seterrorcodeinhandle*/
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);
/*CheckErrorWarningFlag*/
if(tmp1&&tmp2&&tmp3)
{
/*SetCANerrorcodetoEWGerror*/
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);
/*CheckErrorPassiveFlag*/
if(tmp1&&tmp2&&tmp3)
{
/*SetCANerrorcodetoEPVerror*/
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);
/*CheckBus-OffFlag*/
if(tmp1&&tmp2&&tmp3)
{
/*SetCANerrorcodetoBOFerror*/
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);
/*CheckLasterrorcodeFlag*/
if((!tmp1)&&tmp2&&tmp3)
{
tmp1=(hcan->Instance->ESR)&CAN_ESR_LEC;
switch(tmp1)
{
case(CAN_ESR_LEC_0):
/*SetCANerrorcodetoSTFerror*/
hcan->ErrorCode|=HAL_CAN_ERROR_STF;
break;
case(CAN_ESR_LEC_1):
/*SetCANerrorcodetoFORerror*/
hcan->ErrorCode|=HAL_CAN_ERROR_FOR;
break;
case(CAN_ESR_LEC_1|CAN_ESR_LEC_0):
/*SetCANerrorcodetoACKerror*/
hcan->ErrorCode|=HAL_CAN_ERROR_ACK;
break;
case(CAN_ESR_LEC_2):
/*SetCANerrorcodetoBRerror*/
hcan->ErrorCode|=HAL_CAN_ERROR_BR;
break;
case(CAN_ESR_LEC_2|CAN_ESR_LEC_0):
/*SetCANerrorcodetoBDerror*/
hcan->ErrorCode|=HAL_CAN_ERROR_BD;
break;
case(CAN_ESR_LEC_2|CAN_ESR_LEC_1):
/*SetCANerrorcodetoCRCerror*/
hcan->ErrorCode|=HAL_CAN_ERROR_CRC;
break;
default:
break;
}
/*ClearLasterrorcodeFlag*/
hcan->Instance->ESR&=~(CAN_ESR_LEC);
}
/*CalltheErrorcallBackincaseofErrors*/
if(hcan->ErrorCode!=HAL_CAN_ERROR_NONE)
{
/*ClearERRIFlag*/
hcan->Instance->MSR=CAN_MSR_ERRI;
/*SettheCANstatereadytobeabletostartagaintheprocess*/
hcan->State=HAL_CAN_STATE_READY;
/*Disableinterrupts:*/
/*-DisableErrorwarningInterrupt*/
/*-DisableErrorpassiveInterrupt*/
/*-DisableBus-offInterrupt*/
/*-DisableLasterrorcodeInterrupt*/
/*-DisableErrorInterrupt*/
/*-DisableFIFO0messagependingInterrupt*/
/*-DisableFIFO0OverrunInterrupt*/
/*-DisableFIFO1messagependingInterrupt*/
/*-DisableFIFO1OverrunInterrupt*/
/*-DisableTransmitmailboxemptyInterrupt*/
__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);
/*CallErrorcallbackfunction*/
HAL_CAN_ErrorCallback(hcan);
}
}
最后,找到無法進入接收中斷的原因,是CAN總線出現發送錯誤的情況,從而觸發了錯誤警告標志位EWGF
,進而將關閉了消息掛起中斷。
總結
本文簡單介紹了在STM32F407上的CAN總線調試過程,項目中難免會遇到各種問題,解決之后,大家要及時做好總結和復盤,技術在于積累和沉淀,相互學習,共同進步。
-
CAN
+關注
關注
57文章
2744瀏覽量
463621 -
總線
+關注
關注
10文章
2878瀏覽量
88051 -
主控芯片
+關注
關注
2文章
198瀏覽量
24636
原文標題:又踩坑了!這次敗給CAN總線了
文章出處:【微信號:knifewheat,微信公眾號:小麥大叔】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論