加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • SPI 特點(diǎn)
    • 工作機(jī)制
    • 軟件中如何設(shè)置 SPI 的極性和相位
    • 數(shù)據(jù)交換(Data Exchanges)
    • SPI 舉例
    • 硬件設(shè)計(jì)
    • 管腳連接說(shuō)明
    • Cortex-A9 SPI 控制器
    • SPI 寄存器說(shuō)明
    • SPI 初始化流程
    • 收發(fā)數(shù)據(jù)流程
    • 簡(jiǎn)介
    • 與 MCP2515 通信
    • CAN
    • can 緩沖區(qū)數(shù)據(jù)收發(fā)
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

SPI協(xié)議,MCP2515裸機(jī)驅(qū)動(dòng)詳解

2020/10/01
537
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

SPI 概述

Serial Peripheral interface 通用串行外圍設(shè)備接口是 Motorola 首先在其 MC68HCXX 系列處理器上定義的。SPI 接口主要應(yīng)用在 EEPROM,F(xiàn)LASH,實(shí)時(shí)時(shí)鐘,AD 轉(zhuǎn)換器,還有數(shù)字信號(hào)處理器和數(shù)字信號(hào)解碼器之間。

SPI,是一種高速的,全雙工,同步的通信總線,并且在芯片的管腳上只占用四根線,節(jié)約了芯片的管腳,同時(shí)為 PCB 的布局上節(jié)省空間。

SPI 特點(diǎn)

采用主 - 從模式(Master-Slave) 的控制方式

SPI 規(guī)定了兩個(gè) SPI 設(shè)備之間通信必須由主設(shè)備 (Master) 來(lái)控制次設(shè)備 (Slave). 一個(gè) Master 設(shè)備可以通過(guò)提供 Clock 以及對(duì) Slave 設(shè)備進(jìn)行片選 (Slave Select) 來(lái)控制多個(gè) Slave 設(shè)備, SPI 協(xié)議還規(guī)定 Slave 設(shè)備的 Clock 由 Master 設(shè)備通過(guò) SCK 管腳提供給 Slave 設(shè)備, Slave 設(shè)備本身不能產(chǎn)生或控制 Clock, 沒(méi)有 Clock 則 Slave 設(shè)備不能正常工作。

而這里的 SPI 中的時(shí)鐘和相位,指的就是 SCLk 時(shí)鐘的特性,即保證主從設(shè)備兩者的時(shí)鐘的特性一致了,以保證兩者可以正常實(shí)現(xiàn) SPI 通訊。

采用同步方式(Synchronous)傳輸數(shù)據(jù)

Master 設(shè)備會(huì)根據(jù)將要交換的數(shù)據(jù)來(lái)產(chǎn)生相應(yīng)的時(shí)鐘脈沖(Clock Pulse), 時(shí)鐘脈沖組成了時(shí)鐘信號(hào)(Clock Signal) , 時(shí)鐘信號(hào)通過(guò)時(shí)鐘極性 (CPOL) 和 時(shí)鐘相位 (CPHA) 控制著兩個(gè) SPI 設(shè)備間何時(shí)數(shù)據(jù)交換以及何時(shí)對(duì)接收到的數(shù)據(jù)進(jìn)行采樣, 來(lái)保證數(shù)據(jù)在兩個(gè)設(shè)備之間是同步傳輸的?。

工作機(jī)制

概述

首先看下 SPI Data Transfer 模塊圖。

上圖只是對(duì) SPI 設(shè)備間通信的一個(gè)簡(jiǎn)單的描述, 下面詳細(xì)解釋一下圖中所示的幾個(gè)組件(Module):

SSPBUF

Synchronous Serial Port Buffer, 泛指 SPI 設(shè)備里面的內(nèi)部緩沖區(qū), 一般在物理上是以 FIFO 的形式, 保存?zhèn)鬏斶^(guò)程中的臨時(shí)數(shù)據(jù);

我們知道, 在每個(gè)時(shí)鐘周期內(nèi), Master 與 Slave 之間交換的數(shù)據(jù)其實(shí)都是 SPI 內(nèi)部移位寄存器從 SSPBUF 里面拷貝的 . 我們可以通過(guò)往 SSPBUF 對(duì)應(yīng)的寄存器 (Tx-Data / Rx-Data register) 里讀寫數(shù)據(jù), 間接地操控 SPI 設(shè)備內(nèi)部的 SSPBUF。

例如, 在發(fā)送數(shù)據(jù)之前, 我們應(yīng)該先往 Master 的 Tx-Data 寄存器寫入將要發(fā)送出去的數(shù)據(jù), 這些數(shù)據(jù)會(huì)被 Master-SSPSR 移位寄存器根據(jù) Bus-Width 自動(dòng)移入 Master-SSPBUF 里, 然后這些數(shù)據(jù)又會(huì)被 Master-SSPSR 根據(jù) Channel-Width 從 Master-SSPBUF 中移出, 通過(guò) Master-SDO 管腳傳給 Slave-SDI 管腳, Slave-SSPSR 則把從 Slave-SDI 接收到的數(shù)據(jù)移入 Slave-SSPBUF 里 . 與此同時(shí), Slave-SSPBUF 里面的數(shù)據(jù)根據(jù)每次接收數(shù)據(jù)的大小(Channel-Width), 通過(guò) Slave-SDO 發(fā)往 Master-SDI, Master-SSPSR 再把從 Master-SDI 接收的數(shù)據(jù)移入 Master-SSPBUF. 在單次數(shù)據(jù)傳輸完成之后, 用戶程序可以通過(guò)從 Master 設(shè)備的 Rx-Data 寄存器讀取 Master 設(shè)備數(shù)據(jù)交換得到的數(shù)據(jù)。

SSPSR

Synchronous Serial Port Register, 泛指 SPI 設(shè)備里面的移位寄存器(Shift Regitser), 它的作用是根據(jù)設(shè)置好的數(shù)據(jù)位寬(bit-width) 把數(shù)據(jù)移入或者移出 SSPBUF;

SSPSR 是 SPI 設(shè)備內(nèi)部的移位寄存器(Shift Register). 它的主要作用是根據(jù) SPI 時(shí)鐘信號(hào)狀態(tài), 往 SSPBUF 里移入或者移出數(shù)據(jù), 每次移動(dòng)的數(shù)據(jù)大小由 Bus-Width 以及 Channel-Width 所決定。Bus-Width 的作用是指定地址總線到 Master 設(shè)備之間數(shù)據(jù)傳輸?shù)膯挝?. 例如, 我們想要往 Master 設(shè)備里面的 SSPBUF 寫入 16 Byte 大小的數(shù)據(jù): 首先, 給 Master 設(shè)備的配置寄存器設(shè)置 Bus-Width 為 Byte; 然后往 Master 設(shè)備的 Tx-Data 移位寄存器在地址總線的入口寫入數(shù)據(jù), 每次寫入 1 Byte 大小的數(shù)據(jù)(使用 writeb 函數(shù)); 寫完 1 Byte 數(shù)據(jù)之后, Master 設(shè)備里面的 Tx-Data 移位寄存器會(huì)自動(dòng)把從地址總線傳來(lái)的 1 Byte 數(shù)據(jù)移入 SSPBUF 里; 上述動(dòng)作一共需要重復(fù)執(zhí)行 16 次。

Channel-Width 的作用是指定 Master 設(shè)備與 Slave 設(shè)備之間數(shù)據(jù)傳輸?shù)膯挝?. 與 Bus-Width 相似, Master 設(shè)備內(nèi)部的移位寄存器會(huì)依據(jù) Channel-Width 自動(dòng)地把數(shù)據(jù)從 Master-SSPBUF 里通過(guò) Master-SDO 管腳搬運(yùn)到 Slave 設(shè)備里的 Slave-SDI 引腳, Slave-SSPSR 再把每次接收的數(shù)據(jù)移入 Slave-SSPBUF 里 . 通常情況下, Bus-Width 總是會(huì)大于或等于 Channel-Width, 這樣能保證不會(huì)出現(xiàn)因 Master 與 Slave 之間數(shù)據(jù)交換的頻率比地址總線與 Master 之間的數(shù)據(jù)交換頻率要快, 導(dǎo)致 SSPBUF 里面存放的數(shù)據(jù)為無(wú)效數(shù)據(jù)這樣的情況

Controller

泛指 SPI 設(shè)備里面的控制寄存器, 可以通過(guò)配置它們來(lái)設(shè)置 SPI 總線的傳輸模式。通常情況下, 我們只需要對(duì)上圖所描述的四個(gè)管腳(pin) 進(jìn)行編程即可控制整個(gè) SPI 設(shè)備之間的數(shù)據(jù)通信。

Master 設(shè)備里面的 Controller 主要通過(guò)時(shí)鐘信號(hào)(Clock Signal)以及片選信號(hào)(Slave Select Signal)來(lái)控制 Slave 設(shè)備 . Slave 設(shè)備會(huì)一直等待, 直到接收到 Master 設(shè)備發(fā)過(guò)來(lái)的片選信號(hào), 然后根據(jù)時(shí)鐘信號(hào)來(lái)工作。

Master 設(shè)備的片選操作必須由程序所實(shí)現(xiàn) . 例如: 由程序把 SS/CS 管腳的時(shí)鐘信號(hào)拉低電平, 完成 SPI 設(shè)備數(shù)據(jù)通信的前期工作; 當(dāng)程序想讓 SPI 設(shè)備結(jié)束數(shù)據(jù)通信時(shí), 再把 SS/CS 管腳上的時(shí)鐘信號(hào)拉高電平 .

SCK

Serial Clock, 主要的作用是 Master 設(shè)備往 Slave 設(shè)備傳輸時(shí)鐘信號(hào), 控制數(shù)據(jù)交換的時(shí)機(jī)以及速率;

SS/CS

Slave Select/Chip Select, 用于 Master 設(shè)備片選 Slave 設(shè)備, 使被選中的 Slave 設(shè)備能夠被 Master 設(shè)備所訪問(wèn)。

SDO/MOSI

Serial Data Output/Master Out Slave In, 在 Master 上面也被稱為 Tx-Channel, 作為數(shù)據(jù)的出口, 主要用于 SPI 設(shè)備發(fā)送數(shù)據(jù)。

SDI/MISO

Serial Data Input/Master In Slave Out, 在 Master 上面也被稱為 Rx-Channel, 作為數(shù)據(jù)的入口, 主要用于 SPI 設(shè)備接收數(shù)據(jù)。

SPI 設(shè)備在進(jìn)行通信的過(guò)程中, Master 設(shè)備和 Slave 設(shè)備之間會(huì)產(chǎn)生一個(gè)數(shù)據(jù)鏈路回環(huán)(Data Loop), 就像上圖所畫的那樣, 通過(guò) SDO 和 SDI 管腳, SSPSR 控制數(shù)據(jù)移入移出 SSPBUF, Controller 確定 SPI 總線的通信模式, SCK 傳輸時(shí)鐘信號(hào)。

極性和相位

要想搞清楚 SPI 的數(shù)據(jù)傳輸,首先要搞清楚相位和極性的概念,即 SPI 的極性 Polarity 和相位 Phase。

最常見(jiàn)的寫法是 CPOL 和 CPHA,不過(guò)也有一些其他寫法,簡(jiǎn)單總結(jié)如下:

  • (1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (時(shí)鐘)極性(2) CKPHA (Clock Phase) ? = CPHA = PHA = Phase = (時(shí)鐘)相位(3) SCK=SCLK=SPI 的時(shí)鐘(4) Edge=邊沿,即時(shí)鐘電平變化的時(shí)刻,即上升沿(rising edge)或者下降沿(falling edge)

對(duì)于一個(gè)時(shí)鐘周期內(nèi),有兩個(gè) edge,分別稱為:

(1)Leading edge=前一個(gè)邊沿=第一個(gè)邊沿,對(duì)于開(kāi)始電壓是 1, 那么就是 1 變成 0 的時(shí)候,對(duì)于開(kāi)始電壓是 0,那么就是 0 變成 1 的時(shí)候;

(2)Trailing edge=后一個(gè)邊沿=第二個(gè)邊沿,對(duì)于開(kāi)始電壓是 1, 那么就是 0 變成 1 的時(shí)候(即在第一次 1 變成 0 之后,才可能有后面的 0 變成 1), 對(duì)于開(kāi)始電壓是 0,那么就是 1 變成 0 的時(shí)候;

本博文采用如下用法:

極性=CPOL 相位=CPHASCLK=時(shí)鐘第一個(gè)邊沿和第二個(gè)邊沿

CPOL 和 CPHA,分別都可以是 0 或時(shí) 1,對(duì)應(yīng)的四種組合就是:

模式 極性 相位
Mode 1 CPOL=0,CPHA=0
Mode 2 CPOL=0,CPHA=1
Mode 3 CPOL=1,CPHA=0
Mode 4 CPOL=1,CPHA=1

采樣示意圖

CPOL=0,CPHA=0

脈沖傳輸前和完成后都保持在低電平狀態(tài),所以 CPOL=0,即低電平是空閑時(shí)的電平。在第一個(gè)邊沿(上升沿)采樣數(shù)據(jù),第二個(gè)邊沿(下降沿)輸出數(shù)據(jù),對(duì)應(yīng)著 CPHA=0。

CPOL=0,CPHA=1

脈沖傳輸前和完成后都保持在低電平狀態(tài),所以 CPOL=0,即低電平是空閑時(shí)的電平。在第二個(gè)邊沿(下降沿)采樣數(shù)據(jù),第一個(gè)邊沿(上升沿)輸出數(shù)據(jù),對(duì)應(yīng)著 CPHA=1。

CPOL=1,CPHA=0

脈沖傳輸前和完成后都保持在高電平狀態(tài),所以 CPOL=1,即高電平是空閑時(shí)的電平。在第一個(gè)邊沿(下降沿)采樣數(shù)據(jù),第二個(gè)邊沿(上升沿)輸出數(shù)據(jù),對(duì)應(yīng)著 CPHA=0。

CPOL=1,CPHA=1

脈沖傳輸前和完成后都保持在高電平狀態(tài),所以 CPOL=1,即高電平是空閑時(shí)的電平。在第二個(gè)邊沿(上升沿)采樣數(shù)據(jù),第一個(gè)邊沿(下降沿)輸出數(shù)據(jù),對(duì)應(yīng)著 CPHA=1。

CPOL=1,CPHA=1

軟件中如何設(shè)置 SPI 的極性和相位

SPI 分主設(shè)備和從設(shè)備,兩者通過(guò) SPI 協(xié)議通訊。而設(shè)置 SPI 的模式,是從設(shè)備的模式,決定了主設(shè)備的模式。所以要先去搞懂從設(shè)備的 SPI 是何種模式,然后再將主設(shè)備的 SPI 的模式,設(shè)置和從設(shè)備相同的模式,即可正常通訊。

對(duì)于從設(shè)備的 SPI 是什么模式,有兩種:

(1)固定的,有 SPI 從設(shè)備硬件決定的 SPI 從設(shè)備,具體是什么模式,相關(guān)的 datasheet 中會(huì)有描述,需要自己去 datasheet 中找到相關(guān)的描述,即:關(guān)于 SPI 從設(shè)備,在空閑的時(shí)候,是高電平還是低電平,即決定了 CPOL 是 0 還是 1; 然后再找到關(guān)于設(shè)備是在上升沿還是下降沿去采樣數(shù)據(jù),這樣就是,在定了 CPOL 的值的前提下,對(duì)應(yīng)著可以推算出 CPHA 是 0 還是 1 了。

(2)可配置的,由軟件自己設(shè)定 從設(shè)備也是一個(gè) SPI 控制器,4 種模式都支持,此時(shí)只要自己設(shè)置為某種模式即可。然后知道了從設(shè)備的模式后,再去將 SPI 主設(shè)備的模式,設(shè)置為和從設(shè)備模式一樣即可。對(duì)于如何配置 SPI 的 CPOL 和 CPHA 的話,不多細(xì)說(shuō),多數(shù)都是直接去寫對(duì)應(yīng)的 SPI 控制器中對(duì)應(yīng)寄存器中的 CPOL 和 CPHA 那兩位,寫 0 或?qū)?1 即可。

數(shù)據(jù)交換(Data Exchanges)

SPI 只有主模式和從模式之分,沒(méi)有讀和寫的說(shuō)法,因?yàn)閷?shí)質(zhì)上每次 SPI 是主從設(shè)備在交換數(shù)據(jù)。也就是說(shuō),你發(fā)一個(gè)數(shù)據(jù)必然會(huì)收到一個(gè)數(shù)據(jù);你要收一個(gè)數(shù)據(jù)必須也要先發(fā)一個(gè)數(shù)據(jù)。

SPI 設(shè)備間的數(shù)據(jù)傳輸之所以又被稱為數(shù)據(jù)交換, 是因?yàn)?SPI 協(xié)議規(guī)定一個(gè) SPI 設(shè)備不能在數(shù)據(jù)通信過(guò)程中僅僅只充當(dāng)一個(gè) "發(fā)送者(Transmitter)" 或者 "接收者(Receiver)"。在每個(gè) Clock 周期內(nèi), SPI 設(shè)備都會(huì)發(fā)送并接收一個(gè) bit 大小的數(shù)據(jù), 相當(dāng)于該設(shè)備有一個(gè) bit 大小的數(shù)據(jù)被交換了。

一個(gè) Slave 設(shè)備要想能夠接收到 Master 發(fā)過(guò)來(lái)的控制信號(hào), 必須在此之前能夠被 Master 設(shè)備進(jìn)行訪問(wèn) (Access)。所以, Master 設(shè)備必須首先通過(guò) SS/CS pin 對(duì) Slave 設(shè)備進(jìn)行片選, 把想要訪問(wèn)的 Slave 設(shè)備選上。 在數(shù)據(jù)傳輸?shù)倪^(guò)程中, 每次接收到的數(shù)據(jù)必須在下一次數(shù)據(jù)傳輸之前被采樣 . 如果之前接收到的數(shù)據(jù)沒(méi)有被讀取, 那么這些已經(jīng)接收完成的數(shù)據(jù)將有可能會(huì)被丟棄, 導(dǎo)致 SPI 物理模塊最終失效。

因此, 在程序中一般都會(huì)在 SPI 傳輸完數(shù)據(jù)后, 去讀取 SPI 設(shè)備里的數(shù)據(jù), 即使這些數(shù)據(jù)(Dummy Data)在我們的程序里是無(wú)用的。

SPI 舉例

下面舉一個(gè)例子幫助大家理解。

SPI 是一個(gè)環(huán)形總線結(jié)構(gòu),由 ss(cs)、sck、sdi、sdo 構(gòu)成,其時(shí)序其實(shí)很簡(jiǎn)單,主要是在 sck 的控制下,兩個(gè)雙向移位寄存器進(jìn)行數(shù)據(jù)交換。

假設(shè)下面的 8 位寄存器裝的是待發(fā)送的數(shù)據(jù) 10101010,上升沿發(fā)送、下降沿接收、高位先發(fā)送。那么第一個(gè)上升沿來(lái)的時(shí)候 數(shù)據(jù)將會(huì)是 sdo=1;寄存器=0101010x。下降沿到來(lái)的時(shí)候,sdi 上的電平將所存到寄存器中去,那么這時(shí)寄存器=0101010sdi,這樣在 8 個(gè)時(shí)鐘脈沖以后,兩個(gè)寄存器的內(nèi)容互相交換一次。這樣就完成里一個(gè) spi 時(shí)序。

舉例:假設(shè)主機(jī)和從機(jī)初始化就緒:并且主機(jī)的 sbuff=0xaa,從機(jī)的 sbuff=0x55,下面將分步對(duì) spi 的 8 個(gè)時(shí)鐘周期的數(shù)據(jù)情況演示一遍,假設(shè)上升沿發(fā)送數(shù)據(jù)

這樣就完成了兩個(gè)寄存器 8 位的交換,上面的上表示上升沿、下表示下降沿,sdi、sdo 相對(duì)于主機(jī)而言的。

下一步就是把 上面的過(guò)程轉(zhuǎn)為動(dòng)畫

交換數(shù)據(jù)

交換前

在這里插入圖片描述

請(qǐng)仔細(xì)比較下交換后的 bit 順序。

了解了 SPI 協(xié)議說(shuō)明之后,下面我們基于 Cortex-A9 架構(gòu)的 exynos-4412,講解 SPI 控制器的使用。

Cortex-A9 SPI 控制器

硬件設(shè)計(jì)

本例是基于 FS4412 開(kāi)發(fā)板,SPI 控制器外接了 MCP2515,MCP2515 與 exynos-4412 的硬件連接圖如下圖所示。

管腳連接說(shuō)明

由上圖可知 SPI 個(gè)引腳與 SOC 的 pin 連接關(guān)系:

CS ? ?<---------> BUF_BK_LED ? <---------> GPC1_2

SO ? ?<---------> BUF_I2C_SDA6 ? ? <---------> GPC1_3

SI ? ? ?<---------> BUF_I2C_SCL6 ? ? ? <---------> GPC1_4

SCK ?<---------> BUF_GPC1_1 ? ? ? ?<---------> GPC1_1

INT ? <-------------------------------------------> BUF_GPX0_0

MCP2515 芯片連接在 4412 芯片的 SPI2 上。

中斷連接在 GPX0_0 上;CS、SO、SI、SCK 復(fù)用了 GPIO 引腳 GPC1 的引腳。

MCP2515 輸出連接 SN65HVD230 CAN 總線收發(fā)器,SN65HVD230 是德州儀器公司生產(chǎn)的 3.3V CAN 收發(fā)器。為了節(jié)省功耗,縮小電路體積,MCP2515 CAN 總線控制器的邏輯電平采用 LVTTL,SN65HVD230 就是與其配套的收發(fā)器。

Cortex-A9 SPI 控制器

exynos4412 scp 中的串行外設(shè)接口(SPI)通過(guò)各種外設(shè)來(lái)傳輸串行數(shù)據(jù)。SPI 包括兩個(gè) 8、16 和 32 位移位寄存器,用于傳輸和接收數(shù)據(jù)。在 SPI 傳輸過(guò)程中,它同時(shí)傳輸(串行移出)和接收(串行移位)數(shù)據(jù)。

特性

  • 全雙工用于 Tx/Rx 的 8/16/32 位移位寄存器支持 8 位 /16 位 /32 位總線接口支持摩托羅拉 SPI 協(xié)議和 National Semiconductor Microwire 兩個(gè)獨(dú)立的 32 位寬的發(fā)送和接收 FIFO:端口 0 的深度為 64,端口 1 和 2 中的深度為 16 主模式和從模式接收而不發(fā)送操作發(fā)送 / 接收最高頻率為 50 MHz

SPI 的操作

SPI 在 Exynos 4412 SCP 和外部設(shè)備之間傳輸 1 位串行數(shù)據(jù)。Exynos 4412 SCP 中的 SPI 支持 CPUDMA 分別同時(shí)發(fā)送或接收 FIFO 和雙向傳輸數(shù)據(jù)。SPI 有兩個(gè)信道,即 Tx 信道和 Rx 信道。Tx 信道有來(lái)自 Tx 的路徑 FIFO 到外部設(shè)備。Rx 通道有從外部設(shè)備到 Rx FIFO 的路徑。

CPU(或 DMA)必須將數(shù)據(jù)寫入寄存器 SPI_TX_DATA,才能將數(shù)據(jù)寫入 FIFO。寄存器上的數(shù)據(jù)會(huì)自動(dòng)移動(dòng)到 Tx FIFO。要從 Rx FIFO 讀取數(shù)據(jù),CPU(或 DMA)必須訪問(wèn)寄存器 SPI_RX_DATA,數(shù)據(jù)會(huì)自動(dòng)發(fā)送到 SPI_RX_DATA 寄存器。

CMU 寄存器可以控制 SPI 的工作頻率。

操作模式

SPI 有兩種模式,即主模式和從模式。在主模式下,生成 SPICLK 并將其傳輸?shù)酵獠吭O(shè)備。XspiCS#是選擇從機(jī)的信號(hào),它指示在設(shè)置 XspiCS 時(shí)數(shù)據(jù)有效低水平。在發(fā)送或接收數(shù)據(jù)包之前,必須將 XspiCS 設(shè)置為低。

FIFO 存取

SPI 支持對(duì) fifo 的 CPU 訪問(wèn)和 DMA 訪問(wèn)。對(duì) fifo 的 CPU 訪問(wèn)和 DMA 訪問(wèn)的數(shù)據(jù)大小從 8 位、16 位或 32 位數(shù)據(jù)中選擇。當(dāng)它選擇 8 位數(shù)據(jù)大小時(shí),有效位是 0 到 7 比特。用戶可以定義觸發(fā)閾值來(lái)引發(fā) CPU 中斷。端口 0 中每個(gè) FIFO 的觸發(fā)電平由從 0 到 252 字節(jié)的步進(jìn)為 4 個(gè)字節(jié),端口 1 中每個(gè) FIFO 的字節(jié)從 0 到 63 字節(jié)按 1 個(gè)字節(jié)的步長(zhǎng)設(shè)置。SPI_MODE_CFG 寄存器的 TxDMAOn 或 RxDMAOn 位必須設(shè)置為使用 DMA 訪問(wèn)。DMA 訪問(wèn)支持只有單次傳輸和 4 突發(fā)傳輸。在 Tx FIFO 中,DMA 請(qǐng)求信號(hào)是高的,直到 Tx FIFO 滿為止。在 Rx FIFO 中,如果 FIFO 不為空,DMA 請(qǐng)求信號(hào)高。

片選控制

芯片選擇 XspiCS 是激活的低信號(hào)。換句話說(shuō),當(dāng) XspiCS 輸入為 0 時(shí),選擇芯片。您可以自動(dòng)或手動(dòng)控制 XspiCS。不需要改變。手動(dòng)模式當(dāng)您使用手動(dòng)控制模式時(shí),您應(yīng)清除 AUTO_N_MANUAL(默認(rèn)值為 0)。NSSOUT 位控制 XspiCS 級(jí)別。自動(dòng)模式使用自動(dòng)控制模式時(shí),必須將 AUTO_N_MANUAL 設(shè)置為 1。XspiCS 在數(shù)據(jù)包和自動(dòng)打包。NCS_TIME_COUNT 控制 XspiCS 的非激活期。NSSOUT 在此時(shí)不可用。

SPI 寄存器說(shuō)明

配置寄存器 CH_CFGn

【bit:5】 ?軟件復(fù)位的時(shí)候必須先將此位設(shè)置為 1,給一個(gè)延時(shí)后再設(shè)置為 0;【bit:4】 ?設(shè)置 SPI 端口是主機(jī)還是從機(jī),0:主機(jī),1:從機(jī);【bit:2-3】設(shè)置相位和極性;【bit:1】接收數(shù)據(jù)的通道使能位;【bit:0】發(fā)送數(shù)據(jù)的通道使能位。

數(shù)據(jù)寬度寄存器 MODE_CFGn

設(shè)置總線數(shù)據(jù)寬度,一般設(shè)置為 1 個(gè)字節(jié)。

片選寄存器 CS_REGn

只有 NSSOUT 拉低,才會(huì)進(jìn)行數(shù)據(jù)的傳輸

移位寄存器狀態(tài)寄存器 SPI_STATUSn

發(fā)送操作開(kāi)始,如果移位寄存器空了,該值置 1,通過(guò)該值判斷數(shù)據(jù)是否發(fā)送出去。

發(fā)送緩沖寄存器 SPI_TX_DATA

SPI_TX_DATA

接收緩沖寄存器 SPI_RX_DATA

SPI_RX_DATA

SPI 初始化流程

  1. 設(shè)置 GPIO 引腳為 SPI 模式;設(shè)置 clock;軟件復(fù)位;設(shè)置 CPOL CPHA ?為 00 模式,并設(shè)置為主機(jī)模式;設(shè)置數(shù)據(jù)位;片選。

1. 設(shè)置 GPIO 引腳為 SPI 模式

因?yàn)?CS、SO、SI、SCK 復(fù)用了 GPIO 引腳 GPC1 的引腳。首先需要設(shè)置 GPIO 引腳為 SPI 模式。

如上所示,該寄存器設(shè)置方式如下:

GPC1.CON?=?(GPC1.CON?&?~0xffff0)?|?0x55550;// 設(shè)置 IO 引腳為 SPI 模式

Step 1 ?設(shè)置 CPOL CPHA ?為 00 模式

SPI2.CH_CFG&=~((0x1<< 4)|(0x1<<3)|(0x1<< 2)|0x3);

//master mode, CPOL = 0, CPHA = 0 (Format A)

2 設(shè)置 clock

時(shí)鐘的設(shè)置需要依賴鎖相環(huán)(PLL)時(shí)鐘產(chǎn)生器。

從 30.2.1 節(jié)可知,時(shí)鐘配置需要參考 CMU 這一章。

由上圖可知 SPI 的 clock 源是 SCLK_SPI。

搜索 SCLK_SPI

從第七章搜所有的 SPI

繼續(xù)搜索

由此可知 SPI0~2 的之中受 CLKMPLL_USER_T 控制,繼續(xù)搜索。而此時(shí)鐘位于寄存器 CLK_SRC_PERIL1 的 bit[27:24]。

繼續(xù)搜索 SPI2。

搜索到 CLK_SRC_MASK_PERIL1、CLK_DIV_PERIL2??芍拇嫫?CLK_SRC_MASK_PERIL1 默認(rèn)是打開(kāi)的,所以不用設(shè)置。CLK_DIV_PERIL2 用來(lái)設(shè)置 SPI2 分頻的,于是寄存器設(shè)置如下:

CLK_SRC_PERIL1?=?(CLK_SRC_PERIL1?&?~(0xF<<24))?|?6<<24;
//?0x6:?0110?=?SCLKMPLL_USER_T?800Mhz
CLK_DIV_PERIL2?=?19?<<8?|?3;//SPI_CLK?=?800/(19+1)/(3+1)

3. 軟件復(fù)位

在這里插入圖片描述

void?soft_reset(void)
{?SPI2.CH_CFG?|=?0x1?<<?5;
?delay(1);?????????????????????// 延時(shí)
?SPI2.CH_CFG?&=?~(0x1?<<?5);
}

4. 設(shè)置 CPOL CPHA ?為 00 模式,并設(shè)置為主機(jī)模式

SPI2.CH_CFG?&=?~(?(0x1?<<?4)?|?(0x1?<<?3)?|?(0x1?<<?2)?|?0x3);

5. ?設(shè)置數(shù)據(jù)位 MODE_CFG

MODE_CFG

SPI2.MODE_CFG?&=?~((0x3?<<?17)?|?(0x3?<<?29));
???//BUS_WIDTH=8bit,CH_WIDTH=8bit?

6. ?片選

SPI2.CS_REG?&=?~(0x1?<<?1);????????// 選擇手動(dòng)選擇芯片

初始化搞定后,下面我們來(lái)看如何利用 SPI 收發(fā)數(shù)據(jù)。

收發(fā)數(shù)據(jù)流程

收發(fā)數(shù)據(jù)流程如下:

  1. spi 復(fù)位片選從機(jī)收發(fā)數(shù)據(jù)取消片選

【注意】一下代碼為設(shè)置每次只讀寫 1 個(gè) byte 數(shù)據(jù),每次讀寫 1 個(gè) byte 數(shù)據(jù)都要按照上述步驟操作。

1 spi 復(fù)位

void?soft_reset(void)
{?SPI2.CH_CFG?|=?0x1?<<?5;
??delay(1);?????????????????????// 延時(shí)
??SPI2.CH_CFG?&=?~(0x1?<<?5);
}

2 片選從機(jī)

void?slave_enable(void)
{
?SPI2.CS_REG?&=?~0x1;?//enable?salve
?delay(3);
}

3.1 發(fā)送數(shù)據(jù)

void?send_byte(unsigned?char?data)
{
?SPI2.CH_CFG?|=?0x1;?//?enable?Tx?Channel
?delay(1);
?SPI2.SPI_TX_DATA?=?data;
?while(?!(SPI2.SPI_STATUS?&?(0x1?<<?25))?);
?SPI2.CH_CFG?&=?~0x1;?//?disable?Tx?Channel
}

3.2 接收數(shù)據(jù)

unsigned?char?recv_byte()
{
?unsigned?char?data;
?SPI2.CH_CFG?|=?0x1?<<?1;?//?enable?Rx?Channel
?delay(1);
?data?=?SPI2.SPI_RX_DATA;
?delay(1);
?SPI2.CH_CFG?&=?~(0x1?<<?1);?//disable?Rx?Channel
?return??data;
}

取消片選

void?slave_disable(void)
{

?SPI2.CS_REG?|=?0x1;?//disable?salve
?delay(1);
}

OK,到底為止,如何通過(guò) SPI 收發(fā)數(shù)據(jù),我們已經(jīng)可以實(shí)現(xiàn)了,但是 SPI 往往外部回接各種各樣的從設(shè)備。下面我們來(lái)看 SPI 如何操作從設(shè)備。

上面我們說(shuō)了,fs4412 開(kāi)發(fā)板的 SPI2 外設(shè)外接了 MCP2515,現(xiàn)在我們來(lái)分析一下 MCP2515。

MCP2515

MCP2515 詳細(xì)資料,大家自行搜索 MCP2515 datasheet 《帶有 SPI 接口的獨(dú)立 CAN 控制器》。

簡(jiǎn)介

MCP2515 是一種獨(dú)立的 CAN 總線通信控制器,是 Microchip 公司首批獨(dú)立 CAN 解決方案的升級(jí)器件,其傳輸能力較 Microchip 公司原有 CAN 控制器(MCP2510)高兩倍,高通信速率可達(dá)到 1Mbps。MCP2515 能夠接收和發(fā)送標(biāo)準(zhǔn)數(shù)據(jù)幀和擴(kuò)展數(shù)據(jù)幀以及遠(yuǎn)程幀,通過(guò)兩個(gè)接收屏蔽寄存器和六個(gè)接收過(guò)濾寄存器濾除無(wú)關(guān)報(bào)文,從而減輕 CPU 負(fù)擔(dān)。

特性

MCP2515 主要功能參數(shù)及電氣特性如下:

  • (1)支持 CAN 技術(shù)規(guī)范 2.0A/B, 高傳輸速率達(dá)到 1Mbps;(2)支持標(biāo)準(zhǔn)數(shù)據(jù)幀、擴(kuò)展數(shù)據(jù)幀和遠(yuǎn)程幀,每幀數(shù)據(jù)域長(zhǎng)度可為 0~8 個(gè)字節(jié);(3)內(nèi)含兩個(gè)的接收緩沖器和三個(gè)發(fā)送緩沖器,并且可編程設(shè)定優(yōu)先級(jí);(4)內(nèi)含六個(gè) 29 位(bit)的接收過(guò)濾寄存器和兩個(gè) 29 位(bit)的接收屏蔽寄存器;(5)高速 SPI 接口,支持 SPI 0,0 和 1,1 模式;(6)一次性模式可確保報(bào)文被一次性傳輸;(7)具有可編程時(shí)鐘脈沖輸出引腳,可作為其他芯片時(shí)鐘信號(hào)源;(8) 幀起始(SOF)信號(hào)輸出功能可被用于在確定的系統(tǒng)中(如時(shí)間觸發(fā) CAN-TTCAN)執(zhí)行時(shí)隙功能,或在 CAN 總線診斷中決定早期總線出級(jí);(9) 采用低功耗 CMOS 技術(shù),工作電壓:2.7V~5.5V, 工作電流:5mA(待機(jī)狀態(tài) 1μA);(10)工作溫度范圍:(I)-40℃到+85℃,(E)-40℃到+125℃。

結(jié)構(gòu)框圖

CAN 模塊

MCP2515 是一款獨(dú)立 CAN 控制器, 可簡(jiǎn)化需要與 CAN 總線連接的應(yīng)用。如上圖 簡(jiǎn)要顯示了 MCP2515 的結(jié)構(gòu)框圖。該器件主要由三個(gè)部分組成:

CAN 模塊,包括 CAN 協(xié)議引擎、驗(yàn)收濾波寄存 器、驗(yàn)收屏蔽寄存器、發(fā)送和接收緩沖器。用于配置該器件及其運(yùn)行的控制邏輯和寄存器。SPI 協(xié)議模塊。

CAN 模塊的功能是處理所有 CAN 總線上的報(bào)文接收和發(fā)送。報(bào)文發(fā)送時(shí),首先將報(bào)文裝載到正確的報(bào)文緩沖器和控制寄存器中。通過(guò) SPI 接口設(shè)置控制寄存器中的相應(yīng)位或使用發(fā)送使能引腳均可啟動(dòng)發(fā)送操作。通過(guò)讀取相應(yīng)的寄存器可以檢查通訊狀態(tài)和錯(cuò)誤。會(huì)對(duì)在 CAN 總線上檢測(cè)到的任何報(bào)文進(jìn)行錯(cuò)誤檢查,然后與用戶定義的濾波器進(jìn)行匹配,以確定是否將報(bào)文移到兩個(gè)接收緩沖器中的一個(gè)。

SPI 協(xié)議模塊

MCU 通過(guò) SPI 接口與該器件連接。使用標(biāo)準(zhǔn)的 SPI 讀 / 寫指令以及專門的 SPI 命令來(lái)讀 / 寫所有的寄存器。MCP2515 設(shè)計(jì)可與許多單片機(jī)的串行外設(shè)接口( SPI)直接相連,支持 0,0 和 1,1 運(yùn)行模式。外部數(shù)據(jù)和命令通過(guò) SI 引腳傳送到器件中,且數(shù)據(jù)在 SCK 時(shí)鐘信號(hào)的上升沿傳送進(jìn)去。MCP2515 在 SCK 的下降沿通過(guò) SO 引腳傳送出去。在進(jìn)行任何操作時(shí), CS 引腳都必須保持為低電平。

與 MCP2515 通信

SPI 指令集

我們要想操作 MCP2515,只能用過(guò) SPI 總線向 MCP2515 發(fā)送一些數(shù)據(jù),根據(jù)規(guī)定,發(fā)送不同的數(shù)據(jù)就代表不同的操作,于是就形成了對(duì)應(yīng)的指令集。

如上圖所示,比如我們要執(zhí)行復(fù)位操作,那么我只需要通過(guò) SPI 總線寫入 11000000,即 0xC0 即可。

下面我們來(lái)詳細(xì)分析如何向 MCP2515 發(fā)送復(fù)位命令,如何讀寫數(shù)據(jù)。

復(fù)位

因?yàn)閺?fù)位只需要發(fā)送 0XC0 即可,并不需要其他額外操作,所以我們只需要根據(jù)我們之前章節(jié)所講的 SPI 發(fā)送數(shù)據(jù)流程操作接口。

void?reset_2515()
{
?soft_reset();??????// 復(fù)位 spi 控制器
????slave_enable()?;???// 片選從機(jī)
?send_byte(0xc0);???// 發(fā)送復(fù)位命令
?slave_disable()?;??// 取消片選

}

讀取數(shù)據(jù)

上圖可知,讀取數(shù)據(jù)流程如下:

片選從機(jī)通過(guò) SPI 發(fā)送指令 0x03 發(fā)送地址讀取數(shù)據(jù)取消片選

unsigned?char?read_byte_2515(unsigned?char?Addr)
{
?unsigned?char?ret;
????slave_enable();
????send_byte(0x03);
????send_byte(Addr);
????ret?=?recv_byte();
????slave_disable();
????return(ret);
}

發(fā)送數(shù)據(jù)

由上圖可知,發(fā)送數(shù)據(jù)流程如下:

  1. 片選從機(jī)通過(guò) SPI 發(fā)送指令 0x02 發(fā)送地址發(fā)送數(shù)據(jù)取消片選
void?write_byte_2515(unsigned?char?addr,unsigned?char?data)
{
????slave_enable();
????send_byte(0x02);
????send_byte(addr);
????send_byte(data);
????slave_disable();
}

讀 RX 緩沖器

裝載 TX 緩沖器

請(qǐng)求發(fā)送(RTS)指令

位修改指令

有些寄存器要修改對(duì)應(yīng)的 bit,必須先發(fā)送該指令,發(fā)送屏蔽字節(jié),然后才可以修改屏蔽字節(jié)中對(duì)應(yīng)位為 1 的值,后面會(huì)有例子給出。

CAN

知道該如何和 CAN 通信,下面我們來(lái)看如何初始化 CAN。

CAN 初始化

CAN 的初始化步驟如下:

  1. MCP2515 復(fù)位設(shè)置 MCP2515 為配置模式位定時(shí)配置,有配置寄存器 CNF1,CNF2,CNF3 控制中斷使能接收緩沖器配置引腳控制寄存器和狀態(tài)寄存器

此外為方便測(cè)試,我們?cè)偌右徊剑夯丨h(huán)模式,用于測(cè)試

1. MCP2515 復(fù)位

上一節(jié)已經(jīng)實(shí)現(xiàn)。

void?reset_2515()

2. 設(shè)置 MCP2515 為配置模式

由上圖所示,只需要向地址 0X0F 寫入數(shù)據(jù) 0x80 即可。

?write_byte_2515(0x0f,?0x80);????//CANCTRL 寄存器--進(jìn)入配置模式?中文 DATASHEET?58 頁(yè)

3. 位定時(shí)配置,有配置寄存器 CNF1,CNF2,CNF3 控制

CAN 總線上的所有節(jié)點(diǎn)都必須具有相同的標(biāo)稱比特率。CAN 協(xié)議采用不歸零( Non Return to Zero, NRZ)編碼方式,在數(shù)據(jù)流中不對(duì)時(shí)鐘信號(hào)進(jìn)行編碼。因此,接收時(shí)鐘信號(hào)必須由接收節(jié)點(diǎn)恢復(fù)并與發(fā)送器的時(shí)鐘同步。

由于不同節(jié)點(diǎn)的振蕩器頻率和傳輸時(shí)間不同,接收器應(yīng)具有某種能與數(shù)據(jù)傳輸邊沿同步的鎖相環(huán)( Phase Lock Loop, PLL)來(lái)同步時(shí)鐘并保持這種同步。

鑒于數(shù)據(jù)采用 NRZ 編碼,有必要進(jìn)行位填充以確保至少每 6 位時(shí)間發(fā)生一次邊沿,使數(shù)字鎖相環(huán) ( Digital Phase LockLoop, DPLL)同步。MCP2515 通過(guò) DPLL 實(shí)現(xiàn)位定時(shí)。DPLL 被配置成同輸入數(shù)據(jù)同步,并為發(fā)送數(shù)據(jù)提供標(biāo)稱定時(shí)。DPLL 將每一 個(gè) 位 時(shí) 間 分 割 為 由 最 小 單 位 為 時(shí) 間 份 額 ( Time Quanta, TQ)所組成的多個(gè)時(shí)間段。

在位時(shí)間幀中執(zhí)行的總線定時(shí)功能,例如與本地振蕩器同步、網(wǎng)絡(luò)傳輸延遲補(bǔ)償和采樣點(diǎn)定位等,都是由 DPLL 的可編程位定時(shí)邏輯來(lái)規(guī)定的。

CNF1BRP<5:0> 控制波特率預(yù)分頻比的設(shè)置。這些位根據(jù) OSC1 輸入頻率設(shè)置 TQ 的時(shí)間長(zhǎng)度。當(dāng) BRP<5:0> =‘b000000’, TQ 最小值取 2 TOSC。通過(guò) SJW<1:0> 選擇以 TQ 計(jì)的同步跳轉(zhuǎn)寬度。

CNF2PRSEG<2:0> 位設(shè)定以 TQ 計(jì)的傳播段時(shí)間長(zhǎng)度。PHSEG1<2:0>位設(shè)定以 TQ 計(jì)的相位緩沖段 PS1 的時(shí)間長(zhǎng)度。

CNF3 如果 CNF2.BTLMODE 位為 1,則相位緩沖段 PS2 的時(shí)間長(zhǎng)度將由 PHSEG2<2:0> 位設(shè)定,以 TQ 計(jì)。如果 BTLMODE 位為 0,則 PHSEG2<2:0> 位不起作用。

MCP2515 波特率配置以 16M 晶振為例, ? ? SJW ?段數(shù)(1+PRSEG+PRSEG1+PRSEG2)

#define?CNF1_20K?????0xd3????????//4???20(1+4+8+7)?
#define?CNF2_20K?????0xfb??????????
#define?CNF3_20K?????0x46???????????

// 可以設(shè)置的波特率?5K?10K?15K?20K?25K?40K?50K?80K?100K?125K?200K?400K?500K?667K?800K?1M
write_byte_2515(0x2A,?CNF1_20K);?//CNF1 位定時(shí)配置寄器??
write_byte_2515(0x29,?CNF2_20K);?//CNF2 位定時(shí)配置寄器?
write_byte_2515(0x28,?CNF3_20K);?//CNF3 位定時(shí)配置寄器?

4. 中斷使能

MCP2515 有八個(gè)中斷源。CANINTE 寄存器包含了使能各中斷源的中斷使能位。CANINTF 寄存器包含了各中斷源的中斷標(biāo)志位。當(dāng)發(fā)生中斷時(shí), INT 引腳將被 MCP2515 拉為低電平, 并保持低電平狀態(tài)直至 MCU 清除中斷。中斷只有在引起相應(yīng)中斷的條件消失后,才會(huì)被清除。建議在對(duì) CANINTF 寄存器中的標(biāo)志位進(jìn)行復(fù)位操作時(shí),采用位修改命令而不要使用一般的寫操作。這是為了避免在寫命令執(zhí)行過(guò)程中無(wú)意間修改了標(biāo)志位,進(jìn)而導(dǎo)致中斷丟失。應(yīng)該注意的是, CANINTF 中的中斷標(biāo)志位是可讀寫位,因此在相關(guān) CANINTE 中斷使能位置 1 的前提下,對(duì)上述任一位置 1 均可使 MCU 產(chǎn)生中斷請(qǐng)求。

write_byte_2515(0x2B,?0x1f);?????//CANINTE 中斷使能寄存器??

5. 接收緩沖器配置

MCP2515 具有兩個(gè)全接收緩沖器。每個(gè)接收緩沖器配備 有多 個(gè) 驗(yàn) 收濾 波 器。除 上述 專 用 接收 緩 沖 器外,MCP2515 還具有單獨(dú)的報(bào)文集成緩沖器 ( Message Assembly Buffer, MAB) ,可作為第三個(gè)接收緩沖器。

bit5:6 設(shè)置為 1,其余位暫時(shí)不用,設(shè)置為 0.

?write_byte_2515(0x60,?0x60);???//RXB0CTRL 接收緩沖器 0?控制寄存器?

6. 引腳控制寄存器和狀態(tài)寄存器

當(dāng)引腳配置為數(shù)字輸出引腳時(shí),相應(yīng)的接收緩沖器中的 BFPCTRL.BxBFM 位應(yīng)被清零, 而 BFPCTRL.BnBFE 位應(yīng)被置 1。在這種工作模式下,引腳的狀態(tài)由 BFPCTRL.BnBFS 位控制。BnBFS 位寫入 1 時(shí),將使相應(yīng)的緩沖器滿中斷引腳輸出高電平,寫入 0 將使該引腳輸出低電平。當(dāng)引腳處于這種模式時(shí),該引腳的狀態(tài)只應(yīng)通過(guò)位修改 SPI 命令來(lái)修改,以避免任何緩沖器滿中斷引腳出現(xiàn)干擾。

void?bit_modify_2515(unsigned?char?addr,?unsigned?char?mask,?unsigned?char?data)
{
//????CS_SPI?=?0?;
????slave_enable()?;
????send_byte(0x05)?;
????send_byte(addr)?;
????send_byte(mask)?;
????send_byte(data)?;
????slave_disable()?;
//????CS_SPI?=?1?;
}
bit_modify_2515(0x0C,?0x0f,?0x0f);?//BFPCTRL_RXnBF?引腳控制寄存器和狀態(tài)寄存器?中文 DATASHEET?29?頁(yè)

7. 回環(huán)模式,用于測(cè)試

write_byte_2515(0x0f,?0x40);???//CAN 控制寄存器--回環(huán)模式,用于測(cè)試

can 緩沖區(qū)數(shù)據(jù)收發(fā)

MCP2515 采用三個(gè)發(fā)送緩沖器。每個(gè)發(fā)送緩沖器占用 14 字節(jié)的 SRAM,并映射到器件存儲(chǔ)器中。其中第一個(gè)字節(jié) TXBnCTRL 是與報(bào)文緩沖器相關(guān)的控制寄存器。該寄存器中的信息決定了報(bào)文在何種條件下發(fā)送,并在報(bào)文發(fā)送時(shí)指示其狀態(tài) 。

用 5 個(gè)字節(jié)來(lái)裝載標(biāo)準(zhǔn)和擴(kuò)展標(biāo)識(shí)符以及其他報(bào)文仲裁信息(見(jiàn)寄存器 3-3 到寄存器 3-7) 。最后 8 個(gè)字節(jié)用于裝載等待發(fā)送報(bào)文的 8 個(gè)可能的數(shù)據(jù)字節(jié) 。

至少須將 TXBnSIDH、 TXBnSIDL 和 TXBnDLC 寄存器裝載數(shù)據(jù)。如果報(bào)文包含數(shù)據(jù)字節(jié),還需要對(duì) TXBnDm 寄存器進(jìn)行裝載。若報(bào)文采用擴(kuò)展標(biāo)識(shí)符,應(yīng)對(duì) TXBnEIDm 寄存器進(jìn)行裝載,并將 TXBnSIDL.EXIDE 位置 1。

下面我們看如何向 CAN 的緩沖區(qū) 0 發(fā)送和接收數(shù)據(jù)。

數(shù)據(jù)發(fā)送

can 緩沖區(qū) 0 數(shù)據(jù)發(fā)送流程如下:

  1. 設(shè)置為發(fā)送最高優(yōu)先級(jí)設(shè)置發(fā)送緩沖器 0 標(biāo)準(zhǔn)標(biāo)識(shí)符高位設(shè)置發(fā)送緩沖器 0 標(biāo)準(zhǔn)標(biāo)識(shí)符低位設(shè)置發(fā)送緩沖器 0 數(shù)據(jù)長(zhǎng)度碼 8 字節(jié)向緩沖區(qū)寫入數(shù)據(jù),地址從 0x36 起發(fā)送請(qǐng)求命令 0x81,發(fā)送數(shù)據(jù)

1. 設(shè)置為發(fā)送最高優(yōu)先級(jí)

TXBnCTRL

write_byte_2515(0x30,?0x03);?// 設(shè)置為發(fā)送最高優(yōu)先級(jí)

2. 設(shè)置發(fā)送緩沖器 0 標(biāo)準(zhǔn)標(biāo)識(shí)符高位

write_byte_2515(0x31,?0xff);?// 發(fā)送緩沖器 0 標(biāo)準(zhǔn)標(biāo)識(shí)符高位

3. 設(shè)置發(fā)送緩沖器 0 標(biāo)準(zhǔn)標(biāo)識(shí)符低位

write_byte_2515(0x32,?0x00);?// 發(fā)送緩沖器 0 標(biāo)準(zhǔn)標(biāo)識(shí)符低位

4. 設(shè)置發(fā)送緩沖器 0 數(shù)據(jù)長(zhǎng)度碼 8 字節(jié)

write_byte_2515(0x35,?0x08);??// 發(fā)送緩沖器 0 數(shù)據(jù)長(zhǎng)度碼 8 字節(jié)

5. 向緩沖區(qū)寫入數(shù)據(jù),地址從 0x36 起

write_byte_2515(0x36+i?,tx_buff[i]);?// 向 txb 緩沖器中寫入 8 個(gè)字節(jié)

6. 發(fā)送請(qǐng)求命令 0x81,發(fā)送數(shù)據(jù)

void?send_req_2515()
{
?//???CS_SPI?=?0;?// 復(fù)位
?soft_reset();??????// 復(fù)位 spi 控制器
????slave_enable()?;???// 片選從機(jī)
?send_byte(0x81);???// 發(fā)送請(qǐng)求命令
?slave_disable()?;??// 取消片選
//?CS_SPI=1;
}

Can 數(shù)據(jù)的接收

從 CAN 緩沖區(qū)讀取數(shù)據(jù)流程如下:

  1. 讀取中斷標(biāo)志寄存器 0x2c 的 value,判斷 bit0 是否為 1 從接收緩沖區(qū)讀走數(shù)據(jù),地址從 0X66 開(kāi)始軟復(fù)位向中斷標(biāo)志寄存器 0x2c 寫入位掩碼向中斷標(biāo)志寄存器 0x2c,寫入數(shù)據(jù) 0,清終端

1. 讀取中斷標(biāo)志寄存器 0x2c 的 value,判斷 bit0 是否為 1

當(dāng)報(bào)文傳送至某一接收緩沖器時(shí),與該接收緩沖器對(duì)應(yīng)的 CANINTF.RXnIF 位將置 1。一旦緩沖器中的報(bào)文處理完畢, MCU 就必須將該位清零以接收下一條報(bào)文。該控制位提供的鎖定功能確保 MCU 尚未處理完上一條報(bào)文前, MCP2515 不會(huì)將新的報(bào)文載入接收緩沖器。

如果 CANINTE.RXnIE 位被置 1,器件會(huì)在 INT 引腳產(chǎn)生一個(gè)中斷,顯示接收到報(bào)文有效。另外,如果被配置為接收緩沖器滿中斷引腳,與之相應(yīng)的 RXnBF 引腳會(huì)被拉低。

MCP2515 有八個(gè)中斷源。CANINTE 寄存器包含了使能各中斷源的中斷使能位。CANINTF 寄存器包含了各中斷源的中斷標(biāo)志位。當(dāng)發(fā)生中斷時(shí), INT 引腳將被 MCP2515 拉為低電平, 并保持低電平狀態(tài)直至 MCU 清除中斷。中斷只有在引起相應(yīng)中斷的條件消失后,才會(huì)被清除。

建議在對(duì) CANINTF 寄存器中的標(biāo)志位進(jìn)行復(fù)位操作時(shí),采用位修改命令而不要使用一般的寫操作。這是為了避免在寫命令執(zhí)行過(guò)程中無(wú)意間修改了標(biāo)志位,進(jìn)而導(dǎo)致中斷丟失。

應(yīng)該注意的是, CANINTF 中的中斷標(biāo)志位是可讀寫位,因此在相關(guān) CANINTE 中斷使能位置 1 的前提下,對(duì)上述任一位置 1 均可使 MCU 產(chǎn)生中斷請(qǐng)求。

2. 從接收緩沖區(qū)讀走數(shù)據(jù)

rx_buff[i]=?read_byte_2515(0x66+i);

3. 軟復(fù)位

soft_reset();

4. 向中斷標(biāo)志寄存器 0x2c 寫入位掩碼

bit_modify_2515(0x2c,0x01,0x00);// 修改 bit?0

5. 清中斷

write_byte_2515(0x2c,?0x00);

最終操作代碼如下

節(jié)省篇幅,重復(fù)函數(shù)不貼了。

#define?CNF1_20K?????0xd3????????//4???20(1+4+8+7)?
#define?CNF2_20K?????0xfb??????????
#define?CNF3_20K?????0x46??
?????????
void??Init_can(void)
{
????reset_2515();?// 復(fù)位
????write_byte_2515(0x0f,?0x80);?//CANCTRL 寄存器--進(jìn)入配置模式?中文 DATASHEET?58 頁(yè)
?// 可以設(shè)置的波特率?5K?10K?15K?20K?25K?40K?50K?80K?100K?125K?200K?400K?500K?667K?800K?1M
????write_byte_2515(0x2A,?CNF1_20K);?//CNF1 位定時(shí)配置寄器???中文 DATASHEET?41-42 頁(yè)
????write_byte_2515(0x29,?CNF2_20K);?//CNF2 位定時(shí)配置寄器???中文 DATASHEET?41-42 頁(yè)
????write_byte_2515(0x28,?CNF3_20K);?//CNF3 位定時(shí)配置寄器???中文 DATASHEET?41-43 頁(yè)
????write_byte_2515(0x2B,?0x1f);?????//CANINTE 中斷使能寄存器??中文 DATASHEET?50?頁(yè)
????write_byte_2515(0x60,?0x60);?????//RXB0CTRL 接收緩沖器 0?控制寄存器?中文 DATASHEET?27?頁(yè)
????//write_byte_2515(0x70,?0x20);???// 接收緩沖器 1 控制寄存器
????bit_modify_2515(0x0C,?0x0f,?0x0f);?//BFPCTRL_RXnBF?引腳控制寄存器和狀態(tài)寄存器?中文 DATASHEET?29?頁(yè)
????write_byte_2515(0x0f,?0x40);???//CAN 控制寄存器--回環(huán)模式,用于測(cè)試
}
void?send_byte(unsigned?char?data)
{
?SPI2.CH_CFG?|=?0x1;?//?enable?Tx?Channel
?delay(1);
?SPI2.SPI_TX_DATA?=?data;
?while(?!(SPI2.SPI_STATUS?&?(0x1?<<?25))?);
?SPI2.CH_CFG?&=?~0x1;?//?disable?Tx?Channel
}
unsigned?char?recv_byte()
{
?unsigned?char?data;
?SPI2.CH_CFG?|=?0x1?<<?1;?//?enable?Rx?Channel
?delay(1);
?data?=?SPI2.SPI_RX_DATA;
?delay(1);
?SPI2.CH_CFG?&=?~(0x1?<<?1);?//disable?Rx?Channel
?return??data;
}
void?bit_modify_2515(unsigned?char?addr,?unsigned?char?mask,?unsigned?char?data)
{
//????CS_SPI?=?0?;
????slave_enable()?;
????send_byte(0x05)?;
????send_byte(addr)?;
????send_byte(mask)?;
????send_byte(data)?;
????slave_disable()?;
//????CS_SPI?=?1?;
}
void?Can_send(unsigned?char?*tx_buff)
{
?unsigned?char?i;
?write_byte_2515(0x30,?0x03);?// 設(shè)置為發(fā)送最高優(yōu)先級(jí)
?write_byte_2515(0x31,?0xff);?// 發(fā)送緩沖器 0 標(biāo)準(zhǔn)標(biāo)識(shí)符高位
?write_byte_2515(0x32,?0x00);?// 發(fā)送緩沖器 0 標(biāo)準(zhǔn)標(biāo)識(shí)符低位
?write_byte_2515(0x35,?0x08);??// 發(fā)送緩沖器 0 數(shù)據(jù)長(zhǎng)度碼 8 字節(jié)
?for(i?=?0;?i?<?8;?i++)
?{
??write_byte_2515(0x36+i?,tx_buff[i]);?// 向 txb 緩沖器中寫入 8 個(gè)字節(jié)
//??printf("%x?",tx_buff[i]);
?}
?send_req_2515();

}

unsigned?char?Can_receive(unsigned?char?*rx_buff)
{
?unsigned?char?i,flag;
????flag?=?read_byte_2515(0x2c);?//CANINTF——中斷標(biāo)志寄存器
????printf("flag=%xn",flag);
??//??printf("?SPI2.SPI_STATUS?=%xn",?SPI2.SPI_STATUS?);
?//???soft_reset();
????if?(flag&0x1)????????????????// 接收緩沖器 0 滿中斷標(biāo)志位
????{
?????for(i?=?0;?i?<?16;?i++)
??{
??????rx_buff[i]=?read_byte_2515(0x66+i);
????//??printf("%x?",rx_buff[i]);
????//??printf("?SPI2.SPI_STATUS?=%xn",?SPI2.SPI_STATUS?);
??????soft_reset();
??}
?????bit_modify_2515(0x2c,0x01,0x00);
?????write_byte_2515(0x2c,?0x00);
??if?(!(rx_buff[1]&0x08))?return(1);???// 接收標(biāo)準(zhǔn)數(shù)據(jù)幀
????}
????return(0);
}
int?main(void)
{
?GPX2.CON?=?0x1?<<?28;
?uart_init();

?unsigned?char?ID[4],buff[8];?????????????????// 狀態(tài)字
?unsigned?char?key;
?unsigned?char?ret;//,j,k,ret0,ret1,ret2,ret3,ret4,ret5,ret6,ret7,ret8,ret9;
?unsigned?int?rx_counter;
?volatile?int?i=0;

?GPC1.CON?=?(GPC1.CON?&?~0xffff0)?|?0x55550;// 設(shè)置 IO 引腳為 SPI 模式

/*spi?clock?config*/
?CLK_SRC_PERIL1?=?(CLK_SRC_PERIL1?&?~(0xF<<24))?|?6<<24;//?0x6:?0110?=?SCLKMPLL_USER_T?800Mhz
?CLK_DIV_PERIL2?=?19?<<8?|?3;//SPI_CLK?=?800/(19+1)/(3+1)

?soft_reset();????????????????????//?軟復(fù)位 SPI 控制器
?SPI2.CH_CFG?&=?~(?(0x1?<<?4)?|?(0x1?<<?3)?|?(0x1?<<?2)?|?0x3);//master?mode,?CPOL?=?0,?CPHA?=?0?(Format?A)
?SPI2.MODE_CFG?&=?~((0x3?<<?17)?|?(0x3?<<?29));???//BUS_WIDTH=8bit,CH_WIDTH=8bit
?SPI2.CS_REG?&=?~(0x1?<<?1);????????// 選擇手動(dòng)選擇芯片
?mydelay_ms(10);????// 延時(shí)
????Init_can();???// 初始化 MCP2515

????printf("n************?SPI?CAN?test!!?************n");

????while(1)
????{
??//Turn?on?led
??GPX2.DAT?=?GPX2.DAT?|?0x1?<<?7;
??mydelay_ms(50);

?????printf("nplease?input?8?bytesn");

?????for(i=0;i<8;i++)
?????{
??????src[i]?=?getchar();
??????putc(src[i]);
?????}
?????printf("n");

?????Can_send(src);?// 發(fā)送標(biāo)準(zhǔn)幀
????????mydelay_ms(100);
????????ret?=?Can_receive(dst);?// 接收 CAN 總線數(shù)據(jù)
????????printf("ret=%xn",ret);
????????printf("src=");
????????for(i=0;i<8;i++)?printf("?%x",?src[i]);// 將 CAN 總線上收到的數(shù)據(jù)發(fā)到串行口

????????printf("n");

????????printf("dst=");
????????for(i=0;i<8;i++)?printf("?%x",dst[6+i]);?// 將 CAN 總線上收到的數(shù)據(jù)發(fā)到串行口
??printf("n");

??//Turn?off
??GPX2.DAT?=?GPX2.DAT?&?~(0x1?<<?7);
??mydelay_ms(100);
????}?//while(1)

?return?0;
}?//main

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

公眾號(hào)『一口Linux』號(hào)主彭老師,擁有15年嵌入式開(kāi)發(fā)經(jīng)驗(yàn)和培訓(xùn)經(jīng)驗(yàn)。曾任職ZTE,某研究所,華清遠(yuǎn)見(jiàn)教學(xué)總監(jiān)。擁有多篇網(wǎng)絡(luò)協(xié)議相關(guān)專利和軟件著作。精通計(jì)算機(jī)網(wǎng)絡(luò)、Linux系統(tǒng)編程、ARM、Linux驅(qū)動(dòng)、龍芯、物聯(lián)網(wǎng)。原創(chuàng)內(nèi)容基本從實(shí)際項(xiàng)目出發(fā),保持原理+實(shí)踐風(fēng)格,適合Linux驅(qū)動(dòng)新手入門和技術(shù)進(jìn)階。