議從入門到實(shí)踐:手把手教你玩轉(zhuǎn)STM32的CAN通信)
1. CAN總線協(xié)議基礎(chǔ)從物理層到協(xié)議層第一次接觸STM32的CAN通信時(shí)我和大多數(shù)初學(xué)者一樣感到一頭霧水。這個(gè)在汽車電子和工業(yè)控制中廣泛使用的協(xié)議確實(shí)比I2C、SPI這些同步通信復(fù)雜得多。但別擔(dān)心我會(huì)用最接地氣的方式帶你理解它。CAN總線最特別的地方在于它采用差分信號(hào)傳輸。想象一下兩個(gè)人抬轎子——CAN_HIGH和CAN_LOW就像轎子的兩根桿子只有當(dāng)兩根線電壓差達(dá)到2V時(shí)才表示邏輯0顯性電平0V時(shí)表示邏輯1隱性電平。這種設(shè)計(jì)讓CAN總線天生具備超強(qiáng)抗干擾能力我在工廠環(huán)境測試時(shí)即便旁邊有大型電機(jī)干擾通信依然穩(wěn)定。物理層有兩種典型拓?fù)溟]環(huán)總線像地鐵環(huán)線首尾相連。適合短距離≤40m高速通信1Mbps開環(huán)總線像公交線路終端需要120Ω電阻。適合長距離≤1km低速通信125kbps協(xié)議層才是真正的精髓所在。CAN節(jié)點(diǎn)沒有主從之分所有設(shè)備平等接入總線。當(dāng)多個(gè)節(jié)點(diǎn)同時(shí)發(fā)送時(shí)通過ID仲裁機(jī)制決定誰先說——就像一群人開會(huì)重要的事情ID值小的幀會(huì)打斷不重要的發(fā)言。這里有個(gè)實(shí)用技巧規(guī)劃項(xiàng)目時(shí)緊急消息要分配較小的ID值。2. STM32的CAN外設(shè)實(shí)戰(zhàn)配置拿到STM32開發(fā)板后我建議先用CubeMX工具快速搭建工程。記得勾選CAN外設(shè)后會(huì)自動(dòng)配置PA11(CAN_RX)和PA12(CAN_TX)引腳。不過有次我忘了接120Ω終端電阻結(jié)果通信距離超過3米就丟包這個(gè)坑大家一定要避開。工作模式選擇很有講究CAN_InitStructure.CAN_Mode CAN_Mode_LoopBack; // 環(huán)回模式(自測試) // CAN_Mode_Normal // 正常模式 // CAN_Mode_Silent // 靜默模式(只聽不發(fā))波特率配置是新手最容易出錯(cuò)的地方。STM32的位時(shí)序分為三段同步段(SYNC_SEG)固定1個(gè)時(shí)間單位(Tq)BS1段包含傳播段和相位緩沖段1BS2段相位緩沖段2假設(shè)我們使用APB1時(shí)鐘36MHz配置如下CAN_InitStructure.CAN_SJW CAN_SJW_1tq; // 同步跳轉(zhuǎn)寬度 CAN_InitStructure.CAN_BS1 CAN_BS1_9tq; // BS1時(shí)間段 CAN_InitStructure.CAN_BS2 CAN_BS2_8tq; // BS2時(shí)間段 CAN_InitStructure.CAN_Prescaler 4; // 分頻系數(shù)此時(shí)波特率 36MHz / [(198)*4] 500Kbps。我在汽車電子項(xiàng)目中常用這個(gè)速率兼顧速度和穩(wěn)定性。3. 驗(yàn)收篩選器的妙用篩選器是CAN通信的門衛(wèi)決定哪些消息能進(jìn)入FIFO。STM32提供兩種工作模式標(biāo)識(shí)符列表模式像白名單只接收特定ID的消息掩碼模式像模糊匹配可設(shè)置ID的某些位必須匹配配置示例接收標(biāo)準(zhǔn)ID 0x123的消息CAN_FilterInitStructure.CAN_FilterNumber 0; // 使用篩選器0 CAN_FilterInitStructure.CAN_FilterMode CAN_FilterMode_IdMask; CAN_FilterInitStructure.CAN_FilterScale CAN_FilterScale_32bit; CAN_FilterInitStructure.CAN_FilterIdHigh 0x123 5; // STDID[10:0]對(duì)齊到高位 CAN_FilterInitStructure.CAN_FilterIdLow 0; CAN_FilterInitStructure.CAN_FilterMaskIdHigh 0xFFFF; // 必須完全匹配 CAN_FilterInitStructure.CAN_FilterMaskIdLow 0x0000;有個(gè)實(shí)用技巧工業(yè)設(shè)備通常需要接收多個(gè)ID這時(shí)可以啟用多個(gè)篩選器組。我在一個(gè)紡織機(jī)控制項(xiàng)目中就用篩選器實(shí)現(xiàn)了同時(shí)接收電機(jī)狀態(tài)(0x201)和傳感器數(shù)據(jù)(0x301)。4. 從發(fā)送到接收的完整代碼實(shí)現(xiàn)先看發(fā)送函數(shù)注意標(biāo)準(zhǔn)幀和擴(kuò)展幀的區(qū)別u8 Can_Send_Msg(u8* msg, u8 len) { CanTxMsg TxMessage; TxMessage.StdId 0x123; // 標(biāo)準(zhǔn)ID(11位) TxMessage.ExtId 0x12345678; // 擴(kuò)展ID(29位) TxMessage.IDE CAN_Id_Standard; // 使用標(biāo)準(zhǔn)幀 TxMessage.RTR CAN_RTR_Data; // 數(shù)據(jù)幀(非遠(yuǎn)程幀) TxMessage.DLC len; // 數(shù)據(jù)長度(0-8) for(u8 i0; ilen; i) TxMessage.Data[i] msg[i]; u8 mailbox CAN_Transmit(CAN1, TxMessage); while(CAN_TransmitStatus(CAN1, mailbox) ! CAN_TxStatus_Ok); return 0; }接收端建議使用中斷方式避免輪詢消耗CPU資源void USB_LP_CAN1_RX0_IRQHandler(void) { CanRxMsg RxMessage; CAN_Receive(CAN1, CAN_FIFO0, RxMessage); // 打印接收到的數(shù)據(jù) for(u8 i0; iRxMessage.DLC; i){ printf(Data[%d]: %02X\n, i, RxMessage.Data[i]); } }在main函數(shù)中初始化后就可以測試環(huán)回通信了int main(void) { u8 canbuf[8] {0x11, 0x22, 0x33, 0x44}; CAN_Mode_Init(CAN_SJW_1tq, CAN_BS2_8tq, CAN_BS1_9tq, 4, CAN_Mode_LoopBack); while(1){ if(KEY0_Pressed){ // 按下按鍵發(fā)送 Can_Send_Msg(canbuf, 4); } // 中斷會(huì)自動(dòng)處理接收 } }當(dāng)切換到正常模式時(shí)需要兩塊開發(fā)板互聯(lián)。記得用雙絞線連接CAN_H和CAN_L兩端都必須接120Ω終端電阻確保波特率設(shè)置完全一致我在調(diào)試多節(jié)點(diǎn)通信時(shí)曾因?yàn)橐粋€(gè)節(jié)點(diǎn)的BS2參數(shù)配置不同導(dǎo)致持續(xù)出錯(cuò)。后來用邏輯分析儀抓取波形才發(fā)現(xiàn)這個(gè)問題所以建議大家在復(fù)雜系統(tǒng)中加入錯(cuò)誤回調(diào)函數(shù)CAN_ITConfig(CAN1, CAN_IT_ERR, ENABLE); void CAN1_ERR_IRQHandler(void) { if(CAN_GetITStatus(CAN1, CAN_IT_BOF)){ printf(Bus-off error detected!\n); CAN_ClearITPendingBit(CAN1, CAN_IT_BOF); } }5. 常見問題排查指南根據(jù)我的項(xiàng)目經(jīng)驗(yàn)這些問題最常出現(xiàn)通信完全失敗檢查終端電阻用萬用表測量CAN_H-CAN_L間阻值應(yīng)為60Ω左右確認(rèn)GPIO模式設(shè)置正確PA11上拉輸入PA12復(fù)用推挽輸出驗(yàn)證時(shí)鐘配置APB1時(shí)鐘必須使能能發(fā)送但收不到回復(fù)檢查篩選器配置先用掩碼模式0x0000接收所有消息測試確認(rèn)工作模式正常模式需要兩個(gè)節(jié)點(diǎn)查看CAN狀態(tài)寄存器CAN_ESR偶發(fā)性數(shù)據(jù)錯(cuò)誤降低波特率測試建議從125Kbps開始檢查總線負(fù)載用CAN分析儀監(jiān)控錯(cuò)誤幀優(yōu)化布線雙絞線長度盡量短避免星型拓?fù)溆袀€(gè)特別隱蔽的坑STM32的CAN時(shí)鐘來自APB1如果系統(tǒng)時(shí)鐘配置錯(cuò)誤實(shí)際波特率會(huì)和計(jì)算值不符。我建議用這個(gè)公式校驗(yàn)實(shí)際波特率 APB1時(shí)鐘 / [(SYNC_SEG BS1 BS2) * Prescaler]最后分享一個(gè)高級(jí)技巧在工業(yè)現(xiàn)場可以通過設(shè)置自動(dòng)重傳禁用(CAN_NART)來確保關(guān)鍵消息的實(shí)時(shí)性但需要配合應(yīng)用層確認(rèn)機(jī)制。我在數(shù)控機(jī)床項(xiàng)目中就用這種方法實(shí)現(xiàn)了毫秒級(jí)的狀態(tài)同步。