下面我們還是用兩塊Arduino來實踐一下如何利用I2C協議來傳輸數據。如圖連接好兩塊Arduino:
一臺我們作為主設備(Master),燒錄以下代碼:
#include
void setup() {
Serial.begin(9600); /* begin serial comm. */
Wire.begin(); /* join i2c bus as master */
Serial.println("I am I2C Master");
}
void loop() {
Wire.beginTransmission(8); /* begin with device address 8 */
Wire.write("Hello Slave"); /* sends hello string */
Wire.endTransmission(); /* stop transmitting */
Wire.requestFrom(8, 9); /* request & read data of size 9 from slave */
while(Wire.available()){
char c = Wire.read();/* read data received from slave */
Serial.print(c);
}
Serial.println();
delay(1000);
}
另一塊作為從設備(Slave),燒錄以下代碼:
#include
void setup() {
Wire.begin(8); /* join i2c bus with address 8 */
Wire.onReceive(receiveEvent); /* register receive event */
Wire.onRequest(requestEvent); /* register request event */
Serial.begin(9600); /* start serial comm. */
Serial.println("I am I2C Slave");
}
void loop() {
delay(100);
}
// function that executes whenever data is received from master
void receiveEvent(int howMany) {
while (0 char c = Wire.read(); /* receive byte as a character */
Serial.print(c); /* print the character */
}
Serial.println(); /* to newline */
}
// function that executes whenever data is requested from master
void requestEvent() {
Wire.write("Hi Master"); /*send string on request */
}
這樣,我們就實現了主從設備的雙向傳輸。打開主機Arduino的串口監視器我們可以看見如下的輸出:
從機Arduino的串口輸出:
I2C雖然只需要兩根線,就能支持多主機多從機的數據傳輸,但由于只有一根用于數據傳輸,它通過在“接收”和“傳輸”兩種狀態之間但切換實現了雙向傳輸,但犧牲了不少傳輸速率。I2C還有典型的開漏問題,總線需要加上拉電阻。
SPI協議
最后,我們來看一下SPI協議。SPI全稱Serial Peripheral Interface(串行外設接口),由摩托羅拉公司提出的一種同步串行數據傳輸協議。SPI類似I2C也是同步通信的協議,但是全雙工,支持數據的同時輸出和輸入。這兩個特征使SPI的傳輸速率比UART和I2C都高,這對于像SD卡、或者屏幕等數據型模塊來說,是非常具有優勢的。
SPI支持一主多從的模式,但SPI也是三種協議中需要線最多的協議,一共需要4條信號線:
但Arduino UNO默認的SPI引腳分別為D13(SCK), D12(MISO), D11(MOSI), D10(SS),其中SS是從機選擇引腳,沒有強制要求,你也可以選其他的引腳。
同樣,我們來實踐一下用SPI實現數據傳輸。
如圖連接好兩塊Arduino UNO。還是一塊作為主機(Master), 另一塊作為從機(Slave)。Arduino對SPI協議也做了類封裝:
https://www.arduino.cc/en/reference/SPI
主機燒錄以下代碼:
#include
void setup (void)
{
Serial.begin(115200);
digitalWrite(SS, HIGH);
SPI.begin ();
SPI.setClockDivider(SPI_CLOCK_DIV8);
}
void loop (void)
{
char c;
// enable Slave Select
digitalWrite(SS, LOW); // SS is pin 10
// send test string
for (const char * p = "Hello, world!\\n" ; c = *p; p++) {
SPI.transfer (c);
Serial.print(c);
}
// disable Slave Select
digitalWrite(SS, HIGH);
delay (1000);
}
從機燒錄:
#include
char buf [100];
volatile byte pos;
volatile boolean process_it;
void setup (void)
{
Serial.begin (115200); // debugging
// turn on SPI in slave mode
SPCR |= bit (SPE);
// have to send on master in, *slave out*
pinMode(MISO, OUTPUT);
// get ready for an interrupt
pos = 0; // buffer empty
process_it = false;
// now turn on interrupts
SPI.attachInterrupt();
} // end of setup
// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR; // grab byte from SPI Data Register
// add to buffer if room
if (pos < sizeof buf)
{
buf [pos++] = c;
// example: newline means time to process buffer
if (c == '\\n')
process_it = true;
} // end of room available
} // end of interrupt routine SPI_STC_vect
// main loop - wait for flag set in interrupt routine
void loop (void)
{
if (process_it)
{
buf [pos] = 0;
Serial.println(buf);
pos = 0;
process_it = false;
} // end of flag set
} // end of loop
這樣從機就能接受到主機發過來的消息了。
總結
今天,我們粗略地介紹了一下Arduino數據通信中最常用的三種協議:UART、I2C和SPI。
| **協議
** | **通信方式
** | **通信方向
** | **信號線
** | **傳輸速率
** | **主從模式
** | |||||
---|---|---|---|---|---|
UART | |||||
異步 | |||||
全雙工 | 2線RX、TX | 最低 | |||
一對一 | |||||
I2C | |||||
同步 | |||||
半雙工 | |||||
2線SDA、SCL,以地址選擇從機 | 低 | ||||
多主機多從機 | |||||
SPI | |||||
同步 | |||||
全雙工 | 4線MOSI、MISO、SCLK、CS(或SS),以CS選擇從機 | 高 | |||
一主多從 | |||||
它們各自都有自己的優缺點和適用的場景,并沒有絕對的好壞,這也是這三種協議經久不衰的原因。只有了解并掌握它們,我們才能在具體的應用場景里選擇最合適的協議。當然在嵌入式世界里,還有其他很多協議,小編以后再介紹吧。如果對這三種協議的底層感興趣的朋友,也可以自己再去深入了解。
-
SPI
+關注
關注
17文章
1707瀏覽量
91697 -
I2C
+關注
關注
28文章
1489瀏覽量
123907 -
uart
+關注
關注
22文章
1237瀏覽量
101454
發布評論請先 登錄
相關推薦
評論