STM32 CAN總線通信從原理到實(shí)踐:核心協(xié)議、驅(qū)動(dòng)實(shí)現(xiàn)與調(diào)試指南
1. 項(xiàng)目概述從零開始理解CAN通信最近在做一個(gè)車載設(shè)備相關(guān)的項(xiàng)目不可避免地要和CAN總線打交道。說實(shí)話第一次接觸CANController Area Network控制器局域網(wǎng)時(shí)看著那一堆縮寫和協(xié)議幀格式確實(shí)有點(diǎn)懵。它不像我們熟悉的UART、I2C或SPI那樣“直來直去”發(fā)送和接收都靠?jī)筛€還得考慮仲裁、錯(cuò)誤幀這些復(fù)雜機(jī)制。但當(dāng)你真正理解了它的設(shè)計(jì)哲學(xué)和應(yīng)用場(chǎng)景后就會(huì)明白為什么在汽車、工業(yè)自動(dòng)化這些對(duì)可靠性和實(shí)時(shí)性要求極高的領(lǐng)域CAN總線幾乎是不可替代的選擇。這篇筆記就是我結(jié)合STM32平臺(tái)從零開始梳理CAN通信核心概念和基礎(chǔ)實(shí)操的總結(jié)目標(biāo)是讓后來者能少走些彎路快速建立起對(duì)CAN的直觀認(rèn)識(shí)。簡(jiǎn)單來說CAN是一種多主、廣播式的串行通信總線。它的核心思想是“用消息標(biāo)識(shí)符ID來區(qū)分優(yōu)先級(jí)而不是設(shè)備地址”。所有節(jié)點(diǎn)都掛載在兩條差分信號(hào)線CAN_H和CAN_L上任何一個(gè)節(jié)點(diǎn)都可以主動(dòng)發(fā)送消息成為主節(jié)點(diǎn)總線上所有其他節(jié)點(diǎn)都會(huì)收到這條消息并根據(jù)消息ID來決定是否接收和處理它。這種設(shè)計(jì)非常適合分布式控制系統(tǒng)比如汽車?yán)锏陌l(fā)動(dòng)機(jī)控制單元ECU、車身控制器、儀表盤等模塊之間的數(shù)據(jù)交換。你不需要為每?jī)蓚€(gè)設(shè)備之間單獨(dú)布線所有信息都通過這一對(duì)“高速公路”廣播出去誰需要誰就“聽”大大簡(jiǎn)化了線束復(fù)雜度也提升了系統(tǒng)的可擴(kuò)展性。那么為什么我們要學(xué)習(xí)CAN特別是在STM32上首先STM32系列微控制器幾乎全系都集成了至少一個(gè)CAN控制器bxCAN硬件支持完善學(xué)習(xí)成本相對(duì)較低。其次無論是做汽車電子后裝產(chǎn)品如OBD診斷儀、車載娛樂系統(tǒng)、無人機(jī)飛控、還是工業(yè)現(xiàn)場(chǎng)的PLC通訊CAN都是必須掌握的技能。理解CAN不僅僅是會(huì)調(diào)通一個(gè)發(fā)送接收的例程更重要的是理解其背后的錯(cuò)誤管理、總線仲裁、幀格式等機(jī)制這樣才能設(shè)計(jì)出穩(wěn)定、可靠的通信系統(tǒng)。接下來我們就從最基礎(chǔ)的物理層和協(xié)議層開始拆解。2. CAN通信的核心原理與協(xié)議層詳解要玩轉(zhuǎn)CAN不能只停留在調(diào)用HAL庫函數(shù)的層面必須對(duì)其底層原理有清晰的認(rèn)識(shí)。這部分內(nèi)容可能有點(diǎn)枯燥但它是后續(xù)一切調(diào)試和問題排查的基礎(chǔ)請(qǐng)務(wù)必耐心理解。2.1 物理層差分信號(hào)與總線拓?fù)銫AN的物理層決定了它的抗干擾能力和通信距離。它使用ISO 11898標(biāo)準(zhǔn)定義的兩種邏輯電平顯性電平Dominant和隱性電平Recessive。顯性電平對(duì)應(yīng)邏輯‘0’。此時(shí)CAN_H電壓比CAN_L高典型值為CAN_H3.5V CAN_L1.5V差分電壓Vdiff 2V。隱性電平對(duì)應(yīng)邏輯‘1’。此時(shí)CAN_H和CAN_L電壓相等都約為2.5V差分電壓Vdiff 0V。這里有一個(gè)非常關(guān)鍵的設(shè)計(jì)顯性電平優(yōu)先級(jí)高于隱性電平。當(dāng)多個(gè)節(jié)點(diǎn)同時(shí)發(fā)送時(shí)只要有一個(gè)節(jié)點(diǎn)發(fā)送顯性位‘0’總線就被拉成顯性狀態(tài)。這個(gè)特性是實(shí)現(xiàn)“線與”邏輯和總線仲裁的基礎(chǔ)。物理層通常需要一個(gè)CAN收發(fā)器芯片如TJA1050、SN65HVD230來連接微控制器的CAN控制器和實(shí)際的CAN總線??刂破鳟a(chǎn)生的是數(shù)字信號(hào)TX RX而收發(fā)器負(fù)責(zé)將其轉(zhuǎn)換為差分模擬信號(hào)并驅(qū)動(dòng)總線。關(guān)于拓?fù)銫AN總線要求兩端必須各接一個(gè)120歐姆的終端電阻用于阻抗匹配消除信號(hào)反射。很多新手容易忽略這一點(diǎn)導(dǎo)致通信不穩(wěn)定或根本無法通信??偩€一般采用直線型拓?fù)涓删€節(jié)點(diǎn)通過短支線Stub接入支線應(yīng)盡可能短以減少信號(hào)完整性問題。注意終端電阻必須且只能有兩個(gè)分別位于總線物理距離的兩端。如果網(wǎng)絡(luò)中只有兩個(gè)節(jié)點(diǎn)那么每個(gè)節(jié)點(diǎn)內(nèi)部都應(yīng)配置一個(gè)120歐姆電阻。使用開發(fā)板時(shí)務(wù)必檢查板載的CAN收發(fā)器附近是否有跳線帽選擇是否接入終端電阻避免重復(fù)接入導(dǎo)致總線負(fù)載過重。2.2 數(shù)據(jù)鏈路層幀格式、仲裁與錯(cuò)誤處理這是CAN協(xié)議的核心。CAN2.0規(guī)范定義了兩種幀格式標(biāo)準(zhǔn)幀11位標(biāo)識(shí)符和擴(kuò)展幀29位標(biāo)識(shí)符。我們以最常見的標(biāo)準(zhǔn)幀為例拆解其構(gòu)成。一個(gè)完整的CAN數(shù)據(jù)幀由以下字段順序構(gòu)成幀起始SOF一個(gè)顯性位‘0’標(biāo)志一幀的開始用于同步。仲裁場(chǎng)包含標(biāo)識(shí)符ID和遠(yuǎn)程傳輸請(qǐng)求位RTR。ID決定了消息的優(yōu)先級(jí)和內(nèi)容。ID數(shù)值越小優(yōu)先級(jí)越高。RTR位用于區(qū)分?jǐn)?shù)據(jù)幀顯性‘0’和遠(yuǎn)程幀隱性‘1’。控制場(chǎng)包含一個(gè)保留位IDE用于區(qū)分標(biāo)準(zhǔn)/擴(kuò)展幀和4位數(shù)據(jù)長(zhǎng)度碼DLC指示后續(xù)數(shù)據(jù)場(chǎng)包含0-8個(gè)字節(jié)的數(shù)據(jù)。數(shù)據(jù)場(chǎng)實(shí)際要傳輸?shù)臄?shù)據(jù)0-8字節(jié)。這是CAN幀的載荷部分。CRC場(chǎng)15位循環(huán)冗余校驗(yàn)碼 1位隱性CRC界定符用于接收方校驗(yàn)數(shù)據(jù)在傳輸過程中是否出錯(cuò)。應(yīng)答場(chǎng)ACK包括ACK槽和ACK界定符。發(fā)送節(jié)點(diǎn)在ACK槽發(fā)出一個(gè)隱性位‘1’所有正確接收到該幀的節(jié)點(diǎn)會(huì)在此時(shí)刻向總線發(fā)送一個(gè)顯性位‘0’作為應(yīng)答。如果發(fā)送節(jié)點(diǎn)沒檢測(cè)到這個(gè)顯性位它會(huì)認(rèn)為傳輸失敗并嘗試重發(fā)。幀結(jié)束EOF7個(gè)連續(xù)的隱性位‘1’標(biāo)志幀的結(jié)束??偩€仲裁機(jī)制是CAN的精髓。當(dāng)多個(gè)節(jié)點(diǎn)同時(shí)開始發(fā)送時(shí)它們從SOF開始同步地逐位發(fā)送自己的ID。在發(fā)送ID的過程中每個(gè)節(jié)點(diǎn)同時(shí)也在監(jiān)聽總線電平。如果某個(gè)節(jié)點(diǎn)發(fā)送了一個(gè)隱性位‘1’但監(jiān)聽到的卻是顯性位‘0’它立刻意識(shí)到有更高優(yōu)先級(jí)的消息正在發(fā)送于是立即退出發(fā)送轉(zhuǎn)為接收模式等待總線空閑后再嘗試重發(fā)。這個(gè)過程完全由硬件在位級(jí)別實(shí)時(shí)完成沒有任何延遲保證了最高優(yōu)先級(jí)的消息總能無中斷地發(fā)送出去實(shí)現(xiàn)了非破壞性的仲裁。錯(cuò)誤處理是CAN高可靠性的保障。每個(gè)CAN控制器內(nèi)部都有錯(cuò)誤計(jì)數(shù)器發(fā)送錯(cuò)誤計(jì)數(shù)器TEC和接收錯(cuò)誤計(jì)數(shù)器REC。檢測(cè)到錯(cuò)誤如位錯(cuò)誤、填充錯(cuò)誤、CRC錯(cuò)誤、格式錯(cuò)誤、應(yīng)答錯(cuò)誤時(shí)節(jié)點(diǎn)會(huì)發(fā)送一個(gè)“錯(cuò)誤幀”連續(xù)6個(gè)顯性或隱性位來主動(dòng)破壞當(dāng)前幀通知所有節(jié)點(diǎn)“這幀出錯(cuò)了請(qǐng)丟棄”。同時(shí)錯(cuò)誤計(jì)數(shù)器會(huì)根據(jù)錯(cuò)誤類型增減。根據(jù)計(jì)數(shù)器的值節(jié)點(diǎn)會(huì)處于三種狀態(tài)主動(dòng)錯(cuò)誤狀態(tài)可正常收發(fā)發(fā)現(xiàn)錯(cuò)誤時(shí)發(fā)送主動(dòng)錯(cuò)誤標(biāo)志6個(gè)連續(xù)顯性位。被動(dòng)錯(cuò)誤狀態(tài)可正常收發(fā)但發(fā)現(xiàn)錯(cuò)誤時(shí)只能發(fā)送被動(dòng)錯(cuò)誤標(biāo)志6個(gè)連續(xù)隱性位且發(fā)送后需等待一段額外時(shí)間??偩€關(guān)閉狀態(tài)當(dāng)TEC超過255時(shí)節(jié)點(diǎn)自動(dòng)從總線上斷開無法收發(fā)只能等待恢復(fù)。這套復(fù)雜的機(jī)制確保了即使個(gè)別節(jié)點(diǎn)出現(xiàn)故障也不會(huì)長(zhǎng)期阻塞整個(gè)總線。2.3 CAN控制器的工作模式與波特率計(jì)算以STM32的bxCAN為例它支持幾種關(guān)鍵的工作模式需要通過配置寄存器來設(shè)定。正常模式控制器參與總線通信正常收發(fā)。靜默模式控制器可以接收消息但不會(huì)發(fā)送任何數(shù)據(jù)包括ACK位和錯(cuò)誤幀。它向總線發(fā)送的都是隱性位。常用于監(jiān)控總線流量而不干擾總線。環(huán)回模式發(fā)送端輸出直接反饋到接收端不與外部總線相連。用于自測(cè)試在不連接其他節(jié)點(diǎn)的情況下驗(yàn)證CAN控制器的軟硬件功能是否正常。環(huán)回靜默模式結(jié)合了以上兩者內(nèi)部環(huán)回且不對(duì)外發(fā)送用于最徹底的自檢。波特率計(jì)算是配置的第一步也是最容易出錯(cuò)的地方。CAN總線上的位時(shí)間被劃分為4個(gè)不重疊的段同步段Sync_Seg固定為1個(gè)時(shí)間份額Tq用于同步跳變沿。傳播時(shí)間段Prop_Seg用于補(bǔ)償網(wǎng)絡(luò)中的物理延遲。相位緩沖段1Phase_Seg1用于補(bǔ)償邊沿的相位誤差可被重新同步拉長(zhǎng)。相位緩沖段2Phase_Seg2用于補(bǔ)償邊沿的相位誤差可被重新同步縮短。采樣點(diǎn)通常位于Phase_Seg1結(jié)束的位置。計(jì)算公式如下位時(shí)間 (Tbit) Tq * (Sync_Seg Prop_Seg Phase_Seg1 Phase_Seg2) 波特率 1 / Tbit Tq (PCLK1的預(yù)分頻值) / (CAN外設(shè)時(shí)鐘頻率)其中Sync_Seg固定為1其他段的長(zhǎng)度以Tq為單位可以配置。對(duì)于常見的1Mbps波特率在APB1時(shí)鐘為36MHz時(shí)一種典型的配置是預(yù)分頻值3Prop_Seg1Phase_Seg13Phase_Seg23。這樣Tq 3 / 36MHz 83.33ns 位時(shí)間Tbit 83.33ns * (1133) 666.67ns 波特率 ≈ 1.5Mbps等等這里算錯(cuò)了。實(shí)際上Tq (PCLK1的預(yù)分頻值) / (CAN外設(shè)時(shí)鐘頻率)這個(gè)表述不準(zhǔn)確。更準(zhǔn)確的說法是CAN外設(shè)的時(shí)鐘源來自APB1PCLK1經(jīng)過一個(gè)可編程的預(yù)分頻器得到時(shí)間份額Tq的時(shí)鐘。即Tq (BRP 1) / PCLK1其中BRP是波特率預(yù)分頻器寄存器的值。那么對(duì)于PCLK136MHz 目標(biāo)波特率1Mbps位時(shí)間1000ns 假設(shè)我們?cè)O(shè)置總的時(shí)間份額數(shù)TS1TS21為 15410 Tq其中TS1Phase_Seg1 TS2Phase_Seg2 Prop_Seg合并到了TS1中這是STM32 bxCAN的簡(jiǎn)化模型。 則 Tq 位時(shí)間 / 10 1000ns / 10 100ns。 所以 BRP 1 Tq * PCLK1 100ns * 36MHz 100e-9 * 36e6 3.6。 取整 BRP 3因?yàn)锽RP是整數(shù) 則實(shí)際 Tq (31)/36e6 111.11ns 實(shí)際位時(shí)間 111.11ns * 10 1.111us 實(shí)際波特率 900kbps。要得到精確的1Mbps需要調(diào)整PCLK1或時(shí)間份額的分配。許多STM32CubeMX工具可以幫你自動(dòng)計(jì)算這些參數(shù)。實(shí)操心得波特率配置不匹配是導(dǎo)致通信失敗的最常見原因之一。務(wù)必確??偩€上的所有節(jié)點(diǎn)使用完全相同的波特率設(shè)置包括Tq各段長(zhǎng)度。在復(fù)雜電磁環(huán)境中建議將采樣點(diǎn)設(shè)置在位時(shí)間的75%-80%左右以提高抗干擾能力??梢允褂肅AN分析儀抓取總線波形觀察實(shí)際位時(shí)序來驗(yàn)證配置。3. 基于STM32CubeMX與HAL庫的CAN驅(qū)動(dòng)實(shí)現(xiàn)理論鋪墊完畢我們進(jìn)入實(shí)戰(zhàn)環(huán)節(jié)。我將以STM32F103系列藍(lán)色Pill板常見為例使用STM32CubeMX圖形化配置工具和HAL庫一步步搭建一個(gè)能收能發(fā)的CAN節(jié)點(diǎn)。3.1 硬件連接與CubeMX工程配置首先準(zhǔn)備硬件兩塊STM32開發(fā)板如F103C8T6、兩個(gè)CAN收發(fā)器模塊如帶TJA1050的模塊、若干杜邦線。連接方式如下兩塊板的CAN_TXMCU側(cè)分別接各自收發(fā)器模塊的TX。兩塊板的CAN_RXMCU側(cè)分別接各自收發(fā)器模塊的RX。兩塊收發(fā)器模塊的CAN_H連在一起CAN_L連在一起。在總線兩端即兩個(gè)模塊上的CAN_H和CAN_L之間各接一個(gè)120歐姆電阻。很多模塊自帶120歐姆終端電阻通過跳線帽選擇是否啟用確保只有兩個(gè)端點(diǎn)啟用。共地將兩塊開發(fā)板和兩個(gè)模塊的GND連接在一起。打開STM32CubeMX創(chuàng)建新工程選擇你的芯片型號(hào)。時(shí)鐘配置在RCC中將HSE設(shè)置為Crystal/Ceramic Resonator。在Clock Configuration標(biāo)簽頁配置系統(tǒng)時(shí)鐘。對(duì)于F103通常使用外部8MHz晶振經(jīng)過PLL倍頻到72MHz系統(tǒng)時(shí)鐘。然后配置APB1 Prescaler使PCLK1時(shí)鐘為36MHzCAN外設(shè)掛載在APB1總線上。CAN配置在Pinout Configuration標(biāo)簽頁的左側(cè)找到Connectivity-CAN1。將Mode設(shè)置為Normal。進(jìn)入Parameter Settings子標(biāo)簽Bit Timing Parameters: 這是關(guān)鍵。假設(shè)我們配置500kbps波特率PCLK136MHz。在Prescaler (for Time Quantum)里填寫6。Time Quantum (tq) (BRP1)/PCLK1 (51)/36MHz 166.67ns。然后配置位時(shí)間段Time Segment 113 tqTime Segment 22 tq。這里Time Segment 1包含了標(biāo)準(zhǔn)的Prop_Seg Phase_Seg1Time Segment 2對(duì)應(yīng)Phase_Seg2。同步段固定為1tq。所以總位時(shí)間 1 13 2 16 tq 166.67ns * 16 2.6667us 對(duì)應(yīng)波特率 1/2.6667us ≈ 375kbps看來又需要調(diào)整。我們可以利用CubeMX的自動(dòng)計(jì)算功能在Bit Rate欄直接輸入目標(biāo)波特率如500000然后調(diào)整Time Quanta in Bit Segment 1和2使Actual Bit Rate最接近目標(biāo)值且誤差在可接受范圍1%。經(jīng)過嘗試Prescaler9Time Segment 110Time Segment 23時(shí)實(shí)際波特率為500kbps位時(shí)間2us 20tq Tq100ns。Operating Mode: 選擇Normal。Slave Start Filter Bank: 如果只有一個(gè)CAN保持默認(rèn)。進(jìn)入Filter Configuration子標(biāo)簽過濾器我們稍后詳細(xì)講可以先添加一個(gè)過濾器Filter Mode選Mask modeFilter Scale選32-bitFilter ID High/Low和Filter Mask High/Low都設(shè)為0表示不過濾任何消息全接收?;氐絇inout視圖檢查PA11被自動(dòng)配置為CAN_RXPA12被配置為CAN_TX這是F103的默認(rèn)引腳其他芯片可能不同。生成代碼在Project Manager標(biāo)簽設(shè)置好工程名、路徑、IDE如MDK-ARM然后點(diǎn)擊GENERATE CODE。3.2 過濾器配置詳解精準(zhǔn)接收所需消息CAN控制器在接收時(shí)會(huì)收到總線上所有的幀。如果不加篩選CPU會(huì)被大量無關(guān)的中斷淹沒。硬件過濾器Filter的作用就是在消息到達(dá)接收郵箱FIFO之前根據(jù)ID進(jìn)行篩選。STM32的bxCAN提供了多達(dá)28個(gè)取決于型號(hào)可配置的過濾器組每個(gè)組可以配置為以下兩種模式之一標(biāo)識(shí)符列表模式Identifier List Mode過濾器寄存器中存放的是具體的CAN ID列表。只有當(dāng)接收到的幀ID與列表中某個(gè)ID完全匹配時(shí)才會(huì)被接收。這相當(dāng)于“白名單”。標(biāo)識(shí)符掩碼模式Identifier Mask Mode過濾器寄存器中存放一個(gè)ID值和一個(gè)掩碼Mask值。掩碼位為1表示該ID位必須嚴(yán)格匹配為0表示該ID位不關(guān)心可以是0或1。這允許接收一個(gè)ID范圍內(nèi)的幀。每個(gè)過濾器還可以關(guān)聯(lián)到兩個(gè)接收FIFOFIFO0或FIFO1之一。配置過濾器的步驟通常在MX_CAN1_Init()函數(shù)之后進(jìn)行。下面是一個(gè)示例配置一個(gè)掩碼模式過濾器只接收標(biāo)準(zhǔn)ID為0x123的幀CAN_FilterTypeDef canfilter; canfilter.FilterBank 0; // 使用過濾器組0 canfilter.FilterMode CAN_FILTERMODE_IDMASK; // 掩碼模式 canfilter.FilterScale CAN_FILTERSCALE_32BIT; // 32位寬 canfilter.FilterIdHigh 0x123 5; // STDID[10:0]放在[15:5]位右對(duì)齊 canfilter.FilterIdLow 0x0000; canfilter.FilterMaskIdHigh 0x7FF 5; // 掩碼低11位標(biāo)準(zhǔn)ID位必須匹配 canfilter.FilterMaskIdLow 0x0000; canfilter.FilterFIFOAssignment CAN_RX_FIFO0; // 匹配的幀放入FIFO0 canfilter.FilterActivation ENABLE; canfilter.SlaveStartFilterBank 14; // 對(duì)于單CAN此參數(shù)無效 if (HAL_CAN_ConfigFilter(hcan1, canfilter) ! HAL_OK) { Error_Handler(); }這里的關(guān)鍵是ID和掩碼的移位操作。對(duì)于標(biāo)準(zhǔn)ID11位它需要被左移5位放到FilterIdHigh寄存器的[15:5]位因?yàn)榍懊孢€有擴(kuò)展ID位和IDE、RTR等控制位。掩碼同樣需要移位。對(duì)于擴(kuò)展ID29位配置更為復(fù)雜需要用到FilterIdHigh和FilterIdLow兩個(gè)寄存器。注意事項(xiàng)過濾器配置必須在CAN啟動(dòng)HAL_CAN_Start之前完成。如果運(yùn)行時(shí)需要?jiǎng)討B(tài)修改過濾器必須先調(diào)用HAL_CAN_Stop修改配置后再調(diào)用HAL_CAN_Start和HAL_CAN_ActivateNotification如果使用中斷。3.3 中斷方式收發(fā)數(shù)據(jù)實(shí)戰(zhàn)配置好過濾器后我們啟用中斷并實(shí)現(xiàn)數(shù)據(jù)的發(fā)送和接收。首先在CubeMX的NVIC Settings中使能CAN1_RX0和CAN1_TX中斷如果需要發(fā)送中斷通知的話。在main.c的用戶代碼區(qū)添加以下代碼1. 啟動(dòng)CAN并激活接收中斷// 啟動(dòng)CAN控制器 if (HAL_CAN_Start(hcan1) ! HAL_OK) { Error_Handler(); } // 激活FIFO0消息掛起中斷即收到消息時(shí)產(chǎn)生中斷 if (HAL_CAN_ActivateNotification(hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) ! HAL_OK) { Error_Handler(); }2. 發(fā)送函數(shù)我們編寫一個(gè)簡(jiǎn)單的發(fā)送函數(shù)。通常我們會(huì)準(zhǔn)備一個(gè)發(fā)送郵箱TxMailbox配置好幀頭和數(shù)據(jù)然后啟動(dòng)發(fā)送。uint8_t CAN_Send_Msg(uint32_t id, uint8_t* data, uint8_t len) { CAN_TxHeaderTypeDef TxHeader; uint32_t TxMailbox; TxHeader.StdId id; // 標(biāo)準(zhǔn)ID TxHeader.ExtId 0; // 擴(kuò)展ID標(biāo)準(zhǔn)幀時(shí)設(shè)為0 TxHeader.IDE CAN_ID_STD; // 標(biāo)準(zhǔn)幀 TxHeader.RTR CAN_RTR_DATA; // 數(shù)據(jù)幀 TxHeader.DLC len; // 數(shù)據(jù)長(zhǎng)度0-8 TxHeader.TransmitGlobalTime DISABLE; // 啟動(dòng)發(fā)送使用阻塞模式等待發(fā)送完成或超時(shí) if (HAL_CAN_AddTxMessage(hcan1, TxHeader, data, TxMailbox) ! HAL_OK) { return 1; // 發(fā)送失敗 } // 如果需要等待發(fā)送完成可以輪詢郵箱狀態(tài)但中斷方式更高效 // while(HAL_CAN_GetTxMailboxesStatusLevel(hcan1) ! 0) {} // 等待所有郵箱空 return 0; // 發(fā)送成功 }在實(shí)際應(yīng)用中更推薦使用中斷或DMA方式發(fā)送避免阻塞主程序??梢酝ㄟ^HAL_CAN_ActivateNotification(hcan1, CAN_IT_TX_MAILBOX_EMPTY)激活發(fā)送郵箱空中斷在中斷回調(diào)函數(shù)中填充下一個(gè)要發(fā)送的消息。3. 接收中斷回調(diào)函數(shù)當(dāng)FIFO0收到新消息時(shí)會(huì)觸發(fā)中斷并跳轉(zhuǎn)到弱定義的回調(diào)函數(shù)。我們需要重寫這個(gè)函數(shù)。// 在main.c文件末尾用戶代碼區(qū)添加 void HAL_CAN_RxFifo0MsgPendingCallback(CANHandleTypeDef *hcan) { CAN_RxHeaderTypeDef RxHeader; uint8_t RxData[8]; // 從FIFO0讀取消息 if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, RxHeader, RxData) HAL_OK) { // 成功接收到一幀數(shù)據(jù) uint32_t id RxHeader.StdId; // 如果是標(biāo)準(zhǔn)幀 uint8_t len RxHeader.DLC; // 在這里處理接收到的數(shù)據(jù)例如打印、存儲(chǔ)或觸發(fā)動(dòng)作 // 注意此回調(diào)函數(shù)在中斷上下文中應(yīng)盡快處理避免耗時(shí)操作 // 常見的做法是將數(shù)據(jù)拷貝到全局緩沖區(qū)并設(shè)置一個(gè)標(biāo)志位在主循環(huán)中處理 printf(Received ID: 0x%03X, Data: , id); for(int i0; ilen; i) { printf(%02X , RxData[i]); } printf(\n); } }4. 主循環(huán)中的調(diào)用示例int main(void) { // ... HAL初始化系統(tǒng)時(shí)鐘配置CAN外設(shè)初始化由CubeMX生成... // ... 調(diào)用前面的過濾器配置函數(shù) ... // ... 啟動(dòng)CAN并激活中斷 ... uint8_t send_data[8] {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; while (1) { // 每隔1秒發(fā)送一幀ID為0x456的數(shù)據(jù) CAN_Send_Msg(0x456, send_data, 8); HAL_Delay(1000); // 主循環(huán)中可以進(jìn)行其他任務(wù)接收由中斷回調(diào)函數(shù)處理 } }將兩塊板子的代碼分別燒錄一塊定時(shí)發(fā)送ID為0x456的數(shù)據(jù)另一塊配置為接收所有幀或只接收0x456。連接好硬件打開串口助手應(yīng)該能看到接收方打印出發(fā)送的數(shù)據(jù)。至此一個(gè)最基本的CAN雙向通信就實(shí)現(xiàn)了。4. 常見問題排查與調(diào)試技巧實(shí)錄在實(shí)際動(dòng)手過程中你幾乎一定會(huì)遇到各種問題。下面是我踩過的一些坑和總結(jié)的排查思路希望能幫你快速定位問題。4.1 硬件連接與電源問題這是最基礎(chǔ)也最容易被忽視的環(huán)節(jié)。癥狀完全無法通信邏輯分析儀或示波器上看不到任何差分信號(hào)。排查步驟檢查供電確保所有節(jié)點(diǎn)MCU、收發(fā)器供電穩(wěn)定且電壓正確。CAN收發(fā)器通常需要5V或3.3V供電務(wù)必核對(duì)數(shù)據(jù)手冊(cè)。檢查終端電阻用萬用表測(cè)量CAN_H和CAN_L之間的電阻。在總線兩端都正確接入120歐姆電阻的情況下并聯(lián)阻值應(yīng)為60歐姆左右。如果測(cè)得120歐姆說明只有一個(gè)終端電阻如果阻值很大或開路說明終端電阻沒接或接觸不良如果阻值遠(yuǎn)小于60歐姆可能有多個(gè)節(jié)點(diǎn)都使能了終端電阻。檢查差分線確保CAN_H接CAN_H CAN_L接CAN_L沒有接反。接反可能導(dǎo)致通信異常。檢查共地所有節(jié)點(diǎn)的地GND必須連接在一起構(gòu)成共同的參考電位。不共地是導(dǎo)致通信失敗的常見原因。檢查收發(fā)器模式有些收發(fā)器如TJA1050有靜默模式引腳STB需要接高或接低才能進(jìn)入正常工作模式。4.2 波特率配置錯(cuò)誤癥狀自發(fā)自收環(huán)回模式正常但兩個(gè)節(jié)點(diǎn)之間無法通信或者能收到一些亂碼或錯(cuò)誤幀。排查方法嚴(yán)格核對(duì)配置確保兩個(gè)節(jié)點(diǎn)的Prescaler、Time Segment 1、Time Segment 2、SJW等所有位時(shí)序參數(shù)完全一致。一個(gè)字節(jié)一個(gè)字節(jié)地對(duì)比初始化代碼。使用CAN分析儀這是最有效的工具。將分析儀接入總線它可以直觀地顯示總線上的波形、解碼出的幀內(nèi)容、錯(cuò)誤統(tǒng)計(jì)并能直接測(cè)量出實(shí)際波特率。對(duì)比測(cè)量值與你的配置值。計(jì)算時(shí)鐘源確認(rèn)給CAN外設(shè)提供時(shí)鐘的PCLK1頻率是多少。如果使用了PLL檢查倍頻、分頻系數(shù)是否正確。在代碼中打印或通過調(diào)試器查看系統(tǒng)時(shí)鐘配置寄存器的值。4.3 過濾器配置不當(dāng)導(dǎo)致收不到數(shù)據(jù)癥狀發(fā)送方似乎正常用分析儀能看到總線有波形但接收方毫無反應(yīng)中斷不觸發(fā)。排查步驟關(guān)閉過濾器測(cè)試先將所有過濾器禁用FilterActivation DISABLE或者配置一個(gè)全接收的掩碼ID0 Mask0。如果此時(shí)能收到數(shù)據(jù)說明問題出在過濾器配置上。檢查ID格式確認(rèn)你配置的是標(biāo)準(zhǔn)幀還是擴(kuò)展幀IDE位與發(fā)送方發(fā)出的幀格式是否一致。標(biāo)準(zhǔn)幀和擴(kuò)展幀的過濾器配置方式不同。檢查移位操作這是最容易出錯(cuò)的地方。對(duì)于標(biāo)準(zhǔn)ID需要左移5位放入FilterIdHigh對(duì)于擴(kuò)展ID需要拆分到FilterIdHigh和FilterIdLow并注意IDE位的設(shè)置。仔細(xì)閱讀參考手冊(cè)中關(guān)于過濾器寄存器位映射的說明。檢查過濾器組分配確保接收中斷如FIFO0與過濾器關(guān)聯(lián)的FIFOFilterFIFOAssignment一致。4.4 錯(cuò)誤幀頻發(fā)與總線狀態(tài)異常癥狀通信時(shí)斷時(shí)續(xù)用分析儀能看到大量錯(cuò)誤幀或者節(jié)點(diǎn)自動(dòng)進(jìn)入總線關(guān)閉狀態(tài)。可能原因與對(duì)策總線負(fù)載過高如果短時(shí)間內(nèi)發(fā)送大量高優(yōu)先級(jí)幀可能導(dǎo)致低優(yōu)先級(jí)幀一直無法獲得總線仲裁從而觸發(fā)發(fā)送錯(cuò)誤計(jì)數(shù)器增長(zhǎng)。優(yōu)化通信協(xié)議減少不必要的數(shù)據(jù)發(fā)送頻率或合理分配ID優(yōu)先級(jí)。電磁干擾EMI在工業(yè)環(huán)境中強(qiáng)電磁干擾可能導(dǎo)致位錯(cuò)誤。檢查布線CAN雙絞線絞距應(yīng)盡量小并遠(yuǎn)離電源線等干擾源??梢钥紤]使用帶屏蔽層的CAN電纜并將屏蔽層單點(diǎn)接地。節(jié)點(diǎn)硬件故障某個(gè)節(jié)點(diǎn)的CAN收發(fā)器損壞可能會(huì)持續(xù)向總線發(fā)送顯性電平導(dǎo)致整個(gè)總線癱瘓“總線鎖死”??梢試L試逐個(gè)斷開節(jié)點(diǎn)來定位故障點(diǎn)。地線環(huán)路如果網(wǎng)絡(luò)跨度大多點(diǎn)接地可能形成地線環(huán)路引入共模干擾。確保網(wǎng)絡(luò)采用單點(diǎn)接地或使用隔離型CAN收發(fā)器模塊。4.5 調(diào)試工具與技巧速查表工具/方法用途使用技巧萬用表測(cè)量終端電阻、檢查電源和通斷。斷電測(cè)量電阻上電測(cè)量電壓。CAN_H和CAN_L對(duì)地電壓在靜態(tài)隱性時(shí)都應(yīng)約為2.5V。示波器觀察CAN_H和CAN_L的差分波形。使用差分探頭或分別測(cè)量CAN_H和CAN_L后做數(shù)學(xué)運(yùn)算。觀察波形是否干凈上升/下降沿是否陡峭有無過沖或振鈴。邏輯分析儀抓取并解碼CAN總線數(shù)字信號(hào)。配合CAN解碼軟件可以直觀看到每一幀的ID、數(shù)據(jù)、幀類型是分析通信邏輯的利器。專用CAN分析儀最強(qiáng)大的調(diào)試工具可模擬節(jié)點(diǎn)、壓力測(cè)試、統(tǒng)計(jì)錯(cuò)誤。如PCAN ZLG的CAN卡等。可以實(shí)時(shí)監(jiān)控總線負(fù)載、錯(cuò)誤幀計(jì)數(shù)、精確測(cè)量波特率并模擬發(fā)送任意幀對(duì)復(fù)雜問題診斷至關(guān)重要。軟件打印調(diào)試在代碼中插入狀態(tài)打印。在初始化成功、發(fā)送完成、接收回調(diào)、錯(cuò)誤回調(diào)等位置打印信息通過串口輸出了解程序運(yùn)行到哪一步出了錯(cuò)。STM32 CubeMonitor圖形化監(jiān)控CAN總線數(shù)據(jù)。需要配合ST-Link可以實(shí)時(shí)繪制信號(hào)變化曲線適合分析周期性發(fā)送的數(shù)據(jù)。實(shí)操心得遇到問題務(wù)必遵循“從硬件到軟件從簡(jiǎn)單到復(fù)雜”的排查順序。首先用萬用表和示波器確認(rèn)物理層正常然后用CAN分析儀確認(rèn)數(shù)據(jù)鏈路層有正確的幀在傳輸最后再深入調(diào)試軟件配置和邏輯。養(yǎng)成在初始化后檢查HAL_CAN_GetError和HAL_CAN_GetState函數(shù)返回值的習(xí)慣能快速定位大部分配置錯(cuò)誤。對(duì)于穩(wěn)定性要求高的項(xiàng)目一定要在代碼中實(shí)現(xiàn)錯(cuò)誤回調(diào)函數(shù)如HAL_CAN_ErrorCallback并記錄錯(cuò)誤類型這對(duì)于后期運(yùn)維和故障診斷有極大幫助。

相關(guān)新聞

麥昆STEAM教育機(jī)器人:從硬件解析到編程進(jìn)階的完整學(xué)習(xí)路徑

麥昆STEAM教育機(jī)器人:從硬件解析到編程進(jìn)階的完整學(xué)習(xí)路徑

1. 從“玩具”到“伙伴”:麥昆STEAM成長(zhǎng)記的緣起如果你是一位關(guān)注青少年科技教育或創(chuàng)客文化的家長(zhǎng)、老師,或者你本身就是個(gè)對(duì)機(jī)器人、編程充滿好奇的大朋友,那么“麥昆”這個(gè)名字你大概率不會(huì)陌生。它可能不是那個(gè)動(dòng)畫片里的賽車,…

2026/7/29 7:36:08 閱讀更多
Intel Edison開發(fā)板WiFi連接全攻略:從connmanctl配置到物聯(lián)網(wǎng)應(yīng)用實(shí)戰(zhàn)

Intel Edison開發(fā)板WiFi連接全攻略:從connmanctl配置到物聯(lián)網(wǎng)應(yīng)用實(shí)戰(zhàn)

1. 項(xiàng)目概述:為什么需要連接WiFi?如果你手頭有一塊Intel Edison開發(fā)板,并且已經(jīng)用它點(diǎn)亮過LED,或者跑通了基礎(chǔ)的“Hello World”,那么下一步,你大概率會(huì)想讓它“上網(wǎng)”。讓Edison連接上WiFi,是把…

2026/7/29 7:36:08 閱讀更多
物聯(lián)網(wǎng)設(shè)備安全芯片選型與SE050+PIC18F45K22方案解析

物聯(lián)網(wǎng)設(shè)備安全芯片選型與SE050+PIC18F45K22方案解析

1. 為什么物聯(lián)網(wǎng)設(shè)備需要專用安全芯片?在智能家居和工業(yè)物聯(lián)網(wǎng)項(xiàng)目中,開發(fā)者常面臨一個(gè)兩難選擇:使用通用MCU實(shí)現(xiàn)基礎(chǔ)功能雖成本低廉,但安全防護(hù)薄弱;而采用高端安全方案又會(huì)導(dǎo)致BOM成本飆升。這正是SE050 Plug&Tr…

2026/7/29 7:36:08 閱讀更多
終極指南:如何使用WechatSogou微信公眾號(hào)爬蟲快速獲取海量微信數(shù)據(jù)

終極指南:如何使用WechatSogou微信公眾號(hào)爬蟲快速獲取海量微信數(shù)據(jù)

終極指南:如何使用WechatSogou微信公眾號(hào)爬蟲快速獲取海量微信數(shù)據(jù) 【免費(fèi)下載鏈接】WechatSogou 基于搜狗微信搜索的微信公眾號(hào)爬蟲接口 項(xiàng)目地址: https://gitcode.com/gh_mirrors/we/WechatSogou 想要高效獲取微信公眾號(hào)數(shù)據(jù)嗎?WechatSogou是一…

2026/7/29 15:07:17 閱讀更多
數(shù)字生命技能工具:動(dòng)態(tài)流程引擎與協(xié)作效率提升

數(shù)字生命技能工具:動(dòng)態(tài)流程引擎與協(xié)作效率提升

1. 數(shù)字生命技能工具的核心價(jià)值解析在2023年Q3的團(tuán)隊(duì)效率調(diào)研報(bào)告中,一個(gè)令人震驚的數(shù)據(jù)浮現(xiàn):知識(shí)工作者平均每天要花費(fèi)2.1小時(shí)在重復(fù)性的流程溝通上。這正是我們團(tuán)隊(duì)開發(fā)數(shù)字生命技能工具的初衷——將那些"只存在于老員工腦子里"的協(xié)作經(jīng)驗(yàn)轉(zhuǎn)…

2026/7/29 15:07:17 閱讀更多
COMSOL納米孔陣列超表面透射譜仿真技巧與優(yōu)化

COMSOL納米孔陣列超表面透射譜仿真技巧與優(yōu)化

1. 項(xiàng)目概述:納米孔陣列超表面透射譜仿真 第一次在COMSOL中嘗試仿真納米孔陣列超表面時(shí),我遇到了一個(gè)有趣的現(xiàn)象:理論上應(yīng)該出現(xiàn)明顯透射峰的位置,仿真結(jié)果卻顯示為平坦的直線。這個(gè)問題困擾了我整整三天,直到發(fā)現(xiàn)是網(wǎng)…

2026/7/29 15:07:17 閱讀更多
從C語言文件管理系統(tǒng)到Linux裝NAS:開發(fā)者的自建存儲(chǔ)之路

從C語言文件管理系統(tǒng)到Linux裝NAS:開發(fā)者的自建存儲(chǔ)之路

開發(fā)者論壇里的一次討論 “用易語言寫個(gè)內(nèi)網(wǎng)穿透軟件難不難?”這是某開發(fā)者社區(qū)里一個(gè)常見問題?;卮饏^(qū)里的思路大致分兩派:一派認(rèn)為易語言上手快,做個(gè)簡(jiǎn)單的端口映射工具并不復(fù)雜;另一派則提醒,穩(wěn)定的內(nèi)網(wǎng)穿透涉及NAT…

2026/7/29 14:57:16 閱讀更多
面試官大笑:“一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,不比 1 個(gè)快 5 倍?“我搖頭:“快不了,還可能更慢“

面試官大笑:“一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,不比 1 個(gè)快 5 倍?“我搖頭:“快不了,還可能更慢“

前兩個(gè)月,我在重構(gòu) AlgoMooc 網(wǎng)站過程中,發(fā)現(xiàn)一個(gè)問題:在 Claude Code 里把一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,結(jié)果可能比 1 個(gè) agent 從頭干到尾還慢? 大多數(shù)人的第一反應(yīng)是反過來的:活是并行干的&#…

2026/7/29 0:15:24 閱讀更多
# 鴻蒙 HarmonyOS 應(yīng)用開發(fā)實(shí)戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號(hào)與動(dòng)畫渲染精講

# 鴻蒙 HarmonyOS 應(yīng)用開發(fā)實(shí)戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號(hào)與動(dòng)畫渲染精講

一、應(yīng)用概述 骰子(Dice Roller) 是一款經(jīng)典的休閑娛樂應(yīng)用,模擬了真實(shí)擲骰子的過程。應(yīng)用投擲兩個(gè)骰子(六面標(biāo)準(zhǔn)骰),使用 Unicode 骰面符號(hào)直觀展示每個(gè)骰子的點(diǎn)數(shù),并伴有快速滾動(dòng)的動(dòng)畫效果?!?/p>

2026/7/29 0:15:24 閱讀更多