知識來源于網際網路,回饋于網際網路!
目錄
- 1. 總體概述
-
- 1.1 基本概念
- 1.2 通訊方式
- 1.3 為什麼使用CAN?
- 1.4 CAN的協定及組成
- 2. 上帝視角看CAN的通訊過程
-
- 2.1 資料傳輸原理實作
- 2.2 通信的整個過程
-
- 2.2.1 空閑狀态
- 2.2.2 開始資料傳輸
- 2.2.3 仲裁機制
- 2.2.4 位時序
- 2.2.5 一次資料傳輸的例子
- 3. CAN總線協定層的詳細介紹
-
- 3.1 幀的種類
-
- 3.1.1 資料幀
- 3.1.2 遙控幀
- 3.1.3 錯誤幀
- 3.1.4 過載幀
- 3.1.5 幀間隔
- 3.2 位填充
- 3.3 錯誤的種類
- 3.4 錯誤通知
-
- 3.4.1 節點錯誤狀态
- 3.4.2 錯誤狀态的轉換
- 3.4.3 錯誤幀的發送
- 3.4.4 仲裁機制與位時序
- 3.4.5 舉個栗子
- 3.5 CAN的特性總結
- 4. STM32中的CAN
-
- 4.1 bxCAN主要特性
- 4.2 bxCAN的架構
- 4.3 控制核心中的主要寄存器
-
- 4.3.1 主要制寄存器 (CAN_MCR)
- 4.3.2 位時序寄存器 (CAN_BTR)
- 4.3.3 bxCAN的位時序和波特率
- 4.4 發送郵箱
- 4.5 接收FIFO
- 4.6 驗收篩選器
-
- 4.6.1 CAN過濾器寄存器
- 4.6.2 配置篩選器工作模式
- 5. ST庫函數實作CAN通信
-
- 5.1 CAN控制相關的結構體
-
- 5.1.1 初始化結構體
- 5.1.2 發送及接收結構體
- 5.1.3 篩選器結構體
- 5.2 硬體部分準備
- 5.3 代碼實作回環模式
- 5.4 實驗總結
1. 總體概述
1.1 基本概念
CAN 是 Controller Area Network 的縮寫(以下稱為 CAN),是 ISO 國際标準化的串行通信協定。在北美和西歐,CAN 總線協定已經成為汽車計算機控制系統和嵌入式工業控制區域網路的标準總線,并且擁有以 CAN 為底層協定專為大型貨車和重工機械車輛設計的 J1939 協定。
1.2 通訊方式
回顧前面學到的 RS232 IIC SPI RS485 等通信
UART:(Universal Asynchronous Receiver Transmitter:通用異步收發器/異步串行通信口),是一種通用的串行資料總線,用于異步通信,支援全雙工。它包括了RS232、RS499、RS423、RS422和RS485等接口标準規範和總線标準規範,即UART是異步串行通信口的總稱。
IIC總線協定:I2C總線是由Philips公司開發的一種簡單、雙向二線制同步串行總線。它隻需要兩根線即可在連接配接于總線上的器件之間傳送資訊。
SPI總線協定:SPI是串行外設接口(Serial Peripheral Interface)的縮寫。SPI,是一種高速的,全雙工,同步的通信總線,并且在晶片的管腳上隻占用四根線,節約了晶片的管腳,同時為PCB的布局上節省空間,提供友善,正是出于這種簡單易用的特性,如今越來越多的晶片內建了這種通信協定。
RS232接口缺陷:
(1)接口的信号電平值較高( +/-12V),易損壞接口電路的晶片。
(2)傳輸速率較低,在異步傳輸時,波特率為20Kbps。
(3)接口使用一根信号線和一根信号傳回線而構成共地的傳輸形式,這種共地傳輸容易産生共模幹擾,是以抗噪聲幹擾性弱。
(4)傳輸距離有限,最大傳輸距離标準值為50英尺,實際上也隻能用在50米左右。
RS485是對RS232的改進,特點包括:
①接口電平低,不易損壞晶片。RS485的電氣特性:邏輯“1”以兩線間的電壓差為+(2 ~ 6)V表示,邏輯“0”以兩線間的電壓差為-(2~6)V表示。接口信号電平比RS232降低了,不易損壞接口電路的晶片。
②傳輸速率高。10米時,RS485的資料最高傳輸速率可達35Mbps,在1200m時,傳輸速度可達100Kbps。
③抗幹擾能力強。RS485接口是采用平衡驅動器和差分接收器的組合,抗共模幹擾能力增強,即抗噪聲幹擾性好。
④傳輸距離遠,支援節點多。RS485總線最長可以傳輸1200m以上(速率≤100Kbps)一般最大支援32個節點,如果使用特制的485晶片,可以達到128個或者256個節點,最大的可以支援到400個節點。
CAN總線通信系統是串行通信的一種,要優于RS485總線,是目前比較常用的一種工業總線,如汽車的電氣部分就采用CAN總線實作通信。與I2C、SPI等具有時鐘信号的同步通訊方式不同,CAN通訊并不是以時鐘信号來進行同步的,它是一種異步半雙工通訊。(同步即在同一個時鐘驅動下資料通信,半雙工即接受與發送不能同時進行)
1.3 為什麼使用CAN?
汽車工業蓬勃發展,汽車的電子控制單元逐漸增多。各電控單元之間的信号交換導緻汽車線束的級數增加,複雜粗大的線束與汽車有限的布線空間之間沖突越來越突出,繁多的線束導緻電氣系統可靠性下降,同時增加了重量。
CAN總線将汽車内部各電控單元之間連接配接成一個區域網路絡,實作了資訊的共享,大大減少了汽車的線束,如下面的示意圖:
比如上圖中,每個部分的多個器件都挂載在CAN總線上(一個CAN總線上的所有器件通訊速率必須相同),各個部分再彙集到網關,由網關配置設定實作各個不同速率的部分之間通訊,這樣就很友善輕松實作了對汽車整體電控部分的檢測與控制。
在汽車、工業控制領域,資料通信的穩定性和正确性要求極高,因為裝置的工作環境既有振蕩、高溫、輻射等各種不定因素,那不是一般的通信協定能夠滿足的。除此之外,CAN通信還有許多優秀的特點,比如多主要制、故障封閉功能等,非常适用于工控領域方面,将在下文再提。
1.4 CAN的協定及組成
CAN協定經過ISO标準化後有兩個标準ISO11898标準和IS011519-2标準。其中ISO11898是針對通信速率為125Kbps~1Mbps的高速通信标準(閉環),而IS011519-2是針對通信速率為125Kbps以下的低速通信标準(開環)。
Kbps:總線的通信速率,指的是位速率。或稱為比特率(和波特率不是一回事),表示的是:機關時間内,通信線路上傳輸的二進制位的數量,其基本機關是 bps 或者 b/s (bit per second)。
CAN的組成一般有兩種方式:一種是CPU與CAN控制器內建到一起、再外接CAN收發器;另一種是CPU與CAN控制器分開的,使用的時候需要配置CAN接口電路,比較麻煩。STM32中就是采用第一種方式,将CAN接口內建在晶片内,使用的時候再外接CAN收發器(顧名思義,可發送,可接收),常用的有TJA1050或者82C250。
CAN收發器是用于TTL電平與差分電壓信号互相轉換的,TTL電平即單片機引腳直接提供的電平,邏輯0代表低電平,邏輯1代表高電平;而差分電壓信号則為固定的電壓值。
2. 上帝視角看CAN的通訊過程
以ISO11898 标準的高速、短距離閉環網絡為例,總線最大長度為 40m,通信速度最高為 1Mbps。在 CAN 總線的起止端有一個 120Ω的終端電阻,是用來來做阻抗比對,以減少回波反射。
2.1 資料傳輸原理實作
先知道CAN通信隻由兩根線完成的,一條稱為CAN_H(CAN High),一條稱為CAN_L(CAN Low),共同構成一組差分信号線。整個過程是以差分信号的形式進行通訊的,即信号的邏輯 0 和邏輯 1 由兩根差分信号線的電壓差來表示。
CAN_Rx和CAN_Tx分别是從MCU中接出來的引腳,比如MCU要發送一個邏輯1,則隻要将CAN_Tx設定為1,經過CAN收發器轉換,CAN_High和CAN_Low 線上的電壓均為 2.5v,即傳到總線的電壓差 Vh-Vl=0V,總線上的狀态則就是邏輯1。同樣,當CAN_High和CAN_Low 讀取到 CAN總線電壓分别3.5V和1.5V,即壓差為2V,經過收發器轉換,MCU則可通過CAN_Rx讀取到信号0。
可以想象到以差分信号形式傳送,穩定性更好,因為即使環境問題導緻CAN_High電壓發送變化,則CAN_L也會發送同等變化,兩者做差即可抵消由于這個環境引起的變化。
- 發送方通過使總線電平發生變化,将其資訊傳遞到CAN總線上。
- 接收方通過監聽總線電平,将總線上的消息讀入自己的接收器。
2.2 通信的整個過程
那麼是如何通過一個節點的MCU向總線上那麼多節點中的某個節點進行發送資訊的?一個節點又是如何知道某個資訊是發給自己的或者不是發給自己的?這裡或許讓你想起 IIC 中以位址方式進行主從點對點的通信,但其實在CAN中并無位址的概念。
少了像IIC中的SCL位址線、SPI中的片選信号線,簡潔的實體層決定了 CAN 必然要配上一套更複雜的協定。如何用一個信号通道實作同樣、甚至更強大的功能呢?答案是對資料或操作指令進行打包。
2.2.1 空閑狀态
先規定空閑狀态,所謂的空閑狀态就是指沒有節點正在傳輸資料的時候,在CAN協定中,當總線上的上出現連續的
11位隐性電平
,那麼總線就處于空閑狀态。也就是說對于任意一個節點而言,隻要它監聽到總線上連續出現了11位隐性電平,那麼該節點就會認為總線目前處于空閑狀态。
顯/隐性電平
: 在總線上通常邏輯1表示隐性。而0表示顯性。總線上的邏輯0即差分電壓差為2V左右(CAN_H=3.5,CAN_L=1.5),總線上的邏輯1即差分電壓差為0V(CAN_H=2,CAN_L=2)
怎麼讓總線連續出現11位隐形電平呢?在總線上顯性電平具有優先權,隻要有一個節點輸出顯性電平,總線上即為顯性電平。而隐形電平則具有包容的意味,隻有所有的單元都輸出隐性電平,總線上才為隐性電平(顯性電平比隐性電平更強)。
是以,現在可以先簡單地了解為,需要在總線一開始工作的時候,所有節點都輸出隐性電平;已知在一次傳輸時該節點輸出顯性電平,則在傳輸完成後該節點再輸出隐性電平即可,這樣就能将總線在無資料傳輸時保持空閑狀态。(真實的實作過程比較複雜,這裡僅作為暫時了解,後面會繼續提到)
2.2.2 開始資料傳輸
每次發送資料前,節點都會監聽總線的狀态,如果總線狀态為空閑時,它就會立即向總線上發送自己的資料,這個資料裡不僅有資料,還有本身的ID資訊或者其他的控制指令,應稱為資料包(資料幀),也叫做
封包
。當封包被傳輸到其它節點時,隻要這些節點按格式去解讀,就能還原出原始資料。
封包
: 在原始資料段的前面加上傳輸起始标簽、片選(識别)标簽、控制标簽,在資料的尾段加上 CRC 校驗标簽、應答标簽和傳輸結束标簽。類似這樣的資料包就被稱為 CAN 的資料幀。為了更有效地控制通訊,CAN 一共規定了 5 種類型的幀,幀也稱為封包。
資料幀是在 CAN 通訊中最主要、最複雜的封包,它以一個顯性位(邏輯 0)開始,以 7 個連續的隐性位(邏輯 1)結束。在它們之間,分為仲裁段、控制段、資料段、CRC 段和 ACK 段,以标準資料幀為例。
資料幀标準格式各個位的介紹
SOF:聯系前文可知,當資料幀發出第一位時(0為顯性電位),總線就由空閑狀态轉為傳輸狀态,同一時間隻能允許一個節點傳輸資料。
Identify:接下來的仲裁段有11位,即本資料幀的 ID 資訊,決定着資料幀發送的優先級,也決定着其它裝置是否會接收這個資料幀,禁止高 7 位都為隐性(禁止設定:ID=1111111XXXX), ID 資訊的作用:① 如果同時有多個節點發送資料時,作為優先級依據(仲裁機制);② 目标節點通過 ID 資訊來接受資料(驗收濾波技術)。這些将在下文提出。
RTR:(Remote Transmission Request BIT) 位用于辨別是否是遠端幀(0,資料幀;1,遠端幀),在資料幀裡這一位為顯性(邏輯 0)。
IDE:(Identifier Extension Bit),是用于區分标準格式與擴充格式,在标準格式中 IDE 位為顯性,在擴充格式裡 IDE 位為隐性。
r0:保留位,必須以顯性電平發送。
DLC:由 4 位組成,MSB 先行(高位先行),它的二進制編碼用于表示本封包中的資料段含有多少個位元組,DLC 段表示的數字為0到8,若接收方接收到 9~15 的時候并不認為是錯誤。
Data:資料幀的核心内容,它由 0~8 個位元組(0 ~ 64位)組成,MSB 先行。
CRC:該段用于檢查幀傳輸錯誤,發送方以一定的方法計算包括:幀起始、仲裁段、控制段、資料段;接收方以同樣的算法計算 CRC 值并進行比較,如果不同則會向發送端回報出錯資訊,重新發送;計算和出錯處理一般由 CAN 控制器硬體完成或由軟體控制最大重發數。該段由 15 個位的 CRC 順序和 1 個位的 CRC 界定符(用于分隔的位)組成,它為隐性位,主要作用是把CRC 校驗碼與後面的 ACK 段間隔起來。
ACK:由 ACK 槽(ACK Slot)和 ACK 界定符 2 個位組成,在 ACK 槽位中,發送端發送的為隐性位,而接收端則在這一位中發送顯性位以示應答。在 ACK 槽和幀結束之間由 ACK 界定符間隔開,為隐性位。(發送 ACK/傳回 ACK這個過程使用到回讀機制,即發送方先在 ACK 槽發送隐性位後,回讀到的總線上的電平為顯性0,發送方才知道它發送成功了,不用重發)
EOF:由發送端發送 7 個隐性位表示結束。
比如總線上有3個節點,節點1設定ID為000101 00010,節點2驗收濾波ID表中有節點1的ID号,而節點3中的驗收濾波ID表中沒有節點1的ID号,節點1向節點2發送1位元組的資訊。
- 封包資訊:0 000101 00010 0 0 0 0 0001 0101 1000 XXXXXXXXXXXXXXX 1 1 1 1111111
- 通過總線發送時,在ID資訊發送階段,隻有節點2才能收到總線上的資料,因為節點3的驗收濾波ID表中沒有節點1的ID号
- 在封包發送到ACK槽時,會等待并回讀節點2的回報,從節點2的角度看,此時總線為空閑狀态,當驗證CRC正确,則向總線發送顯性電平,接着當節點1回讀到顯性電平,才會繼續發送剩下的EOF
以上隻是簡單的了解,實際傳輸的過程比這個複雜許多,下文繼續。
2.2.3 仲裁機制
運用到線與機制和回讀機制 以上隻是節點1主動發送資料,但是萬一節點1和節點2同時向節點3發送資料的時候,如何判定先後呢?采用非破壞性位仲裁機制,即對各個消息的辨別符(即ID号)進行逐位仲裁(比較),如果某個節點發送的消息仲裁獲勝,那麼這個節點将擷取總線的發送權,仲裁失敗的節點則立即停止發送并轉變為監聽(接收)狀态。
從上文可知,顯性的優先級高于隐性,即仲裁比較的就是哪個ID中的0多,0最多的那個就可以獲得發送權,比如 000000 00010 就比 000000 00011 的優先級要高,仲裁的過程由硬體實作;同時要注意,仲裁段除了封包 ID 外,還有 RTR、IDE、SRR 位(在拓展模式中,下文價紹),也就是說當ID全都一樣時,會繼續比較接下來的幾位。
至于如何做到“0多即勝”,可以了解為一種回讀和線與機制,即顯性能夠将隐性覆寫,将自己要比較的位與總線上的狀态相與,隻有線與的結果與本身一緻時,仲裁才能夠通過。
其實在封包發送上去的過程,采用的是廣播的方式,在節點1和節點2總裁的同時,總線上所有的節點都能夠監聽到它們的ID号,隻不過也在同時進行驗收濾波,隻有監聽到的ID号存在ID表中,該節點才會選擇繼續監聽後面的封包。
2.2.4 位時序
以上已經基本解決了CAN通信的基本問題,可以思考一下,由于 CAN 沒有時鐘信号線,而且它的封包中并沒有包含用于同步的标志,要怎麼做才能對總線的電平進行正确的采樣呢?比如我節點1發送3個位出去了,節點2應該在什麼時候接收才能保證此時此刻它所接收到的就是第3位或者接收到的電平是正确的? CAN中提出了位同步的方式來確定通訊時序。
位時序的主要知識點 CAN總線通訊協定的每一個資料幀可以看作一連串的電平信号,每一個電平信号代表一位(一個位元組8位的位),是以一幀中包含了很多個位,由發送單元在非同步的情況下發送的每秒鐘的位數稱為位速率。 一位又分為4段, 同步段(SS)、傳播時間段(PTS)、相位緩沖段 1(PBS1)、相位緩沖段 2(PBS2)。分解後最小的時間機關是 Tq,而一個完整的位由 8~25 個 Tq 組成。
- 1 位分為 4 個段,每個段又由若幹個 Tq 構成,這稱為位時序。
- 1 位由多少個 Tq 構成、每個段又由多少個 Tq 構成等,可以任意設定位時序。通過設定位時序,多個單元可同時采樣,也可任意設定采樣點。
SS 段(SYNC SEG):同步段,比如當總線上出現幀起始信号(SOF)時,其它節點上的控制器根據總線上的這個下降沿,對自己的位時序進行調整,把該下降沿包含到 SS 段内,這樣根據起始幀來進行同步的方式稱為硬同步。其中 SS 段的大小為 1Tq。總線上信号的跳變沿被包含在節點的 SS 段的範圍之内,則表示節點與總線的時序是同步的,采樣點采集到的總線電平即可被确定為該位的電平。
PTS 段(PROP SEG):傳播時間段,這個時間段是用于補償網絡的實體延時時間,包括發送單元的輸出延遲、總線上信号的傳播延遲、接收單元的輸入延遲,這個段的時間為以上各延遲時間的和的兩倍。大小可以為 1~8Tq。
PBS1 段(PHASE SEG1):相位緩沖段,主要用來補償邊沿階段的誤差,它的時間長度在重新同步的時候可以加長。 PBS1 段的初始大小可以為 1~8Tq。
PBS2 段(PHASE SEG2):另一個相位緩沖段,也是用來補償邊沿階段誤差的,它的時間長度在重新同步時可以縮短。 PBS2 段的初始大小可以為 2~8Tq。
(對于PBS段而言,當信号邊沿不能被包含于 SS 段中時,可在此段進行補償,以及可以吸收時鐘誤差)
SJW (reSynchronization Jump Width):重新同步補償寬度,即在重新同步的時候,PBS1 和 PBS2 段的允許加長或縮短的時間長度,SJW 加大後允許誤差加大,但通信速度下降。SJW 為補償此誤差的最大值(即每一次誤差補償都不能超過這個值,1~4Tq)。
CAN 的同步分為硬同步和重新同步:
-
:在幀起始信号時同步總線上所有器件的位時序,無法確定後續一連串的位時序都是同步的。硬同步
-
:在檢測到總線上的時序與節點使用的時序有相位差時(即總線上的跳變沿不在節點時序的 SS 段範圍),通過延長 PBS1 段或縮短 PBS2 段,來獲得同步。重新同步
采樣點: 讀取總線電平的時刻,并将讀到的電平作為位值的點。位置在 PBS1 結束處。
延長/縮短PBS段來達到同步: PTS+PBS1小而PBS2加大時采樣點前移,PTS+PBS1大而PBS2減小時采樣點後移。
同步過程:
在硬同步階段,當節點檢測到本身SS段并不在總線電平下降沿跳變處,節點則會把自己的位時序中的 SS 段平移至總線出現下降沿的部分,後面三段也跟着上去,以獲得同步。(可以了解為節點在檢測到幀起始信号時才開始“設定段”)
在重新同步階段,利用普通資料位的高至低電平的跳變沿來同步(幀起始信号是特殊的跳變沿)。重新同步與硬同步方式相似的地方是它們都使用 SS 段來進行檢測,同步的目的都是使節點内的 SS 段把跳變沿包含起來。重新同步的方式分為超前和滞後兩種情況,以總線跳變沿與 SS 段的相對位置進行區分,下面舉例設SJW為2Tq。
① 相位超前,節點從總線的邊沿跳變中,檢測到它内部的時序比總線的時序相對超前 2Tq,這時控制器在下一個位時序中的 PBS1 段增加 2Tq 的時間長度,使得節點與總線時序重新同步。
② 相位相位滞後,節點從總線的邊沿跳變中,檢測到它的時序比總線的時序相對滞後 2Tq,這時控制器在前一個位時序中的 PBS2 段減少 2Tq 的時間長度,獲得同步。
了解上面重新同步過程時,需要知道前一次SS到下一次SS之間的長度是可伸展的(暫時稱之為 L ),當檢測到前一次SS出現得太快(還沒等到下降沿到來),這就是相位超前了,如果不把 L 縮短一點,那麼下一次SS将也會超前。縮短 L 的方法就是調整 PBS 的長度,這個過程由CAN控制器完成,即(新的PBS長度 = 目前 PBS長度-SJW)。
2.2.5 一次資料傳輸的例子
比如總線上有3個節點,節點1設定ID為000100 00110,節點2設定ID為000100 00111,節點3驗收濾波ID表中有節點1和節點2的ID号,節點1和節點2同時向節點3發送1位元組的資訊。
- 總線空閑,節點1和節點2同時發送幀起始信号,3個節點同時調整位時序(硬同步);
- 節點1和節點2開始仲裁,兩者同時向總線發送第一位0,同時回讀總線狀态與本身狀态相與,得0,兩者第1位仲裁均通過;一直持續到第9位1,兩者同時向總線發送1,同時回讀總線狀态,得1,兩者第9位仲裁均通過;
- 直到第11位,當兩個節點回讀總線狀态與本身狀态相與時,總線得顯性将隐性屏蔽,即總線狀态為顯性,則節點1得0(與本身狀态相同),而節點2得0(與本身狀态不同),此時節點1仲裁勝利,節點2放棄發送請求;
- 從第1位仲裁到第11位仲裁得同時,節點1向其他節點廣播了本身的ID,當然節點1本身也接收到節點2的ID資訊,是以節點2和節點3也都收到了節點1的ID資訊,隻不過節點2對節點1不敢興趣,因而選擇了忽略節點1後續的資訊,節點3則開始接收節點1的資料;
- 從硬同步之後,每當節點1和節點2發出一個仲裁位,三個節點的CAN控制器都在檢測本身的位時序與總線位時序是否一緻,當有相位超前或者滞後時則自動進行位時序的重新同步。在後續的封包傳送中亦是如此。
3. CAN總線協定層的詳細介紹
CAN 協定經 ISO 标準化後有 ISO11898 标準和 ISO11519-2 标準兩種。ISO11898 和 ISO11519-2 标準對于資料鍊路層的定義相同,但實體層不同。
在閱讀完上文後,對CAN總線中的一個極其重要的知識點需要搞明白:回讀機制。
指的是節點在向總線上發送封包的過程中,同時也對總線上的二進制位進行“回讀”。通過這種機制,節點就可以判斷出本節點發出的二進制位與總線上目前的二進制位是否一緻。
3.1 幀的種類
幀類型 | 幀用途 |
---|---|
資料幀 | 用于發送單元向接收單元傳送資料的幀。 |
遙控幀 | 用于接收單元向具有相同 ID 的發送單元請求資料的幀。 |
錯誤幀 | 用于當檢測出錯誤時向其它單元通知錯誤的幀。 |
過載幀 | 用于接收單元通知其尚未做好接收準備的幀。 |
幀間隔 | 用于将資料幀及遙控幀與前面的幀分離開來的幀 |
另外,資料幀和遙控幀有标準格式和擴充格式兩種格式。标準格式有 11 個位的辨別符(Identifier: 以下稱 ID),擴充格式有 29 個位的 ID。
3.1.1 資料幀
關于資料幀的标準格式各個位的介紹可以檢視本文的【2.2.2節】,在資料幀的拓展格式中,與标準格式不同處在于:
- 仲裁段為 29 位;
- 多出的SRR 位(Substitute Remote Request BIT),用于替代标準格式中的 RTR 位。SRR 位為隐性位,由于 RTR 在資料幀為顯性位,是以在兩個 ID 相同的标準格式封包與擴充格式封包中,标準格式的優先級較高;
- 控制段中的 r1 和 r0 一樣都為保留位,預設設定為顯性位;
- 擴充格式的 ID 有 29 個位。基本 ID 從 ID28 到 ID18,擴充 ID 由 ID17 到 ID0 表示。基本 ID 和标準格式的 ID 相同。禁止高 7 位都為隐性。(禁止設定:基本 ID=1111111XXXX)
3.1.2 遙控幀
接收單元向發送單元請求發送資料所用的幀。遙控幀由 6 個段組成。遙控幀沒有資料幀的資料段。舉個例子,車鑰匙需要知道車門的狀态,一個遠端幀過去,車門把自己的狀态發回來了。
遙控幀與資料幀的不同之處:
- 遙控幀的 RTR 位為隐性位,沒有資料段;
- 沒有資料段的資料幀和遙控幀可通過 RTR 位差別開來;
- 遙控幀的資料長度碼以所請求資料幀的資料長度碼表示;
3.1.3 錯誤幀
用于在接收和發送消息時檢測出錯誤通知錯誤的幀。錯誤幀由錯誤标志和錯誤界定符構成。
(1) 錯誤标志
錯誤标志包括主動錯誤标志和被動錯誤标志兩種。
① 主動錯誤标志:6 個位的顯性位。
② 被動錯誤标志:6 個位的隐性位。
(2) 錯誤界定符
錯誤界定符由 8 個位的隐性位構成。
(3)錯誤标志之後還有0~6個錯誤标志重疊部分
處于主動錯誤狀态的節點檢測到錯誤時會發送主動錯誤标志,6個連續顯性位會違反位填充規則和位場的固定形式,進而造成其它節點也檢測到錯誤并發送錯誤标志。所有節點所發送的顯性序列疊加組成錯誤标志重疊部分,錯誤标志重疊部分的長度在6-12個顯性位之間。
對主動錯誤和被動錯誤的通俗了解:
首先建議把廣泛使用的“主動錯誤”和“被動錯誤”概念換成“主動報錯”和“被動報錯”。
1.主動報錯站點
隻要檢查到錯誤,它立即“主動地”發出錯辨別。所謂“出錯辨別“,它本身就是一個“錯誤的位序列”(連續的6 個顯性位,不滿足CAN協定的“最多5個連續的同性位”要求),目的是“主動地”告訴大家:即使你們沒有發現“剛才我已發現”的錯誤,現在我“以身作則”出錯啦!你們該看到這個錯誤了吧!
2.被動報錯站點
如果檢查到錯誤,它隻能幹瞪眼“被動地”等别人(主動報錯站點)報錯,等待的時候它可不能去動總線,直到識别出由主動報錯站點發出的“錯誤的位序列”,它才松了一口氣:有人正式報錯了!然後他就可以去競争總線,該幹啥幹啥。
(錯誤幀這裡看不明白沒關系,後文會有詳細介紹)
3.1.4 過載幀
過載幀是接收節點向總線上其它節點報告自身接收能力達到極限的幀,可以了解為:接收節點Node_A接收封包的能力達到極限了,于是Node_A就會發出過載幀來告訴總線上的其它節點(包括發送節點),我接收節點Node_A已經沒有能力處理你們發來的封包了。過載幀由過載标志和過載界定符構成。
(1) 過載标志
6 個位的顯性位。
過載标志的構成與主動錯誤标志的構成相同。
(2) 過載界定符
8 個位的隐性位。
過載界定符的構成與錯誤界定符的構成相同。
3.1.5 幀間隔
幀間隔是用于分隔資料幀和遙控幀的幀。資料幀和遙控幀可通過插入幀間隔将本幀與前面的任何幀(資料幀、遙控幀、錯誤幀、過載幀)分開。過載幀和錯誤幀前不能插入幀間隔。
(1) 間隔
3 個位的隐性位。
(2) 總線空閑
隐性電平,無長度限制(0 亦可)。
本狀态下,可視為總線空閑,要發送的單元可開始通路總線。
(3) 延遲傳送(發送暫時停止)
8 個位的隐性位。
隻在處于被動錯誤狀态的單元剛發送一個消息後的幀間隔中包含的段。
3.2 位填充
為防止突發錯誤而設定,CAN協定中規定,當相同極性的電平持續五位時,則添加一個極性相反的位。填充位的添加和删除是由發送節點和接收節點完成的,CAN-BUS隻負責傳輸,不會操縱信号。
-
對于發送節點而言:
在發送資料幀和遙控幀時,對于SOF~CRC(除去CRC界定符) 之間的位流,相同極性的電平如果持續5位,那麼在下一個位插入一個與之前5位反型的電平;
-
對于接收節點而言:
在接收資料幀和遙控幀時,對于SOF~CRC(除去CRC界定符)之間的位流,相同極性的電平如果持續5位,那麼需要删除下一位再接收。如果這個第 6 個位的電平與前 5 位相同,将被視為錯誤并發送錯誤幀。
3.3 錯誤的種類
位錯誤(Bit Check Error):節點将自己發送到總線上的電平與同時從總線上回讀到的電平進行比較,如果發現二者不一緻,那麼這個節點就會檢測出一個位錯誤。
有三種例外情況不屬于位錯誤:
① 在仲裁區,節點向總線發送隐性位卻回讀到顯性位,不認為是位錯誤,這種情況表示該節點仲裁失敗;
② 在ACK槽,節點向總線發送隐性位卻回讀到顯性位,不認為是位錯誤,這種情況表示,該節點目前發送的這一幀封包至少被一個其它節點正确接收;
③ 該節點發送被動錯誤标志,節點Node_A向總線發送連續六個隐性位(被動錯誤标志)卻回讀到顯性位,不認為是位錯誤。因為被動錯誤标志是六個連續的隐性位,是以在總線上按照線與機制,有可能這六個連續隐性位被其它節點發送的顯性電平“吃掉”;
ACK錯誤(Acknowledgment Error):按照CAN協定的規定,在一幀封包(資料幀或者遙控幀)發出之後,如果接收節點Node_B成功接收了該幀封包,那麼接收節點Node_B就要在該幀封包ACK槽對應的時間段内向總線上發送一個顯性位來應答發送節點Node_A。這樣發送節點Node_A就會在ACK槽時間段内從總線上回讀到一個顯性位。
當發送節點Node_A在ACK槽時間段内沒有回讀到顯性位,那麼發送節點Node_A就會檢測到一個ACK應答錯誤。這表示沒有一個節點成功接收該幀封包。
填充錯誤(Fill Error):幀起始到CRC之間,接收節點檢測到有6個連續相同的位電平時,也就是違反5位相同位插入1位相反位的“位填充”原則。
CRC錯誤:發送節點Node_A在發送資料幀或者遙控幀時,會計算出該幀封包的CRC序列。接收節點Node_B在接收封包時也會執行相同的CRC算法,如果接收節點Node_B計算出的CRC序列值與發送節點Node_A發來的CRC序列值不一緻,那麼接收節點就檢測到一個CRC錯誤。
格式錯誤:在一幀封包發送時,如果在必須發送預定值的區域内檢測到了非法值,即與任何一種幀格式都不符;CAN封包中,有預定值的區域包括:
- 資料幀和遙控幀的CRC界定符、ACK界定符、EOF;
- 錯誤幀界定符
-
過載幀界定符
3.4 錯誤通知
在檢測到錯誤之後,檢測到錯誤的節點就要發送錯誤幀到總線上來通知總線上的其他節點。
3.4.1 節點錯誤狀态
對于錯誤界定,節點存在如下三種狀态:
- 主動錯誤: 錯誤标志由6個連續的顯性位組成(這種連續的6個顯性位與正常的填充位和其它幀固定格式不相同,正因為如此,硬體才容易差別)
- 被動錯誤 :被動錯誤标志由6個連續的隐性位組成,當發送結束後,處于被動錯誤狀态的節點在下一次再次發送時之前需要等待一些額外時間。
- 總線關閉: 處于總線關閉狀态的節點不允許發送和接收任何形式的幀封包。且隻能通過使用者請求進行恢複。
3.4.2 錯誤狀态的轉換
一個CAN節點在什麼情況下處于主動錯誤狀态,什麼情況下處于被動錯誤狀态呢?
在CAN節點内,有兩個計數器:發送錯誤計數器(TEC)和接收錯誤計數器(REC)
錯誤界定并非是依據錯誤的類型去界定CAN節點的錯誤狀态,而是依據錯誤計數器【TEC/REC】的值來界定CAN節點的錯誤狀态;當該節點檢測到錯誤後,内部REC/TEC計數器會相應的增加,基于REC/TEC的值判定節點狀态。
需要注意的是:這兩個計數器計得不是收發封包的數量,也不是收發錯誤幀的數量。TEC和RCE計數值的變化,是根據下表的規定來進行的。
節點錯誤狀态的轉換就是一個 “量變”到“質變” 的過程:
-
主動錯誤狀态:【REC<127 且TEC<127】
初步可判定該節點相對穩定可靠,該錯誤計數很可能是由于某個節點異常導緻的,那麼其他節點很可能也會觸發該錯誤,那麼允許該節點破壞CAN總線的異常封包并告知其他節點;
節點檢測到一個錯誤就會發送帶有主動錯誤标志的錯誤幀,因為主動錯誤标志是連續六個顯性位,是以這個時候主動錯誤标志将會“覆寫”掉總線上其它節點的發送,而之前在CAN總線上傳輸的封包就被這“六個連續顯性位”破壞掉了。
如果發出主動錯誤幀的節點是發送節點,這個情況下就相當于:剛剛發送的那一幀封包我發錯了,現在我破壞掉它(發送主動錯誤幀),你們不管收到什麼都不算數;
如果發出主動錯誤幀的節點是接收節點,這個情況就相當于:剛剛我收封包的時候發現了錯誤,不管你們有沒有發現這個錯誤,我現在主動站出來告訴大家這個錯誤,并把這一幀封包破壞掉(發送主動錯誤幀),剛才你們收到的東西不管對錯都不算數了。
-
被動錯誤狀态:【REC>128 或TEC>128】
節點發送錯誤幀的次數較多,初步可判定該節點相對不可靠,該錯誤計數很可能是由于自身節點問題導緻,即該錯誤很可能僅有該節點才有,對于其他節點而言是可以正常互動的,總線不信任該節點提供的錯誤辨別,将不允許破壞總線資料,那麼允許該節點發送錯誤幀“6個連續隐性位”至CAN總線,僅告知其他節點異常;
如果發出被動錯誤幀的節點為封包的發送節點,那麼在發送被動錯誤幀之後,剛剛正在發送的封包被破壞,并且該節點不能在錯誤幀之後随着連續發送剛剛發送失敗的那個封包。随之而來的是幀間隔,并且連帶着8位隐性位的 “延遲傳送” 段;這樣總線電平就呈現出連續11位隐性位,總線上的其它節點就能判定總線處于空閑狀态,就能參與總線競争。
此時如果該節點能夠競争成功,那麼它就能接着發送,如果競争不能成功,那麼就接着等待下一次競争。這種機制的目的正是為了讓其它正常節點(處于主動錯誤)優先使用總線。
-
總線關閉狀态:【TEC>255】
一個處于被動錯誤狀态的節點,仍然多次發送被動錯誤幀,使該節點轉為總線關閉态;
該節點不能向總線上發送封包,也不能從總線上接收封包,整個節點脫離總線。等到檢測到128次11個連續的隐性位時,TEC和REC置0,重新回到主動錯誤狀态。
由于存在實作方式的不同,CAN總線關閉狀态存在隻允許使用者請求恢複和檢測到128個11位連續的隐性位時自恢複兩種不同的恢複形式。
如果總線上隻有一個節點,該節點發送資料幀後得不到應答,TEC最大隻能計數到128,即這種情況下節點隻會進入被動錯誤狀态而不會進入總線關閉狀态。
3.4.3 錯誤幀的發送
按照CAN協定的規定:
發生位錯誤、填充錯誤、格式錯誤、ACK錯誤時,則在錯誤産生的那一位的下一位開始發送錯誤幀。
發生CRC錯誤時,緊随ACK界定符後的位發送錯誤幀。
錯誤幀發送完成後,總線空閑時自動重發出錯的資料幀。
3.4.4 仲裁機制與位時序
仲裁存在于當同時有多個節點競争總線發送權時,關鍵詞是同時,比如節點1和節點2兩者同時向總線發送一個位,是同時發送的,總線隻是導線(了解成自帶線與功能),如果節點1發送的是1,節點2發送的是0,線與後總線的狀态則為0;
與此同時,這裡運用到回讀機制,即:節點發送位的同時會回讀總線的狀态并與自身相比較,可知節點1本身狀态與總線狀态一緻,則節點1仲裁勝利。(可查閱本文【【2.2.3節】】)
了解位時序,重點在于同步,分為硬同步和重新同步,在重新同步中,通過設定SJW來調整PBS段的長度(PBS1增長,PBS2縮短),以達到調整一個位中Tq的個數,即長度。(可查閱本文【【2.2.4節】】)
關于波特率的計算:
總線上的各個通訊節點隻要約定好 1 個 Tq 的時間長度以及每一個資料位占據多少個Tq,就可以确定 CAN 通訊的波特率。例如,假設1Tq=1us,而每個資料位由 19 個 Tq 組成,則傳輸一位資料需要時間 T(1bit) =19us,進而每秒可以傳輸的資料位個數為:1x10^6/19 = 52631.6 (bps),這個每秒可傳輸的資料位的個數即為通訊中的波特率。
3.4.5 舉個栗子
【位錯誤】舉例(情況1):
-
設總線上所有節點處于主動錯誤狀态;
-
當一個發送節點監控到總線上的位數值與發送的位數值不一緻時,檢測為位錯誤,并發送主動錯誤标志(6個連續的顯性位);
-
接收節點接收到發送節點發送的6個連續的顯性位時,會檢測為位填充錯誤,也會發送主動錯誤标志;
-
發送節點發送完主動錯誤标志後,開始監控總線是否為隐性位,當總線為隐性位時,開始發送錯誤界定符(8個連續的隐性位);
-
當接收節點發送完主動錯誤标志後,開始向總線發送錯誤界定符; 等待錯誤幀發送完成,總線空閑後,發送節點重新發送出錯的封包.
由于發送節點發送6個連續的顯性位會破壞位填充規則,觸發接收節點發送主動錯誤标志,發送節點和接收節點的結合是形成錯誤标志疊加部分的原因。
【位錯誤】舉例(情況2):
-
假設發送節點處于被動錯誤狀态,接收節點處于主動錯誤狀态;
-
當發送節點監控到總線上的位數值與發送的位數值不一緻時,檢測為位錯誤,并發送被動錯誤标志(6個連續的隐性位);
-
接收節點接收到發送節點發送的6個連續的隐性位時,會檢測為位填充錯誤,并會發送主動錯誤标志;
-
發送節點發送完被動錯誤标志後,開始監控總線是否為隐性位,當總線為隐性位時,開始發送錯誤界定符(8個連續的隐性位);
-
接收節點發送完主動錯誤标志後,開始監控總線是否為隐性位,當總線為隐性位時,開始發送錯誤界定符(8個連續的隐性位);
3.5 CAN的特性總結
1) 多主要制
在總線空閑時,所有單元都可以發送消息(多主要制),而兩個以上的單元同時開始發送消息時,根據辨別符(Identifier 以下稱為 ID)決定優先級。ID 并不是表示發送的目的位址,而是表示通路總線的消息的優先級。兩個以上的單元同時開始發送消息時,對各消息 ID 的每個位進行逐個仲裁比較。仲裁獲勝(被判定為優先級最高)的單元可繼續發送消息,仲裁失利的單元則立刻停止發送而進行接收工作。
2) 系統的柔軟性
與總線相連的單元沒有類似于“位址”的資訊。是以在總線上增加單元時,連接配接在總線上的其它單元的軟硬體及應用層都不需要改變。
3) 通信速度較快,通信距離遠。最高 1Mbps(距離小于 40M),最遠可達 10KM(速率低于 5Kbps)。
4) 具有錯誤檢測、錯誤通知和錯誤恢複功能。所有單元都可以檢測錯誤(錯誤檢測功能),檢測出錯誤的單元會立即同時通知其他所有單元(錯誤通知功能),正在發送消息的單元一旦檢測出錯誤,會強制結束目前的發送。強制結束發送的單元會不斷反複地重新發送此消息直到成功發送為止(錯誤恢複功能)。
5) 故障封閉功能。CAN 可以判斷出錯誤的類型是總線上暫時的資料錯誤(如外部噪聲等)還是持續的資料錯誤(如單元内部故障、驅動器故障、斷線等)。由此功能,當總線上發生持續資料錯誤時,可将引起此故障的單元從總線上隔離出去。
6) 連接配接節點多。CAN 總線是可同時連接配接多個單元的總線。可連接配接的單元總數理論上是沒有限制的。但實際上可連接配接的單元數受總線上的時間延遲及電氣負載的限制。降低通信速度,可連接配接的單元數增加;提高通信速度,則可連接配接的單元數減少。
4. STM32中的CAN
STM32 的所有型号晶片中都具有 bxCAN 控制器 (Basic Extended CAN),即基本擴充 CAN,它支援 CAN 協定 2.0A(不支援擴充格式) 和2.0B 标準。
它的設計目标是,以最小的 CPU 負荷來高效處理大量收到的封包。它也支援封包發送的優先級要求(優先級特性可軟體配置)。對于安全緊要的應用,bxCAN 提供所有支援時間觸發通信模式所需的硬體功能。
4.1 bxCAN主要特性
- bxCAN 接口可以自動地接收和發送 CAN 封包,支援标準辨別符和擴充辨別符;
- 具有 3 個發送郵箱,發送封包的優先級可以使用軟體,可以記錄發送的時間;
- 有 2 個 3 級深度的接收 FIFO,可以使用過濾功能隻接收或不接收某些 ID号的封包;
- 可以配置成自動重發;
- 不支援使用 DMA 進行資料收發;
- 可變的過濾器組(最多 28 個)。
在 STM32 互聯型産品中,帶有 2 個 CAN 控制器,而常用的 STM32F103ZET6 屬于增強型,不是互聯型,隻有 1 個 CAN 控制器。
4.2 bxCAN的架構
雙 CAN 的框圖如下:
在雙CAN中,對于CAN1(主)來說,它擁有3個發送郵箱,2個FIFO,和CAN2共用28個濾波器組,但在STM32戰艦F1中,隻有14個濾波器組,每個濾波器組 x 由 2 個 32 為寄存器分别為CAN_FxR1 和 CAN_FxR2 組成。
-
Tx Mailboxes(發送郵箱)
STM32 的 CAN 中共有 3 個發送郵箱供軟體來發送封包。發送排程器根據優先級決定哪個郵箱的封包先被發送。
-
Accepttance Filters( 接收過濾器 )
STM32 的 CAN 中共有 14 個位寬可變/可配置的辨別符過濾器組,軟體通過對它們程式設計,進而在 CAN 收到的封包中選擇它需要的封包,而把其它封包丢棄掉。
-
Receive FIFO( 接收 FIFO )
STM32 的 CAN 中共有 2 個接收 FIFO,每個 FIFO 都可以存放 3 個完整的封包。它們完全由硬體來管理。
4.3 控制核心中的主要寄存器
CAN的三種模式:
-
工作模式:
①初始化模式(INRQ=1,SLEEP=0)
②正常模式(INRQ=0,SLEEP=0)
③睡眠模式(SLEEP-1)
-
測試模式
①靜默模式( LBKM=0, SILM=1 )
②環回模式( LBKM=1,SILM=0 )
③環回靜默模式(LBKM=1,SILM=1)
- 調試模式
4.3.1 主要制寄存器 (CAN_MCR)
負責管理CAN的工作模式(保留位由硬體強制為0)
寄存器位 | 說明 |
---|---|
位 16 | DBF: 調試當機 (Debug freeze) 0:在調試時,CAN照常工作 1:在調試時,當機CAN的接收/發送。仍然可以正常地讀寫和控制接收FIFO。 |
位15 | RESET: bxCAN 軟體複位 (bxCAN software master reset) 0:本外設正常工作; 1:對bxCAN進行強行複位,複位後bxCAN進入睡眠模式(FMP位和CAN_MCR寄存器被初始化為其複位值)。此後硬體自動對該位清’0’。 |
位7 | TTCM: 時間觸發通信模式 (Time triggered communication mode) 0:禁止時間觸發通信模式; 1:允許時間觸發通信模式。 |
位6 | ABOM: 自動離線(Bus-Off)管理 (Automatic bus-off management) 該位決定CAN硬體在什麼條件下可以退出離線狀态。 0:離線狀态的退出過程是,軟體對CAN_MCR寄存器的INRQ位進行置’1’随後清’0’後,一旦硬體檢測到128次11位連續的隐性位,則退出離線狀态; 1:一旦硬體檢測到128次11位連續的隐性位,則自動退出離線狀态。 |
位5 | AWUM: 自動喚醒模式 (Automatic wakeup mode) 該位決定CAN處在睡眠模式時由硬體還是軟體喚醒 0:睡眠模式通過清除CAN_MCR寄存器的SLEEP位,由軟體喚醒; 1:睡眠模式通過檢測CAN封包,由硬體自動喚醒。喚醒的同時,硬體自動對CAN_MSR寄存 器的SLEEP和SLAK位清’0’ 。 |
位4 | NART: 禁止封包自動重傳 (No automatic retransmission) 0:按照CAN标準,CAN硬體在發送封包失敗時會一直自動重傳直到發送成功; 1:CAN封包隻被發送1次,不管發送的結果如何(成功、出錯或仲裁丢失)。 |
位3 | RFLM: 接收FIFO鎖定模式 (Receive FIFO locked mode) 0:在接收溢出時FIFO未被鎖定,當接收FIFO的封包未被讀出,下一個收到的封包會覆寫原有的封包; 1:在接收溢出時FIFO被鎖定,當接收FIFO的封包未被讀出,下一個收到的封包會被丢棄。 |
位2 | TXFP: 發送FIFO優先級 (Transmit FIFO priority) 當有多個封包同時在等待發送時,該位決定這些封包的發送順序 0:優先級由封包的辨別符來決定; 1:優先級由發送請求的順序來決定。 |
位1 | SLEEP: 睡眠模式請求 (Sleep mode request) 軟體對該位置’1’可以請求CAN進入睡眠模式,一旦目前的CAN活動(發送或接收封包)結束,CAN就進入睡眠。 軟體對該位清’0’使CAN退出睡眠模式。當設定了AWUM位且在CAN Rx信号中檢測出SOF位時,硬體對該位清’0’。在複位後該位被置’1’,即CAN在複位後處于睡眠模式。 |
位0 | INRQ: 初始化請求 (Initialization request) 軟體對該位清’0’可使CAN從初始化模式進入正常工作模式:當CAN在接收引腳檢測到連續的11個隐性位後,CAN就達到同步,并為接收和發送資料作好準備了。為此,硬體相應地對CAN_MSR寄存器的INAK位清’0’。軟體對該位置1可使CAN從正常工作模式進入初始化模式:一旦目前的CAN活動(發送或接收)結束,CAN就進入初始化模式。相應地,硬體對CAN_MSR寄存器的INAK位置’1’。 |
- TTCM時間觸發模式下,CAN使用它内部定時器産生時間戳,并儲存在CAN_RDTxR或CAN_TDTxR寄存器中。
- AWUM配置可以讓節點在總線無資料傳輸時進入低功耗模式,當有封包過來得時候則自動喚醒。
- INRQ 位用來控制初始化請求,是以在 CAN 初始化的時候,先要設定該位為 1,然後進行初始化(尤其是 CAN_BTR的設定,該寄存器必須在 CAN 正常工作之前設定),之後再設定該位為 0,讓 CAN 進入正常工作模式。
- ABOM、RFLM、TXFP比較重要,需要認真看上面得說明。
4.3.2 位時序寄存器 (CAN_BTR)
用于設定分頻、Tbs1、Tbs2以及 Tsjw 等非常重要的參數,直接決定了 CAN 的波特率,還可以設定 CAN 的工作模式。(保留位由硬體強制為0)
寄存器位 | 說明 |
---|---|
位31 | SILM: 靜默模式(用于調試) (Silent mode (debug)) 0: 正常狀态; 1: 靜默模式。 |
位30 | LBKM: 環回模式(用于調試) (Loop back mode (debug)) 0: 禁止環回模式; 1: 允許環回模式。 |
位25:24 | SJW[1:0]: 重新同步跳躍寬度 (Resynchronization jump width),為了重新同步,該位域定義了CAN硬體在每位中可以延長或縮短多少個時間單元的上限。 tRJW = tCAN x (SJW[1:0] + 1)。 |
位22:20 | TS2[2:0]: 時間段2 (Time segment 2),該位域定義了時間段2占用了多少個時間單元 tBS2 = tCAN x (TS2[2:0] + 1)。 |
位19:16 | TS1[3:0]: 時間段1 (Time segment 1),該位域定義了時間段1占用了多少個時間單元 tBS1 = tCAN x (TS1[3:0] + 1) |
位9:0 | BRP[9:0]: 波特率分頻器 (Baud rate prescaler),該位域定義了時間單元(tq)的時間長度 tq = (BRP[9:0]+1) x tPCLK |
了解其他模式之前,先看正常模式,此時SILM=0,LBKM=0,這種狀态時節點可以正常向節點發送和接收資料。
靜默模式下,節點的輸出端的邏輯0資料會直接傳輸到自己的輸入端,邏輯1可以被發送到總線,是以它不能向總線發送顯
性位(邏輯0), 隻能發送隐性位(邏輯1)。輸入端可以從總線接收内容。
由于它隻可發送的隐性位不會強制影響總線的狀态,是以把它稱為靜默模式。這種模式一般用于監測,它可以用于分析總線上的流量,但又不會因為發送顯性位而影響總線。
回環模式下,節點輸出端的所有内容都直接傳輸到自己的輸入端,輸出端的内容同時也會被傳輸到總線上,即也可使用總線監測它的發送内容。輸入端隻接收自己發送端的内容,不接收來自總線上的内容。使用回環模式可以進行自檢。
CAN 核心忽略确認錯誤(在資料/遠端幀的确認位時刻,不檢測是否有顯性位)。在環回模式下,bxCAN 在内部把 Tx 輸出回饋到 Rx 輸入上,而完全忽略 CANRX 引腳的實際狀态。發送的封包可以在 CANTX 引腳上檢測到。
回環靜默模式是以上兩種模式的結合,節點的輸出端的所有内容都直接傳輸到自己的輸入端,并且不會向總線發送顯性位影響總線,不能通過總線監測它的發送内容。輸入端隻接收自己發送端的内容,不接收來自總線上的内容。這種方式可以在“熱自檢”時使用,即自我檢查的時候,不會幹擾總線。
(回環模式下總線是可以接收到自檢節點的封包的,而回環靜默模式下,節點既不向總線發送資料也不接收資料)
4.3.3 bxCAN的位時序和波特率
與标準CAN中位時序有些許差異,bxCAN将PTS段與原有的PBS1段結合,并重新命名了,如下:
STM32的CAN外設位時序中隻包含3段,分别是同步段SYNC_ SEG、位段BS1及位段BS2,采樣點位于BS1及BS2段的交界處。其中SYNC_ SEG段固定長度為1Tq,而BS1及BS2段可以在位時序寄存器CAN_ BTR設定它們的時間長度,它們可以在重新同步期間增長或縮短,該長度SJW也可在位時序寄存器中配置。
通過前面CAN_BTR寄存器的介紹可知,計算BS1和BS2段的時間公式為:
//需要特别注意公式後的 +1
//比如要設定tBS1為3Tq,則寫入TS1[3:0]的值應為2
tBS1 = tCAN x (TS1[3:0] + 1)//TS1[3:0]
tBS2 = tCAN x (TS2[2:0] + 1)//TS2[2:0]
- 則一個資料位的時間為:
T(1bit) = 1Tq+Ts1+Ts2 = 1 + (TS1[3:0] + 1) + (TS2[2:0]+ 1) = N Tq
- 那麼如果要得出具體的時間長度,則隻要求出Tq的值即可。Tq的值由CAN_BTR中的BRP[9:0]設定,計算公式為:
需要特别注意公式後的 +1 //比如要設定預分頻系數為4,則寫入BRP[9:0]的值應為3 tq = (BRP[9:0]+1) x tPCLK
- 由時鐘樹可知,bxCAN挂載在APB1時鐘下,即tPCLK = APB1 = 36MHz。 最終可以計算出CAN通訊的波特率為:
BaudRate = 1/N Tq
- 下面是配置1Mbps波特率的例子
4.4 發送郵箱
一共有3個發送郵箱,即最多可以緩存3個待發送的封包。每個發送郵箱中包含有辨別符寄存器CAN_TIxR、資料長度控制寄存器CAN_TDTxR及2個資料寄存器CAN_TDLxR、CAN_TDHxR
① 發送郵箱辨別符寄存器 (CAN_TIxR) (x=0…2)
② 發送郵箱資料長度和時間戳寄存器 (CAN_TDTxR) (x=0…2)寄存器位 說明 位31:21 STID[10:0]/EXID[28:18]: 标準辨別符或擴充辨別符 (Standard identifier or extended identifier)
依據IDE位的内容,這些位或是标準辨別符,或是擴充身份辨別的高位元組。
位20:3 EXID[17:0]: 擴充辨別符 (Extended identifier)
擴充身份辨別的低位元組。
位2 IDE: 辨別符選擇 (Identifier extension)
該位決定發送郵箱中封包使用的辨別符類型
0:使用标準辨別符;
1:使用擴充辨別符。
位1 RTR: 遠端發送請求 (Remote transmission request)
0:資料幀;
1:遠端幀。
位0 TXRQ: 發送資料請求 (Transmit mailbox request)
由軟體對其置’1’,來請求發送郵箱的資料。當資料發送完成,郵箱為空時,硬體對其清’0’。
③ 資料寄存器 CAN_TDLxR(低)和 CAN_TDHxR(高)寄存器位 說明 位31:16 TIME[15:0]: 封包時間戳 (Message time stamp)
該域包含了,在發送該封包SOF的時刻,16位定時器的值。
位8 TGT: 發送時間戳 (Transmit global time)
隻有在CAN處于時間觸發通信模式,即CAN_MCR寄存器的TTCM位為’1’時,該位才有效。
0:不發送時間戳TIME[15:0];
1:發送時間戳TIME[15:0]。在長度為8的封包中,時間戳TIME[15:0]是最後2個發送的位元組:TIME[7:0]作為第7個位元組,TIME[15:8]為第8個位元組,它們替換了寫入CAN_TDHxR[31:16]的資料( DATA6[7:0] 和DATA7[7:0])。為了把時間戳的2個位元組發送出去,DLC必須程式設計為8。
位3:0 DLC[15:0]: 發送資料長度 (Data length code)
該域指定了資料封包的資料長度或者遠端幀請求的資料長度。1個封包包含0到8個位元組資料,而這由DLC決定。
- 如果CAN_MCR寄存器的TTCM位為’1’,且該郵箱的TGT位也為’1’,那麼DATA7和DATA6将被TIME時間戳代替。
通過以上的介紹可以知道,資料打包時各個資料位并不是和描述協定時那樣一位緊跟着一位,當要使用CAN外設發送封包時,把封包的各個段分解,按位置寫入到這些寄存器中,同時對辨別符寄存器CAN_TIxR中的發送請求寄存器位TMIDxR_TXRQ置1,即可把資料發送出去。
其中辨別符寄存器CAN_TIxR中 的STID寄存器位比較特别。CAN的标準辨別符的總位數為11位,而擴充辨別符的總位數為29位的。當封包使用擴充辨別符的時候,辨別符寄存器CAN_TlxR中 STID[10:0]等效于EXTID[18:28]位,它與EXTID[17:0]共同組成完整的29位擴充辨別符。
4.5 接收FIFO
一共有2個接收FIFO,每個FIFO中有3個郵箱,即最多可以緩存6個接收到的封包。當接收到封包時,FIFO的封包計數器會自增,而STM32内部讀取FIFO資料之後,封包計數器會自減,通過狀态寄存器可獲知封包計數器的值,而通過前面主要制寄存器的RFLM位,可設定鎖定模式,鎖定模式下FIFO溢出時會丢棄新封包,非鎖定模式下FIFO溢出時新封包會覆寫舊封包。
每個接收FIFO中包含有辨別符寄存器CAN_RIxR、 資料長度控制寄存器CAN_RDTxR及2個資料寄存器CAN_RDLxR、 CAN_RDHxR
① 接收FIFO郵箱辨別符寄存器 (CAN_RIxR) (x=0…1)
② 接收FIFO郵箱資料長度和時間戳寄存器 (CAN_RDTxR)寄存器位 說明 位31:21 STID[10:0]/EXID[28:18]: 标準辨別符或擴充辨別符 (Standard identifier or extended identifier)
依據IDE位的内容,這些位或是标準辨別符,或是擴充身份辨別的高位元組。
位20:3 EXID[17:0]: 擴充辨別符 (Extended identifier)
擴充辨別符的低位元組。
位2 IDE: 辨別符選擇 (Identifier extension)
該位決定接收郵箱中封包使用的辨別符類型
0:使用标準辨別符;
1:使用擴充辨別符。
位1 RTR: 遠端發送請求 (Remote transmission request)
0:資料幀;
1:遠端幀。
③ 資料寄存器 CAN_RDLxR(低)和 CAN_RDHxR(高)寄存器位 說明 位31:16 TIME[15:0]: 封包時間戳 (Message time stamp)
該域包含了,在接收該封包SOF的時刻,16位定時器的值。
位15:8 FMI[15:0]: 過濾器比對序号 (Filter match index)
這裡是存在郵箱中的資訊傳送的過濾器序号。
位3:0 DLC[15:0]: 接收資料長度 (Data length code)
該域表明接收資料幀的資料長度(0~8)。對于遠端幀請求,資料長度DLC恒為0。
CAN 接收到的有效封包,被存儲在 3 級郵箱深度的 FIFO 中。FIFO 完全由硬體來管理,進而節省了 CPU 的處理負荷,簡化了軟體并保證了資料的一緻性。
應用程式隻能通過讀取 FIFO輸出郵箱,來讀取 FIFO 中最先收到的封包。這裡的有效封包是指那些正确被接收的(直到EOF都沒有錯誤)且通過了辨別符過濾的封包。
所謂的3級深度,意思就是說每個FIFO由三個郵箱組成,你暫且可以将這三個郵箱一起看成一個具體三個成員的消息隊列,而此時的FIFO輸出郵箱就相當于這個隊尾的意思,你可以将它看成是一個指向隊尾的指針。
4.6 驗收篩選器
以雙CAN為例,一 共有28 個篩選器組,每個篩選器組有2個寄存器,CAN1和CAN2是共用的篩選器的。
在CAN協定中,消息的辨別符與節點位址無關,但與消息内容有關。是以,發送節點将封包廣播給所有接收器時,接收節點會根據封包辨別符的值來确定軟體是否需要該消息,為了簡化軟體的工作,STM32的CAN外設接收封包前會先使用驗收篩選器檢查,隻接收需要的封包到FIFO中。篩選器工作的時候,可以調整篩選ID的長度及過濾模式。
根據位寬的不同(ID長度不同),每個篩選器組可選擇:
- 1 個 31 位過濾器,包括:STID[10:0]、EXTID[17:0]、IDE 和 RTR 位
- 2 個 16 位過濾器,包括:STID[10:0]、IDE、RTR 和 EXTID[17:15]位
- FSCx=1為31位模式,FSCx=0為16位模式
- 辨別符清單模式:把要接收封包的ID列成一個表,要求封包ID與清單中的某一個辨別符完全相同才可以接收,可以了解為白名單管理。
- 掩碼模式(屏蔽位模式):把可接收封包ID的某幾位作為清單,這幾位被稱為掩碼,可以把它了解成關鍵字搜尋,隻要掩碼(關鍵字)相同,就符合要求,封包就會被儲存到接收FIFO。
- FBMx=0為掩碼模式,FBMx=1為清單模式
- 為了過濾出一組辨別符,應該設定過濾器組工作在屏蔽位模式。
- 為了過濾出一個辨別符,應該設定過濾器組工作在辨別符清單模式。
① 31位下掩碼模式:隻能過濾一個ID
不是一個篩選器組有兩個寄存器嗎?怎麼隻能過濾一個?是因為掩碼模式下,第一個寄存器(CAN_F0R1)用來存放要過濾的ID,第二個寄存器(CAN_F0R2)用來存放掩碼,所謂的掩碼其實就類似于一種标志位,如果這個掩碼位為1,則表示進來參與檢測的位必須和對應CAN_F0R1的這個位一樣才得以通過篩選,如果這個掩碼位為0,則表示不關心。
比如CAN_F0R1=0xFFFF0000,CAN_F0R2=0xFF00FF00,要篩選的信号用映像表示,則映像的ID需要為0xFF##00##才能通過篩選。(關注掩碼的值為1位,#表示不關心,即不管是0是1都可)
② 31位下清單模式:可以過濾兩個ID
CAN_F0R1和CAN_F0R2都用來存放需要篩選的ID,此時隻接收ID為這倆的封包,比如CAN_F0R1=0xFFFF0000,CAN_F0R2=0xFFFF0001,則映像的ID為0xFFFF0000或0xFFFF0001時能篩選通過。
③ 16位下掩碼模式:可過濾兩個ID
原理就是把CAN_F0R1和CAN_F0R2分别一分為2,更多的介紹在下文
③ 16位下清單模式:可過濾四個ID
原理也是把CAN_F0R1和CAN_F0R2分别一分為2,更多的介紹在下文
4.6.1 CAN過濾器寄存器
① 主要寄存器 (CAN_FMR) ② 模式寄存器 (CAN_FM1R) ③ 位寬寄存器 (CAN_FS1R) ④ FIFO關聯寄存器 (CAN_FFA1R) ⑤ 激活寄存器 (CAN_FA1R) ⑥ CAN 過濾器組i的寄存器x (CAN_FiRx)4.6.2 配置篩選器工作模式
當篩選器組寄存器長度為31位時,可為其配置辨別符清單模式或者掩碼模式共兩種;相同,當篩選器組寄存器長度為16位時,此時也有2種配置方式。總共4種。假如對篩選組0進行配置,則 n=0,需要在FIFO關聯寄存器CAN_FFA1R寄存器中将 FFA0 為清零,關聯篩選器組0;第一步,将位寬寄存器CAN_FS1R寄存器中 FSC0 位置1,即選擇篩選器組0為32位位寬模式;第二步,在模式寄存器 CAN_FM1R中将FBM0清零,即選擇辨別符屏蔽模式(掩碼模式);第三步類似,将FBM0位置1則表示使用辨別符清單模式。
繼續上面得情況,當處于掩碼模式時
第一個寄存器存儲要篩選的ID,第二個寄存器存儲掩碼,掩碼為1的部分表示該位必須與ID中的内容一緻,篩選的結果為表中第三行的ID值,它是一組包含多個的ID值,其中x表示該位可以為1可以為0。
在辨別符清單模式時,則兩個寄存器都存儲 ID 号,隻有每一位均一緻才能篩選通過并接收封包存到FIFO中。
16位清單模式下,篩選的ID可放在CAN_F0R1[15:0]、CAN_F0R1[31:16]、CAN_F0R2[15:0]、CAN_F0R2[31:16];16位掩碼模式下,篩選的ID可放在CAN_F0R1[15:0]、CAN_F0R2[15:0],與之對應的掩碼分别放在CAN_F0R1[31:16]、CAN_F0R2[31:16]。
舉個簡單的例子,我們設定過濾器組 0 工作在:1 個 32 為位過濾器-辨別符屏蔽模式,然後設定CAN 過濾器組0的寄存器1 即CAN_F0R1=0XFFFF0000,CAN 過濾器組0的寄存器2 即CAN_F0R2=0XFF00FF00。其中存放到 CAN_F0R1 的值就是期望收到的 ID,即我們希望收到的映像(STID+EXTID+IDE+RTR)最好是:0XFFFF0000。
而0XFF00FF00 就是設定我們需要必須關心的 ID,表示收到的映像,其位[31:24]和位[15:8]這 16個位的必須和 CAN_F0R1 中對應的位一模一樣,而另外的 16 個位則不關心,可以一樣,也可以不一樣,都認為是正确的 ID,即收到的映像必須是 0XFFxx00xx,才算是正确的(x 表示不關心)。
5. ST庫函數實作CAN通信
5.1 CAN控制相關的結構體
5.1.1 初始化結構體
CAN_InitTypeDef用于規定CAN的工作方式、通信波特率、每個位多長等這些
- 下面是配置1Mbps波特率的例子
- 由時鐘樹可知,bxCAN挂載在APB1時鐘下,即tPCLK = APB1 = 36MHz。 最終可以計算出CAN通訊的波特率為:
- 那麼如果要得出具體的時間長度,則隻要求出Tq的值即可。Tq的值由CAN_BTR中的BRP[9:0]設定,計算公式為:
//CAN初始化函數
//STM32F103系列隻有CAN1,是以第一個參數是CAN1
//第二個參數則是CAN_InitTypeDef
uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct);
typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
typedef struct {
uint16_t CAN_Prescaler; /*配置CAN外設的時鐘分頻,可設定為1-1024*/
uint8_t CAN_Mode;/*配置CAN的工作模式,回環或正常模式*/
uint8_t CAN_SJW;/*配置SJW極限值*/
uint8_t CAN_BS1;/*配置BS1段長度*/
uint8_t CAN_BS2;/*配置BS2段長度*/
Functionalstate CAN_TTCM; /* 是否使能TTCM時間觸發功能*/
Functionalstate CAN_ABOM;/* 是否使能ABOM自動離線管理功能*/
Functionalstate CAN_AWUM; /* 是否使能AWUM自動喚醒功能*/
FunctionalState CAN_NART;/*是否使能NART自動重傳功能*/
FunctionalState CAN_RFLM; /*是 否使能RFLM鎖定FIFO功能*/
FunctionalState CAN_TXFP; /*配置 TXFP封包優先級的判定方法*/
} CAN_InitTypeDef;
CAN_Prescaler
:時鐘分頻,用來确定CAN通信的波特率及控制時間片Tq的長度,隻能從1-1024,不能為0,因為這個值會先被減1後才寫入寄存器,計算的時候則再會被加1,是以如果要設定分頻為4的話,則需要将CAN_Prescaler指派為4。
CAN_Mode
:用于設定CAN的工作模式,可設定為正常模式(CAN_Mode_Normal)、 回環模式(CAN_Mode_LoopBack)、 靜默模式(CAN_Mode_Silent)以及回環靜默模式(CAN_Mode_Silent_LoopBack)。
CAN_SJW、CAN_BS1和CAN_BS2
:寫入時直接使用ST提供的宏即可
5.1.2 發送及接收結構體
CanTxMsg 及CanRxMsg在發送與接收封包的時候使用到
//CAN發送封包的函數,第一個參數為CAN1
//第二個參數CanTxMsg
//傳回值代表發送郵箱的編号,即0,1,2
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);
//不能一下子就能發送成功,可以檢測
//将CAN_Transmit傳回的郵箱編号放進以下函數檢視發送狀态
//傳回值為0:代表發送失敗,1:代表成功
uint8_t CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox);
typedef struct {
uint32_t stdId; /*存儲封包的标準辨別符11位,0-0x7FF. */
uint32_t ExtId; /*存儲封包的擴充辨別符29位,0-0x1FFFFF. */
uint8_t IDE; /*存儲IDE擴充标志*/
uint8_t RTR; /*存儲RTR遠端幀标志*/
uint8_t DLC; /*存儲封包資料段的長度,0-8 */
uint8_t Data[8]; /*存儲封包資料段的内容*/
} CanTxMsg;
//用于接收封包
//參數FIFONumber即0或者1,至于是哪個,可使用CAN_GetFlagStatus函數進行查詢
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);
typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
FlagStatus CAN_GetFlagStatus(CAN_TypeDef* CANx, uint32_t CAN_FLAG)
typedef struct{
uint32_t stdId; /*存儲封包的标準辨別符11位,0-0x7FF. */
uint32_t ExtId; /*存儲封包的擴充辨別符29位,0-0x1FFFFF. */
uint8_t IDE; /*存儲IDE擴充标志*/
uint8_t RTR; /*存儲RTR遠端幀标志*/
uint8_t DLC; /*存儲封包資料段的長度,0-8 */
uint8_t Data[8]; /*存儲封包資料段的内容*/
uint8_t FMI; /*存儲了本封包是由經過篩選器存儲進FIFO的(即篩選器的編号),0-0xFF */
} CanRxMsg;
IDE
:通過軟體方式确定封包是标準格式還是擴充格式,如果是标準格式,僅需将stdId的值寫入相關寄存器,如果是擴充格式,則一次性将ExtId寫入相關寄存器即可。(相關寄存器即:發送郵箱辨別符寄存器 CAN_TIxR)
FMI
:隻存在于接收結構體,它存儲了篩選器的編号,表示本封包是經過哪個篩選器存儲進接收FIFO的,可以用它簡化軟體處理。
5.1.3 篩選器結構體
CAN_ FilterlnitTypeDef用于配置篩選器的過濾規則
//初始化篩選器,具體是哪個篩選器組,其包含在結構體中
void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);
typedef struct {
uint16_t CAN_FilterIdHigh;/*CAN_FxR1 寄存器的高16位*/
uint16_t CAN_Filter_IdLow;/*CAN_FxR1寄存器的低16位*/
uint16_t CAN_FilterMaskIdHigh;/*CAN_FxR2 寄存器的高16位*/
uint16_t CAN_FilterMaskIdLow;/*CAN_FxR2 寄存器的低16位*/
uint16_t CAN_FilterFIFOAssignment; /*設定經過篩選後資料存儲到哪個接收FIFO*/
uint8_t CAN_FilterNumber;/*篩選器編号,範圍0-27*/
uint8_t CAN_FilterMode;/*篩選器模式*/
uint8_t CAN_FilterScale;/*設定篩選器的尺度*/
FunctionalState CAN_FilterActivation; /*是否使能本篩選器*/
} CAN_FilterInitTypeDef;
CAN_FilterldHigh
:用于存儲要篩選的ID,若篩選器工作在32位模式,它存儲的是所篩選ID的高16位;若篩選器工作在16位模式,它存儲的就是一個完整的要篩選的ID。
CAN_FilterldLow
:也是用于存儲要篩選的ID,若篩選器工作在32位模式,它存儲的是所篩選ID的低16位;若篩選器工作在16位模式,它存儲的就是一個完整的要篩選的ID。
CAN_FilterMaskldHigh
:存儲的内容分兩種情況,當篩選器工作在辨別符清單模式時,它的功能與CAN_FilterldHigh相同,都是存儲要篩選的ID;而當篩選器工作在掩碼模式時,它存儲的是CAN_FilterldHigh成員對應的掩碼,與CAN_FilterldHigh組成一組篩選器。
CAN_FilterMaskldLow
:存儲的内容也分兩種情況,當篩選器工作在辨別符清單模式時,它的功能與CAN_FilterldLow相同, 都是存儲要篩選的ID;而當篩選器工作在掩碼模式時,它存儲的是CAN_FilterldLow成員對應的掩碼,與CAN_FilterldLow組成一組篩選器。
模式 | FilterldHigh | FilterldLow | FilterMaskldHigh | FilterMaskldLow |
---|---|---|---|---|
32位清單模式 | ID1的高16位 | ID1的低16位 | ID2的高16位 | ID2的低16位 |
16位清單模式 | ID1的完整數值 | ID2的完整數值 | ID3的完整數值 | ID4的完整數值 |
32位掩碼模式 | ID1的高16位 | ID1的低16位 | ID1掩碼的高16位 | ID1掩碼的低16位 |
16位掩碼模式 | ID1的完整數值 | ID2的完整數值 | ID1掩碼的完整數值 | ID2掩碼的完整數值 |
通過前面的學習可以知道,比如CAN_F0R1最小可被分成16位,則可以用16位為一個機關表示其他模式下的狀态,比如當為32位模式時,則用高低兩個機關表示,從軟體程式設計方面優化了篩選器組的配置。
以上可以了解為CAN_F0R1分成CAN_FilterldHigh和CAN_FilterldLow兩個機關,而對于CAN_FilterMaskldHigh和CAN_FilterMaskldLow則是由CAN_F0R2而來,它們在掩碼模式下時存放掩碼,在清單模式時存放ID。
比如對于32位清單模式的配置:
已知這種情況下可篩選2個ID,第一個ID有32位,分别由CAN_FilterldHigh和CAN_FilterldLow存放,第二個ID也是32位,分别由CAN_FilterMaskldHigh和CAN_FilterMaskldLow存放,是以說此時CAN_FilterMaskldHigh和CAN_FilterldHigh功能相同。
比如對于16位掩碼模式的配置:
已知這種情況下可篩選2個ID,對于ID1,由CAN_FilterldHigh存放,則由CAN_FilterMaskldHigh存放ID1對應的掩碼;對于ID2,由CAN_FilterldLow,則由CAN_FilterMaskldLow存放,即:CAN_FilterldHigh和CAN_FilterMaskldHigh組成一組篩選器。
CAN_FilterFIFOAssignment
:用于設定當封包通過篩選器的比對後,該封包會被存儲到哪一個接收FIFO,它的可選值為FIFO0或FIFO1(宏CAN_Filter_FIFO0/1)。
5.2 硬體部分準備
使用回環模式,實作機制為CAN_TX發送的資料并不經過TJA1050,而是直接發送到CAN_RX引腳;發送前和正常模式一樣需要打包資料成封包格式,再進行發送,接收時也是按照正常模式下一樣接收,篩選、解析資料、讀取封包的過程一個也沒少。
5.3 代碼實作回環模式
使用回環模式,節點輸出端的所有内容都直接傳輸到自己的輸入端,輸出端的内容同時也會被傳輸到總線上,即:也可使用總線監測它的發送内容。輸入端隻接收自己發送端的内容,不接收來自總線上的内容。即:bxCAN 在内部把 Tx 輸出回饋到 Rx 輸入上,而完全忽略 CAN_RX 引腳的實際狀态。
CAN_TX發送的資料經過TJA1050到了總線,可以在CAN_H和CAN_L檢測到信号。發送前和正常模式下一樣需要對資料進行打包成封包格式,再進行發送,接收時和正常模式下也一樣,需要經過篩選、解析、讀取資料。
對CAN的配置初始化
使能 CAN 時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1時鐘
設定 PA11 為上拉輸入(CAN_RX 引腳)PA12 為複用輸出(CAN_TX 引腳)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推挽
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉輸入
設定 CAN 工作模式及波特率等
//tsjw:重新同步跳躍時間單元.範圍:CAN_SJW_1tq~ CAN_SJW_4tq
//tbs2:時間段2的時間單元. 範圍:CAN_BS2_1tq~CAN_BS2_8tq;
//tbs1:時間段1的時間單元. 範圍:CAN_BS1_1tq ~CAN_BS1_16tq
//brp :波特率分頻器.範圍:1~1024; tq=(brp)*tpclk1
//波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);
//Fpclk1的時鐘在初始化的時候設定為36M,如果分别設定:
//CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,分頻為4
//則波特率為:36M/((8+9+1)*4)=500Kbps
CAN_InitTypeDef CAN_InitStructure;
//CAN單元設定
CAN_InitStructure.CAN_TTCM=DISABLE; //非時間觸發通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //軟體自動離線管理
CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通過軟體喚醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART=ENABLE; //禁止封包自動傳送
CAN_InitStructure.CAN_RFLM=DISABLE; //封包不鎖定,新的覆寫舊的
CAN_InitStructure.CAN_TXFP=DISABLE; //優先級由封包辨別符決定
CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack;//模式設定: mode:0,普通模式;1,回環模式;
//設定波特率
CAN_InitStructure.CAN_SJW=tsjw; //重新同步跳躍寬度(Tsjw)為tsjw+1個時間機關 CAN_SJW_1tq CAN_SJW_2tq CAN_SJW_3tq CAN_SJW_4tq
CAN_InitStructure.CAN_BS1=tbs1; //Tbs1=tbs1+1個時間機關CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2=tbs2; //Tbs2=tbs2+1個時間機關CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler=brp; //分頻系數(Fdiv)為brp+1
CAN_Init(CAN1, &CAN_InitStructure); //初始化CAN1
//在CAN_Init函數t裡,比較重要的是:
/*在初始化之前,會設定 CAN_MCR 寄存器的 INRQ 為 1 讓其進入初始化模式,然後
初始化 CAN_MCR 寄存器和 CRN_BTR 寄存器之後,會設定 CAN_MCR 寄存器的 INRQ 為 0
讓其退出初始化模式。*/
設定濾波器
//使用篩選器組0,32位掩碼模式,即可過濾一個ID
//ID号存放在CAN_F0R1中,即CAN_FilterIdHigh和CAN_FilterIdLow分别存高低16位
//掩碼存放在CAN_F0R2中,即CAN_FilterMaskIdHigh和CAN_FilterMaskIdLow分别存高低16位
//掩碼全都為0時,則表明不過濾任何ID,接收總線上的所有封包
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber=0; //過濾器0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //屏蔽位(掩碼)模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位寬
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //32位ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//過濾器0關聯到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//激活過濾器0
CAN_FilterInit(&CAN_FilterInitStructure); //濾波器初始化
//在CAN_FilterInit函數體中:
/*在初始化之前,會設定 CAN_FMR 寄存器的 INRQ 為 INIT 讓其進入初始化模式,
然後初始化 CAN 濾波器相關的寄存器之後,會設定 CAN_FMR 寄存器的 FINIT 為 0 讓其退出
初始化模式。*/
發送消息的實作
//can發送一組資料(固定格式:ID為0X12,标準幀,資料幀)
//len:資料長度(最大為8)
//msg:資料指針,最大為8個位元組.
//傳回值:0,成功;
//其他,失敗;
u8 Can_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=0x12; // 标準辨別符
TxMessage.ExtId=0x12; // 設定擴充标示符
TxMessage.IDE=CAN_Id_Standard; // 标準幀
TxMessage.RTR=CAN_RTR_Data; // 資料幀
TxMessage.DLC=len; // 要發送的資料長度
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i];
mbox = CAN_Transmit(CAN1, &TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待發送結束
if(i>=0XFFF)return 1;
return 0;
}
接收消息的實作
//can口接收資料查詢
//buf:資料緩存區;
//傳回值:0,無資料被收到;
// 其他,接收的資料長度;
u8 Can_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0;//沒有接收到資料,直接退出
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//讀取資料
for(i=0;i<8;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC; //傳回資料長度
}
主函數的實作邏輯
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "can.h"
int main(void)
{
u8 key;
u8 i=0,t=0;
u8 cnt=0;
u8 canbuf[8];
u8 res;
u8 mode=CAN_Mode_LoopBack;//CAN工作模式;CAN_Mode_Normal(0):普通模式,CAN_Mode_LoopBack(1):環回模式
delay_init(); //延時函數初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設定中斷優先級分組為組2:2位搶占優先級,2位響應優先級
uart_init(115200); //序列槽初始化為115200
LED_Init(); //初始化與LED連接配接的硬體接口
KEY_Init(); //按鍵初始化
CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);//CAN初始化環回模式,波特率500Kbps
while(1)
{
key=KEY_Scan(0);
if(key==KEY0_PRES)//KEY0按下,發送一次資料
{
printf("發送資料: ");
for(i=0;i<8;i++)
{
canbuf[i]=cnt+i;//填充發送緩沖區
printf("%d\t",canbuf[i]);
}printf("\r\n\r\n");
res=Can_Send_Msg(canbuf,8);//發送8個位元組
if(res) printf("發送失敗!\r\n\r\n"); //提示發送失敗
else printf("發送成功!\r\n\r\n"); //提示發送成功
}
key = Can_Receive_Msg(canbuf); //不斷讀取FIFO中是否有新資料
if(key) //接收到有資料
{
printf("接收到資料: ");
for(i=0;i<key;i++)
{
printf("%d\t",canbuf[i]);
}printf("\r\n\r\n");
}
t++;
delay_ms(10);
if(t==20)
{
LED0=!LED0;//提示系統正在運作
t=0;
cnt++;
}
}
}
運作結果
5.4 實驗總結
從上面代碼來看,可見在篩選器部分的配置很簡單,确定好篩選模式後就能自動過濾封包,實驗裡過濾的 ID 為0000 0000,但掩碼也全都是0,則說明不管映像ID中是啥都可以,也就是接收總線上所有的封包;當需要設定過濾ID為0x12時,則隻需要将掩碼改成0X11,表明隻對高8位感興趣,進來參與篩選的前8位必須和ID的前8位0x12一緻才接收。
當使用兩塊及以上的STM32開發闆相連通信時,總線隻要保留一組比對電阻即可,其他單元的比對電阻需要斷開,并聯到總線上即可。(因為STM32開發闆上都配備了比對電阻)
感謝閱讀,本文是我(純小白)從頭開始學習CAN通信時做的筆記記錄,一邊學習了解一邊做筆記,記錄了我整個的學習過程,感謝網際網路,感謝各路大神在網上分享的知識,讓我稍微知道了CAN通信是個什麼玩意兒。
本文隻記錄了CAN的基礎知識,我個人的了解也未必全都是正确的,是以希望對那些看到本篇文章的朋友能有參考性的幫助(對小白很友好,從頭開始看,就能漸漸了解後面晦澀難懂的專業詞彙),如有了解錯誤之處,歡迎指出!
【參考】:
野火《零死角玩轉stm32》
正點原子《STM32F1開發指南-庫函數版本_V3.1 》
《CAN入門教程》《STM32中文參考手冊_V10》
https://blog.csdn.net/weixin_40528417/article/details/79476186
https://blog.csdn.net/weixin_40528417/article/details/79534483
https://www.cnblogs.com/luoxiao23/p/11210592.html
https://blog.csdn.net/u014156403/article/details/103725924
(吐槽一下正點原子關于這一節的視訊教程好敷衍 https://www.bilibili.com/video/BV1Lx411Z7Qa?p=55)
感謝!🙂
原文位址:https://blog.csdn.net/fengge2018/article/details/107592487