日本a√视频在线,久久青青亚洲国产,亚洲一区欧美二区,免费g片在线观看网站

        <style id="k3y6c"><u id="k3y6c"></u></style>
        <s id="k3y6c"></s>
        <mark id="k3y6c"></mark>
          
          

          <mark id="k3y6c"></mark>

          "); //-->

          博客專欄

          EEPW首頁 > 博客 > 又踩坑了!這次敗給CAN總線了

          又踩坑了!這次敗給CAN總線了

          發(fā)布人:xiaomaidashu 時間:2023-10-13 來源:工程師 發(fā)布文章

          Warning: file_get_contents(): SSL: connection timeout in /var/www/html/www.edw.com.cn/www/rootapp/controllersspace/ArticlesmanageController.php on line 270 Warning: file_get_contents(): Failed to enable crypto in /var/www/html/www.edw.com.cn/www/rootapp/controllersspace/ArticlesmanageController.php on line 270 Warning: file_get_contents(https://mmbiz.qpic.cn/mmbiz_png/zcVcDoKYUnZl6JkGyy6tyFnYlC18c27HK6twaSRm6A4sibculYDv7dnicqDrqw4ph9XicrYQokE0QcNpbjcejjvmg/640?wx_fmt=png&tp=wxpic&wxfrom=5&wx_lazy=1&wx_co=1): failed to open stream: operation failed in /var/www/html/www.edw.com.cn/www/rootapp/controllersspace/ArticlesmanageController.php on line 270

          前言

          最近負責(zé)的一個項目用的主控芯片是STM32F407IGT6,需要和幾個電機控制器進行通訊,有很多參數(shù)需要進行監(jiān)控。
          有一個問題一直無法解決。在開啟CAN的接收中斷,接收不到數(shù)據(jù),問題卡了很久,下面簡單分享一下解決的過程和思路。

          目錄
          • 背景

          • CAN總線

          • CAN控制器

          • CAN收發(fā)器

          • 調(diào)試過程

            • 硬件排查

            • CAN分析儀

            • 芯片CAN控制器調(diào)試

          • 總結(jié)

          CAN總線

          CAN總線是一種串行通信協(xié)議,用于在微控制器和其他設(shè)備之間傳輸數(shù)據(jù)。CAN總線通常用于汽車、工業(yè)自動化和機器人等領(lǐng)域。

          CAN總線的硬件通常由以下幾個部分組成:

          • 控制器區(qū)域:包括CAN控制器和CAN收發(fā)器;
          • 總線電纜:用于連接CAN總線上的所有設(shè)備;
          • 終端電阻:用于終止總線,以減少反射和信號干擾;
          • 外部電源:用于為CAN總線提供電源;

          CAN總線的控制器區(qū)域通常包括CAN控制器和CAN收發(fā)器。

          • CAN控制器負責(zé)處理CAN總線上的數(shù)據(jù)傳輸,包括數(shù)據(jù)發(fā)送和接收、錯誤檢測和糾正等;
          • CAN收發(fā)器則負責(zé)將CAN控制器的信號轉(zhuǎn)換為總線上的電信號,并將總線上的電信號轉(zhuǎn)換為CAN控制器可以理解的信號。
          CAN控制器

          主板上的芯片STM32F407IGT6中帶有兩路的CAN控制器,分別為CAN1 和 CAN2,具體如下圖所示;

          圖片

          CAN收發(fā)器

          主板上使用的是芯片SN65HVD230,這是TI公司的一款性能強大且具體低功耗功能的CAN收發(fā)器,具體的典型應(yīng)用電路如下所示;

          圖片

          調(diào)試過程硬件排查

          設(shè)備的調(diào)試過程中,首先要確保硬件鏈路上是否正常。最常見的方法就是直接用示波器進行檢查。具體如下所示;

          圖片

          1. 檢查CAN控制器和CAN收發(fā)器之間是否正常;
          2. 檢查CAN收發(fā)器的差分信號是否正常,這里可能要了解一下CAN總線電平的顯性電平和隱性電平的特點,以及CAN底層協(xié)議的細節(jié),會比較復(fù)雜;

          個人比較推薦使用上述步驟檢查硬件鏈路是否存在問題,那如何對數(shù)據(jù)進行分析呢?當(dāng)然可以對著示波器的波形一點一點進行分析,但是這樣是很低效的,這里我建議使用CAN分析儀進行數(shù)據(jù)抓包,下面我們繼續(xù)進行介紹。

          CAN分析儀

          至于數(shù)據(jù)傳輸是否正確,可以使用CAN盒進行數(shù)據(jù)監(jiān)聽,下面是我使用的一款CAN分析儀,如圖;

          圖片

          將CAN分析儀的CAN_H和CAN_L分別并聯(lián)到CAN收發(fā)器的CAN_H和CAN_L上,然后打開CAN分析儀廠家提供的PC軟件,就可以對CAN總線的數(shù)據(jù)進行監(jiān)聽;圖片

          1. 將CAN分析儀接入到CAN總線;
          2. 將CAN分析儀連接到電腦(這里是USB接口),需要配置相同的波特率;
          3. 打開CAN分析儀配套的PC軟件,進行數(shù)據(jù)的收發(fā);圖片
          4. 進行到這里,我在項目中遇到的問題是,發(fā)送正常,但是STM32F407無法接收到連續(xù)的數(shù)據(jù),可以接收到一次數(shù)據(jù),后面便無法再進入中斷。這時候,只能再芯片端進行Debug了。
          芯片CAN控制器調(diào)試

          這里的代碼用的HAL庫,庫版本相對來說比較老,是V1.7.10版本的,如下圖所示;


          圖片

          當(dāng)時我把項目升級到最新的HAL庫,發(fā)現(xiàn)CAN部分的驅(qū)動改動比較大,另外,下文都是基于V1.7.10版本的HAL庫。

          CAN控制器的初始化代碼如下所示;

          void MX_CAN_Init(void)
          {
           CAN_FilterConfTypeDef  sFilterConfig;
          
           /*CAN單元初始化*/
           hCAN.Instance = CANx;             /* CAN外設(shè) */
           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  關(guān)閉時間觸發(fā)通信模式使能 */
           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  發(fā)送FIFO優(yōu)先級 DISABLE-優(yōu)先級取決于報文標示符 */
           HAL_CAN_Init(&hCAN);
          
           /*CAN過濾器初始化*/
           sFilterConfig.FilterNumber = 0;                    /* 過濾器組0 */
           sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;  /* 工作在標識符屏蔽位模式 */
           sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; /* 過濾器位寬為單個32位。*/
           /* 使能報文標示符過濾器按照標示符的內(nèi)容進行比對過濾,擴展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;           /* 過濾器被關(guān)聯(lián)到FIFO 0 */
           sFilterConfig.FilterActivation = ENABLE;          /* 使能過濾器 */ 
           sFilterConfig.BankNumber = 14;
           HAL_CAN_ConfigFilter(&hCAN, &sFilterConfig);
            
          }

          根據(jù)注釋,可以大概看懂,另外再簡單分析一下關(guān)鍵的幾點;

          • 波特率設(shè)置為 500Kbps;
          • 對報文不進行過濾,可以接收任何擴展ID的數(shù)據(jù);

          雖然不進行任何過濾,但是還是無法接收到CAN回傳的數(shù)據(jù),無法進入的接收中斷;

          從STM32F407的編程手冊里了解到;

          圖片

          不難發(fā)現(xiàn),CAN1的FIFO0產(chǎn)生接收中斷需要滿足三個條件中的任意一個;

          • FMPIE0置1 且 FMP0置1;FIFO不為空會產(chǎn)生中斷
          • FFIE0置1 且 FULL置1;FIFO滿,會產(chǎn)生中斷
          • FOVIE0置1 且 FOVR0置1;FIFO溢出,會產(chǎn)生中斷

          手冊里是這樣描述的,如下圖所示;


          圖片

          使用仿真器對芯片進行調(diào)試,設(shè)置斷點,發(fā)現(xiàn)FMPIE0被清空了,具體如下圖所示;圖片

          FMPIE0這一位是FIFO0中有掛起的消息會產(chǎn)生中斷的中斷使能標志位;


          圖片圖片

          所以到這里,問題有點明朗了,為什么無法進入中斷?是中斷使能位被清空了。

          那么下面就是檢查代碼,看看是哪里把中斷給disable了。

          繼續(xù)調(diào)試,發(fā)現(xiàn)在ESR寄存器中,TEC的值一直增加,然后EWGF被值1了;具體如下所示;


          圖片

          TEC和REC分別是發(fā)送錯誤計數(shù)器和接收錯誤計數(shù)器;

          如 CAN 協(xié)議所述,錯誤管理完全由硬件通過發(fā)送錯誤計數(shù)器( CAN_ESR 寄存器中的 TEC 值)和接收錯誤計數(shù)器( CAN_ESR 寄存器中的 REC 值)來處理,這兩個計數(shù)器根據(jù)錯誤 狀況進行遞增或遞減。有關(guān) TEC 和 REC 管理的詳細信息,請參見 CAN 標準。兩者均可由軟件讀取,用以確定網(wǎng)絡(luò)的穩(wěn)定性。此外, CAN 硬件還將在 CAN_ESR 寄存器中 提供當(dāng)前錯誤狀態(tài)的詳細信息。通過 CAN_IER 寄存器( ERRIE 位等),軟件可以非常靈活 地配置在檢測到錯誤時生成的中斷。

          當(dāng)TEC大于96的時候,硬件會將EWGF置1(錯誤警告標志位);在代碼中找到了相應(yīng)的宏定義;這下問題越來越清晰了。

          圖片

          全文搜索這個宏定義,在HAL_CAN_IRQHandler中找到了__HAL_CAN_DISABLE_IT(CAN_IT_FMP0),關(guān)閉了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總線出現(xiàn)發(fā)送錯誤的情況,從而觸發(fā)了錯誤警告標志位EWGF,進而將關(guān)閉了消息掛起中斷。

          總結(jié)

          本文簡單介紹了在STM32F407上的CAN總線調(diào)試過程,項目中難免會遇到各種問題,解決之后,大家要及時做好總結(jié)和復(fù)盤,技術(shù)在于積累和沉淀,相互學(xué)習(xí),共同進步。


          *博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點,如有侵權(quán)請聯(lián)系工作人員刪除。



          關(guān)鍵詞: CAN總線

          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉