1. 程式人生 > >CAN總線錯誤分析與解決

CAN總線錯誤分析與解決

[1] specified ace over 成功 keyword 回收 狀態轉換 article

  1. 背景

寫這篇文章是因為我看到網上介紹CAN總線錯誤處理的文章,清一色的都是生搬照抄教科書或是數據文檔的內容,特別是國內很難找到一些有價值的內容,這讓一些真正有需要的人很苦惱,包括我自己。這篇不打算對CAN的錯誤處理機制做進一步的探討,而是從實際工作中碰到的具體問題來分析一些常見的CAN總線錯誤和解決辦法。

  1. CAN節點數據收發過程

了解CAN節點在總線上數據上的收發過程很重要,之前的一篇文章講解了一些CAN總線的錯誤處理機制,但是那些都是理論上的東西,如果不深入了解CAN總線上的數據收發過程,理解那些理論的東西難免有些晦澀。

技術分享圖片

我們知道CAN總線上的每個節點往總線上發送數據的同時會同時讀取總線上的數據,並與自己發送的數據作對比。

CAN信息發送成功後,在這個間隙內,接收節點可以準備要回復的信息,也就是把應答場填充為顯性0,在發送時其為隱性1應答過程可能如下:當信息傳輸到ACK前的Del時,可以認為信息已經傳輸完畢,接收節點也接收到了足夠的信息來檢測接收的信息是否正確,所以這時接收節點就會檢測信號是否正確,如果正確,就將ACK置位為顯性0,註意這時,發送節點因為還在發送而接收節點又將ACK信息置位為1,所以它就會在回讀時檢測到ACK為0,判斷接收成功。註意:這其中有個接收節點用顯性覆蓋隱性---覆蓋ACK位的過程,覆蓋+回讀。

ACK前後各加一個Del,就是為了考慮到時間誤差,讓接收節點有足夠的時間對ACK確認。這個過程說明,CAN發送是個雙向互動的過程,發送節點一邊發送,一邊對節點進行回收確認數據正確,而接收節點也時刻接收,並在正確的時間將ACK設置為1。

  1. CAN總線錯誤

CAN總線錯誤分別有發送和接收錯誤計數,計數達到一定的累計以後就會產生CAN BUS OFF, 這說明CAN總線上出現了嚴重的錯誤。如下圖CAN總線產生錯誤後的狀態轉換機制:

技術分享圖片

如果出現了BUS OFF,總線上的節點需要做一些動作,例如重啟CAN控制器或是重新上電,但是這些都只是一些補救措施,最根本的還是需要找到引起BUS OFF的根源。

CAN總線分析的一些工具和文檔:

  • CAN分析儀或者邏輯分析儀
  • 數字示波器
  • 相關的軟件debug工具
  • CAN控制器芯片數據手冊,這很重要
  • 硬件電路圖
  • CAN協議文檔
  • 相關版本的Linux內核源碼

  1. CAN節點發送錯誤不成功

    1. 問題描述與分析

掛載在CAN總線上的一個節點向總線上發送數據不成功,用邏輯分析儀也看不到任何波形。PS: 這應該是我碰到的最坑爹的事情了。下面具體來看看怎麽不成功。於是調試中斷查看CAN_STATUS即CAN狀態寄存器顯示0xE5, 查看CPU數據手冊:

技術分享圖片

CAN總線狀態直接進入了BUS OFF狀態,這意味著錯誤計數已經超限,查看CPU收發寄存器的收發錯誤計數顯示發送錯誤計數TEC達到248, 接收錯誤計數為0;這很明顯,數據壓根沒有發送到總線上。

再進一步查看寄存器值LEC即LAST ERROR CODE 最後一個錯誤代碼, 顯示是BIT0 ERROR:

技術分享圖片

查看上面的錯誤代碼表可知,BIT0錯誤也就是在發送數據期間,雖然CAN節點設備想要發送一個顯性位,也就是邏輯0,但是CAN總線同時監聽到總線上的數據位為隱性位,即邏輯1。這意味著CAN core往總線上發送的數據第一位就已經出錯了,壓根沒有將數據經過CAN收發器傳送到CAN總線上。

一直在使用CAN總線的我廠和我從來沒遇到這等奇事,但是由於是新的CPU的開發所以在懷疑硬件的問題的同時也在排查軟件問題,但是經過一陣排查,沒有發現軟件上的問題。回頭再分析硬件,又經過一陣排查溯源,發現CPU的CAN收發線與CAN收發氣的收發線接反,直接崩潰(PS: 硬件的大哥你能不能不要坑小弟):

技術分享圖片

  1. 總結

CAN節點發送數據不成功,首先分析是不是CAN控制器本身的問題,查看CPU中的CAN core的狀態寄存器,分析是否有BUS OFF, 如果存在BUS OFF, 則進一步查看具體的錯誤信息,是主動的錯誤還是被動的錯,發送錯誤計數有沒有超限,最後一次發生的錯誤狀態是什麽,查看是位填充錯誤還是格式錯誤等其他錯誤,然後具體問題具體分析。這種錯誤一般是有硬件發送線路出現問題引起,例如光隔次邊不導通,發送接口接觸不良等,再則是一些奇葩的錯誤,例如本例,收發線直接接反了,坑爹啊!

  1. CAN Socket 的CAN節點檢測到錯誤幀

    1. 問題描述

我們看到以下的CAN Socket日誌,在38秒內的三個錯誤幀,但是並沒有引起總線的BUS OFF,這說明總線上檢測到了錯誤,有可能受到了幹擾,也有可能是數據發送太密集導致的總線過載,但是在這38秒內出現錯誤,但是期間又恢復正常。

技術分享圖片

技術分享圖片

技術分享圖片

CAN ID : 0x20000004 = 10 0000 0000 0000 0000 0000 0000 0100, 即仲裁域的值。

  1. Linux內核源碼分析

因為出現此錯誤的是我廠的CAN控制器CPU TI 公司的AM3352, 內核版本為Linux 3.2.0

所以我們通過內核來看內核CAN錯誤can_id的定義:

[cpp] view plain copy print?技術分享圖片技術分享圖片
  1. /* error class (mask) in can_id */
  2. #define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
  3. #define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
  4. #define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
  5. #define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
  6. #define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
  7. #define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
  8. #define CAN_ERR_BUSOFF 0x00000040U /* bus off */
  9. #define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
  10. #define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
技術分享圖片 [cpp] view plain copy print?
  1. /* error class (mask) in can_id */
  2. #define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */
  3. #define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */
  4. #define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */
  5. #define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */
  6. #define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */
  7. #define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */
  8. #define CAN_ERR_BUSOFF 0x00000040U /* bus off */
  9. #define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */
  10. #define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */
技術分享圖片
/* error class (mask) in can_id */

#define CAN_ERR_TX_TIMEOUT 0x00000001U /* TX timeout (by netdevice driver) */

#define CAN_ERR_LOSTARB 0x00000002U /* lost arbitration / data[0] */

#define CAN_ERR_CRTL 0x00000004U /* controller problems / data[1] */

#define CAN_ERR_PROT 0x00000008U /* protocol violations / data[2..3] */

#define CAN_ERR_TRX 0x00000010U /* transceiver status / data[4] */

#define CAN_ERR_ACK 0x00000020U /* received no ACK on transmission */

#define CAN_ERR_BUSOFF 0x00000040U /* bus off */

#define CAN_ERR_BUSERROR 0x00000080U /* bus error (may flood!) */

#define CAN_ERR_RESTARTED 0x00000100U /* controller restarted */

由錯誤幀CAN ID : 0x20000004 = 10 0000 0000 0000 0000 0000 0000 0100, 去除最高為的1(SOFZ幀起始位?),因為仲裁位是29位,所以應該是0 0000 0000 0000 0000 0000 0000 0100 =0x00000004,既不是CAN_ERR_BUSOFF也不是CAN_ERR_BUSERROR, 而是CAN_ERR_CTRL, 即CAN控制器的問題,而我們在看data[1]描述的CAN 控制器錯誤類型描述:

[cpp] view plain copy print?技術分享圖片技術分享圖片
  1. /* error status of CAN-controller / data[1] */
  2. #define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
  3. #define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
  4. #define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
  5. #define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
  6. #define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
  7. #define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
  8. #define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
技術分享圖片 [cpp] view plain copy print?
  1. /* error status of CAN-controller / data[1] */
  2. #define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */
  3. #define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
  4. #define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
  5. #define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
  6. #define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */
  7. #define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */
  8. #define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */
技術分享圖片
/* error status of CAN-controller / data[1] */

#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */

#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */

#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */

#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */

#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */

#define CAN_ERR_CRTL_RX_PASSIVE 0x10 /* reached error passive status RX */

#define CAN_ERR_CRTL_TX_PASSIVE 0x20 /* reached error passive status TX */

我們再看我們截取的錯誤幀數據報文中顯示data[1] = 0x04,如下圖所示:

技術分享圖片

即具體錯誤為:

#define CAN_ERR_CRTL_RX_WARNING  0x04 /* reached warning level for RX errors */

也就是說CAN 控制器接收錯誤計數達到了警告的級別,需要提出警告,如果再這樣下去CAN控制器就要過載了,甚至會引起總線的BUS OFF.

我們再回頭看內核源碼對此錯誤的處理:產生data[1] = CAN_ERR_CRTL_RX_WARNING 錯誤的內核源函數為:

[cpp] view plain copy print?技術分享圖片技術分享圖片
  1. static int ti_hecc_error(struct net_device *ndev, int int_status,
  2. int err_status)
技術分享圖片 [cpp] view plain copy print?
  1. static int ti_hecc_error(struct net_device *ndev, int int_status,
  2. int err_status)
技術分享圖片
static int ti_hecc_error(struct net_device *ndev, int int_status,

int err_status)

HECC也就是TI公司高速終端CAN控制器的簡稱,用以上的函數描述TI CAN core的錯誤處理,如下,我們可以看到也就是CAN控制器接收錯誤計數REC大於96的時候內核就會報此錯誤

[cpp] view plain copy print?
  1. if (int_status & HECC_CANGIF_WLIF) { /* warning level int */
  2. if ((int_status & HECC_CANGIF_BOIF) == 0) {
  3. priv->can.state = CAN_STATE_ERROR_WARNING;
  4. ++priv->can.can_stats.error_warning;
  5. cf->can_id |= CAN_ERR_CRTL;
  6. if (hecc_read(priv, HECC_CANTEC) > 96)
  7. cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
  8. if (hecc_read(priv, HECC_CANREC) > 96)
  9. cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
  10. }
  11. hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW);
  12. dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n");
  13. hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR);
  14. }
[cpp] view plain copy print?
  1. if (int_status & HECC_CANGIF_WLIF) { /* warning level int */
  2. if ((int_status & HECC_CANGIF_BOIF) == 0) {
  3. priv->can.state = CAN_STATE_ERROR_WARNING;
  4. ++priv->can.can_stats.error_warning;
  5. cf->can_id |= CAN_ERR_CRTL;
  6. if (hecc_read(priv, HECC_CANTEC) > 96)
  7. cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
  8. if (hecc_read(priv, HECC_CANREC) > 96)
  9. cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
  10. }
  11. hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW);
  12. dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n");
  13. hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR);
  14. }
技術分享圖片
if (int_status & HECC_CANGIF_WLIF) { /* warning level int */
        if ((int_status & HECC_CANGIF_BOIF) == 0) {
            priv->can.state = CAN_STATE_ERROR_WARNING;
            ++priv->can.can_stats.error_warning;
            cf->can_id |= CAN_ERR_CRTL;
            if (hecc_read(priv, HECC_CANTEC) > 96)
                cf->data[1] |= CAN_ERR_CRTL_TX_WARNING;
            if (hecc_read(priv, HECC_CANREC) > 96)
                cf->data[1] |= CAN_ERR_CRTL_RX_WARNING;
        }
        hecc_set_bit(priv, HECC_CANES, HECC_CANES_EW);
        dev_dbg(priv->ndev->dev.parent, "Error Warning interrupt\n");
        hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR);
    }
技術分享圖片
  1. 總結

出現這個錯誤警告的原因很可能是:

  1. 此CAN總線上有幹擾,導致CAN控制器發生接收錯誤,CAN總線上的信號經過收發器轉化為差分電平信號,此時信號容易受到外界幹擾,這樣容易使CAN控制器發生接收錯誤,接收錯誤寄存器接收錯誤計數累計到一定值後會報此錯誤,如果錯誤計數達到一定程度甚至會導致總線關閉也就是BUS OFF. 如果最終確認是由於幹擾引起的錯誤計數累計,則應該排查幹擾源,然後增加抗幹擾措施。
  2. 此CAN節點經過消息濾波後仍然需要接收大量的消息,導致CPU中的CAN控制器接收出錯,並且錯誤計數達到了錯誤警告的上限。但是慶幸的是總線仍然沒有過載,總線還可以正常收發數據,沒有引起BUS OFF。但是對於一個安全可靠控制系統,這樣的警告是絕對不允許的。我們需要通過一些手段去避免這樣的問題出現,例如降低總線數據並發量,降低總線負載。

  1. CAN總線設備離線與錯誤恢復

這種問題同樣很詭異,但是似乎又是比較常見的問題,這樣的問題出現的情況往往比較多,例如CAN節Power off也就是電斷了,總線上也就肯定監聽不到此CAN節點的心跳,或是CAN總線節點沒有及時發送心跳,阻塞在任務處理裏,又或是此CAN節點物理接線和總線斷開,等等原因很多。

我這裏要說的一種情況是我廠碰到的另一種問題。

  1. 問題描述

在整個系統重啟後發現CAN總線上的某一個Cortex M0設備節點丟失,而其他的設備,也是同樣M0架構的MCU和相同控制軟件的設備則沒有出現丟失的情況。

未完待續。

CAN總線錯誤分析與解決