前言
這篇文章主要講Arduino串行通信裏的SPI通信。SPI(Serial Peripheral Interface,串行外設接口)高速通信接口一般用在於對於數據量要求比較大的設備或者場景裏,例如SD卡,網絡芯片,而Arduino支持SPI總線,從而可以和一些使用SPI總線的設備通信,一根SPI總線上可以連接多個SPI從設備。
SPI設置
- 在一個SPI設備中,通常會有4個引腳。SPI總線有主從機之分,主機負責輸出時鐘信號及選擇通信的從設備。時鐘信號會通過主機的SCK引腳輸出,提供給通信從機使用。而從機的選擇由從機CS引腳來決定,CS引腳爲低電平時,該從機被選中,CS引腳拉高,該從機被斷開。數據的收發則通過MISO和MOSI引腳進行。Arduino Mega2560開發板上引腳位置分別爲:MOSI–51腳,MISO–50腳,SCK–52腳,SS–53腳。或者利用6針的ICSP引腳來使用SPI總線。
MISO(Master In Slave Out):主機數據輸入,從機數據輸出
MOSI(Master Out Slave In):主機數據輸出,從機數據輸入
SCK(Serial Clock):用於通信同步的時鐘信號,由主機產生
SS(Slave Select)或CS(Chip Select):從機使能信號,由主機控制
SPI類庫成員函數
1. SPI.begin()
初始化SPI通信,調用該函數後,SCK/MOSI/SS引腳將被設置爲輸出模式,且SCK/MOSI引腳拉低,SS引腳拉高。
2. SPI.end()
關閉SPI總線通信
3. SPI.setBitOrder(order)
設置傳輸順序。order:傳輸順序,LSBFIRST,低位在前;MSBFIRST,高位在前
4. SPI.setClockDivider(divider)
設置通信時鐘,由主機產生,從機不用配置。divider:SPI通信的系統時鐘分頻得到,可選配置有SPI_CLOCK_DIV2、SPI_CLOCK_DIV4(默認配置)等,最大可達128分頻
5. SPI.setDataMode(mode)
設置數據模式。mode:可配置的模式,可選項有SPI_MODE0、SPI_MODE1、SPI_MODE2、SPI_MODE3
6. SPI.transfer(val)
傳輸1Byte的數據,SPI是全雙工通信,所以發送1B的數據,也會接收到1B的數據。val:要發送的字節數據。
SPI通信示例
- 下面這個例子是我用Arduino通過SPI通信控制下變頻模塊產生兩級變頻。這裏有兩個SPI從設備PLL1和PLL2,所以這裏我需要重新定義SPI從設備的使能引腳,如下圖代碼中所示,PLL1定義爲47腳,PLL2定義爲48腳;SCK腳則使用Mega2560默認的52引腳;因爲我這裏只需要從Arduino發送數據到從設備,並不需要從設備裏讀取數據,所以我只使用到了MOSI 51引腳,MISO引腳就不需要使用。剩下的就是將各個引腳和從設備上對應的引腳相連就行了。如果只有一個從設備的話,可以直接使用Mega2560上默認的SPI引腳。
#include <SPI.h> //引入SPI類庫
const int PLL1 = 47; //定義SPI從設備的使能引腳
const int PLL2 = 48;
//因爲篇幅原因,這裏PLL寄存器的數據接收做了簡化處理,兩個從設備都只發送了一組數據,
//實際上這裏需要發送多組寄存器數據才能使從設備工作,這也是根據不同的外部設備的規格定義去修改的。
//這裏每組4個字節數據
unsigned char PLL1_Reg_buf0[4] = {0x00,0x30,0x1B,0x30};
unsigned char PLL2_Reg_buf0[4] = {0x00,0xBE,0x00,0x00};
void setup()
{
Serial.begin(9600);
pinMode(PLL1,OUTPUT);
pinMode(PLL2,OUTPUT); //管腳設置爲輸出模式
digitalWrite(PLL1,HIGH); //拉高SPI從設備引腳
digitalWrite(PLL2,HIGH);
delay(50);
SPI.setBitOrder(MSBFIRST); // 最高有效位
SPI.setDataMode(SPI_MODE0); // SCK上升沿輸入,下降沿輸出;SCK低電平空閒
SPI.begin(); // SCK MOSI SS設爲輸出模式,SCK MOSI拉低,SS拉高
set_frq();
delay(1000);
}
void loop()
{
}
void WriteToADF4350(unsigned char PLL,unsigned char count, unsigned char *buf)
{
if(PLL == 1) digitalWrite(PLL1,LOW); //拉低電平,選擇對應SPI從設備
if(PLL == 2) digitalWrite(PLL2,LOW);
delay(50);
unsigned char ValueToWrite = 0;
unsigned char i = 0;
for(i=0;i<count;i++)
{
ValueToWrite = *(buf + i);
SPI.transfer(ValueToWrite); // 發送數據到SPI從設備
}
if(PLL == 1) digitalWrite(PLL1,HIGH); //拉高電平,釋放對應SPI從設備
if(PLL == 2) digitalWrite(PLL2,HIGH);
delay(50);
}
void set_frq(void)
{
WriteToADF4350(1,4,PLL1_Reg_buf0);
delay(500);
WriteToADF4350(2,4,PLL2_Reg_buf0);
delay(500);
}