設備與設備之間的通信往往都伴隨着總線的使用,而用得比較多的就當屬於SPI總線和I2C總線,而恰巧NodeMcu也支持這兩種總線通信
1. SPI總線——SPI類庫的使用
SPI是串行外設接口(Serial Peripheral Interface)的縮寫。是Motorola公司推出的一種同步串行接口技術,是一種高速的、全雙工、同步的通信總線。通過它可以連接使用同樣接口的外部設備。例如,ESP8266模組上,ESP8266EX芯片就是通過SPI接口與外接flash芯片連接的
SPI作為一種總線通信方式,可以通過SPI接口連接多個從設備,並通過片選控制來選擇對某一設備進行連接使用。如下圖所示:
1.1 SPI總線概述
SPI的通信原理很簡單,它是全雙工主從通信方式,這種模式下通常有一個主設備和一個或者多個從設備(注意,同一時刻,只有一個主設備和一個從設備進行通信),需要至少4根線,特殊情況下(單向傳輸時)3根線也可以。
SPI的器件工作在SPI規定下的兩種基本模式,即SPI主模式和SPI從模式。在一個SPI設備中,通常有如下表的幾個引腳:
主設備負責啟動通信,負責輸出時鍾信號以及選擇通信的從設備。當有多個從設備的時候,因為每個從設備上都有一個CS引腳接入到主設備中,當我們主設備和某個從設備通信時將需要將從設備的CS引腳電平設置為低電平或者高電平(根據實際情況而定)。數據的收發通過MISO和MOSI進行
1.2 NodeMcu SPI
NodeMcu的SPI(注意與HSPI區分)引腳(SD0-SD3、CLK、CMD)專門用於與ESP-12E的外接flash芯片進行Quad-SPI通信,因此不能用於SPI應用。
基於ESP8266的NodeMcu具有HSPI,具有4個可用於SPI通信的引腳(GPIO12-GPIO15)。通過這個SPI接口,我們可以將任何支持SPI的設備與NodeMcu連接起來,並與其進行通信
知識擴展——標准SPI、Dual SPI和Quad-SPI
1.標准SPI
標准SPI通常就叫做SPI,它是一種串行外設接口規范,有4根引腳信號:clk、cs、mosi、miso;
2.Dual SPI
它只是針對SPI Flash而言,不是針對所有SPI外設。對於SPI Flash,全雙工並不常用,因此擴展了mosi和miso的用法,讓它們工作在半雙工,用以加倍數據傳輸。也就是對於Dual SPI Flash,可以發送一個命令字節進入dual mode,這樣mosi變成SIO0(serial io 0),mosi變成SIO1(serial io 1),這樣一個時鍾周期內就能傳輸2個bit數據,加倍了數據傳輸;
3.Quad SPI
與Dual SPI類似,也是針對SPI Flash,Quad SPI Flash增加了兩根I/O線(SIO2,SIO3),目的是一個時鍾內傳輸4個bit。所以可以理解為:在傳輸速度上,Quad SPI=2Dual SPI=4SPI。
所以對於SPI Flash,有標准spi flash,dual spi , quad spi 三種類型,分別對應3-wire, 4-wire, 6-wire,在相同clock下,線數越多,傳輸速率越高。
溫馨提示
讀者可以自行了解一下NodeMcu的flash是什么標准。說不定燒錄代碼失敗就是因為這個原因(Flash模式是QIO或者DIO)
1.3 ESP8266 SPI類庫成員函數
Arduino Core For ESP8266的SPI類庫定義在SPI.h頭文件中。該類庫只提供了作為SPI主設備的API,其成員函數如下:
1.begin()
該功能用於初始化SPI通信。
語法:SPI.begin()
參數:無;
返回值: 無;
2.end()
該功能用於關閉SPI通信。
語法:SPI.end()
參數:無;
返回值: 無;
3.setBitOrder()
設置數據傳輸順序。
語法:SPI.setBitOrder(order)
參數:
order,傳輸順序,取值為:
~ LSBFIRST,低位在前;
~ MSBFIRST,高位在前。
返回值: 無;
4.setClockDivider()
設置通信時鍾。時鍾信號由主機產生,從機不用配置。但主機的SPI時鍾頻率應該在從機允許的處理速度范圍內。
語法:SPI.setClockDivider(divider)
參數:
divider,SPI通信的時鍾是由系統時鍾分頻得到的。可使用的分頻配置為:
~ SPI_CLOCK_DIV2,2分頻;
~ SPI_CLOCK_DIV4,4分頻(默認配置);
~ SPI_CLOCK_DIV8,8分頻;
~ SPI_CLOCK_DIV16,16分頻;
~ SPI_CLOCK_DIV32,32分頻;
~ SPI_CLOC K_DIV64,64分頻;
~ SPI_CLOCK_DIV128,128分頻;
返回值: 無;
5.setDataMode()
該功能用於設置數據模式。
語法:SPI.setDataMode(mode)
參數:
mode,可配置的模式,包括:
~ SPI_MODE0;
~ SPI_MODE1;
~ SPI_MODE2;
~ SPI_MODE3;
返回值: 無;
注意點:
SPI四種模式中,SPI的相位(CPHA)和極性(CPOL)分別可以為0或者1,對應的4種組合構成了4種模式:
~ SPI_MODE0:CPOL=0,CPHA=0;
~ SPI_MODE1:CPOL=0,CPHA=1;
~ SPI_MODE2:CPOL=1,CPHA=0;
~ SPI_MODE3:CPOL=1,CPHA=1;
時鍾極性CPOL:即SPI空閑時,時鍾信號SCLK的電平(1是空閑時高電平,0是空閑時低電平)。
時鍾相位CPHA:即SPI在SCLK第幾個邊沿開始采樣(0是第一個邊沿開始,1是第二個邊沿開始)
6.transfer()
該功能用於傳輸1B的數據,參數為發送的數據,返回值為接收到的數據。SPI是全雙工通信,因此每發送1B的數據,也會接收到1B的數據。
語法:SPI.transfer(val)
參數:
val,要發送的字節數據。
返回值: 從機返回的1B數據;
7.transfer16()
該功能用於傳輸2B的數據,參數為發送的數據,返回值為接收到的數據。
語法:SPI.transfer16(val)
參數:
val,要發送的16位(uint16_t)數據。
返回值: 從機返回的2B數據;
注意點: 發送的uint16_t數據,其實底層也是分開兩個字節分別發送兩次,接收到的2B數據,也會重新組裝成uint16_t數據;
8.transferBuf()
該功能用於傳輸一個緩沖區數據,參數為發送的緩沖區buf。
語法:SPI.transfer(buf,count)
參數:
buf,要發送的緩沖區(uint8_t*)數據。
count,緩沖區的大小。
返回值: 無;
注意點: 雖然沒有返回值,但是從從機傳輸回來的數據會替換掉buf緩沖區的數據,所以調用完整個方法之后,buf里面的數據就是從機返回的數據;
9.pins()
該功能用於切換SPI引腳映射,需要在SPI.begin()之前調用SPI.pins(6,7,8,0)。
語法:SPI.pins(sck, miso, mosi, ss)
參數:
sck,時鍾引腳,固定為6;
miso,主設備輸入,從設備輸出引腳,固定為7;
mosi,主設備輸出,從設備輸入,固定為8;
ss,使能信號引腳,固定為0。
返回值: 無;
注意點: 通常情況下,ESP8266的SPI對應引腳為MOSI-GPIO13,MISO-GPIO12,SCLK-GPIO14,SS-GPIO15。如果在調用SPI.begin()之前調用SPI.pins(6,7,8,0),那么引腳映射就會變成MOSI-SD1,MISO-SD0,SCLK-CLK,HWCS-GPIO0。可以看出它們和ESP8266模塊的外接Flash共享了SPI引腳。這個時候SPI的SS控制位就不是由我們的代碼來控制,而是由系統硬件本身來調配,因為它必須確保外接Flash的優先級是最高的。在此,筆者不建議這么用
1.4 SPI寄存器
所有的SPI設置都由Arduino SPI控制寄存器(SPCR)來決定。這個寄存器就是微控制器內存的一個字節,它是可讀寫的。寄存器提供的服務通常有3類:控制、數據和狀態。
控制寄存器(SPCR)
編碼設置控制多種微控制器的功能。通常控制寄存器中的一個位影響某個特定的設置(學過單片機系統的讀者應該比較了解這個,比如中斷允許控制寄存器IE、中斷優先級控制寄存器IP、定時器/計數器控制寄存器TCON等)。
數據寄存器(SPDR)
存儲數據的寄存器,比如串行口鎖存器SBUF,僅僅hold住了一個字節。比如,SPI數據寄存器hold住了要發往MOSI線的一個字節,或者這個數據是要從MISO線傳入的。
狀態寄存器(SPSR)
根據多種微控制器的條件改變其狀態。比如,SPI狀態寄存器(SPSR)的第七位被設置為1表示有數據從SPI傳入或傳出。
在這里,我們主要講解一下SPI控制寄存器(SPCR),一共有8位,每一個都控制了一種特定的SPI設置
天子驕龍