秀!靠這篇我竟然2天理解了CAN協定!實戰STM32F4 CAN!

2021-04-28 13:00:48

一. CAN協定概念

1.1 CAN 協定簡介

CAN 是控制器區域網路 (Controller Area Network) 的簡稱,它是由研發和生產汽車電子產品著稱的德國 BOSCH 公司開發的,並最終成為國際標準(ISO11519以及ISO11898),是國際上應用最廣泛的現場匯流排之一。差異點如下:

image.png

CAN 匯流排協定已經成為汽車計算機控制系統和嵌入式工業控制區域網的標準匯流排,並且擁有以CAN 為底層協定專為大型貨車和重工機械車輛設計的 J1939 協定。近年來,它具有的高可靠性和良好的錯誤檢測能力受到重視,被廣泛應用於汽車計算機控制系統和環境溫度惡劣、電磁輻射強及振動大的工業環境。

我們來貼圖一個車載網路構想圖

image.png

1.2 CAN 物理層

與 I2C、SPI 等具有時鐘訊號的同步通訊方式不同,CAN 通訊並不是以時鐘訊號來進行同步的,它是一種非同步通訊,只具有 CAN_High 和 CAN_Low 兩條訊號線,共同構成一組差分訊號線,以差分訊號的形式進行通訊。我們來看一個示意圖

image.png

1.2.1 閉環匯流排網路

CAN 物理層的形式主要有兩種,圖中的 CAN 通訊網路是一種遵循 ISO11898 標準的高速、短距離「閉環網路」,它的匯流排最大長度為 40m,通訊速度最高為 1Mbps,匯流排的兩端各要求有一個「120 歐」的電阻。

image.png

1.2.2 開環匯流排網路

圖中的是遵循 ISO11519-2 標準的低速、遠距離「開環網路」,它的最大傳輸距離為 1km,最高通訊速率為 125kbps,兩根匯流排是獨立的、不形成閉環,要求每根匯流排上各串聯有一個「2.2千歐」的電阻。

image.png

1.2.3 通訊節點

從 CAN 通訊網路圖可瞭解到,CAN 匯流排上可以掛載多個通訊節點,節點之間的訊號經過匯流排傳輸,實現節點間通訊。由於 CAN 通訊協定不對節點進行地址編碼,而是對資料內容進行編碼的,所以網路中的節點個數理論上不受限制,只要匯流排的負載足夠即可,可以通過中繼器增強負載。

CAN 通訊節點由一個 CAN 控制器及 CAN 收發器組成,控制器與收發器之間通過 CAN_Tx 及CAN_Rx 訊號線相連,收發器與 CAN 匯流排之間使用 CAN_High 及 CAN_Low 訊號線相連。其中CAN_Tx 及 CAN_Rx 使用普通的類似 TTL 邏輯訊號,而 CAN_High 及 CAN_Low 是一對差分訊號線,使用比較特別的差分訊號,下一小節再詳細說明。

當 CAN 節點需要傳送資料時,控制器把要傳送的二進位制編碼通過 CAN_Tx 線傳送到收發器,然後由收發器把這個普通的邏輯電平訊號轉化成差分訊號,通過差分線 CAN_High 和 CAN_Low 線輸出到 CAN 匯流排網路。而通過收發器接收匯流排上的資料到控制器時,則是相反的過程,收發器把匯流排上收到的 CAN_High 及 CAN_Low 訊號轉化成普通的邏輯電平訊號,通過 CAN_Rx 輸出到控制器中。

例如,STM32 的 CAN 片上外設就是通訊節點中的控制器,為了構成完整的節點,還要給它外接一個收發器,在我們實驗板中使用型號為 TJA1050 的晶片作為 CAN 收發器。 CAN 控制器與 CAN收發器的關係如同 TTL 串列埠與 MAX3232 電平轉換晶片的關係, MAX3232 晶片把 TTL 電平的串列埠訊號轉換成 RS-232 電平的串列埠訊號,CAN 收發器的作用則是把 CAN 控制器的 TTL 電平訊號轉換成差分訊號 (或者相反) 。

目前有以下CAN電平轉換晶片(不全)

image.png

我們來用TJA1050來看下原理圖:

image.png

1.2.4 差分訊號

差分訊號又稱差模訊號,與傳統使用單根訊號線電壓表示邏輯的方式有區別,使用差分訊號傳輸時,需要兩根訊號線,這兩個訊號線的振幅相等,相位相反,通過兩根訊號線的電壓差值來表示

邏輯 0 和邏輯 1。見圖,它使用了 V+ 與 V-訊號的差值表達出了圖下方的訊號。

image.png

相對於單訊號線傳輸的方式,使用差分訊號傳輸具有如下優點:

• 抗干擾能力強,當外界存在噪聲干擾時,幾乎會同時耦合到兩條訊號線上,而接收端只關心兩個訊號的差值,所以外界的共模噪聲可以被完全抵消。

舉一個例子,正常的單線假設邏輯1是3.3V,邏輯0假設是0V,但是如果有噪聲,把3.3V弄成了0V(極端),把0V弄成了-3.3V,此時就邏輯錯誤,但是有Can高/Can低一般都作用於兩根線,所以兩個雖然都有噪聲影響,但是差值還是不變的

• 能有效抑制它對外部的電磁干擾,同樣的道理,由於兩根訊號的極性相反,他們對外輻射的電磁場可以相互抵消,耦合的越緊密,洩放到外界的電磁能量越少。

舉一個例子,假設一根是10V,一根是-10V,單跟都會對外部造成電磁干擾,但是CAN可以把線擰在一起,跟編麻花一樣,可以互相抵消電子干擾

• 時序定位精確,由於差分訊號的開關變化是位於兩個訊號的交點,而不像普通單端訊號依靠高低兩個閾值電壓判斷,因而受工藝,溫度的影響小,能降低時序上的誤差,同時也更適合於低幅度訊號的電路。

由於差分訊號線具有這些優點,所以在 USB 協定、485 協定、乙太網協定及 CAN 協定的物理層中,都使用了差分訊號傳輸

1.2.5 CAN 協定中的差分訊號

CAN 協定中對它使用的 CAN_High 及 CAN_Low 表示的差分訊號做了規定,見表及圖。以高速 CAN 協定為例,當表示邏輯 1 時 (隱性電平) CAN_High 和 CAN_Low 線上的電壓均為 2.5v,即它們的電壓差 VH-V:sub:L=0V;而表示邏輯 0 時 (顯性電平)CAN_High 的電平為 3.5V,CAN_Low 線的電平為 1.5V,即它們的電壓差為 VH-V:sub:L=2V。例如,當 CAN收發器從 CAN_Tx 線接收到來自 CAN 控制器的低電平訊號時 (邏輯 0),它會使 CAN_High 輸出3.5V,同時 CAN_Low 輸出 1.5V,從而輸出顯性電平表示邏輯 0 。

image.png

image.png

在 CAN 匯流排中,必須使它處於隱性電平 (邏輯 1) 或顯性電平 (邏輯 0) 中的其中一個狀態。假如有兩個 CAN 通訊節點,在同一時間,一個輸出隱性電平,另一個輸出顯性電平類似 I2C 匯流排的「線與」特性將使它處於顯性電平狀態,顯性電平的名字就是這樣來的,即可以認為顯性具有優先的意味。

由於 CAN 匯流排協定的物理層只有 1 對差分線,在一個時刻只能表示一個訊號,所以對通訊節點來說,CAN 通訊是半雙工的,收發資料需要分時進行。在 CAN 的通訊網路中,因為共用匯流排,在整個網路中同一時刻只能有一個通訊節點傳送訊號,其餘的節點在該時刻都只能接收。

1.3 CAN 協定層

1.3.1 CAN 的波特率及位同步

由於 CAN 屬於非同步通訊,沒有時鐘訊號線,連線在同一個匯流排網路中的各個節點會像串列埠非同步通訊那樣,節點間使用約定好的波特率進行通訊,特別地, CAN 還會使用「位同步」的方式來抗干擾、吸收誤差,實現對匯流排電平訊號進行正確的取樣,確保通訊正常。

1.3.2 位時序分解

為了實現位同步,CAN 協定把每一個資料位的時序分解成如圖 所示的 SS 段、PTS 段、PBS1 段、PBS2 段,這四段的長度加起來即為一個 CAN 資料位的長度。分解後最小的時間單位是 Tq,而一個完整的位由 8~25 個 Tq 組成。為方便表示,圖 中的高低電平直接代表訊號邏輯 0 或邏輯 1(不是差分訊號)

image.png

該圖中表示的 CAN 通訊訊號每一個資料位的長度為 19Tq,其中 SS 段佔 1Tq, PTS 段佔 6Tq, PBS1段佔 5Tq, PBS2 段佔 7Tq。訊號的取樣點位於 PBS1 段與 PBS2 段之間,通過控制各段的長度,可以對取樣點的位置進行偏移,以便準確地取樣。

各段的作用如介紹下:

SS 段 (SYNC SEG)

SS 譯為同步段,若通訊節點檢測到匯流排上訊號的跳變沿被包含在 SS 段的範圍之內,則表示節點與匯流排的時序是同步的,當節點與匯流排同步時,取樣點採集到的匯流排電平即可被確定為該位的電平。SS 段的大小固定為 1Tq。

PTS 段 (PROP SEG)

PTS 譯為傳播時間段,這個時間段是用於補償網路的物理延時時間。是匯流排上輸入比較器延時和輸出驅動器延時總和的兩倍。PTS 段的大小可以為 1~8Tq。

PBS1 段 (PHASE SEG1),

PBS1 譯為相位緩衝段,主要用來補償邊沿階段的誤差,它的時間長度在重新同步的時候可以加長。PBS1 段的初始大小可以為 1~8Tq。

PBS2 段 (PHASE SEG2)

PBS2 這是另一個相位緩衝段,也是用來補償邊沿階段誤差的,它的時間長度在重新同步時可以縮短。PBS2 段的初始大小可以為 2~8Tq。

1.3.3 通訊的波特率

匯流排上的各個通訊節點只要約定好 1 個 Tq 的時間長度以及每一個資料位佔據多少個 Tq,就可以確定 CAN 通訊的波特率。

例如,假設上圖中的 1Tq=1us,而每個資料位由 19 個 Tq 組成,則傳輸一位資料需要時間 T1bit=19us,從而每秒可以傳輸的資料位個數為:1x10次方/19 = 52631.6 (bps)

這個每秒可傳輸的資料位的個數即為通訊中的波特率。

1.3.4 同步過程分析

波特率只是約定了每個資料位的長度,資料同步還涉及到相位的細節,這個時候就需要用到資料位內的 SS、PTS、PBS1 及 PBS2 段了。根據對段的應用方式差異, CAN 的資料同步分為硬同步和重新同步。其中硬同步只是當存在「幀起始訊號」時起作用,無法確保後續一連串的位時序都是同步的,而重新同步方式可解決該問題,這兩種方式具體介紹如下:

(1) 硬同步

若某個 CAN 節點通過匯流排傳送資料時,它會傳送一個表示通訊起始的訊號 (即下一小節介紹的幀起始訊號),該訊號是一個由高變低的下降沿。而掛載到 CAN 匯流排上的通訊節點在不傳送資料時,會時刻檢測匯流排上的訊號。見圖 ,可以看到當匯流排出現幀起始訊號時,某節點檢測到匯流排的幀起始訊號不在節點內部時序的 SS 段範圍,所以判斷它自己的內部時序與匯流排不同步,因而這個狀態的取樣點採集得的資料是不正確的。所以節點以硬同步的方式調整,把自己的位時序中的 SS 段平移至匯流排出現下降沿的部分,獲得同步,同步後取樣點就可以採集得正確資料了。

image.png

(2) 重新同步

前面的硬同步只是當存在幀起始訊號時才起作用,如果在一幀很長的資料內,節點訊號與匯流排訊號相位有偏移時,這種同步方式就無能為力了。因而需要引入重新同步方式,它利用普通資料位的高至低電平的跳變沿來同步 (幀起始訊號是特殊的跳變沿)。重新同步與硬同步方式相似的地方是它們都使用 SS 段來進行檢測,同步的目的都是使節點內的 SS 段把跳變沿包含起來。重新同步的方式分為超前和滯後兩種情況,以匯流排跳變沿與 SS 段的相對位置進行區分。第一種相位超前的情況如圖 ,節點從匯流排的邊沿跳變中,檢測到它內部的時序比匯流排的時序相對超前 2Tq,這時控制器在下一個位時序中的 PBS1 段增加 2Tq 的時間長度,使得節點與匯流排時序重新同步。

image.png

第二種相位滯後的情況如圖 ,節點從匯流排的邊沿跳變中,檢測到它的時序比匯流排的時序相對滯後 2Tq,這時控制器在前一個位時序中的 PBS2 段減少 2Tq 的時間長度,獲得同步。

image.png

在重新同步的時候,PBS1 和 PBS2 中增加或減少的這段時間長度被定義為「重新同步補償寬度SJW* (reSynchronization Jump Width)」。一般來說 CAN 控制器會限定 SJW 的最大值,如限定了最大 SJW=3Tq 時,單次同步調整的時候不能增加或減少超過 3Tq 的時間長度,若有需要,控制器會通過多次小幅度調整來實現同步。當控制器設定的 SJW 極限值較大時,可以吸收的誤差加大,但通訊的速度會下降

1.3.5 CAN 的報文種類及結構

在 SPI 通訊中,片選、時鐘訊號、資料輸入及資料輸出這 4 個訊號都有單獨的訊號線,I2C 協定包含有時鐘訊號及資料訊號 2 條訊號線,非同步串列埠包含接收與傳送 2 條訊號線,這些協定包含的訊號都比 CAN 協定要豐富,它們能輕易進行資料同步或區分資料傳輸方向。而 CAN 使用的是兩條差分訊號線,只能表達一個訊號,簡潔的物理層決定了 CAN 必然要配上一套更復雜的協定,如何用一個訊號通道實現同樣、甚至更強大的功能呢?CAN 協定給出的解決方案是對資料、操作命令 (如讀/寫) 以及同步訊號進行打包,打包後的這些內容稱為報文。

1.3.5.1 報文的種類

在原始資料段的前面加上傳輸起始標籤、片選 (識別) 標籤和控制標籤,在資料的尾段加上 CRC校驗標籤、應答標籤和傳輸結束標籤,把這些內容按特定的格式打包好,就可以用一個通道表達各種訊號了,各種各樣的標籤就如同 SPI 中各種通道上的訊號,起到了協同傳輸的作用。當整個封包被傳輸到其它裝置時,只要這些裝置按格式去解讀,就能還原出原始資料,這樣的報文就被稱為 CAN 的「資料框」。

為了更有效地控制通訊,CAN 一共規定了 5 種型別的幀,它們的型別及用途說明如表 

image.png

1.3.5.2 資料框的結構

資料框是在 CAN 通訊中最主要、最複雜的報文,我們來了解它的結構,見圖

image.png

資料框以一個顯性位 (邏輯 0) 開始,以 7 個連續的隱性位 (邏輯 1) 結束,在它們之間,分別有仲裁段、控制段、資料段、CRC 段和 ACK 段。

• 幀起始

SOF 段 (Start OfFrame),譯為幀起始,幀起始訊號只有一個資料位,是一個顯性電平,它用於通知各個節點將有資料傳輸,其它節點通過幀起始訊號的電平跳變沿來進行硬同步。

• 仲裁段

當同時有兩個報文被傳送時,匯流排會根據仲裁段的內容決定哪個封包能被傳輸,這也是它名稱的由來。

仲裁段的內容主要為本資料框的 ID 資訊 (識別符號),資料框具有標準格式和擴充套件格式兩種,區別就在於 ID 資訊的長度,標準格式的 ID 為 11 位,擴充套件格式的 ID 為 29 位,它在標準 ID 的基礎上多出 18 位。在 CAN 協定中, ID 起著重要的作用,它決定著資料框傳送的優先順序,也決定著其它節點是否會接收這個資料框。CAN 協定不對掛載在它之上的節點分配優先順序和地址,對匯流排的佔有權是由資訊的重要性決定的,即對於重要的資訊,我們會給它打包上一個優先順序高的 ID,使它能夠及時地傳送出去。也正因為它這樣的優先順序分配原則,使得 CAN 的擴充套件性大大加強,在匯流排上增加或減少節點並不影響其它裝置。報文的優先順序,是通過對 ID 的仲裁來確定的。根據前面對物理層的分析我們知道如果匯流排上同時出現顯性電平和隱性電平,匯流排的狀態會被置為顯性電平,CAN 正是利用這個特性進行仲裁。

 

若兩個節點同時競爭 CAN 匯流排的佔有權,當它們傳送報文時,若首先出現隱性電平,則會失去對匯流排的佔有權,進入接收狀態。見圖 ,在開始階段,兩個裝置傳送的電平一樣,所以它們一直繼續傳送資料。到了圖中箭頭所指的時序處,節點單元 1 傳送的為隱性電平,而此時節點單元 2 傳送的為顯性電平,由於匯流排的「線與」特性使它表達出顯示電平,因此單元 2 競爭匯流排成功,這個報文得以被繼續傳送出去。

image.png

仲裁段 ID 的優先順序也影響著接收裝置對報文的反應。因為在 CAN 匯流排上資料是以廣播的形式傳送的,所有連線在 CAN 匯流排的節點都會收到所有其它節點發出的有效資料,因而我們的 CAN

控制器大多具有根據 ID 過濾報文的功能,它可以控制自己只接收某些 ID 的報文。回看資料框格式,可看到仲裁段除了報文 ID 外,還有 RTR、IDE 和 SRR 位。

(1) RTR 位 (Remote Transmission Request Bit),譯作遠端傳輸請求位,它是用於區分資料框和遙控幀的,當它為顯性電平時表示資料框,隱性電平時表示遙控幀。

(2) IDE 位 (Identifier ExtensionBit),譯作識別符號擴充套件位,它是用於區分標準格式與擴充套件格式,當它為顯性電平時表示標準格式,隱性電平時表示擴充套件格式。

(3) SRR 位 (Substitute Remote Request Bit),只存在於擴充套件格式,它用於替代標準格式中的 RTR位。由於擴充套件幀中的 SRR 位為隱性位,RTR 在資料框為顯性位,所以在兩個 ID 相同的標準格式報文與擴充套件格式報文中,標準格式的優先順序較高。

• 控制段

在控制段中的 r1 和 r0 為保留位,預設設定為顯性位。它最主要的是 DLC 段 (Data Length Code),譯為資料長度碼,它由 4 個資料位組成,用於表示本報文中的資料段含有多少個位元組, DLC 段表示的數位為 0~8。

• 資料段

資料段為資料框的核心內容,它是節點要傳送的原始資訊,由 0~8 個位元組組成,MSB 先行。

• CRC 段

為了保證報文的正確傳輸,CAN 的報文包含了一段 15 位的 CRC 校驗碼,一旦接收節點算出的CRC 碼跟接收到的 CRC 碼不同,則它會向傳送節點反饋出錯資訊,利用錯誤幀請求它重新傳送。CRC 部分的計算一般由 CAN 控制器硬體完成,出錯時的處理則由軟體控制最大重發數。在 CRC 校驗碼之後,有一個 CRC 界定符,它為隱性位,主要作用是把 CRC 校驗碼與後面的 ACK段間隔起來。

• ACK 段

ACK 段包括一個 ACK 槽位,和 ACK 界定符位。類似 I2C 匯流排,在 ACK 槽位中,傳送節點傳送的是隱性位,而接收節點則在這一位中傳送顯性位以示應答。在 ACK 槽和幀結束之間由 ACK 界定符間隔開。

• 幀結束

EOF 段 (End Of Frame),譯為幀結束,幀結束段由傳送節點傳送的 7 個隱性位表示結束。

1.3.5.3 其它報文的結構

image.png

 image.png

二. STM32F407 CAN Controller介紹

STM32 的晶片中具有 bxCAN 控制器 (Basic Extended CAN),它支援 CAN 協定 2.0A 和 2.0B 標準。該 CAN 控制器支援最高的通訊速率為 1Mb/s;可以自動地接收和傳送 CAN 報文,支援使用標準ID 和擴充套件 ID 的報文;外設中具有 3 個傳送郵箱,傳送報文的優先順序可以使用軟體控制,還可以記錄傳送的時間;具有 2 個 3 級深度的接收 FIFO,可使用過濾功能只接收或不接收某些 ID 號的報文;可設定成自動重發;不支援使用 DMA 進行資料收發。框架示意圖如下:

image.png

STM32 的有兩組 CAN 控制器,其中 CAN1 是主裝置,框圖中的「儲存存取控制器」是由 CAN1控制的,CAN2 無法直接存取儲存區域,所以使用 CAN2 的時候必須使能 CAN1 外設的時鐘。框圖中主要包含 CAN 控制核心、傳送郵箱、接收 FIFO 以及驗收篩選器,下面對框圖中的各個部分進行介紹。

2.1 CAN 控制核心

框圖中標號處的 CAN 控制核心包含了各種控制暫存器及狀態暫存器,我們主要講解其中的主控制暫存器 CAN_MCR 及位時序暫存器 CAN_BTR。

2.1.1 主控制暫存器 CAN_MCR

主控制暫存器 CAN_MCR 負責管理 CAN 的工作模式,它使用以下暫存器位實現控制。

image.png

(1) DBF 偵錯凍結功能

DBF(Debug freeze) 偵錯凍結,使用它可設定 CAN 處於工作狀態或禁止收發的狀態,禁止收發時仍可存取接收 FIFO 中的資料。這兩種狀態是當 STM32 晶片處於程式偵錯模式時才使用的,平時使用並不影響。

(2) TTCM 時間觸發模式

TTCM(Time triggered communication mode) 時間觸發模式,它用於設定 CAN 的時間觸發通訊模式,在此模式下,CAN 使用它內部定時器產生時間戳,並把它儲存在CAN_RDTxR、CAN_TDTxR 暫存器中。內部定時器在每個 CAN 位時間累加,在接收和傳送的幀起始位被取樣,並生成時間戳。利用它可以實現 ISO 11898-4 CAN 標準的分時同步通訊功能。

(3) ABOM 自動離線管理

ABOM (Automatic bus-off management) 自動離線管理,它用於設定是否使用自動離線管理功能。當節點檢測到它傳送錯誤或接收錯誤超過一定值時,會自動進入離線狀態,在離線狀態中, CAN 不能接收或傳送報文。處於離線狀態的時候,可以軟體控制恢復或者直接使用這個自動離線管理功能,它會在適當的時候自動恢復。

(4) AWUM 自動喚醒

AWUM (Automatic bus-off management),自動喚醒功能,CAN 外設可以使用軟體進入低功耗的睡眠模式,如果使能了這個自動喚醒功能,當 CAN 檢測到匯流排活動的時候,會自動喚醒。

(5) NART 自動重傳

NART(No automatic retransmission) 報文自動重傳功能,設定這個功能後,當報文傳送失敗時會自動重傳至成功為止。若不使用這個功能,無論傳送結果如何,訊息只傳送一次。

(6) RFLM 鎖定模式

RFLM(Receive FIFO locked mode)FIFO 鎖定模式,該功能用於鎖定接收 FIFO 。鎖定後,當接收 FIFO 溢位時,會丟棄下一個接收的報文。若不鎖定,則下一個接收到的報文會覆蓋原報文。

(7) TXFP 報文傳送優先順序的判定方法

TXFP(Transmit FIFO priority) 報文傳送優先順序的判定方法,當 CAN 外設的傳送郵箱中有多個待傳送報文時,本功能可以控制它是根據報文的 ID 優先順序還是報文存進郵箱的順序來傳送。

2.1.2 位時序暫存器 (CAN_BTR) 及波特率

image.png

CAN 外設中的位時序暫存器 CAN_BTR 用於設定測試模式、波特率以及各種位內的段引數。

2.1.2.1 模式

位31 SILM:靜默模式(偵錯)(Silent mode (debug))

0:正常工作  

1:靜默模式

位30 LBKM:環回模式(偵錯)(Loop back mode (debug))

0:禁止環回模式  

1:使能環回模式

為方便偵錯,STM32 的 CAN 提供了測試模式,設定位時序暫存器 CAN_BTR 的 SILM 及 LBKM暫存器位可以控制使用正常模式、靜默模式、迴環模式及靜默迴環模式,見圖。

image.png

各個工作模式介紹如下:

• 正常模式

正常模式下就是一個正常的 CAN 節點,可以向匯流排傳送資料和接收資料。

• 靜默模式

靜默模式下,它自己的輸出端的邏輯 0 資料會直接傳輸到它自己的輸入端,邏輯 1 可以被傳送到匯流排,所以它不能向匯流排傳送顯性位 (邏輯 0),只能傳送隱性位 (邏輯 1)。輸入端可以從匯流排接收內容。由於它只可傳送的隱性位不會強制影響匯流排的狀態,所以把它稱為靜默模式。這種模式一般用於監測,它可以用於分析匯流排上的流量,但又不會因為傳送顯性位而影響匯流排。

• 迴環模式

迴環模式下,它自己的輸出端的所有內容都直接傳輸到自己的輸入端,輸出端的內容同時也會被傳輸到匯流排上,即也可使用匯流排監測它的傳送內容。輸入端只接收自己傳送端的內容,不接收來自匯流排上的內容。使用迴環模式可以進行自檢。

• 迴環靜默模式

迴環靜默模式是以上兩種模式的結合,自己的輸出端的所有內容都直接傳輸到自己的輸入端,並且不會向匯流排傳送顯性位影響匯流排,不能通過匯流排監測它的傳送內容。輸入端只接收自己傳送端的內容,不接收來自匯流排上的內容。這種方式可以在「熱自檢」時使用,即自我檢查的時候,不會干擾匯流排。

以上說的各個模式,是不需要修改硬體接線的,例如,當輸出直接連輸入時,它是在 STM32 晶片內部連線的,傳輸路徑不經過 STM32 的 CAN_Tx/Rx 引腳,更不經過外部連線的 CAN 收發器,只有輸出資料到匯流排或從匯流排接收的情況下才會經過 CAN_Tx/Rx 引腳和收發器

2.1.2.2 位時序及波特率

STM32 外設定義的位時序與我們前面解釋的 CAN 標準時序有一點區別,見圖

image.png

STM32 的 CAN 外設位時序中只包含 3 段,分別是同步段 SYNC_SEG、位元欄 BS1 及位元欄 BS2,取樣點位於 BS1 及 BS2 段的交界處。其中 SYNC_SEG 段固定長度為 1Tq,而 BS1 及 BS2 段可以

在位時序暫存器 CAN_BTR 設定它們的時間長度,它們可以在重新同步期間增長或縮短,該長度SJW 也可在位時序暫存器中設定。

理解 STM32 的 CAN 外設的位時序時,可以把它的 BS1 段理解為是由前面介紹的 CAN 標準協定中 PTS 段與 PBS1 段合在一起的而 BS2 段就相當於 PBS2 段

瞭解位時序後,我們就可以設定波特率了。通過設定位時序暫存器 CAN_BTR 的 TS1[3:0] 及

TS2[2:0] 暫存器位設定 BS1 及 BS2 段的長度後,我們就可以確定每個 CAN 資料位的時間:

BS1 段時間:TS1=Tq x (TS1[3:0] + 1),

BS2 段時間:TS2= Tq x (TS2[2:0] + 1),

一個資料位的時間:T1bit =1Tq+TS1+TS2=1+ (TS1[3:0] + 1)+ (TS2[2:0] + 1)= N Tq

其中單個時間片的長度 Tq 與 CAN 外設的所掛載的時鐘匯流排及分頻器設定有關,CAN1 和 CAN2外設都是掛載在 APB1 匯流排上的,而位時序暫存器 CAN_BTR 中的 BRP[9:0] 暫存器位可以設定

CAN波特率=Fpclk1/((CAN_BS1+CAN_BS2+1)*CAN_Prescaler)

其中clk為42M!


image.pngimage.png

推薦一個CAN波特率計算器

image.png

📎CAN波特率計算 f103AHP1_36M f407AHP1_42M 取樣點軟體有說明.rar

2.2 CAN 傳送郵箱

回到圖 中的 CAN 外設框圖,在標號處的是 CAN 外設的傳送郵箱,它一共有 3 個傳送郵箱,即最多可以快取 3 個待傳送的報文。每個傳送郵箱中包含有識別符號暫存器 CAN_TIxR、資料長度控制暫存器 CAN_TDTxR 及 2 個資料暫存器 CAN_TDLxR、CAN_TDHxR,它們的功能見表

image.png

當我們要使用 CAN 外設傳送報文時,把報文的各個段分解,按位元置寫入到這些暫存器中,並對識別符號暫存器 CAN_TIxR 中的傳送請求暫存器位 TMIDxR_TXRQ 置 1,即可把資料傳送出去。其中識別符號暫存器 CAN_TIxR 中的 STDID 暫存器位比較特別。我們知道 CAN 的標準識別符號的總位數為 11 位,而擴充套件識別符號的總位數為 29 位的。當報文使用擴充套件識別符號的時候,識別符號暫存器 CAN_TIxR 中的 STDID[10:0] 等效於 EXTID[18:28] 位,它與 EXTID[17:0] 共同組成完整的 29位擴充套件識別符號。

 

2.3 CAN 接收 FIFO

圖 中的 CAN 外設框圖,在標號處的是 CAN 外設的接收 FIFO,它一共有 2 個接收 FIFO,每個 FIFO 中有 3 個郵箱,即最多可以快取 6 個接收到的報文。當接收到報文時,FIFO 的報文計數器會自增,而 STM32 內部讀取 FIFO 資料之後,報文計數器會自減,我們通過狀態暫存器可獲知報文計數器的值,而通過前面主控制暫存器的 RFLM 位,可設定鎖定模式,鎖定模式下 FIFO溢位時會丟棄新報文,非鎖定模式下 FIFO 溢位時新報文會覆蓋舊報文。跟傳送郵箱類似,每個接收 FIFO 中包含有識別符號暫存器 CAN_RIxR、資料長度控制暫存器CAN_RDTxR 及 2 個資料暫存器 CAN_RDLxR、CAN_RDHxR,它們的功能見表。

image.png

通過中斷或狀態暫存器知道接收 FIFO 有資料後,我們再讀取這些暫存器的值即可把接收到的報文載入到 STM32 的記憶體中

2.4 驗收篩選器

圖 中的 CAN 外設框圖,在標號處的是 CAN 外設的驗收篩選器,一共有 28 個篩選器組,每個篩選器組有 2 個暫存器,CAN1 和 CAN2 共用的篩選器的。在 CAN 協定中,訊息的識別符號與節點地址無關,但與訊息內容有關。因此,傳送節點將報文廣播給所有接收器時,接收節點會根據報文識別符號的值來確定軟體是否需要該訊息,為了簡化軟體的工作,STM32 的 CAN 外設接收報文前會先使用驗收篩選器檢查,只接收需要的報文到 FIFO中。

篩選器工作的時候,可以調整篩選 ID 的長度及過濾模式。根據篩選 ID 長度來分類有有以下兩種:

(1) 檢查 STDID[10:0]、EXTID[17:0]、IDE 和 RTR 位,一共 31 位。

(2) 檢查 STDID[10:0]、RTR、IDE 和 EXTID[17:15],一共 16 位。

通過設定篩選尺度暫存器 CAN_FS1R 的 FSCx 位可以設定篩選器工作在哪個尺度。而根據過濾的方法分為以下兩種模式:

(1) 識別符號列表模式,它把要接收報文的 ID 列成一個表,要求報文 ID 與列表中的某一個識別符號完全相同才可以接收,可以理解為白名單管理

(2) 掩碼模式,它把可接收報文 ID 的某幾位作為列表,這幾位被稱為掩碼,可以把它理解成關鍵字搜尋,只要掩碼 (關鍵字) 相同,就符合要求,報文就會被儲存到接收 FIFO。通過設定篩選模式暫存器 CAN_FM1R 的 FBMx 位可以設定篩選器工作在哪個模式。不同的尺度和不同的過濾方法可使篩選器工作在圖 的 4 種狀態。

image.png

每組篩選器包含 2 個 32 位的暫存器,分別為 CAN_FxR1 和 CAN_FxR2,它們用來儲存要篩選的ID 或掩碼,各個暫存器位代表的意義與圖中兩個暫存器下面「對映」的一欄一致,各個模式的說明見表。

image.png

例如下面的表格所示,在掩碼模式時,第一個暫存器儲存要篩選的 ID,第二個暫存器儲存掩碼,掩碼為 1 的部分表示該位必須與 ID 中的內容一致,篩選的結果為表中第三行的 ID 值,它是一組包含多個的 ID 值,其中 x 表示該位可以為 1 可以為 0。

image.png

而工作在識別符號模式時,2 個暫存器儲存的都是要篩選的 ID,它只包含 2 個要篩選的 ID 值 (32位元模式時)。

如果使能了篩選器,且報文的 ID 與所有篩選器的設定都不匹配,CAN 外設會丟棄該報文,不存入接收 FIFO。

2.5 整體控制邏輯

回到圖 結構框圖,圖中的標號處表示的是 CAN2 外設的結構,它與 CAN1 外設是一樣的,他們共用篩選器且由於儲存存取控制器由 CAN1 控制,所以要使用 CAN2 的時候必須要使能CAN1 的時鐘。其中 STM32F103 系列晶片不具有 CAN2 控制器。

2.6 STM32 HAL庫程式碼邏輯

2.6.1 初始化

注意:網路上基本上用的很久的HAL庫,我們採用很新的1.25.2,最新的庫還是差異挺大的!

從 STM32 的 CAN 外設我們瞭解到它的功能非常多,控制涉及的暫存器也非常豐富,而使用STM32 HAL 庫提供的各種結構體及庫函數可以簡化這些控制過程。跟其它外設一樣,STM32

HAL 庫提供了 CAN 初始化結構體及初始化函數來控制 CAN 的工作方式,提供了收發報文使用的結構體及收發函數,還有設定控制篩選器模式及 ID 的結構體。這些內容都定義在庫檔案「STM32F4xx_hal_can.h」及「STM32F4xx_hal_can.c」中,程式設計時我們可以結合這兩個檔案內的註釋使用或參考庫幫助檔案。首先我們來學習初始化結構體的內容,見程式碼清單 1。程式碼清單 CAN 初始化結構

typedef struct
{
  uint32_t Prescaler;  /* 設定 CAN 外設的時鐘分頻,可設定為 1-1024*/
  uint32_t Mode;       /* 設定 CAN 的工作模式,迴環或正常模式 */
  uint32_t SyncJumpWidth;  /* 設定 SJW 極限值 */
  uint32_t TimeSeg1;   /* 設定 BS1 段長度 */
  uint32_t TimeSeg2;   /* 設定 BS2 段長度 */
  FunctionalState TimeTriggeredMode;   /* 是否使能 TTCM 時間觸發功能 */
  FunctionalState AutoBusOff;     /* 是否使能 ABOM 自動離線管理功能 */
  FunctionalState AutoWakeUp;   /* 是否使能 AWUM 自動喚醒功能 */
  FunctionalState AutoRetransmission;  /* 是否使能 NART 自動重傳功能 */
  FunctionalState ReceiveFifoLocked;   /* 是否使能 RFLM 鎖定 FIFO 功能 */
  FunctionalState TransmitFifoPriority;/* 設定 TXFP 報文優先順序的判定方法 */
} CAN_InitTypeDef;

體這些結構體成員說明如下,其中括號內的文字是對應引數在 STM32 HAL 庫中定義的宏

(1) Prescaler

本成員設定 CAN 外設的時鐘分頻,它可控制時間片 Tq 的時間長度,這裡設定的值最終會減 1 後再寫入 BRP 暫存器位,即前面介紹的 Tq 計算公式:

Tq = (BRP[9:0]+1) x TPCLK

等效於:Tq = CAN_Prescaler x TPCLK

(2) Mode

本成員設定 CAN 的工作模式,可設定為正常模式 (CAN_MODE_NORMAL)、迴環模式 (CAN_MODE_LOOPBACK)、靜默模式 (CAN_MODE_SILENT) 以及迴環靜默模式(CAN_MODE_SILENT_LOOPBACK)。

(3) SyncJumpWidth

本成員可以設定 SJW 的極限長度,即 CAN 重新同步時單次可增加或縮短的最大長度,它可以被設定為 1-4Tq(CAN_SJW_1/2/3/4tq)。

(4) TimeSeg1

本成員用於設定 CAN 位時序中的 BS1 段的長度,它可以被設定為 1-16 個 Tq 長度(CAN_BS1_1/2/3…16tq)。

(5) TimeSeg2

本成員用於設定 CAN 位時序中的 BS2 段的長度,它可以被設定為 1-8 個 Tq 長度(CAN_BS2_1/2/3…8tq)。SYNC_SEG、 BS1 段及 BS2 段的長度加起來即一個資料位的長度,即前面介紹的原來

計算公式:T1bit =1Tq+TS1+TS2=1+ (TS1[3:0] + 1)+ (TS2[2:0] + 1)

等效於:T1bit= 1Tq+CAN_BS1+CAN_BS2

(6) TimeTriggeredMode

本成員用於設定是否使用時間觸發功能 (ENABLE/DISABLE),時間觸發功能在某些CAN 標準中會使用到。

(7) AutoBusOff

本成員用於設定是否使用自動離線管理 (ENABLE/DISABLE),使用自動離線管理可以在節點出錯離線後適時自動恢復,不需要軟體干預。

(8) AutoWakeUp

本成員用於設定是否使用自動喚醒功能 (ENABLE/DISABLE),使能自動喚醒功能後它會在監測到匯流排活動後自動喚醒。

(9) AutoWakeUp

本成員用於設定是否使用自動離線管理功能 (ENABLE/DISABLE),使用自動離線管理可以在出錯時離線後適時自動恢復,不需要軟體干預。

(10) AutoRetransmission

本成員用於設定是否使用自動重傳功能 (ENABLE/DISABLE),使用自動重傳功能時,會一直傳送報文直到成功為止。

(11) ReceiveFifoLocked

本成員用於設定是否使用鎖定接收 FIFO(ENABLE/DISABLE),鎖定接收 FIFO 後,若FIFO 溢位時會丟棄新資料,否則在 FIFO 溢位時以新資料覆蓋舊資料。

(12) TransmitFifoPriority

本成員用於設定傳送報文的優先順序判定方法 (ENABLE/DISABLE),使能時,以報文存入傳送郵箱的先後順序來傳送,否則按照報文 ID 的優先順序來傳送。設定完這些結構體成員後,我們呼叫庫函數 HAL_CAN_Init 即可把這些引數寫入到 CAN 控制暫存器中,實現 CAN 的初始化

2.6.2 CAN 傳送及接收結構體

在傳送或接收報文時,需要往傳送郵箱中寫入報文資訊或從接收 FIFO 中讀取報文資訊,利用STM32HAL 庫的傳送及接收結構體可以方便地完成這樣的工作,它們的定義見程式碼清單 。程式碼清單 39‑2 CAN 傳送及接收結構體

typedef struct
{
  uint32_t StdId;    /* 儲存報文的標準識別符號 11 位,0-0x7FF. */
  uint32_t ExtId;    /* 儲存報文的擴充套件識別符號 29 位,0-0x1FFFFFFF. */
  uint32_t IDE;     /* 儲存 IDE 擴充套件標誌 */
  uint32_t RTR;    /* 儲存 RTR 遠端幀標誌 */
  uint32_t DLC;     /* 儲存報文資料段的長度,0-8 */
  FunctionalState TransmitGlobalTime; 
} CAN_TxHeaderTypeDef;
typedef struct
{
  uint32_t StdId;    /* 儲存報文的標準識別符號 11 位,0-0x7FF. */
  uint32_t ExtId;    /* 儲存報文的擴充套件識別符號 29 位,0-0x1FFFFFFF. */
  uint32_t IDE;     /* 儲存 IDE 擴充套件標誌 */
  uint32_t RTR;      /* 儲存 RTR 遠端幀標誌 */
  uint32_t DLC;     /* 儲存報文資料段的長度,0-8 */
  uint32_t Timestamp; 
  uint32_t FilterMatchIndex; 
} CAN_RxHeaderTypeDef;

這些結構體成員, 說明如下:

(1) StdId

本成員儲存的是報文的 11 位標準識別符號,範圍是 0-0x7FF。

(2) ExtId

本成員儲存的是報文的 29 位擴充套件識別符號,範圍是 0-0x1FFFFFFF。ExtId 與 StdId 這兩個成員根據下面的 IDE 位設定,只有一個是有效的。

(3) IDE

本成員儲存的是擴充套件標誌 IDE 位,當它的值為宏 CAN_ID_STD 時表示本報文是標準幀,使用 StdId 成員儲存報文 ID;當它的值為宏 CAN_ID_EXT 時表示本報文是擴充套件幀,使用 ExtId 成員儲存報文 ID。

(4) RTR

本成員儲存的是報文型別標誌 RTR 位,當它的值為宏 CAN_RTR_Data 時表示本報文是資料框;當它的值為宏 CAN_RTR_Remote 時表示本報文是遙控幀,由於遙控幀沒有資料段,所以當報文是遙控幀時,資料是無效的

(5) DLC

本成員儲存的是資料框資料段的長度,它的值的範圍是 0-8,當報文是遙控幀時 DLC值為 0。

2.6.3 CAN 篩選器結構體

CAN 的篩選器有多種工作模式,利用篩選器結構體可方便設定,它的定義見程式碼清單 。程式碼清單CAN 篩選器結構體

typedef struct
{
  uint32_t FilterIdHigh;         /*CAN_FxR1 暫存器的高 16 位 */
  uint32_t FilterIdLow;         /*CAN_FxR1 暫存器的低 16 位 */
  uint32_t FilterMaskIdHigh;   /*CAN_FxR2 暫存器的高 16 位 */
  uint32_t FilterMaskIdLow;    /*CAN_FxR2 暫存器的低 16 位 */
  uint32_t FilterFIFOAssignment;  /* 設定經過篩選後資料儲存到哪個接收 FIFO */
  uint32_t FilterBank;            /* 篩選器編號,範圍 0-27,資料手冊上說0-27是CAN1/CAN2共用,但是實測發現並不是這樣,CAN1是0-13,CAN2是14-27 */
  uint32_t FilterMode;            /* 篩選器模式 */
  uint32_t FilterScale;           /* 設定篩選器的尺度 */
  uint32_t FilterActivation;      /* 是否使能本篩選器 */
  uint32_t SlaveStartFilterBank;  
} CAN_FilterTypeDef;

這些結構體成員都是「41.2.14 驗收篩選器」小節介紹的內容,可對比閱讀,各個結構體成員的介紹如下:

(1) FilterIdHigh

FilterIdHigh 成員用於儲存要篩選的 ID,若篩選器工作在 32 位元型樣,它儲存的是所篩選 ID 的高 16 位;若篩選器工作在 16 位元型樣,它儲存的就是一個完整的要篩選的 ID。

(2) FilterIdLow

類似地,FilterIdLow 成員也是用於儲存要篩選的 ID,若篩選器工作在 32 位元型樣,它儲存的是所篩選 ID 的低 16 位;若篩選器工作在 16 位元型樣,它儲存的就是一個完整的要篩選的 ID。

(3) FilterMaskIdHigh

FilterMaskIdHigh 儲存的內容分兩種情況,當篩選器工作在識別符號列表模式時,它的功能與 FilterIdHigh 相同,都是儲存要篩選的 ID;而當篩選器工作在掩碼模式時,它儲存的是 FilterIdHigh 成員對應的掩碼,與 FilterIdLow 組成一組篩選器。

(4) FilterMaskIdLow

類似地, FilterMaskIdLow 儲存的內容也分兩種情況,當篩選器工作在識別符號列表模式時,它的功能與 FilterIdLow 相同,都是儲存要篩選的 ID;而當篩選器工作在掩碼模式時,它儲存的是 FilterIdLow 成員對應的掩碼,與 FilterIdLow 組成一組篩選器。上面四個結構體的儲存的內容很容易讓人糊塗,請結合前面的圖 39_0_15 和下面的表 39‑7 理解,如果還搞不清楚,再結合庫函數 FilterInit 的原始碼來分析。

表不同模式下各結構體成員的內容

image.png

對這些結構體成員賦值的時候,還要注意暫存器位的對映,即注意哪部分代表 STID,哪部分代表 EXID 以及 IDE、RTR 位。

(5) FilterFIFOAssignment

本成員用於設定當報文通過篩選器的匹配後,該報文會被儲存到哪一個接收 FIFO,它的可選值為 FIFO0 或 FIFO1(宏 CAN_FILTER_FIFO0/1)。

(6) FilterBank

本成員用於設定篩選器的編號,即本過濾器結構體設定的是哪一組篩選器,CAN 一共有 28 個篩選器,所以它的可輸入引數範圍為 0-27。

(7) FilterMode

本 成 員 用 於 設 置 篩 選 器 的 工 作 模 式, 可 以 設 置 為 列 表 模 式 (宏CAN_FILTERMODE_IDLIST) 及掩碼模式 (宏 CAN_FILTERMODE_IDMASK)。

(8) FilterScale

本成員用於設定篩選器的尺度,可以設定為 32 位長 (宏 CAN_FILTERSCALE_32BIT)及 16 位長 (宏 CAN_FILTERSCALE_16BIT)。

(9) FilterActivation

本成員用於設定是否啟用這個篩選器 (宏 ENABLE/DISABLE)。

 

三. CAN Cubemx設定

我們通過問題來熟悉下cubemx設定,你熟悉了這些問題基本就知道怎麼設定了!

問題:Parameter Settings分別都是設定什麼的?

答案:如圖

image.png

問題:怎麼設定波特率呢?

答案:用我上面貼的工具(CAN波特率計算 f103AHP1_36M  f407AHP1_42M  取樣點軟體有說明.rar)直接設定,舉兩個個例子

例子1:我們要設定成500KHz,那麼我們這樣設定

image.png

image.png

我們用採集點為80%,所以BS1為4tq,BS2為2tq,分頻係數為12,代進公式Fpclk1/((CAN_BS1+CAN_BS2+1)*CAN_Prescaler)=42M/(4+2+1)/12=500kHz

例子2:我們要設定成1M Hz,那麼我們這樣設定

image.png

我們用採集點為75%,所以BS1為3tq,BS2為2tq,分頻係數為7,代進公式Fpclk1/((CAN_BS1+CAN_BS2+1)*CAN_Prescaler)=42M/(3+2+1)/7=1MHz

image.png

 

問題:Basic Parameter分別是啥意思呢?

image.png

Timer Triggered Communication Mode:否使用時間觸發功能 (ENABLE/DISABLE),時間觸發功能在某些CAN 標準中會使用到。

Automatic Bus-Off Management:用於設定是否使用自動離線管理功能 (ENABLE/DISABLE),使用自動離線管理可以在出錯時離線後適時自動恢復,不需要軟體干預。

Automatic Wake-Up Mode:用於設定是否使用自動喚醒功能 (ENABLE/DISABLE),使能自動喚醒功能後它會在監測到匯流排活動後自動喚醒。

Automatic Retransmission:用於設定是否使用自動重傳功能 (ENABLE/DISABLE),使用自動重傳功能時,會一直傳送報文直到成功為止。

Receive Fifo Locked Mode:用於設定是否使用鎖定接收 FIFO(ENABLE/DISABLE),鎖定接收 FIFO 後,若FIFO 溢位時會丟棄新資料,否則在 FIFO 溢位時以新資料覆蓋舊資料。

Transmit Fifo Priority:用於設定傳送報文的優先順序判定方法 (ENABLE/DISABLE),使能時,以報文存入傳送郵箱的先後順序來傳送,否則按照報文 ID 的優先順序來傳送。設定完這些結構體成員後,我們呼叫庫函數 HAL_CAN_Init 即可把這些引數寫入到 CAN 控制暫存器中,實現 CAN 的初始化

 

問題:為啥CAN分為RX0,RX1中斷呢?

image.png

答案:STM32有2個3級深度的接收緩衝區:FIFO0和FIFO1,每個FIFO都可以存放3個完整的報文,它們完全由硬體來管理。如果是來自FIFO0的接收中斷,則用CAN1_RX0_IRQn中斷來處理。如果是來自FIFO1的接收中斷,則用CAN1_RX1_IRQn中斷來處理,如圖:

image.png

問題:CAN SCE中斷時什麼?

image.png

答案:status chanege error,錯誤和狀態變化中斷!

四.CAN分析工具的使用

下面我們會用到CAN分析工具,還是比較好用的,此部分使用作為自己使用

https://www.zhcxgd.com/h-col-112.html

五. 實驗

1.Normal模式測試500K 波特率(定時傳送,輪詢接收)

1.1 CubeMx設定

image.png

image.png

1.2 設定Filter過濾,我們只使能FIFO0,並且不過濾任何訊息

uint8_t bsp_can1_filter_config(void)
{
    CAN_FilterTypeDef filter = {0};
    filter.FilterActivation = ENABLE;
    filter.FilterMode = CAN_FILTERMODE_IDMASK;
    filter.FilterScale = CAN_FILTERSCALE_32BIT;
    filter.FilterBank = 0;
    filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
    filter.FilterIdLow = 0;
    filter.FilterIdHigh = 0;
    filter.FilterMaskIdLow = 0;
    filter.FilterMaskIdHigh = 0;
    HAL_CAN_ConfigFilter(&hcan1, &filter);
    return BSP_CAN_OK;
}

1.3 開啟CAN(注意,預設Cubemx生成的程式碼並沒有can start)

HAL_CAN_Start(&hcan1);

1.4 編寫傳送函數

我們開出了幾個引數,id_type是擴充套件幀還是標準幀,basic_id標準幀ID(在標準幀中有效),ex_id擴充套件幀ID(在擴充套件幀中有效),data要傳送的資料,data_len要傳送的資料長度

uint8_t bsp_can1_send_msg(uint32_t id_type,uint32_t basic_id,uint32_t ex_id,uint8_t *data,uint32_t data_len)
{
    uint8_t index = 0;
    uint32_t *msg_box;
	uint8_t send_buf[8] = {0};
    CAN_TxHeaderTypeDef send_msg_hdr;
    send_msg_hdr.StdId = basic_id;
    send_msg_hdr.ExtId = ex_id;
    send_msg_hdr.IDE = id_type;
    send_msg_hdr.RTR = CAN_RTR_DATA;
    send_msg_hdr.DLC = data_len;
	send_msg_hdr.TransmitGlobalTime = DISABLE;
	for(index = 0; index < data_len; index++)
          send_buf[index] = data[index];
 
    HAL_CAN_AddTxMessage(&hcan1,&send_msg_hdr,send_buf,msg_box);
    return BSP_CAN_OK;
}

我們在main函數中1s傳送一幀,標準幀跟擴充套件幀交叉呼叫,程式碼如下:

    send_data[0]++;
	send_data[1]++;
	send_data[2]++;
	send_data[3]++;
	send_data[4]++;
	send_data[5]++;
	send_data[6]++;
	send_data[7]++;
	if(id_type_std == 1)
	{
      bsp_can1_send_msg(CAN_ID_STD,1,2,send_data,8);
      id_type_std = 0;
	}
	else
	{
      bsp_can1_send_msg(CAN_ID_EXT,1,2,send_data,8);
      id_type_std = 1;
	}
	HAL_Delay(1000);

我們通過CAN協定分析儀來抓下結果

image.png

1.5 編寫輪詢接收函數

uint8_t bsp_can1_polling_recv_msg(uint32_t *basic_id,uint32_t *ex_id,uint8_t *data,uint32_t *data_len)
{
	uint8_t index = 0;
	uint8_t recv_data[8];
    CAN_RxHeaderTypeDef header;
 
  while (HAL_CAN_GetRxFifoFillLevel(&hcan1, CAN_RX_FIFO0) != 0)
  {
    if (__HAL_CAN_GET_FLAG(&hcan1, CAN_FLAG_FOV0) != RESET)
      printf("[CAN] FIFO0 overrun!\n");
   
    HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &header, recv_data);
	if(header.IDE == CAN_ID_STD)
	{
              printf("StdId ID:%d\n",header.StdId);
	}
	else
	{
              printf("ExtId ID:%d\n",header.ExtId);
	}
	printf("CAN IDE:0x%x\n",header.IDE);
	printf("CAN RTR:0x%x\n",header.RTR);
	printf("CAN DLC:0x%x\n",header.DLC);
	printf("RECV DATA:");
	for(index = 0; index < header.DLC; index++)
	{
              printf("0x%x ",recv_data[index]);
	}
	printf("\n");
  }
}

實驗一總結:

1.沒用呼叫HAL_CAN_Start(&hcan1);使能CAN

2.沒有編寫Filter函數,我開始自認為不設定就預設不過濾,現在看來是我想多了,其實想想也合理,你如果不過濾分配FIFO,STM32怎麼決定把收到的放到哪個FIFO中

待提升:

1.目前只用到FIFO0,待把FIFO1使用起來

2.Normal模式測試500K 波特率(定時傳送,中斷接收)

2.1 CubeMx設定

image.png

image.png

步驟2,3,4跟polling完全一致,我們來直接說下中斷怎麼用(主要是使能notifity就行了)

static void MX_CAN1_Init(void)
{
  /* USER CODE BEGIN CAN1_Init 0 */
  /* USER CODE END CAN1_Init 0 */
  /* USER CODE BEGIN CAN1_Init 1 */
  /* USER CODE END CAN1_Init 1 */
  hcan1.Instance = CAN1;
  hcan1.Init.Prescaler = 12;
  hcan1.Init.Mode = CAN_MODE_NORMAL;
  hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
  hcan1.Init.TimeSeg1 = CAN_BS1_4TQ;
  hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
  hcan1.Init.TimeTriggeredMode = DISABLE;
  hcan1.Init.AutoBusOff = ENABLE;
  hcan1.Init.AutoWakeUp = ENABLE;
  hcan1.Init.AutoRetransmission = DISABLE;
  hcan1.Init.ReceiveFifoLocked = DISABLE;
  hcan1.Init.TransmitFifoPriority = DISABLE;
  if (HAL_CAN_Init(&hcan1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN CAN1_Init 2 */
  bsp_can1_filter_config();
	HAL_CAN_Start(&hcan1);
	HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
  /* USER CODE END CAN1_Init 2 */
}

下面我們來編寫下中斷函數

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
	uint8_t index = 0;
	uint8_t recv_data[8];
      CAN_RxHeaderTypeDef header;
 
	HAL_CAN_GetRxMessage(&hcan1, CAN_RX_FIFO0, &header, recv_data);
	if(header.IDE == CAN_ID_STD)
	{
          printf("StdId ID:%d\n",header.StdId);
	}
	else
	{
          printf("ExtId ID:%d\n",header.ExtId);
	}
	printf("CAN IDE:0x%x\n",header.IDE);
	printf("CAN RTR:0x%x\n",header.RTR);
	printf("CAN DLC:0x%x\n",header.DLC);
	printf("RECV DATA:");
	for(index = 0; index < header.DLC; index++)
	{
          printf("0x%x ",recv_data[index]);
	}
	printf("\n");
}

參考內容:

1.[野火EmbedFire]《STM32 HAL庫開發實戰指南——基於野火挑戰者開發板》.pdf

2.STM32F4參考手冊.pdf

3.CAN入門書.pdf