戰(zhàn):TWAI外設(shè)踩坑與波特率誤差排查)
開(kāi)頭部分作為從業(yè)者我可以直接告訴你用ESP32做CAN通信最坑的不是協(xié)議本身而是TWAI外設(shè)那些藏在手冊(cè)角落里的硬件細(xì)節(jié)。這個(gè)外包項(xiàng)目前后跑了六周從原理圖評(píng)審到產(chǎn)線治具聯(lián)調(diào)踩過(guò)的坑比預(yù)期多得多。如果你正準(zhǔn)備在ESP32上跑CAN總線或者已經(jīng)被波特率誤差、總線仲裁這種問(wèn)題折磨過(guò)這篇實(shí)戰(zhàn)記錄應(yīng)該能幫你省下不少?gòu)澛?。先說(shuō)下項(xiàng)目背景一套工業(yè)設(shè)備的數(shù)據(jù)采集模塊MCU選型定了ESP32-WROOM-32E需要掛接到現(xiàn)場(chǎng)已有的CAN 2.0B總線上采集六個(gè)傳感器節(jié)點(diǎn)的數(shù)據(jù)并透?jìng)髦罻i-Fi網(wǎng)關(guān)。硬件上要自己畫接口板軟件用ESP-IDF 4.4.2的驅(qū)動(dòng)層API做二次開(kāi)發(fā)。整個(gè)項(xiàng)目交付物包含原理圖、PCB封裝庫(kù)、TWAI驅(qū)動(dòng)封裝層、應(yīng)用層協(xié)議解析Demo以及產(chǎn)線用的通信測(cè)試治具上位機(jī)。這個(gè)過(guò)程中最有價(jià)值的不是最終跑通的代碼而是那些讓通信從時(shí)好時(shí)壞到穩(wěn)定可靠的排查思路和設(shè)計(jì)取舍。下面我把整個(gè)項(xiàng)目的技術(shù)決策和踩坑過(guò)程拆開(kāi)來(lái)講。1. 硬件設(shè)計(jì)階段的三個(gè)關(guān)鍵決策1.1 為什么ESP32的TWAI不是完整的CAN控制器這是很多人第一次在ESP32上做CAN通信時(shí)忽略的問(wèn)題。ESP32的TWAITwo-Wire Automotive Interface雖然兼容CAN 2.0B協(xié)議但它的控制器實(shí)現(xiàn)和市場(chǎng)主流的SJA1000、MCP2515不完全一樣。最明顯的差異是兩個(gè)一是沒(méi)有硬件消息郵箱而是在內(nèi)存里通過(guò)軟件管理的發(fā)送/接收緩沖RX FIFO深度可以配到64條消息二是波特率分頻器的限制不像SJA1000那樣可以通過(guò)BRP和SJW自由組合出任意波特率。我的項(xiàng)目需求是500 kbps系統(tǒng)時(shí)鐘用默認(rèn)的APB 80 MHz。按照ESP-IDF的driver/twai.h頭文件說(shuō)明計(jì)算bit timing的公式是BRP (APB_CLK / (2 * (SEG_1 SEG_2 3) * baud_rate)) - 1實(shí)測(cè)下來(lái)500 kbps下SEG_1和SEG_2的組合還算充裕波特率誤差可以控制在0.1%以內(nèi)。但如果你的項(xiàng)目要求跑1 Mbps或者125 kbps這種非標(biāo)速率建議先用樂(lè)鑫官方文檔里的TWAI bit timing計(jì)算器算一遍確認(rèn)誤差是否在CAN協(xié)議規(guī)定的±0.5%容差內(nèi)。我的選擇是硬件接口板上預(yù)留了CAN收發(fā)器的模式選擇引腳通過(guò)跳線電阻把某款收發(fā)器的工作模式固定為高速模式同時(shí)兼容待機(jī)和靜音模式切換——這是為了配合軟件層做總線診斷用。1.2 收發(fā)器選型與終端電阻放置的位置這個(gè)項(xiàng)目選的是TJA1051T/3一款口碑穩(wěn)固的CAN收發(fā)器耐壓和EMC表現(xiàn)中規(guī)中矩貨源也穩(wěn)定。不過(guò)真正值得一說(shuō)的是終端電阻的放置。行業(yè)內(nèi)對(duì)每個(gè)節(jié)點(diǎn)都加120歐終端還是只在總線兩端加一直有爭(zhēng)論。外包項(xiàng)目里常見(jiàn)的情況是工程現(xiàn)場(chǎng)總線上掛了7、8個(gè)設(shè)備線纜也不知道是誰(shuí)布設(shè)的節(jié)點(diǎn)間距參差不齊。這時(shí)如果你在每個(gè)節(jié)點(diǎn)上都焊了120歐電阻總線等效阻抗會(huì)被拉得很低CAN_H和CAN_L之間的差分電壓幅度直接衰減通信距離大打折扣。這個(gè)項(xiàng)目里我做了個(gè)設(shè)計(jì)決策在接口板上把終端電阻焊盤設(shè)計(jì)成可選用0805封裝的0歐跳線來(lái)切換出廠默認(rèn)不焊接。同時(shí)在外包交付文檔里明確寫給客戶如果你這個(gè)節(jié)點(diǎn)正好在總線物理末端請(qǐng)把跳線焊上。事實(shí)證明這個(gè)靈活性救了大命——后來(lái)客戶現(xiàn)場(chǎng)做樣機(jī)聯(lián)調(diào)時(shí)發(fā)現(xiàn)總線掛載了三個(gè)節(jié)點(diǎn)但波形振鈴嚴(yán)重最后查出是一臺(tái)第三方設(shè)備內(nèi)部已經(jīng)接了120歐終端導(dǎo)致總線上出現(xiàn)了三處終端。1.3 布局與隔離設(shè)計(jì)對(duì)TWAI外設(shè)工作的影響CAN收發(fā)器和ESP32之間用的是標(biāo)準(zhǔn)UART電平3.3V邏輯但到收發(fā)器之后就是差分信號(hào)了。板上布局要特別注意收發(fā)器的TXD/RXD引腳到ESP32的GPIO走線盡量短避免靠近電源開(kāi)關(guān)節(jié)點(diǎn)或Wi-Fi天線饋點(diǎn)。這個(gè)項(xiàng)目里第一次改板后出現(xiàn)了偶發(fā)性總線繁忙Bus Off排查了半天最后發(fā)現(xiàn)在2.4 GHz Wi-Fi發(fā)射瞬間CAN_RX線上耦合了大約200 mV的噪聲毛刺觸發(fā)了TWAI控制器的位錯(cuò)誤檢測(cè)。解決方案很樸實(shí)在RXD線路上串聯(lián)了一個(gè)33歐電阻并靠近收發(fā)器放置了一個(gè)100 pF到地的濾波電容實(shí)測(cè)對(duì)1 Mbps以下波特率沒(méi)有明顯邊沿退化同時(shí)把RXD走線從PCB底層挪到了內(nèi)層問(wèn)題就消失了。另外客戶要求隔離設(shè)計(jì)車載/工業(yè)現(xiàn)場(chǎng)常見(jiàn)需求我用了一顆6N137光耦做RXD/TXD的雙向隔離板上分成兩個(gè)地平面之間用0歐電阻單點(diǎn)連接。建議如果你也做隔離務(wù)必把隔離電源模塊如B0505S-1WR2放在收發(fā)器和MCU隔離帶中間避免電源耦合噪聲直接串進(jìn)信號(hào)區(qū)。2. TWAI驅(qū)動(dòng)封裝從裸寄存器到應(yīng)用層調(diào)用的中間層設(shè)計(jì)2.1 驅(qū)動(dòng)層架構(gòu)怎么拆ESP-IDF自帶的TWAI驅(qū)動(dòng)是FreeRTOS任務(wù)模型twai_driver_install之后收發(fā)邏輯都跑在驅(qū)動(dòng)內(nèi)部的定時(shí)器線程里。用起來(lái)確實(shí)簡(jiǎn)單但外包項(xiàng)目里有個(gè)麻煩客戶要求的應(yīng)用層協(xié)議帶有自己的幀間隔比如500毫秒周期里前100毫秒收6幀、后400毫秒空閑直接用驅(qū)動(dòng)默認(rèn)的RX FIFO 事件回調(diào)會(huì)發(fā)現(xiàn)消息擠在一起時(shí)間戳對(duì)不上。所以我在驅(qū)動(dòng)之上封裝了一個(gè)輕量中間層對(duì)外只暴露三個(gè)接口esp_err_t can_mgr_init(can_mgr_config_t *cfg); esp_err_t can_mgr_send(uint32_t id, uint8_t *data, uint8_t len, uint32_t timeout_ms); esp_err_t can_mgr_recv(can_msg_t *msg, uint32_t timeout_ms);內(nèi)部做兩件事一是維護(hù)一個(gè)帶時(shí)間戳的環(huán)形接收隊(duì)列用esp_timer_get_time()打點(diǎn)單位微秒這樣上層邏輯拿到消息后可以直接計(jì)算幀間隔而不用依賴回調(diào)順序二是把TWAI驅(qū)動(dòng)要求的twai_message_t數(shù)據(jù)結(jié)構(gòu)翻譯成自己的can_msg_t隱藏掉擴(kuò)展幀標(biāo)志位組合等容易記錯(cuò)的細(xì)節(jié)。這個(gè)設(shè)計(jì)實(shí)際上只增加了幾百行代碼但對(duì)上層應(yīng)用開(kāi)發(fā)效率提升很大。特別是客戶找的另一家軟件團(tuán)隊(duì)要用這個(gè)中間層開(kāi)發(fā)PLC協(xié)議棧他們完全不需要關(guān)心底層的TWAI速率配置和消息格式。2.2 過(guò)濾器配置單播還是全收TWAI控制器內(nèi)置了2個(gè)接受過(guò)濾器Acceptance Filter支持單濾波Single Filter和雙濾波Dual Filter兩種模式。很多人不知道esp32這里有個(gè)坑默認(rèn)情況下twai_reconfigure_alerts()不會(huì)幫你清空已收到的RX FIFO。你在配置過(guò)濾器時(shí)如果不小心改了濾波模式之前收到的舊消息依然會(huì)留在隊(duì)列里應(yīng)用層取出來(lái)時(shí)一臉懵。我在中間層里做的是單濾波模式只接受標(biāo)準(zhǔn)幀ID落在 0x180~0x1FF 范圍內(nèi)的消息其他的全丟棄。這個(gè)配置用下面的結(jié)構(gòu)體twai_filter_config_t f { .acceptance_code (0x180 21), .acceptance_mask (~(0x7F 21)) 0x1FFFFFFF, .single_filter true, };注意CAN標(biāo)準(zhǔn)幀ID在TWAI里是左對(duì)齊存放在29位地址空間中的所以需要左移21位。如果用的是擴(kuò)展幀29位ID這個(gè)移位要改成左移3位。我就因?yàn)檫@個(gè)左移位數(shù)錯(cuò)過(guò)一次調(diào)試時(shí)發(fā)現(xiàn)收到的ID值總是比實(shí)際大很多排查了半天。2.3 低功耗場(chǎng)景下TWAI外設(shè)的特殊處理ESP32的低功耗模式對(duì)TWAI外設(shè)來(lái)說(shuō)有點(diǎn)蛋疼。因?yàn)門WAI外設(shè)的時(shí)鐘來(lái)源于APB而APB在Light-sleep模式下會(huì)關(guān)閉。如果你的設(shè)備有總線要一直在線監(jiān)聽(tīng)的需求TWAI是做不到的——它沒(méi)有獨(dú)立的CAN喚醒引腳中斷機(jī)制。這個(gè)項(xiàng)目后來(lái)用了一個(gè)折中方案系統(tǒng)常態(tài)下Wi-Fi和TWAI都保持activeMCU主頻降到80 MHzESP32最低可用頻率實(shí)測(cè)整板功耗大約 60~70 mA含傳感器供電客戶能接受。如果真要做低功耗喚醒建議在收發(fā)器后面加一顆獨(dú)立的總線活動(dòng)檢測(cè)電路用外部GPIO中斷把芯片從deep sleep拉起來(lái)——這是另一個(gè)話題了但設(shè)計(jì)硬件時(shí)要先把引腳留出來(lái)。3. 波特率誤差與重同步一度讓通信時(shí)好時(shí)壞的元兇3.1 CAN波特率誤差的麻煩在哪CAN總線上每個(gè)節(jié)點(diǎn)都有自己的時(shí)鐘源沒(méi)有任何全局時(shí)鐘同步機(jī)制。保證通信可靠的關(guān)鍵一個(gè)是每個(gè)節(jié)點(diǎn)的位時(shí)間參數(shù)設(shè)置要足夠接近理想值另一個(gè)是靠CAN控制器硬件層面的重同步機(jī)制去容忍一定程度的節(jié)點(diǎn)間時(shí)鐘漂移。這個(gè)項(xiàng)目的第一個(gè)樣機(jī)階段我用的是默認(rèn)的外部晶振40 MHz但后來(lái)因?yàn)锽OM替換客戶換了一顆國(guó)產(chǎn)晶振標(biāo)稱精度±30 ppm。換完之后原本穩(wěn)如老狗的通信開(kāi)始出現(xiàn)偶發(fā)性錯(cuò)誤幀一天大概幾次到幾十次不等頻率隨溫度升高而增加。用示波器抓CAN_H和CAN_L差分波形肉眼看邊沿對(duì)齊程度還可以但拿協(xié)議分析儀看錯(cuò)誤幀類型發(fā)現(xiàn)都是Form Error和Stuff Error。這說(shuō)明問(wèn)題出在位采樣點(diǎn)上——接收節(jié)點(diǎn)在錯(cuò)誤的時(shí)刻采樣了位電平把正常的隱性位采成了顯性位或者反過(guò)來(lái)。3.2 重同步到底是怎么起作用的CAN協(xié)議的位時(shí)間由同步段Sync_Seg、傳播段Prop_Seg、相位緩沖段1Phase_Seg1和相位緩沖段2Phase_Seg2組成。每個(gè)節(jié)點(diǎn)在幀起始的下降沿SOF位從顯性到隱性會(huì)做一次硬同步把本地位時(shí)間的相位對(duì)齊到總線的邊沿。后續(xù)每收到一個(gè)邊沿會(huì)做重同步如果邊沿比本地預(yù)期提前到達(dá)就縮短Phase_Seg1相位緩沖段1如果邊沿滯后就延長(zhǎng)Phase_Seg1。重同步的調(diào)整步長(zhǎng)由SJW同步跳轉(zhuǎn)寬度決定。關(guān)鍵點(diǎn)來(lái)了如果SJW設(shè)置太小比如只配了1個(gè)時(shí)間量子TQ而本地時(shí)鐘誤差已經(jīng)超過(guò)容差重同步跟不上就會(huì)出現(xiàn)采樣點(diǎn)偏移導(dǎo)致我上面撞到的Form Error。ESP-IDF的twai_timing_config_t里SJW在默認(rèn)配置下等于1個(gè)TQ——這在振蕩器精度很好的情況下夠用但換晶振后就不行了。我的解決方法是手動(dòng)把sjw設(shè)為3個(gè)TQ同時(shí)把采樣點(diǎn)從默認(rèn)的87.5%調(diào)整到80%。修改后的代碼片段twai_timing_config_t t { .brp 3, // 80 MHz / (2 * (16 3 3) * 500k) ≈ 3 .tseg_1 16, // 重新分配相位緩沖段1 .tseg_2 3, // 相位緩沖段2 .sjw 3, // 關(guān)鍵增大重同步跳轉(zhuǎn)寬度 .triple_sampling false, };這個(gè)配置下位時(shí)間總共 1 16 3 3 23 TQ采樣點(diǎn) (1 16 3) / 23 ≈ 86.9%在500 kbps下不算激進(jìn)但SJW從1加到3之后重同步能力大幅提高。實(shí)測(cè)48小時(shí)連續(xù)通信零錯(cuò)誤幀。3.3 如何驗(yàn)證時(shí)鐘誤差是否在安全范圍強(qiáng)烈建議在樣機(jī)階段用邏輯分析儀或CAN協(xié)議分析儀抓取總線空閑期間節(jié)點(diǎn)自發(fā)發(fā)出的錯(cuò)誤幀統(tǒng)計(jì)。如果錯(cuò)誤類型集中在Bit Error和Stuff Error基本可以鎖定是波特率配置問(wèn)題先加SJW試試如果錯(cuò)誤幀大量出現(xiàn)在特定ID附近更可能是仲裁或位填充邏輯的邊界問(wèn)題。另外可以做一個(gè)簡(jiǎn)單計(jì)算來(lái)驗(yàn)證配置是否安全最大允許振蕩器誤差 (SJW / (2 * 10 * NBT)) * 100%其中NBT是單個(gè)位時(shí)間內(nèi)的TQ總數(shù)。以我的配置為例SJW3NBT23最大允許誤差 (3 / (2 * 10 * 23)) * 100% ≈ 0.65%。而國(guó)產(chǎn)晶振±30 ppm的溫度漂移遠(yuǎn)小于這個(gè)值理論上沒(méi)問(wèn)題。但如果SJW1允許誤差只有0.217%對(duì)晶振要求就苛刻得多——這就是為什么默認(rèn)配置在換晶振后就翻車了。4. 整車聯(lián)調(diào)中遇到的仲裁與總線繁忙問(wèn)題4.1 多節(jié)點(diǎn)同時(shí)上電仲裁機(jī)制救了場(chǎng)面項(xiàng)目驗(yàn)收前最后一周客戶在裝配車間搭了一個(gè)模擬環(huán)境一個(gè)主控 6個(gè)傳感器節(jié)點(diǎn) 我這套ESP32網(wǎng)關(guān)全部掛在同一條CAN總線上。第一次上電7個(gè)節(jié)點(diǎn)同時(shí)發(fā)送初始化報(bào)文總線上瞬間產(chǎn)生大量沖突——但CAN協(xié)議層面的載波監(jiān)聽(tīng)多路訪問(wèn)/沖突避免CSMA/CA機(jī)制把這場(chǎng)混亂處理得井井有條。CAN的仲裁是逐位進(jìn)行的每個(gè)節(jié)點(diǎn)在發(fā)送顯性位邏輯0時(shí)會(huì)同時(shí)監(jiān)測(cè)總線電平。如果某個(gè)節(jié)點(diǎn)發(fā)送隱性位邏輯1但讀到總線是顯性說(shuō)明有更高優(yōu)先級(jí)的節(jié)點(diǎn)在發(fā)送它立即退出仲裁轉(zhuǎn)為接收狀態(tài)。這個(gè)過(guò)程在硬件里完成軟件無(wú)感知。不過(guò)我在這個(gè)項(xiàng)目的應(yīng)用層里確實(shí)做過(guò)一件幫忙的事給不同優(yōu)先級(jí)的報(bào)文分配了不同的CAN ID區(qū)間比如傳感器周期數(shù)據(jù)用 0x180~0x1AF網(wǎng)關(guān)心跳用 0x100注意ID越低優(yōu)先級(jí)越高診斷請(qǐng)求用 0x200~0x3FF。這讓高優(yōu)先級(jí)的傳感器數(shù)據(jù)在總線繁忙時(shí)能優(yōu)先搶到發(fā)送權(quán)診斷報(bào)文即使被延遲也不會(huì)影響控制流。4.2 總線繁忙錯(cuò)誤碼表面現(xiàn)象背后是兩個(gè)原因在聯(lián)調(diào)的頭兩天我反復(fù)收到ESP_ERR_TWAI_BUSY錯(cuò)誤碼表現(xiàn)是網(wǎng)關(guān)偶爾發(fā)不出去消息。一開(kāi)始以為是ID分配不合理導(dǎo)致網(wǎng)關(guān)優(yōu)先級(jí)太低后來(lái)把代碼里的錯(cuò)誤碼打印日志加上時(shí)間戳發(fā)現(xiàn)總線繁忙只在上電后的第3~5秒出現(xiàn)之后就完全消失。用CAN協(xié)議分析儀抓包后真相大白傳感器節(jié)點(diǎn)上電后有個(gè)自檢過(guò)程期間會(huì)以非常高的頻率發(fā)送設(shè)備自檢狀態(tài)幀每隔10毫秒一幀持續(xù)約2秒。我這臺(tái)ESP32網(wǎng)關(guān)同時(shí)也在嘗試發(fā)送心跳幀由于傳感器幀ID0x180比網(wǎng)關(guān)心跳ID0x100高按CAN仲裁規(guī)則應(yīng)該是心跳幀優(yōu)先——但問(wèn)題在于網(wǎng)關(guān)的發(fā)送請(qǐng)求被驅(qū)動(dòng)層的發(fā)送超時(shí)機(jī)制擋了回來(lái)。根源是內(nèi)部發(fā)送緩沖區(qū)的占位問(wèn)題TWAI控制器只有3個(gè)發(fā)送緩沖區(qū)默認(rèn)發(fā)送超時(shí)設(shè)為10毫秒。傳感器節(jié)點(diǎn)雖然仲裁輸了會(huì)讓出總線但持續(xù)的高負(fù)載使得網(wǎng)關(guān)的發(fā)送請(qǐng)求不斷排隊(duì)超時(shí)。解決方式簡(jiǎn)單粗暴把發(fā)送超時(shí)從10毫秒加大到100毫秒同時(shí)在應(yīng)用層把心跳幀的發(fā)送周期從50毫秒調(diào)整為100毫秒避開(kāi)傳感器自檢風(fēng)暴的時(shí)間窗口。這個(gè)案例說(shuō)明在高負(fù)載CAN總線上合理的應(yīng)用層發(fā)送窗口規(guī)劃和超時(shí)重試策略和硬件仲裁設(shè)計(jì)同等重要。4.3 第三方節(jié)點(diǎn)干擾如何快速定位問(wèn)題節(jié)點(diǎn)聯(lián)調(diào)后期出現(xiàn)了一個(gè)很詭異的現(xiàn)象總線上偶爾出現(xiàn)一個(gè)ID為0x7FF、數(shù)據(jù)全為0的報(bào)文頻率不固定而且只要它出現(xiàn)后面的正常報(bào)文就會(huì)出現(xiàn)CRC錯(cuò)誤。排查鏈路比較慢但值得記錄第一步用協(xié)議分析儀過(guò)濾出所有錯(cuò)誤幀的關(guān)聯(lián)時(shí)間點(diǎn)發(fā)現(xiàn)0x7FF報(bào)文總是出現(xiàn)在錯(cuò)誤幀前幾十微秒第二步把6個(gè)傳感器節(jié)點(diǎn)逐個(gè)斷電發(fā)現(xiàn)有一個(gè)節(jié)點(diǎn)斷電后0x7FF報(bào)文消失第三步檢查這個(gè)節(jié)點(diǎn)的硬件發(fā)現(xiàn)它的CAN收發(fā)器TXD引腳被軟件錯(cuò)誤拉低了導(dǎo)致該節(jié)點(diǎn)一直認(rèn)為自己在發(fā)送數(shù)據(jù)但實(shí)際電平被總線上的其他節(jié)點(diǎn)壓制形成位錯(cuò)誤第四步根因定位到對(duì)方工程師在寫底層驅(qū)動(dòng)時(shí)把GPIO模式腳配置成了普通的開(kāi)漏輸出而非推挽輸出導(dǎo)致發(fā)送顯性位時(shí)驅(qū)動(dòng)能力不足。這個(gè)排查過(guò)程本身不算特別難但耗時(shí)兩天主要原因在于0x7FF報(bào)文恰好落在我的接受濾波器范圍內(nèi)掩蓋了它的非法報(bào)文身份。如果接受濾波器能配置成統(tǒng)計(jì)所有錯(cuò)誤幀而不只接收特定ID這個(gè)問(wèn)題可能半天就定位了。所以大家在設(shè)計(jì)診斷工具時(shí)強(qiáng)烈建議留一個(gè)總線監(jiān)聽(tīng)模式的軟件開(kāi)關(guān)允許臨時(shí)把TWAI配置為接收所有幀且關(guān)閉過(guò)濾器便于現(xiàn)場(chǎng)排查。5. 交付物中容易被忽略的細(xì)節(jié)產(chǎn)線測(cè)試治具與文檔外包項(xiàng)目不只在實(shí)驗(yàn)室里跑通Demo就完了產(chǎn)線端的測(cè)試治具往往占據(jù)最后30%的時(shí)間。這個(gè)項(xiàng)目里我做了兩件交付物個(gè)人認(rèn)為能給你參考。5.1 產(chǎn)線測(cè)試盒是一個(gè)小型的ESP32 TWAI OLED產(chǎn)線上需要快速驗(yàn)證每一塊新焊好的網(wǎng)關(guān)板能不能正常收發(fā)CAN報(bào)文??紤]到車間不方便用電腦我直接用另一塊ESP32做了一個(gè)測(cè)試治具板上焊了CAN收發(fā)器、一個(gè)OLED屏和三個(gè)按鈕。治具固件邏輯很簡(jiǎn)單上電自動(dòng)以500 kbps初始化TWAI然后周期發(fā)送ID 0x100、數(shù)據(jù)為板號(hào)信息的報(bào)文被測(cè)板收到后通過(guò)Wi-Fi回傳數(shù)據(jù)到治具治具把OK或NG顯示在OLED上。整個(gè)測(cè)試流程3秒內(nèi)完成產(chǎn)線工人不需要任何培訓(xùn)。這個(gè)治具最有價(jià)值的代碼反而很簡(jiǎn)單把ESP32的MAC地址后兩個(gè)字節(jié)作為板號(hào)編進(jìn)CAN報(bào)文的第一個(gè)數(shù)據(jù)字節(jié)。這樣產(chǎn)線主控端掃一眼報(bào)文就能自動(dòng)識(shí)別是哪塊板子發(fā)出的不用手動(dòng)輸序列號(hào)。這個(gè)思路其實(shí)借鑒了標(biāo)準(zhǔn)CANopen的服務(wù)數(shù)據(jù)對(duì)象SDO節(jié)點(diǎn)ID分配方式。5.2 文檔里必須寫清楚重新上電行為外包項(xiàng)目交付后的一大隱患是客戶換人維護(hù)。如果你的設(shè)備需要掉電保存配置或者IAP升級(jí)這類功能務(wù)必在文檔里描述清楚重新上電后的狀態(tài)轉(zhuǎn)移圖——ESP32的NVS默認(rèn)區(qū)域很小但存幾個(gè)波特率和節(jié)點(diǎn)ID的參數(shù)完全夠用。我在這項(xiàng)目里做了一個(gè)挫舉把配置參數(shù)存到NVS后如果檢測(cè)到參數(shù)非法比如波特率不在合法枚舉值內(nèi)固件會(huì)強(qiáng)制恢復(fù)默認(rèn)配置并回發(fā)一條錯(cuò)誤碼報(bào)文而不是直接罷工。這個(gè)防御式啟動(dòng)邏輯后來(lái)被客戶表?yè)P(yáng)了說(shuō)某臺(tái)設(shè)備在產(chǎn)線上被人把波特率誤改成了250 kbps后依然能自愈上線。6. 回頭再看ESP32做CAN通信的產(chǎn)品級(jí)建議項(xiàng)目收尾后團(tuán)隊(duì)內(nèi)部復(fù)盤時(shí)總結(jié)了幾個(gè)產(chǎn)品級(jí)建議有些踩坑心得不是技術(shù)文檔里能查到的這里一并列出來(lái)如果你的CAN節(jié)點(diǎn)數(shù)量超過(guò)5個(gè)、總線距離超過(guò)10米不要省終端電阻的跳線設(shè)計(jì)成本。這個(gè)靈活性在現(xiàn)場(chǎng)聯(lián)調(diào)時(shí)價(jià)值極大。強(qiáng)烈建議軟件層里始終開(kāi)啟錯(cuò)誤幀中斷TWAI_ALERT_ERR_PASS、TWAI_ALERT_BUS_OFF并把這些事件打印到日志里。很多時(shí)候通信時(shí)好時(shí)壞其實(shí)就是總線正在頻繁進(jìn)入Error Passive狀態(tài)而你完全不知道。TWAI和Wi-Fi同時(shí)工作時(shí)Wi-Fi的高功耗射頻信號(hào)對(duì)CAN接收的影響真實(shí)存在。我的經(jīng)驗(yàn)是PCB鋪地越完整、CAN走線離天線饋點(diǎn)越遠(yuǎn)問(wèn)題越小。翻不過(guò)去的坎再加RC濾波或隔離光耦。不用過(guò)度追求高波特率。500 kbps在大多數(shù)工業(yè)采集場(chǎng)景下已經(jīng)足夠而且兼容性最好。1 Mbps對(duì)線纜質(zhì)量、接頭工藝和終端配置的要求都高一個(gè)量級(jí)現(xiàn)場(chǎng)排查難度也會(huì)增加不少。項(xiàng)目實(shí)際跑下來(lái)我認(rèn)為ESP32的TWAI外設(shè)并不是一個(gè)照著手冊(cè)就能輕松調(diào)通的東西——它的便利性在于和Wi-Fi/藍(lán)牙同芯片集成省掉了外部CAN控制器芯片但代價(jià)是配置項(xiàng)的容錯(cuò)空間不如獨(dú)立CAN控制器芯片。如果你的產(chǎn)品會(huì)面臨長(zhǎng)期高低溫工作、總線節(jié)點(diǎn)多、線纜不確定的現(xiàn)場(chǎng)環(huán)境最好在硬件設(shè)計(jì)階段就把隔離和可配置終端電阻做進(jìn)去軟件層預(yù)留好總線監(jiān)聽(tīng)模式。這兩件事前期不做后期現(xiàn)場(chǎng)聯(lián)調(diào)時(shí)一定會(huì)以更痛苦的方式補(bǔ)回來(lái)。