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