CAN節(jié)點數(shù)據(jù)收發(fā)過程
了解CAN節(jié)點在總線上數(shù)據(jù)上的收發(fā)過程很重要,之前的一篇文章講解了一些CAN總線的錯誤處理機(jī)制,但是那些都是理論上的東西,如果不深入了解CAN總線上的數(shù)據(jù)收發(fā)過程,理解那些理論的東西難免有些晦澀。
我們知道CAN總線上的每個節(jié)點往總線上發(fā)送數(shù)據(jù)的同時會同時讀取總線上的數(shù)據(jù),并與自己發(fā)送的數(shù)據(jù)作對比。
CAN信息發(fā)送成功后,在這個間隙內(nèi),接收節(jié)點可以準(zhǔn)備要回復(fù)的信息,也就是把應(yīng)答場填充為顯性0,在發(fā)送時其為隱性1應(yīng)答過程可能如下:當(dāng)信息傳輸?shù)?a target="_blank">ACK前的Del時,可以認(rèn)為信息已經(jīng)傳輸完畢,接收節(jié)點也接收到了足夠的信息來檢測接收的信息是否正確,所以這時接收節(jié)點就會檢測信號是否正確,如果正確,就將ACK置位為顯性0,注意這時,發(fā)送節(jié)點因為還在發(fā)送而接收節(jié)點又將ACK信息置位為1,所以它就會在回讀時檢測到ACK為0,判斷接收成功。注意:這其中有個接收節(jié)點用顯性覆蓋隱性---覆蓋ACK位的過程,覆蓋+回讀。
ACK前后各加一個Del,就是為了考慮到時間誤差,讓接收節(jié)點有足夠的時間對ACK確認(rèn)。這個過程說明,CAN發(fā)送是個雙向互動的過程,發(fā)送節(jié)點一邊發(fā)送,一邊對節(jié)點進(jìn)行回收確認(rèn)數(shù)據(jù)正確,而接收節(jié)點也時刻接收,并在正確的時間將ACK設(shè)置為1。
CAN總線錯誤
CAN總線錯誤分別有發(fā)送和接收錯誤計數(shù),計數(shù)達(dá)到一定的累計以后就會產(chǎn)生CAN BUS OFF, 這說明CAN總線上出現(xiàn)了嚴(yán)重的錯誤。如下圖CAN總線產(chǎn)生錯誤后的狀態(tài)轉(zhuǎn)換機(jī)制:
如果出現(xiàn)了BUS OFF,總線上的節(jié)點需要做一些動作,例如重啟CAN控制器或是重新上電,但是這些都只是一些補(bǔ)救措施,最根本的還是需要找到引起B(yǎng)US OFF的根源。
CAN總線分析的一些工具和文檔:
CAN分析儀或者邏輯分析儀數(shù)字示波器相關(guān)的軟件debug工具CAN控制器芯片數(shù)據(jù)手冊,這很重要硬件電路圖CAN協(xié)議文檔相關(guān)版本的Linux內(nèi)核源碼
CAN節(jié)點發(fā)送錯誤不成功
問題描述與分析
掛載在CAN總線上的一個節(jié)點向總線上發(fā)送數(shù)據(jù)不成功,用邏輯分析儀也看不到任何波形。PS: 這應(yīng)該是我碰到的最坑爹的事情了。下面具體來看看怎么不成功。于是調(diào)試中斷查看CAN_STATUS即CAN狀態(tài)寄存器顯示0xE5, 查看CPU數(shù)據(jù)手冊:
CAN總線狀態(tài)直接進(jìn)入了BUS OFF狀態(tài),這意味著錯誤計數(shù)已經(jīng)超限,查看CPU收發(fā)寄存器的收發(fā)錯誤計數(shù)顯示發(fā)送錯誤計數(shù)TEC達(dá)到248, 接收錯誤計數(shù)為0;這很明顯,數(shù)據(jù)壓根沒有發(fā)送到總線上。
再進(jìn)一步查看寄存器值LEC即LAST ERROR CODE 最后一個錯誤代碼, 顯示是BIT0 ERROR:
查看上面的錯誤代碼表可知,BIT0錯誤也就是在發(fā)送數(shù)據(jù)期間,雖然CAN節(jié)點設(shè)備想要發(fā)送一個顯性位,也就是邏輯0,但是CAN總線同時監(jiān)聽到總線上的數(shù)據(jù)位為隱性位,即邏輯1。這意味著CAN core往總線上發(fā)送的數(shù)據(jù)第一位就已經(jīng)出錯了,壓根沒有將數(shù)據(jù)經(jīng)過CAN收發(fā)器傳送到CAN總線上。
一直在使用CAN總線的我廠和我從來沒遇到這等奇事,但是由于是新的CPU的開發(fā)所以在懷疑硬件的問題的同時也在排查軟件問題,但是經(jīng)過一陣排查,沒有發(fā)現(xiàn)軟件上的問題?;仡^再分析硬件,又經(jīng)過一陣排查溯源,發(fā)現(xiàn)CPU的CAN收發(fā)線與CAN收發(fā)氣的收發(fā)線接反,直接崩潰(PS: 硬件的大哥你能不能不要坑小弟):
總結(jié)
CAN節(jié)點發(fā)送數(shù)據(jù)不成功,首先分析是不是CAN控制器本身的問題,查看CPU中的CAN core的狀態(tài)寄存器,分析是否有BUS OFF, 如果存在BUS OFF, 則進(jìn)一步查看具體的錯誤信息,是主動的錯誤還是被動的錯,發(fā)送錯誤計數(shù)有沒有超限,最后一次發(fā)生的錯誤狀態(tài)是什么,查看是位填充錯誤還是格式錯誤等其他錯誤,然后具體問題具體分析。這種錯誤一般是有硬件發(fā)送線路出現(xiàn)問題引起,例如光隔次邊不導(dǎo)通,發(fā)送接口接觸不良等,再則是一些奇葩的錯誤,例如本例,收發(fā)線直接接反了,坑爹?。?/p>
CAN Socket 的CAN節(jié)點檢測到錯誤幀
問題描述
我們看到以下的CAN Socket日志,在38秒內(nèi)的三個錯誤幀,但是并沒有引起總線的BUS OFF,這說明總線上檢測到了錯誤,有可能受到了干擾,也有可能是數(shù)據(jù)發(fā)送太密集導(dǎo)致的總線過載,但是在這38秒內(nèi)出現(xiàn)錯誤,但是期間又恢復(fù)正常。
CAN ID : 0x20000004 = 10 0000 0000 0000 0000 0000 0000 0100, 即仲裁域的值。
Linux內(nèi)核源碼分析
因為出現(xiàn)此錯誤的是我廠的CAN控制器CPU TI 公司的AM3352, 內(nèi)核版本為Linux 3.2.0
所以我們通過內(nèi)核來看內(nèi)核CAN錯誤can_id的定義:
[cpp]
view plain
copy
print?
/* 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 */
/* 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位,所以應(yīng)該是0 0000 0000 0000 0000 0000 0000 0100 =0x00000004,既不是CAN_ERR_BUSOFF也不是CAN_ERR_BUSERROR, 而是CAN_ERR_CTRL,
即CAN控制器的問題,而我們在看data[1]描述的CAN 控制器錯誤類型描述:
?。踓pp]
view plain
copy
print?
/* 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 */
/* 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 */
我們再看我們截取的錯誤幀數(shù)據(jù)報文中顯示data[1] = 0x04,如下圖所示:
即具體錯誤為:
#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */
也就是說CAN 控制器接收錯誤計數(shù)達(dá)到了警告的級別,需要提出警告,如果再這樣下去CAN控制器就要過載了,甚至?xí)鹂偩€的BUS OFF.
我們再回頭看內(nèi)核源碼對此錯誤的處理:產(chǎn)生data[1] = CAN_ERR_CRTL_RX_WARNING 錯誤的內(nèi)核源函數(shù)為:
?。踓pp]
view plain
copy
print?
static int ti_hecc_error(struct net_device *ndev, int int_status, int err_status)
static int ti_hecc_error(struct net_device *ndev, int int_status, int err_status)
HECC也就是TI公司高速終端CAN控制器的簡稱,用以上的函數(shù)描述TI CAN core的錯誤處理,如下,我們可以看到也就是CAN控制器接收錯誤計數(shù)REC大于96的時候內(nèi)核就會報此錯誤
[cpp]
view plain
copy
print?
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); } 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); }
總結(jié)
出現(xiàn)這個錯誤警告的原因很可能是:
此CAN總線上有干擾,導(dǎo)致CAN控制器發(fā)生接收錯誤,CAN總線上的信號經(jīng)過收發(fā)器轉(zhuǎn)化為差分電平信號,此時信號容易受到外界干擾,這樣容易使CAN控制器發(fā)生接收錯誤,接收錯誤寄存器接收錯誤計數(shù)累計到一定值后會報此錯誤,如果錯誤計數(shù)達(dá)到一定程度甚至?xí)?dǎo)致總線關(guān)閉也就是BUS
OFF. 如果最終確認(rèn)是由于干擾引起的錯誤計數(shù)累計,則應(yīng)該排查干擾源,然后增加抗干擾措施。
此CAN節(jié)點經(jīng)過消息濾波后仍然需要接收大量的消息,導(dǎo)致CPU中的CAN控制器接收出錯,并且錯誤計數(shù)達(dá)到了錯誤警告的上限。但是慶幸的是總線仍然沒有過載,總線還可以正常收發(fā)數(shù)據(jù),沒有引起B(yǎng)US
OFF。但是對于一個安全可靠控制系統(tǒng),這樣的警告是絕對不允許的。我們需要通過一些手段去避免這樣的問題出現(xiàn),例如降低總線數(shù)據(jù)并發(fā)量,降低總線負(fù)載。
CAN總線設(shè)備離線與錯誤恢復(fù)
這種問題同樣很詭異,但是似乎又是比較常見的問題,這樣的問題出現(xiàn)的情況往往比較多,例如CAN節(jié)Power off也就是電斷了,總線上也就肯定監(jiān)聽不到此CAN節(jié)點的心跳,或是CAN總線節(jié)點沒有及時發(fā)送心跳,阻塞在任務(wù)處理里,又或是此CAN節(jié)點物理接線和總線斷開,等等原因很多。
我這里要說的一種情況是我廠碰到的另一種問題。
評論