30.1實(shí)驗(yàn)內(nèi)容
通過(guò)本實(shí)驗(yàn)主要學(xué)習(xí)以下內(nèi)容:
30.2實(shí)驗(yàn)原理
30.2.1CAN概述
CAN 是Controller Area Network的縮寫,是由德國(guó)BOSCH公司開發(fā)的,已成為ISO國(guó)際標(biāo)準(zhǔn)化的串行通信協(xié)議。其主要應(yīng)用場(chǎng)合為汽車和工業(yè)控制。CAN具有傳輸距離長(zhǎng),傳輸可靠、強(qiáng)大的糾錯(cuò)機(jī)制等特點(diǎn),其高性能和可靠性已被廣泛認(rèn)同,現(xiàn)在已經(jīng)成為汽車、工業(yè)自動(dòng)化、醫(yī)療設(shè)備等領(lǐng)域應(yīng)用最廣泛的總線之一。
30.2.2CAN總線拓?fù)?/strong>
CAN總線拓?fù)鋱D如下:
CAN 控制器根據(jù)兩根線上的電位差來(lái)判斷總線電平,一般將兩根線分別命名為CAN_H和CAN_L。總線電平分為顯性電平和隱性電平,二者必居其一。發(fā)送方通過(guò)使總線電平發(fā)生變化,將消息發(fā)送給接收方。 當(dāng)CAN總線上的電位差為0V時(shí),表示隱性電平,隱性電平代表邏輯“1”;當(dāng)CAN總線上有電位差時(shí)(大概在2.5V左右),表示顯性電平,顯性電平代表邏輯“0”。總線空閑時(shí),默認(rèn)為隱性電平,即總線電位差為0。
關(guān)于電位差、隱性/顯性電平及邏輯電平,非常容易弄混,讀者需要熟記。 |
CAN總線的特點(diǎn)可以總結(jié)為:
- 多主
與USART-485這種一主多從類型總線不同,CAN總線是多主控制,即總線上沒(méi)有主機(jī)從機(jī)之分,所有設(shè)備都是處于平等的地位。
- 消息格式
CAN總線上的消息都以固定格式發(fā)送。當(dāng)兩個(gè)以上的單元同時(shí)開始發(fā)送消息時(shí),根據(jù)標(biāo)識(shí)符(Identifier以下稱為ID)決定優(yōu)先級(jí)。ID并不是表示發(fā)送的目的地址,而是表示訪問(wèn)總線的消息的優(yōu)先級(jí)。
- 通信速度快,通信距離遠(yuǎn)
CAN最高可達(dá)1Mbps波特率,理論最遠(yuǎn)距離可達(dá)10Km,當(dāng)然此時(shí)通訊速率較低,只有5Kbps以下。
- 具有錯(cuò)誤檢測(cè)、錯(cuò)誤通知和錯(cuò)誤恢復(fù)功能
CAN總線具有強(qiáng)大的錯(cuò)誤檢測(cè)、通知和恢復(fù)功能。當(dāng)一個(gè)單元發(fā)生錯(cuò)誤時(shí)其他單元會(huì)進(jìn)行報(bào)錯(cuò),正在發(fā)送的單元檢測(cè)到錯(cuò)誤后,會(huì)立即強(qiáng)制結(jié)束當(dāng)前發(fā)送,并嘗試重新發(fā)送(功能可配置),當(dāng)發(fā)送錯(cuò)誤的次數(shù)達(dá)到一定值后,該單元會(huì)自動(dòng)從總線中退出,直到應(yīng)用程序讓其重新加入總線為止。
- 半雙工異步通訊
CAN的總線的查分信號(hào),決定了CAN總線實(shí)際為半雙工通訊,另外由于CAN總線沒(méi)有時(shí)鐘線,所以是異步通訊,故要求CAN總線上的所有單元的波特率都要設(shè)置一致。
30.2.3CAN幀的種類
CAN總共有如下五種類型的幀種類:
- 數(shù)據(jù)幀
用于數(shù)據(jù)傳輸?shù)膸?,也是最常用的一種幀種類
- 遙控幀
接收單元向具有相同ID的發(fā)送單元請(qǐng)求數(shù)據(jù)時(shí)所需要的幀種類
- 錯(cuò)誤幀
一種非常重要的幀種類,錯(cuò)誤幀是當(dāng)總線上有錯(cuò)誤時(shí),檢測(cè)到錯(cuò)誤的單元向其他單元通知錯(cuò)誤的幀。
- 過(guò)載幀
用于接收單元通知其他單元其還沒(méi)有準(zhǔn)備好的幀種類
- 幀間隔
用于幀和幀之間分離的幀
30.2.4CAN協(xié)議的解析
介紹了CAN的一些基本指示后,可能讀者還是不太明白幀ID是什么,CAN的發(fā)送和接收是怎么實(shí)現(xiàn)的,是否就像串口一樣發(fā)送數(shù)據(jù)就可以?實(shí)際上CAN需要遵循CAN協(xié)議,這樣每個(gè)CAN單元才可以準(zhǔn)確無(wú)誤的發(fā)送和接收數(shù)據(jù),CAN強(qiáng)大的錯(cuò)誤檢測(cè)、錯(cuò)誤通知等機(jī)制也是依托于標(biāo)準(zhǔn)CAN協(xié)議。下面以數(shù)據(jù)幀來(lái)解析下CAN的協(xié)議。
數(shù)據(jù)幀的格式如下圖,其中D表示顯性位,即邏輯“0”,R表示隱性位,即邏輯“1”,D/R表示隱性位或顯性位,另外下圖中的數(shù)字表示bit位數(shù):
- 幀開始
每次CAN通訊都始于幀開始段,幀開始是1個(gè)bit位的顯性電平(邏輯“0”),由發(fā)送方發(fā)出,接收方檢測(cè)到一個(gè)bit邏輯“0”,開始準(zhǔn)備接收數(shù)據(jù)。
- 仲裁段
好了,我們終于看到了幀ID的廬山真面目了。數(shù)據(jù)幀分為兩種——標(biāo)準(zhǔn)幀和擴(kuò)展幀,標(biāo)準(zhǔn)幀的幀ID由11bit組成,擴(kuò)展幀有11+18共29bit組成。需要注意,無(wú)論是標(biāo)準(zhǔn)幀ID還是擴(kuò)展幀ID,都不允許設(shè)置最高7bit為1(即不允許幀ID=0b,1111111xxx··),以為幀結(jié)束段就是7個(gè)“1”組成。
沖裁段中的 RTR 位用于標(biāo)識(shí)是否是遠(yuǎn)程幀(0:數(shù)據(jù)幀;1:遠(yuǎn)程幀),IDE位為標(biāo)識(shí)符選擇位(0:使用標(biāo)準(zhǔn)標(biāo)符;1:使用擴(kuò)展標(biāo)識(shí)符),SRR位為代替遠(yuǎn)程請(qǐng)求位,為隱性位,它代替了標(biāo)準(zhǔn)幀中的RTR位。
我們需要先明確一個(gè)概念,CAN總線上的每個(gè)單元并不是只能發(fā)送固定幀ID,而是可以發(fā)送任意幀ID,幀ID不代表CAN設(shè)備號(hào),代表的是當(dāng)前數(shù)據(jù)幀/遠(yuǎn)程幀的ID號(hào)。當(dāng)一幀數(shù)據(jù)發(fā)送到總線后,所有接收方會(huì)對(duì)幀ID進(jìn)行識(shí)別,當(dāng)識(shí)別到是自己需要的ID時(shí),則會(huì)將該幀數(shù)據(jù)收取到內(nèi)部;而當(dāng)識(shí)別到不是自己需要的ID時(shí),則不會(huì)接收數(shù)據(jù)。
因?yàn)橛锌赡艹霈F(xiàn)兩個(gè)或更多CAN單元同時(shí)發(fā)送數(shù)據(jù)的情況,此時(shí)幀ID還起到仲裁的作用,各發(fā)送單元從幀ID的第一位開始進(jìn)行仲裁。連續(xù)輸出顯性電平最多的單元可繼續(xù)發(fā)送,比如兩個(gè)CAN單元同時(shí)發(fā)送數(shù)據(jù),其中單元一發(fā)出的數(shù)標(biāo)準(zhǔn)幀的ID為0b,00110000000,而單元二發(fā)出的標(biāo)準(zhǔn)幀ID為0b,00100000000,可以看到發(fā)送到第4位時(shí),單元一發(fā)出的是邏輯“1”,單元二的是邏輯“0”,因?yàn)榭偩€上“0”比“1”優(yōu)先級(jí)搞,所以此時(shí)單元二獲得總線發(fā)送權(quán),單元一將自動(dòng)停止發(fā)送。
- 控制段
控制段由6個(gè)bit組成,其中DLC占用4個(gè)bit,表示要發(fā)送的字節(jié)數(shù)。
- 數(shù)據(jù)段
數(shù)據(jù)段即幀載有的有效數(shù)據(jù)了,CAN最多一次可發(fā)送8個(gè)字節(jié)(CANFD最多可以發(fā)送64字節(jié),這個(gè)后面介紹帶CANFD功能的開發(fā)板時(shí)再細(xì)說(shuō)),也就是說(shuō)上面描述的DLC最大值為0b,1000。
- CRC段
CRC段總共有16bit,其中15個(gè)bit表示CRC值,另1個(gè)bit為CRC界定符。此段CRC的值計(jì)算范圍包括:幀起始、仲裁段、控制段、數(shù)據(jù)段。發(fā)送會(huì)根據(jù)發(fā)送的數(shù)據(jù)來(lái)計(jì)算出一個(gè)CRC值并通過(guò)CRC段發(fā)到總線,接收方以同樣的算法計(jì)算CRC值并和發(fā)送方發(fā)出的CRC段進(jìn)行比較,當(dāng)不一致時(shí)接收方會(huì)向總線報(bào)錯(cuò)。
- ACK段
ACK段用來(lái)確認(rèn)接收方是否正常接收。ACK段由ACK槽(ACK Slot)和ACK界定符2個(gè)位組成。
發(fā)送單元的 ACK,發(fā)送2個(gè)位的隱性位,而接收到正確消息的單元在ACK槽(ACK Slot)發(fā)送顯性位,通知發(fā)單元正常接收結(jié)束,這個(gè)過(guò)程叫發(fā)送ACK/返回ACK。發(fā)送ACK的是在既不處于總線關(guān)閉態(tài)也不處于休眠態(tài)的所有收單元中,接收到正常消息的單元(發(fā)送單元不發(fā)送ACK)。
- 幀結(jié)束
幀結(jié)束由7個(gè)bit的邏輯“1”組成,表示該幀結(jié)束。
30.2.5CAN的波特率
前面有提到CAN的波特率,波特率代表每秒鐘傳輸?shù)奈粩?shù)(1位就表示1bit),我們來(lái)看下GD32F303的波特率是怎么計(jì)算的。
首先需要了解一個(gè)概念,CAN時(shí)序的最小單位是一個(gè)叫Tq的東西,CAN的每個(gè)位即每個(gè)bit是由若干個(gè)Tq組成的,那么這個(gè)Tq的長(zhǎng)度是多少呢?GD32F303的CAN是掛載在APB1總線的:
如果讀者將GD32F303的主頻配置為最高120M,且將APB1的時(shí)鐘配置為最高60M的話,那么CAN的時(shí)鐘就是60M,然后CAN有個(gè)分頻系數(shù),在位時(shí)序寄存器CAN_BT中:
一個(gè)Tq占用的時(shí)間計(jì)算公式:分頻系數(shù)/CAN時(shí)鐘,舉個(gè)例子,我們?cè)O(shè)置這個(gè)分頻系數(shù)為6的話,一個(gè)Tq的時(shí)間就是6/60M = 100us,也可以說(shuō)Tq的傳輸速率為10M。那么由多少個(gè)Tq組成CAN的一個(gè)位呢?還是看CAN_BT寄存器:
寄存器中有BS1和BS2,這兩個(gè)位域用于設(shè)置位段1和位段2(關(guān)于位段后面會(huì)介紹),一個(gè)CAN位占用的Tq個(gè)數(shù)等于位段1+位段2+1,舉個(gè)例子,設(shè)置位段1為5(即BS1=4),設(shè)置位段2為4(即BS2=3),那么一個(gè)CAN位占用的Tq個(gè)數(shù)為5+4+1=10。好,現(xiàn)在就可以來(lái)算CAN的波特率了,按照CAN分頻系數(shù)為6,位段1為5,位段2為4,一個(gè)位占用的時(shí)間為6/60M*10 = 1ms,也就是波特率=1M。我們可以把這個(gè)計(jì)算轉(zhuǎn)化為公式:
30.2.6CAN的位時(shí)序和采樣點(diǎn)
我們現(xiàn)在來(lái)看下上一節(jié)提到的位段1和位段2。CAN總線控制器將位時(shí)間分為3個(gè)部分。
- 同步段(Synchronization segment),記為SYNC_SEG。該段占用1個(gè)時(shí)間單元(1 ×????)。
- 位段1(Bit segment 1),記為BS1。該段占用1到16個(gè)Tq(由位時(shí)序寄存器配置)。相對(duì)于CAN協(xié)議而言,BS1相當(dāng)于傳播時(shí)間段(Propagation delay segment)和相位緩沖段1(Phase buffer segment 1)。
- 位段2(Bit segment 2),記為BS2。該段占用1到8個(gè)Tq(由位時(shí)序寄存器配置)。相對(duì)于CAN協(xié)議而言,BS2相當(dāng)于相位緩沖段2(Phase buffer segment 2)。
位時(shí)序圖:
這里提到的BS1(即位段1)和CAN時(shí)序寄存器中的BS1[3:0]位域不是一個(gè)概念,位段1=BS1[3:0]+1 |
說(shuō)完位時(shí)序我們來(lái)介紹下GD32F303的采樣點(diǎn)。
對(duì)于接收方來(lái)說(shuō),需要對(duì)發(fā)送方發(fā)出的每個(gè)bit進(jìn)行采樣,那么具體是采樣哪個(gè)點(diǎn)呢?按照CAN標(biāo)準(zhǔn)來(lái)說(shuō),采樣點(diǎn)為BS1和BS2的交界處,即:
而GD32F303為了更好的容錯(cuò)性,會(huì)在標(biāo)準(zhǔn)采樣點(diǎn)前一個(gè)以及前前一個(gè)Tq加了兩個(gè)采樣點(diǎn),取兩個(gè)有效位,所以GD32F303的采樣點(diǎn)為:
如果三個(gè)采樣點(diǎn)分別采樣到的為010,則認(rèn)為該位為“1”。
30.2.7GD32F303 CAN過(guò)濾器
前面提到CAN節(jié)點(diǎn)發(fā)送數(shù)據(jù)的時(shí)候,幀ID是任意的,那么接收方是不是任意ID都可以接收呢?當(dāng)然是可以的,但一般不會(huì)這么做,一個(gè)CAN節(jié)點(diǎn)一般只接收一個(gè)ID或幾個(gè)ID的報(bào)文,那么如何實(shí)現(xiàn)呢?這就要介紹CAN的過(guò)濾器了,只有總線上的報(bào)文幀ID通過(guò)了CAN節(jié)點(diǎn)的過(guò)濾,才會(huì)被接收。
GD32F303共有14個(gè)過(guò)濾器組(對(duì)于互聯(lián)性GD32F305/F307,是28個(gè)過(guò)濾器組),每個(gè)過(guò)濾器組有兩個(gè)過(guò)濾器寄存器。程序中需要設(shè)置過(guò)濾器對(duì)應(yīng)哪個(gè)接收FIFO(接收FIFO會(huì)在下一節(jié)中介紹)。
GD32F303過(guò)濾器(x) 數(shù)據(jù)(y) 寄存器(CAN_FxDATAy)(x= 0...13, y = 0,1)( 僅CAN0可用):
過(guò)濾器可以配置為2種位寬:32-bit位寬和16-bit位寬。32-bit位寬CAN_FDATAx包含字段:SFID[10:0],EFID[17:0],F(xiàn)F和FT。
16-bit 位寬CAN_FDATAx包含字段:SFID[10:0],F(xiàn)T,F(xiàn)F和EFID[17:15]。
過(guò)濾器可以設(shè)置為兩種模式——掩碼模式和列表模式:
- 掩碼模式
對(duì)于一個(gè)待過(guò)濾的數(shù)據(jù)幀的標(biāo)識(shí)符(Identifier),掩碼模式用來(lái)指定哪些位必須與預(yù)設(shè)的標(biāo)識(shí)符相同,哪些位無(wú)需判斷。掩碼模式有兩種位寬:32bit和16bit。
一個(gè) 32-bit 位寬掩碼模式過(guò)濾器如下:
可以看到,在掩碼模式下,F(xiàn)DATA0用于目標(biāo)ID,F(xiàn)DATA1用于Mask。舉個(gè)例子,設(shè)置FDATA0為0x55550000,F(xiàn)DATA1為0xFF00FF00(第31~24以及第15~8位為1),那么意味著總線上的報(bào)文ID的第31~24以及第15~8位必須和FDATA0相應(yīng)位相同,就可以通過(guò)這個(gè)過(guò)濾器,而其他位則不需要關(guān)心,也就是說(shuō)幀ID為0x55xx00xx可以通過(guò)過(guò)濾器。
明白了32bit掩碼模式過(guò)濾器,16位位寬就很好理解了。一個(gè)16-bit位寬掩碼模式過(guò)濾器如下:
和32-bit的不用,16-bit位寬掩碼過(guò)濾器的ID為FDATA0的高16bit,Mask為FDATA0的低16bit,這也意味著16-bit位寬掩碼模式可以設(shè)置28個(gè)過(guò)濾ID掩碼類型。
- 列表模式
列表模式和掩碼模式不同,列表模式設(shè)置了一個(gè)個(gè)具體ID,只有和這些ID完全相同的幀才可以通過(guò)過(guò)濾器,同樣分成兩種位寬模式。
32-bit位寬列表模式過(guò)濾器:
16-bit位寬列表模式過(guò)濾器:
30.2.8GD32F303 CAN的發(fā)送和接收
通過(guò)上面的學(xué)習(xí),我們已經(jīng)基本了解了CAN的工作原理了,這節(jié)我們來(lái)講GD32F303 CAN的收發(fā)。首先我們需要了解GD32F303 CAN的結(jié)構(gòu)框圖:
可以看到,GD32F303是有3個(gè)發(fā)送郵箱和兩個(gè)深度為3的接收FIFO,下面我們分別介紹數(shù)據(jù)發(fā)送和數(shù)據(jù)接收。
- 數(shù)據(jù)發(fā)送
發(fā)送寄存器的框圖:
三個(gè)發(fā)送郵箱對(duì)于三組發(fā)送寄存器TMIx、TMPx、TMDATA0x和TMDATA1x(x=0,1,2):
發(fā)送郵箱標(biāo)識(shí)符寄存器(CAN_TMIx)
發(fā)送郵箱屬性寄存器(CAN_TMPx):
發(fā)送郵箱 data0 寄存器(CAN_TMDATA0x):
發(fā)送郵箱 data1 寄存器(CAN_TMDATA1x):
當(dāng)需要發(fā)送數(shù)據(jù)時(shí),選擇一個(gè)空閑(empty)的郵箱(讀取CAN_TSTAT寄存器獲?。?,然后將該郵箱對(duì)應(yīng)TMIx、TMPx、TMDATA0x和TMDATA1x寄存器填好后,使能TMIx的TEN位,寄存器中的數(shù)據(jù)就自動(dòng)轉(zhuǎn)移到郵箱。
實(shí)際上數(shù)據(jù)到郵箱后也不一定就馬上發(fā)送到總線,因?yàn)橛锌赡芸偩€上正有數(shù)據(jù)發(fā)送,或者其他的郵箱中也有數(shù)據(jù),這就涉及到CAN發(fā)送郵箱的調(diào)度:
當(dāng)發(fā)送郵箱被填入新的數(shù)據(jù)后,郵箱狀態(tài)從empty轉(zhuǎn)到pending狀態(tài)。當(dāng)超過(guò)1個(gè)郵箱處于pending狀態(tài)時(shí),需要對(duì)多個(gè)郵箱進(jìn)行調(diào)度,這時(shí)發(fā)送郵箱處于scheduled狀態(tài)。當(dāng)調(diào)度完成后,發(fā)送郵箱中的數(shù)據(jù)開始向CAN總線上發(fā)送,這時(shí)發(fā)送郵箱處于transmit狀態(tài)。當(dāng)數(shù)據(jù)發(fā)送完成,郵箱變?yōu)榭臻e,可以再次交給應(yīng)用程序使用,這時(shí)發(fā)送郵箱重新變?yōu)閑mpty狀態(tài)。
發(fā)送郵箱狀態(tài)轉(zhuǎn)換圖:
當(dāng)多個(gè)發(fā)送郵箱處于等待狀態(tài)下時(shí),可以通過(guò)CAN_CTL的TFO位的值可以決定發(fā)送順序:
當(dāng)TFO為1,所有等待發(fā)送的郵箱按照先來(lái)先發(fā)送(FIFO)的順序進(jìn)行。
當(dāng)TFO為0,具有最小標(biāo)識(shí)符(Identifier)的郵箱最先發(fā)送。如果所有的標(biāo)識(shí)符(Identifier)相等,具有最小郵箱編號(hào)的郵箱最先發(fā)送。
- 數(shù)據(jù)接收
接收寄存器的框圖:
兩個(gè)接收FIFO對(duì)應(yīng)了兩組接收寄存器RFIFOMIx,RFIFOMPx,RFIFOMDATA0x和RFIFOMDATA1x(x=0,1):
接收 FIFO 郵箱標(biāo)識(shí)符寄存器(CAN_RFIFOMIx):
接收 FIFO 郵箱屬性寄存器(CAN_RFIFOMPx) :
接收 FIFO 郵箱data0寄存器(CAN_RFIFOMDATA0x) :
接收 FIFO 郵箱data1寄存器(CAN_RFIFOMDATA1x) :
當(dāng)總線上報(bào)文通過(guò)CAN接收過(guò)濾器后(過(guò)濾器需要設(shè)置對(duì)應(yīng)的FIFO號(hào)),數(shù)據(jù)就會(huì)保存到接收郵箱中,每個(gè)接收FIFO包含3個(gè)接收郵箱,用來(lái)接收存儲(chǔ)數(shù)據(jù)幀。這些郵箱按照先進(jìn)先出方式進(jìn)行組織,最早從CAN網(wǎng)絡(luò)接收的數(shù)據(jù),最早被應(yīng)用程序處理。
寄存器CAN_RFIFOx包含F(xiàn)IFO狀態(tài)信息和幀的數(shù)量。當(dāng)FIFO中包含數(shù)據(jù)時(shí),可以通過(guò)寄存器CAN_RFIFOMIx,CAN_RFIFOMPx,CAN_RFIFOMDATA0x和CAN_RFIFOMDATA1x讀取數(shù)據(jù),之后將寄存器CAN_RFIFOx的RFD置1釋放郵箱。
用戶可以通過(guò)讀取寄存器CAN_RFIFOx來(lái)獲取FIFO的一些信息,比如接收FIFO中目前還有多少個(gè)郵箱內(nèi)容沒(méi)有被讀取,是否有FIFO溢出的情況等。關(guān)于溢出時(shí)的處理方式,可以通過(guò)CAN_CTL寄存器的RFOD位來(lái)進(jìn)行設(shè)置(讀者可閱讀GD32F30x用戶手冊(cè)來(lái)查看相關(guān)寄存器含義)。
30.2.9GD32F303 CAN工作模式
CAN 總線控制器有3種工作模式:
- 睡眠工作模式;
- 初始化工作模式;
- 正常工作模式。
睡眠工作模式
芯片復(fù)位后, CAN總線控制器處于睡眠工作模式。該模式下CAN總線控制器的時(shí)鐘停止工作并處于一種低功耗狀態(tài)。
將CAN_CTL寄存器的SLPWMOD位置1,可以使CAN總線控制器進(jìn)入睡眠工作模式。 當(dāng)進(jìn)入睡眠工作模式后,CAN_STAT寄存器的SLPWS位將被硬件置1。
將CAN_CTL寄存器的AWU位置1,并當(dāng)CAN檢測(cè)到總線活動(dòng)時(shí),CAN總線控制器將自動(dòng)退出睡眠工作模式。將CAN_CTL寄存器的SLPWMOD位清0,也可以退出睡眠工作模式。
由睡眠模式進(jìn)入初始化工作模式:將CAN_CTL寄存器的IWMOD位置1,SLPWMOD位清0。
由睡眠模式進(jìn)入正常工作模式:將CAN_CTL寄存器的IWMOD位和SLPWMOD位清0。
初始化工作模式
如果需要配置 CAN 總線通信參數(shù),CAN總線控制器必須進(jìn)入初始化工作模式。將CAN_CTL寄存器的IWMOD位置1,使CAN總線控制器進(jìn)入初始化工作模式,將其清0則離開初始化 工作模式。在進(jìn)入初始化工作模式后,CAN_STAT寄存器的IWS位將被硬件置1。
由初始化模式進(jìn)入睡眠模式: CAN_CTL 寄存器的SLPWMOD位置1,IWMOD位清0。
由初始化模式進(jìn)入正常工作模式: CAN_CTL 寄存器的SLPWMOD位和IWMOD位清0。
正常工作模式
在初始化工作模式中配置完CAN 總線通信參數(shù)后,將CAN_CTL寄存器的IWMOD位清0可以進(jìn)入正常工作模式并與CAN總線網(wǎng)絡(luò)中的節(jié)點(diǎn)進(jìn)行正常通信。
由正常工作模式進(jìn)入睡眠工作模式: CAN_CTL 寄存器的SLPWMOD位置1,并等待當(dāng)前數(shù)據(jù)收發(fā)過(guò)程結(jié)束。
由正常工作模式初始化工作模式: CAN_CTL 寄存器的IWMOD位置1,并等待當(dāng)前數(shù)據(jù)收發(fā)過(guò)程結(jié)束。
30.2.10GD32F303 CAN通信模式
CAN 總線控制器有4種通信模式:
- 靜默(Silent)通信模式;
- 回環(huán)(Loopback)通信模式;
- 回環(huán)靜默(Loopback and Silent)通信模式;
- 正常(Normal)通信模式。
靜默(Silent)通信模式
在靜默通信模式下,可以從 CAN 總線接收數(shù)據(jù),但不向總線發(fā)送任何數(shù)據(jù)。將CAN_BT寄存器中的SCMOD位置1,使CAN總線控制器進(jìn)入靜默通信模式,將其清0可以退出靜默通信模式。
靜默通信模式可以用來(lái)監(jiān)控CAN 網(wǎng)絡(luò)上的數(shù)據(jù)傳輸。
回環(huán)(Loopback)通信模式
在回環(huán)通信模式下,由 CAN 總線控制器發(fā)送的數(shù)據(jù)可以被自己接收并存入接收FIFO,同時(shí)這些發(fā)送數(shù)據(jù)也送至CAN網(wǎng)絡(luò)。將CAN_BT寄存器中的LCMOD位置1,使CAN總線控制器進(jìn)入回環(huán)通信模式,將其清0可以退出回環(huán)通信模式。本實(shí)驗(yàn)中就用到了CAN的回環(huán)通訊模式。
回環(huán)通信模式通常用來(lái)進(jìn)行CAN 通信自測(cè)。
回環(huán)靜默(Loopback and Silent)通信模式
在回環(huán)靜默通信模式下, CAN 的RX和TX引腳與CAN網(wǎng)絡(luò)斷開。CAN總線控制器既不從CAN網(wǎng)絡(luò)接收數(shù)據(jù),也不向CAN網(wǎng)絡(luò)發(fā)送數(shù)據(jù),其發(fā)送的數(shù)據(jù)僅可以被自己接收。將CAN_BT寄存器中的LCMOD位和SCMOD位置1,使CAN總線控制器進(jìn)入回環(huán)靜默通信模式,將它們清0可以退出回環(huán)靜默通信模式。
回環(huán)靜默通信模式通常用來(lái)進(jìn)行CAN 通信自測(cè)。對(duì)外TX引腳保持隱性狀態(tài)(邏輯1),RX引腳保持高阻態(tài)。
正常(Normal)通信模式
CAN 總線控制器通常工作在正常通信模式下,可以從CAN總線接收數(shù)據(jù),也可以向CAN總線發(fā)送數(shù)據(jù)。這時(shí)需要將CAN_BT寄存器的LCMOD位和SCMOD位清0。
30.3硬件設(shè)計(jì)
本實(shí)驗(yàn)CAN的硬件設(shè)計(jì)如下:
30.4代碼解析
30.4.1CAN 配置函數(shù)
在driver_can.c中定義了driver_can_config函數(shù),用于CAN的基本參數(shù)和過(guò)濾器配置:
C void driver_can_config(typdef_can_general can_general) { rcu_periph_clock_enable(can_general.rcu_can); //CAN時(shí)鐘使能 rcu_periph_clock_enable(can_general.rcu_IO_port); //IO時(shí)鐘使能 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初始化結(jié)構(gòu)體的初始化 can_struct_para_init(CAN_INIT_STRUCT, &can_general.can_filter); //CAN過(guò)濾器結(jié)構(gòu)體的初始化 can_deinit(can_general.can_port); //CAN的deinit can_general.can_parameter.time_triggered = DISABLE; //時(shí)間觸發(fā)功能 can_general.can_parameter.auto_bus_off_recovery = DISABLE;//busoff自恢復(fù)功能 can_general.can_parameter.auto_wake_up = DISABLE; //自動(dòng)喚醒功能 can_general.can_parameter.no_auto_retrans = DISABLE;//自動(dòng)重發(fā)功能,需要注意DISABLE為使能自動(dòng)重發(fā) can_general.can_parameter.rec_fifo_overwrite = DISABLE;//接收溢出模式 can_general.can_parameter.trans_fifo_order = DISABLE;//發(fā)送郵箱順序配置 can_general.can_parameter.working_mode = CAN_LOOPBACK_MODE;//回環(huán)模式 can_general.can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;//再同步補(bǔ)償 can_general.can_parameter.time_segment_1 = CAN_BT_BS1_5TQ;//BS1設(shè)置,注意這里設(shè)置為5,寄存器BS1[3:0]實(shí)際為4 can_general.can_parameter.time_segment_2 = CAN_BT_BS2_4TQ;//BS2設(shè)置,注意這里設(shè)置為4,寄存器BS2[2:0]實(shí)際為3 /* 1MBps */ #if CAN_BAUDRATE == 1000 //波特率設(shè)置 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; //過(guò)濾器號(hào) 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設(shè)置 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; //過(guò)濾器關(guān)聯(lián)接收FIFO號(hào) can_general.can_filter.filter_enable = ENABLE; //過(guò)濾器使能 can_filter_init(&can_general.can_filter); //過(guò)濾器初始化 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中預(yù)定義:
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 發(fā)送函數(shù)
在driver_can.c中定義了CAN發(fā)送函數(shù):
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中斷接收函數(shù)
在bsp_can.c中定義了CAN FIFO0和FIFO1的中斷接收處理函數(shù):
C void can0_rx0_interrupt_handler(void) { can_message_receive(CAN0, CAN_FIFO0, &can0_receive_message_fifo0);//將數(shù)據(jù)從FIFO中轉(zhuǎn)移到接收寄存器組中 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);//將數(shù)據(jù)從FIFO中轉(zhuǎn)移到接收寄存器組中 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函數(shù)實(shí)現(xiàn)
main函數(shù)實(shí)現(xiàn)如下:
C int main(void) { driver_init();//delay函數(shù)初始化 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);//發(fā)送一幀數(shù)據(jù) printf("\r\n can0 transmit data:%x,%x", bsp_can_transmit_message_1.tx_data[0], bsp_can_transmit_message_1.tx_data[1]);//發(fā)送數(shù)據(jù)打印 delay_ms(1000); //延時(shí)1s bsp_can_transmit(BSP_CAN,&bsp_can_transmit_message_2);//發(fā)送一幀數(shù)據(jù) printf("\r\n can0 transmit data:%x,%x", bsp_can_transmit_message_2.tx_data[0], bsp_can_transmit_message_2.tx_data[1]);//發(fā)送數(shù)據(jù)打印 delay_ms(1000); bsp_can_transmit(BSP_CAN,&bsp_can_transmit_message_3);//發(fā)送一幀數(shù)據(jù) printf("\r\n can0 transmit data:%x,%x", bsp_can_transmit_message_3.tx_data[0], bsp_can_transmit_message_3.tx_data[1]);//發(fā)送數(shù)據(jù)打印 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]);//接收數(shù)據(jù)打印 can0_receive_fifo0_flag = RESET; //標(biāo)志位清除 } 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]);//接收數(shù)據(jù)打印 can0_receive_fifo1_flag = RESET; } } } |
BSP_CAN實(shí)參結(jié)構(gòu)體初始化在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函數(shù)中實(shí)現(xiàn)的功能是每隔1s,分別發(fā)送幀ID為0x300,0x500和0x400的文到CAN總線,每幀發(fā)送兩個(gè)數(shù)據(jù),數(shù)據(jù)結(jié)構(gòu)體初始化在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, }; |
因?yàn)槭褂昧嘶丨h(huán)模式,故發(fā)送的報(bào)文同時(shí)也會(huì)被CAN接收,而由于過(guò)濾器的配置,ID為0x300的會(huì)被接收到FIFO0中,ID為0x500的會(huì)被接收到FIFO1中,而ID為0x400的由于無(wú)法通過(guò)過(guò)濾器,被CAN舍棄。
30.5實(shí)驗(yàn)結(jié)果
使用USB-TypeC線,連接電腦和板上USB to UART口后,配置好串口調(diào)試助手,即可看到CAN發(fā)送和接收數(shù)據(jù)的情況:
本教程由GD32 MCU方案商聚沃科技原創(chuàng)發(fā)布,了解更多GD32 MCU教程,關(guān)注聚沃科技官網(wǎng)
-
單片機(jī)
+關(guān)注
關(guān)注
6030文章
44489瀏覽量
631958 -
CAN通信
+關(guān)注
關(guān)注
5文章
93瀏覽量
17799 -
CAN
+關(guān)注
關(guān)注
57文章
2694瀏覽量
463142 -
通信
+關(guān)注
關(guān)注
18文章
5949瀏覽量
135782 -
開發(fā)板
+關(guān)注
關(guān)注
25文章
4895瀏覽量
97055 -
GD32
+關(guān)注
關(guān)注
7文章
403瀏覽量
24106
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論