30.1實驗內容
通過本實驗主要學習以下內容:
30.2實驗原理
30.2.1CAN概述
CAN 是Controller Area Network的縮寫,是由德國BOSCH公司開發的,已成為ISO國際標準化的串行通信協議。其主要應用場合為汽車和工業控制。CAN具有傳輸距離長,傳輸可靠、強大的糾錯機制等特點,其高性能和可靠性已被廣泛認同,現在已經成為汽車、工業自動化、醫療設備等領域應用最廣泛的總線之一。
30.2.2CAN總線拓撲
CAN總線拓撲圖如下:
CAN 控制器根據兩根線上的電位差來判斷總線電平,一般將兩根線分別命名為CAN_H和CAN_L。總線電平分為顯性電平和隱性電平,二者必居其一。發送方通過使總線電平發生變化,將消息發送給接收方。 當CAN總線上的電位差為0V時,表示隱性電平,隱性電平代表邏輯“1”;當CAN總線上有電位差時(大概在2.5V左右),表示顯性電平,顯性電平代表邏輯“0”。總線空閑時,默認為隱性電平,即總線電位差為0。
關于電位差、隱性/顯性電平及邏輯電平,非常容易弄混,讀者需要熟記。 |
CAN總線的特點可以總結為:
- 多主
與USART-485這種一主多從類型總線不同,CAN總線是多主控制,即總線上沒有主機從機之分,所有設備都是處于平等的地位。
- 消息格式
CAN總線上的消息都以固定格式發送。當兩個以上的單元同時開始發送消息時,根據標識符(Identifier以下稱為ID)決定優先級。ID并不是表示發送的目的地址,而是表示訪問總線的消息的優先級。
- 通信速度快,通信距離遠
CAN最高可達1Mbps波特率,理論最遠距離可達10Km,當然此時通訊速率較低,只有5Kbps以下。
- 具有錯誤檢測、錯誤通知和錯誤恢復功能
CAN總線具有強大的錯誤檢測、通知和恢復功能。當一個單元發生錯誤時其他單元會進行報錯,正在發送的單元檢測到錯誤后,會立即強制結束當前發送,并嘗試重新發送(功能可配置),當發送錯誤的次數達到一定值后,該單元會自動從總線中退出,直到應用程序讓其重新加入總線為止。
- 半雙工異步通訊
CAN的總線的查分信號,決定了CAN總線實際為半雙工通訊,另外由于CAN總線沒有時鐘線,所以是異步通訊,故要求CAN總線上的所有單元的波特率都要設置一致。
30.2.3CAN幀的種類
CAN總共有如下五種類型的幀種類:
- 數據幀
用于數據傳輸的幀,也是最常用的一種幀種類
- 遙控幀
接收單元向具有相同ID的發送單元請求數據時所需要的幀種類
- 錯誤幀
一種非常重要的幀種類,錯誤幀是當總線上有錯誤時,檢測到錯誤的單元向其他單元通知錯誤的幀。
- 過載幀
用于接收單元通知其他單元其還沒有準備好的幀種類
- 幀間隔
用于幀和幀之間分離的幀
30.2.4CAN協議的解析
介紹了CAN的一些基本指示后,可能讀者還是不太明白幀ID是什么,CAN的發送和接收是怎么實現的,是否就像串口一樣發送數據就可以?實際上CAN需要遵循CAN協議,這樣每個CAN單元才可以準確無誤的發送和接收數據,CAN強大的錯誤檢測、錯誤通知等機制也是依托于標準CAN協議。下面以數據幀來解析下CAN的協議。
數據幀的格式如下圖,其中D表示顯性位,即邏輯“0”,R表示隱性位,即邏輯“1”,D/R表示隱性位或顯性位,另外下圖中的數字表示bit位數:
- 幀開始
每次CAN通訊都始于幀開始段,幀開始是1個bit位的顯性電平(邏輯“0”),由發送方發出,接收方檢測到一個bit邏輯“0”,開始準備接收數據。
- 仲裁段
好了,我們終于看到了幀ID的廬山真面目了。數據幀分為兩種——標準幀和擴展幀,標準幀的幀ID由11bit組成,擴展幀有11+18共29bit組成。需要注意,無論是標準幀ID還是擴展幀ID,都不允許設置最高7bit為1(即不允許幀ID=0b,1111111xxx··),以為幀結束段就是7個“1”組成。
沖裁段中的 RTR 位用于標識是否是遠程幀(0:數據幀;1:遠程幀),IDE位為標識符選擇位(0:使用標準標符;1:使用擴展標識符),SRR位為代替遠程請求位,為隱性位,它代替了標準幀中的RTR位。
我們需要先明確一個概念,CAN總線上的每個單元并不是只能發送固定幀ID,而是可以發送任意幀ID,幀ID不代表CAN設備號,代表的是當前數據幀/遠程幀的ID號。當一幀數據發送到總線后,所有接收方會對幀ID進行識別,當識別到是自己需要的ID時,則會將該幀數據收取到內部;而當識別到不是自己需要的ID時,則不會接收數據。
因為有可能出現兩個或更多CAN單元同時發送數據的情況,此時幀ID還起到仲裁的作用,各發送單元從幀ID的第一位開始進行仲裁。連續輸出顯性電平最多的單元可繼續發送,比如兩個CAN單元同時發送數據,其中單元一發出的數標準幀的ID為0b,00110000000,而單元二發出的標準幀ID為0b,00100000000,可以看到發送到第4位時,單元一發出的是邏輯“1”,單元二的是邏輯“0”,因為總線上“0”比“1”優先級搞,所以此時單元二獲得總線發送權,單元一將自動停止發送。
- 控制段
控制段由6個bit組成,其中DLC占用4個bit,表示要發送的字節數。
- 數據段
數據段即幀載有的有效數據了,CAN最多一次可發送8個字節(CANFD最多可以發送64字節,這個后面介紹帶CANFD功能的開發板時再細說),也就是說上面描述的DLC最大值為0b,1000。
- CRC段
CRC段總共有16bit,其中15個bit表示CRC值,另1個bit為CRC界定符。此段CRC的值計算范圍包括:幀起始、仲裁段、控制段、數據段。發送會根據發送的數據來計算出一個CRC值并通過CRC段發到總線,接收方以同樣的算法計算CRC值并和發送方發出的CRC段進行比較,當不一致時接收方會向總線報錯。
- ACK段
ACK段用來確認接收方是否正常接收。ACK段由ACK槽(ACK Slot)和ACK界定符2個位組成。
發送單元的 ACK,發送2個位的隱性位,而接收到正確消息的單元在ACK槽(ACK Slot)發送顯性位,通知發單元正常接收結束,這個過程叫發送ACK/返回ACK。發送ACK的是在既不處于總線關閉態也不處于休眠態的所有收單元中,接收到正常消息的單元(發送單元不發送ACK)。
- 幀結束
幀結束由7個bit的邏輯“1”組成,表示該幀結束。
30.2.5CAN的波特率
前面有提到CAN的波特率,波特率代表每秒鐘傳輸的位數(1位就表示1bit),我們來看下GD32F303的波特率是怎么計算的。
首先需要了解一個概念,CAN時序的最小單位是一個叫Tq的東西,CAN的每個位即每個bit是由若干個Tq組成的,那么這個Tq的長度是多少呢?GD32F303的CAN是掛載在APB1總線的:
如果讀者將GD32F303的主頻配置為最高120M,且將APB1的時鐘配置為最高60M的話,那么CAN的時鐘就是60M,然后CAN有個分頻系數,在位時序寄存器CAN_BT中:
一個Tq占用的時間計算公式:分頻系數/CAN時鐘,舉個例子,我們設置這個分頻系數為6的話,一個Tq的時間就是6/60M = 100us,也可以說Tq的傳輸速率為10M。那么由多少個Tq組成CAN的一個位呢?還是看CAN_BT寄存器:
寄存器中有BS1和BS2,這兩個位域用于設置位段1和位段2(關于位段后面會介紹),一個CAN位占用的Tq個數等于位段1+位段2+1,舉個例子,設置位段1為5(即BS1=4),設置位段2為4(即BS2=3),那么一個CAN位占用的Tq個數為5+4+1=10。好,現在就可以來算CAN的波特率了,按照CAN分頻系數為6,位段1為5,位段2為4,一個位占用的時間為6/60M*10 = 1ms,也就是波特率=1M。我們可以把這個計算轉化為公式:
30.2.6CAN的位時序和采樣點
我們現在來看下上一節提到的位段1和位段2。CAN總線控制器將位時間分為3個部分。
- 同步段(Synchronization segment),記為SYNC_SEG。該段占用1個時間單元(1 ×????)。
- 位段1(Bit segment 1),記為BS1。該段占用1到16個Tq(由位時序寄存器配置)。相對于CAN協議而言,BS1相當于傳播時間段(Propagation delay segment)和相位緩沖段1(Phase buffer segment 1)。
- 位段2(Bit segment 2),記為BS2。該段占用1到8個Tq(由位時序寄存器配置)。相對于CAN協議而言,BS2相當于相位緩沖段2(Phase buffer segment 2)。
位時序圖:
這里提到的BS1(即位段1)和CAN時序寄存器中的BS1[3:0]位域不是一個概念,位段1=BS1[3:0]+1 |
說完位時序我們來介紹下GD32F303的采樣點。
對于接收方來說,需要對發送方發出的每個bit進行采樣,那么具體是采樣哪個點呢?按照CAN標準來說,采樣點為BS1和BS2的交界處,即:
而GD32F303為了更好的容錯性,會在標準采樣點前一個以及前前一個Tq加了兩個采樣點,取兩個有效位,所以GD32F303的采樣點為:
如果三個采樣點分別采樣到的為010,則認為該位為“1”。
30.2.7GD32F303 CAN過濾器
前面提到CAN節點發送數據的時候,幀ID是任意的,那么接收方是不是任意ID都可以接收呢?當然是可以的,但一般不會這么做,一個CAN節點一般只接收一個ID或幾個ID的報文,那么如何實現呢?這就要介紹CAN的過濾器了,只有總線上的報文幀ID通過了CAN節點的過濾,才會被接收。
GD32F303共有14個過濾器組(對于互聯性GD32F305/F307,是28個過濾器組),每個過濾器組有兩個過濾器寄存器。程序中需要設置過濾器對應哪個接收FIFO(接收FIFO會在下一節中介紹)。
GD32F303過濾器(x) 數據(y) 寄存器(CAN_FxDATAy)(x= 0...13, y = 0,1)( 僅CAN0可用):
過濾器可以配置為2種位寬:32-bit位寬和16-bit位寬。32-bit位寬CAN_FDATAx包含字段:SFID[10:0],EFID[17:0],FF和FT。
16-bit 位寬CAN_FDATAx包含字段:SFID[10:0],FT,FF和EFID[17:15]。
過濾器可以設置為兩種模式——掩碼模式和列表模式:
- 掩碼模式
對于一個待過濾的數據幀的標識符(Identifier),掩碼模式用來指定哪些位必須與預設的標識符相同,哪些位無需判斷。掩碼模式有兩種位寬:32bit和16bit。
一個 32-bit 位寬掩碼模式過濾器如下:
可以看到,在掩碼模式下,FDATA0用于目標ID,FDATA1用于Mask。舉個例子,設置FDATA0為0x55550000,FDATA1為0xFF00FF00(第31~24以及第15~8位為1),那么意味著總線上的報文ID的第31~24以及第15~8位必須和FDATA0相應位相同,就可以通過這個過濾器,而其他位則不需要關心,也就是說幀ID為0x55xx00xx可以通過過濾器。
明白了32bit掩碼模式過濾器,16位位寬就很好理解了。一個16-bit位寬掩碼模式過濾器如下:
和32-bit的不用,16-bit位寬掩碼過濾器的ID為FDATA0的高16bit,Mask為FDATA0的低16bit,這也意味著16-bit位寬掩碼模式可以設置28個過濾ID掩碼類型。
- 列表模式
列表模式和掩碼模式不同,列表模式設置了一個個具體ID,只有和這些ID完全相同的幀才可以通過過濾器,同樣分成兩種位寬模式。
32-bit位寬列表模式過濾器:
16-bit位寬列表模式過濾器:
30.2.8GD32F303 CAN的發送和接收
通過上面的學習,我們已經基本了解了CAN的工作原理了,這節我們來講GD32F303 CAN的收發。首先我們需要了解GD32F303 CAN的結構框圖:
可以看到,GD32F303是有3個發送郵箱和兩個深度為3的接收FIFO,下面我們分別介紹數據發送和數據接收。
- 數據發送
發送寄存器的框圖:
三個發送郵箱對于三組發送寄存器TMIx、TMPx、TMDATA0x和TMDATA1x(x=0,1,2):
發送郵箱標識符寄存器(CAN_TMIx)
發送郵箱屬性寄存器(CAN_TMPx):
發送郵箱 data0 寄存器(CAN_TMDATA0x):
發送郵箱 data1 寄存器(CAN_TMDATA1x):
當需要發送數據時,選擇一個空閑(empty)的郵箱(讀取CAN_TSTAT寄存器獲取),然后將該郵箱對應TMIx、TMPx、TMDATA0x和TMDATA1x寄存器填好后,使能TMIx的TEN位,寄存器中的數據就自動轉移到郵箱。
實際上數據到郵箱后也不一定就馬上發送到總線,因為有可能總線上正有數據發送,或者其他的郵箱中也有數據,這就涉及到CAN發送郵箱的調度:
當發送郵箱被填入新的數據后,郵箱狀態從empty轉到pending狀態。當超過1個郵箱處于pending狀態時,需要對多個郵箱進行調度,這時發送郵箱處于scheduled狀態。當調度完成后,發送郵箱中的數據開始向CAN總線上發送,這時發送郵箱處于transmit狀態。當數據發送完成,郵箱變為空閑,可以再次交給應用程序使用,這時發送郵箱重新變為empty狀態。
發送郵箱狀態轉換圖:
當多個發送郵箱處于等待狀態下時,可以通過CAN_CTL的TFO位的值可以決定發送順序:
當TFO為1,所有等待發送的郵箱按照先來先發送(FIFO)的順序進行。
當TFO為0,具有最小標識符(Identifier)的郵箱最先發送。如果所有的標識符(Identifier)相等,具有最小郵箱編號的郵箱最先發送。
- 數據接收
接收寄存器的框圖:
兩個接收FIFO對應了兩組接收寄存器RFIFOMIx,RFIFOMPx,RFIFOMDATA0x和RFIFOMDATA1x(x=0,1):
接收 FIFO 郵箱標識符寄存器(CAN_RFIFOMIx):
接收 FIFO 郵箱屬性寄存器(CAN_RFIFOMPx) :
接收 FIFO 郵箱data0寄存器(CAN_RFIFOMDATA0x) :
接收 FIFO 郵箱data1寄存器(CAN_RFIFOMDATA1x) :
當總線上報文通過CAN接收過濾器后(過濾器需要設置對應的FIFO號),數據就會保存到接收郵箱中,每個接收FIFO包含3個接收郵箱,用來接收存儲數據幀。這些郵箱按照先進先出方式進行組織,最早從CAN網絡接收的數據,最早被應用程序處理。
寄存器CAN_RFIFOx包含FIFO狀態信息和幀的數量。當FIFO中包含數據時,可以通過寄存器CAN_RFIFOMIx,CAN_RFIFOMPx,CAN_RFIFOMDATA0x和CAN_RFIFOMDATA1x讀取數據,之后將寄存器CAN_RFIFOx的RFD置1釋放郵箱。
用戶可以通過讀取寄存器CAN_RFIFOx來獲取FIFO的一些信息,比如接收FIFO中目前還有多少個郵箱內容沒有被讀取,是否有FIFO溢出的情況等。關于溢出時的處理方式,可以通過CAN_CTL寄存器的RFOD位來進行設置(讀者可閱讀GD32F30x用戶手冊來查看相關寄存器含義)。
30.2.9GD32F303 CAN工作模式
CAN 總線控制器有3種工作模式:
- 睡眠工作模式;
- 初始化工作模式;
- 正常工作模式。
睡眠工作模式
芯片復位后, CAN總線控制器處于睡眠工作模式。該模式下CAN總線控制器的時鐘停止工作并處于一種低功耗狀態。
將CAN_CTL寄存器的SLPWMOD位置1,可以使CAN總線控制器進入睡眠工作模式。 當進入睡眠工作模式后,CAN_STAT寄存器的SLPWS位將被硬件置1。
將CAN_CTL寄存器的AWU位置1,并當CAN檢測到總線活動時,CAN總線控制器將自動退出睡眠工作模式。將CAN_CTL寄存器的SLPWMOD位清0,也可以退出睡眠工作模式。
由睡眠模式進入初始化工作模式:將CAN_CTL寄存器的IWMOD位置1,SLPWMOD位清0。
由睡眠模式進入正常工作模式:將CAN_CTL寄存器的IWMOD位和SLPWMOD位清0。
初始化工作模式
如果需要配置 CAN 總線通信參數,CAN總線控制器必須進入初始化工作模式。將CAN_CTL寄存器的IWMOD位置1,使CAN總線控制器進入初始化工作模式,將其清0則離開初始化 工作模式。在進入初始化工作模式后,CAN_STAT寄存器的IWS位將被硬件置1。
由初始化模式進入睡眠模式: CAN_CTL 寄存器的SLPWMOD位置1,IWMOD位清0。
由初始化模式進入正常工作模式: CAN_CTL 寄存器的SLPWMOD位和IWMOD位清0。
正常工作模式
在初始化工作模式中配置完CAN 總線通信參數后,將CAN_CTL寄存器的IWMOD位清0可以進入正常工作模式并與CAN總線網絡中的節點進行正常通信。
由正常工作模式進入睡眠工作模式: CAN_CTL 寄存器的SLPWMOD位置1,并等待當前數據收發過程結束。
由正常工作模式初始化工作模式: CAN_CTL 寄存器的IWMOD位置1,并等待當前數據收發過程結束。
30.2.10GD32F303 CAN通信模式
CAN 總線控制器有4種通信模式:
- 靜默(Silent)通信模式;
- 回環(Loopback)通信模式;
- 回環靜默(Loopback and Silent)通信模式;
- 正常(Normal)通信模式。
靜默(Silent)通信模式
在靜默通信模式下,可以從 CAN 總線接收數據,但不向總線發送任何數據。將CAN_BT寄存器中的SCMOD位置1,使CAN總線控制器進入靜默通信模式,將其清0可以退出靜默通信模式。
靜默通信模式可以用來監控CAN 網絡上的數據傳輸。
回環(Loopback)通信模式
在回環通信模式下,由 CAN 總線控制器發送的數據可以被自己接收并存入接收FIFO,同時這些發送數據也送至CAN網絡。將CAN_BT寄存器中的LCMOD位置1,使CAN總線控制器進入回環通信模式,將其清0可以退出回環通信模式。本實驗中就用到了CAN的回環通訊模式。
回環通信模式通常用來進行CAN 通信自測。
回環靜默(Loopback and Silent)通信模式
在回環靜默通信模式下, CAN 的RX和TX引腳與CAN網絡斷開。CAN總線控制器既不從CAN網絡接收數據,也不向CAN網絡發送數據,其發送的數據僅可以被自己接收。將CAN_BT寄存器中的LCMOD位和SCMOD位置1,使CAN總線控制器進入回環靜默通信模式,將它們清0可以退出回環靜默通信模式。
回環靜默通信模式通常用來進行CAN 通信自測。對外TX引腳保持隱性狀態(邏輯1),RX引腳保持高阻態。
正常(Normal)通信模式
CAN 總線控制器通常工作在正常通信模式下,可以從CAN總線接收數據,也可以向CAN總線發送數據。這時需要將CAN_BT寄存器的LCMOD位和SCMOD位清0。
30.3硬件設計
本實驗CAN的硬件設計如下:
30.4代碼解析
30.4.1CAN 配置函數
在driver_can.c中定義了driver_can_config函數,用于CAN的基本參數和過濾器配置:
C void driver_can_config(typdef_can_general can_general) { rcu_periph_clock_enable(can_general.rcu_can); //CAN時鐘使能 rcu_periph_clock_enable(can_general.rcu_IO_port); //IO時鐘使能 if(can_general.can_remap != 0) //如IO有remap,需要配置remap功能 { rcu_periph_clock_enable(RCU_AF); gpio_pin_remap_config(can_general.can_remap,ENABLE); } gpio_init(can_general.IO_port,GPIO_MODE_IPU,can_general.gpio_speed,can_general.pin_rx); //CAN RX IO配置 gpio_init(can_general.IO_port,GPIO_MODE_AF_PP,can_general.gpio_speed,can_general.pin_tx); //CAN TX IO配置 can_struct_para_init(CAN_INIT_STRUCT, &can_general.can_parameter); //CAN初始化結構體的初始化 can_struct_para_init(CAN_INIT_STRUCT, &can_general.can_filter); //CAN過濾器結構體的初始化 can_deinit(can_general.can_port); //CAN的deinit can_general.can_parameter.time_triggered = DISABLE; //時間觸發功能 can_general.can_parameter.auto_bus_off_recovery = DISABLE;//busoff自恢復功能 can_general.can_parameter.auto_wake_up = DISABLE; //自動喚醒功能 can_general.can_parameter.no_auto_retrans = DISABLE;//自動重發功能,需要注意DISABLE為使能自動重發 can_general.can_parameter.rec_fifo_overwrite = DISABLE;//接收溢出模式 can_general.can_parameter.trans_fifo_order = DISABLE;//發送郵箱順序配置 can_general.can_parameter.working_mode = CAN_LOOPBACK_MODE;//回環模式 can_general.can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;//再同步補償 can_general.can_parameter.time_segment_1 = CAN_BT_BS1_5TQ;//BS1設置,注意這里設置為5,寄存器BS1[3:0]實際為4 can_general.can_parameter.time_segment_2 = CAN_BT_BS2_4TQ;//BS2設置,注意這里設置為4,寄存器BS2[2:0]實際為3 /* 1MBps */ #if CAN_BAUDRATE == 1000 //波特率設置 can_general.can_parameter.prescaler = 6; /* 500KBps */ #elif CAN_BAUDRATE == 500 can_general.can_parameter.prescaler = 12; /* 250KBps */ #elif CAN_BAUDRATE == 250 can_general.can_parameter.prescaler = 24; /* 125KBps */ #elif CAN_BAUDRATE == 125 can_general.can_parameter.prescaler = 48; /* 100KBps */ #elif CAN_BAUDRATE == 100 can_general.can_parameter.prescaler = 60; /* 50KBps */ #elif CAN_BAUDRATE == 50 can_general.can_parameter.prescaler = 120; /* 20KBps */ #elif CAN_BAUDRATE == 20 can_general.can_parameter.prescaler = 300; #else #error "please select list can baudrate in private defines in main.c " #endif /* initialize CAN */ can_init(can_general.can_port, &can_general.can_parameter);//CAN初始化 /* initialize filter */ can_general.can_filter.filter_number=0; //過濾器號 can_general.can_filter.filter_mode = CAN_FILTERMODE_MASK;//掩碼模式 can_general.can_filter.filter_bits = CAN_FILTERBITS_32BIT;//掩碼位寬 can_general.can_filter.filter_list_high = 0x3000<<1; //掩碼和ID設置 can_general.can_filter.filter_list_low = 0x0000; can_general.can_filter.filter_mask_high = 0x3000<<1; can_general.can_filter.filter_mask_low = 0x0000; can_general.can_filter.filter_fifo_number = CAN_FIFO0; //過濾器關聯接收FIFO號 can_general.can_filter.filter_enable = ENABLE; //過濾器使能 can_filter_init(&can_general.can_filter); //過濾器初始化 can_general.can_filter.filter_number=1; can_general.can_filter.filter_list_high = 0x5000<<1; can_general.can_filter.filter_list_low = 0x0000; can_general.can_filter.filter_mask_high = 0x5000<<1; can_general.can_filter.filter_mask_low = 0x0000; can_general.can_filter.filter_fifo_number = CAN_FIFO1; can_filter_init(&can_general.can_filter); if(can_general.can_rx_use_interrupt == SET)//打開CAN接收中斷 { can_interrupt_enable(can_general.can_port, CAN_INT_RFNE0); can_interrupt_enable(can_general.can_port, CAN_INT_RFNE1); } } |
其中波特率CAN_BAUDRATE在driver_can.h中預定義:
C /* select CAN baudrate */ /* 1MBps */ #define CAN_BAUDRATE 1000 /* 500kBps */ /* #define CAN_BAUDRATE 500 */ /* 250kBps */ /* #define CAN_BAUDRATE 250 */ /* 125kBps */ /* #define CAN_BAUDRATE 125 */ /* 100kBps */ /* #define CAN_BAUDRATE 100 */ /* 50kBps */ /* #define CAN_BAUDRATE 50 */ /* 20kBps */ /* #define CAN_BAUDRATE 20 */ |
30.4.2CAN 發送函數
在driver_can.c中定義了CAN發送函數:
C void driver_can_transmit(typdef_can_general can_general,can_trasnmit_message_struct *transmit_message) { can_message_transmit(can_general.can_port,transmit_message); } |
30.4.3CAN中斷接收函數
在bsp_can.c中定義了CAN FIFO0和FIFO1的中斷接收處理函數:
C void can0_rx0_interrupt_handler(void) { can_message_receive(CAN0, CAN_FIFO0, &can0_receive_message_fifo0);//將數據從FIFO中轉移到接收寄存器組中 if((0x300 == can0_receive_message_fifo0.rx_sfid)&&(CAN_FF_STANDARD == can0_receive_message_fifo0.rx_ff)&&(2 == can0_receive_message_fifo0.rx_dlen)){ can0_receive_fifo0_flag = SET; }else{ can0_receive_fifo0_flag = RESET; } } |
C void can0_rx1_interrupt_handler(void) { can_message_receive(CAN0, CAN_FIFO1, &can0_receive_message_fifo1);//將數據從FIFO中轉移到接收寄存器組中 if((0x500 == can0_receive_message_fifo1.rx_sfid)&&(CAN_FF_STANDARD == can0_receive_message_fifo1.rx_ff)&&(2 == can0_receive_message_fifo1.rx_dlen)){ can0_receive_fifo1_flag = SET; }else{ can0_receive_fifo1_flag = RESET; } } |
30.4.4main函數實現
main函數實現如下:
C int main(void) { driver_init();//delay函數初始化 bsp_uart_init(&BOARD_UART);//BOARD_UART串口初始化 bsp_can_config(BSP_CAN);//BOARD_CAN初始化 nvic_irq_enable(USBD_LP_CAN0_RX0_IRQn,0,0);//使能CAN0 FIFO0 NVIC nvic_irq_enable(CAN0_RX1_IRQn,0,0);//使能CAN0 FIFO1 NVIC while (1) { bsp_can_transmit(BSP_CAN,&bsp_can_transmit_message_1);//發送一幀數據 printf("\r\n can0 transmit data:%x,%x", bsp_can_transmit_message_1.tx_data[0], bsp_can_transmit_message_1.tx_data[1]);//發送數據打印 delay_ms(1000); //延時1s bsp_can_transmit(BSP_CAN,&bsp_can_transmit_message_2);//發送一幀數據 printf("\r\n can0 transmit data:%x,%x", bsp_can_transmit_message_2.tx_data[0], bsp_can_transmit_message_2.tx_data[1]);//發送數據打印 delay_ms(1000); bsp_can_transmit(BSP_CAN,&bsp_can_transmit_message_3);//發送一幀數據 printf("\r\n can0 transmit data:%x,%x", bsp_can_transmit_message_3.tx_data[0], bsp_can_transmit_message_3.tx_data[1]);//發送數據打印 delay_ms(1000); if(can0_receive_fifo0_flag == SET) { printf("\r\n can0_fifo0 receive ID = %x data:%x,%x", can0_receive_message_fifo0.rx_sfid,can0_receive_message_fifo0.rx_data[0], can0_receive_message_fifo0.rx_data[1]);//接收數據打印 can0_receive_fifo0_flag = RESET; //標志位清除 } if(can0_receive_fifo1_flag == SET) { printf("\r\n can0_fifo1 receive ID = %x data:%x,%x", can0_receive_message_fifo1.rx_sfid,can0_receive_message_fifo1.rx_data[0], can0_receive_message_fifo1.rx_data[1]);//接收數據打印 can0_receive_fifo1_flag = RESET; } } } |
BSP_CAN實參結構體初始化在bsp_can.c中:
C typdef_can_general BSP_CAN = { .can_port = CAN0, .rcu_can = RCU_CAN0, .rcu_IO_port = RCU_GPIOB, .IO_port = GPIOB, .pin_tx = GPIO_PIN_9, .pin_rx = GPIO_PIN_8, .can_remap = GPIO_CAN_PARTIAL_REMAP, .gpio_speed = GPIO_OSPEED_50MHZ , .can_rx_use_interrupt = SET }; |
main函數中實現的功能是每隔1s,分別發送幀ID為0x300,0x500和0x400的文到CAN總線,每幀發送兩個數據,數據結構體初始化在bsp_can.c中:
C can_trasnmit_message_struct bsp_can_transmit_message_1 = { .tx_sfid = 0x300, .tx_efid = 0x00, .tx_ft = CAN_FT_DATA, .tx_ff = CAN_FF_STANDARD, .tx_dlen = 2, .tx_data[0] = 0x55, .tx_data[1] = 0xAA, }; can_trasnmit_message_struct bsp_can_transmit_message_2 = { .tx_sfid = 0x500, .tx_efid = 0x00, .tx_ft = CAN_FT_DATA, .tx_ff = CAN_FF_STANDARD, .tx_dlen = 2, .tx_data[0] = 0x01, .tx_data[1] = 0x02, }; can_trasnmit_message_struct bsp_can_transmit_message_3 = { .tx_sfid = 0x400, .tx_efid = 0x00, .tx_ft = CAN_FT_DATA, .tx_ff = CAN_FF_STANDARD, .tx_dlen = 2, .tx_data[0] = 0x02, .tx_data[1] = 0x01, }; |
因為使用了回環模式,故發送的報文同時也會被CAN接收,而由于過濾器的配置,ID為0x300的會被接收到FIFO0中,ID為0x500的會被接收到FIFO1中,而ID為0x400的由于無法通過過濾器,被CAN舍棄。
30.5實驗結果
使用USB-TypeC線,連接電腦和板上USB to UART口后,配置好串口調試助手,即可看到CAN發送和接收數據的情況:
-
單片機
+關注
關注
6037文章
44558瀏覽量
635227 -
CAN通信
+關注
關注
5文章
93瀏覽量
17838 -
CAN
+關注
關注
57文章
2754瀏覽量
463698 -
通信
+關注
關注
18文章
6032瀏覽量
135990 -
開發板
+關注
關注
25文章
5050瀏覽量
97456 -
GD32
+關注
關注
7文章
403瀏覽量
24351
發布評論請先 登錄
相關推薦
評論