您现在的位置是:网站首页> 硬件

Arduino常用的三种通信协议UART, I2C和SPI

  • 硬件
  • 2021-03-29
  • 945人已阅读
摘要

UART协议UART的全称叫 Universal Asynchronous Reception and Transmission通用异步收发。从名字我们就可以知道,UART是异步串行通信的。利用UART协议传输数据时,需要两个数据引脚,一个用于传输数据的TX,另一个用于接收的RX。Arduino板上默认的RX和TX引脚分别是Pin0和Pin1。UART硬件层和软件层的功能都是Arduino集成的,一般我们不需要从底层操作数据。一个UART口(一组RX和TX)只能连接一个UART设备进行通信,我们需要这样就接线:

1.png

注意,一个设备的TX接的是另一设备的RX,反之RX接TX,最后需要将两个设备共地,共地是让它们的参考电压一致。下面,我们用两块Arduino实际实践一下。如图连接两个Arduino:

2.jpg

我们在Arduino A里烧录以下代码:

void setup() {  

// put your setup code here, to run once:  

Serial.begin(9600);

}

void loop() {  // put your main code here, to run repeatedly:   

 char s[]="I am Arduino A";//每隔1s发送一次字符数组s    

 Serial.print(s);  

 delay(1000);

 }

再在Arduino B里烧录以下代码:


String a=""; //定义字符串,接收数据

void setup() {  // put your setup code here, to run once: 

 Serial.begin(9600);

 }

 void loop() {  

  while(Serial.available())//当发现缓存中有数据时,将数据送至字符数组a中  

   {

     a+=char(Serial.read());

delay(3); 

   } 

   if (a.length() > 0){

    Serial.println(a);

a = ""   

}

}

这样就A就通过UART向B发送了“ I am Arduino A”消息,B接收到以后输出。当然你可以修改A的代码实现双向数据传输,这里我们就点到即止了。UART是最常见的通信协议之一,它可以实现全双工传输,但它的传输速度比较慢,而且只能支持一对一的设备。 I2C协议I2C是Philips公司在1980年代发明的一种协议,全称是Inter-Integrated Circuit。I2C最常被应用于模块或者传感器之间的通信,因为I2C基于二根传输线,同步通信的半双工协议,而且协议也非常简单可靠。 I2C使用两根传输线实现一个主设备与多个从设备,甚至是多个主设备与对应从设备之间的通信。 这两根通讯线一根为控制时钟线,称之为SCL,用于同步设备间的数据传输时钟; 另一根为数据线,称之为SDA,用于携带数据。理论上,一条I2C总线上能支持挂载128台设备。

1.png

Arduino UNO的I2C引脚是A4(SDA), A5(SDL)。Arduino对I2C协议也进行了库封装:

https://www.arduino.cc/en/Reference/Wire

下面我们还是用两块Arduino来实践一下如何利用I2C协议来传输数据。如图连接好两块Arduino:

2.png

3.jpg

一台我们作为主设备(Master),烧录以下代码:



 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),烧录以下代码:


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 (1 < Wire.available()) 

  char c = Wire.read();      /* receive byte as a character */   

   Serial.print(c);           /* print the character */ 

}

Serial.println("ok");     

/* to newline */

}

//function that executes whenever data is requested from master

void requestEvent() 

 Wire.write("Hi Master");  /*send string on request */

}

这样,我们就实现了主从设备的双向传输。打开主机Arduino的串口监视器我们可以看见如下的输出:

1.png

从机Arduino的串口输出:

1.png

I2C虽然只需要两根线,就能支持多主机多从机的数据传输,但由于只有一根用于数据传输,它通过在“接收”和“传输”两种状态之间但切换实现了双向传输,但牺牲了不少传输速率。I2C还有典型的开漏问题,总线需要加上拉电阻。

SPI协议

最后,我们来看一下SPI协议。SPI全称Serial Peripheral Interface(串行外设接口),由摩托罗拉公司提出的一种同步串行数据传输协议。SPI类似I2C也是同步通信的协议,但是全双工,支持数据的同时输出和输入。这两个特征使SPI的传输速率比UART和I2C都高,这对于像SD卡、或者屏幕等数据型模块来说,是非常具有优势的。

1.png

SPI支持一主多从的模式,但SPI也是三种协议中需要线最多的协议,一共需要4条信号线:

11458f5de8c9a6adc57e185a24b592c8.png

但Arduino UNO默认的SPI引脚分别为D13(SCK), D12(MISO), D11(MOSI), D10(SS),其中SS是从机选择引脚,没有强制要求,你也可以选其他的引脚。2.jpg

同样,我们来实践一下用SPI实现数据传输。

3.png

4.jpg

如图连接好两块Arduino UNO。还是一块作为主机(Master), 另一块作为从机(Slave)。Arduino对SPI协议也做了类封装:

https://www.arduino.cc/en/reference/SPI

主机烧录以下代码:

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);

}


从机烧录:

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 

   routineISR (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 routinevoid 

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选择从机

一主多从

它们各自都有自己的优缺点和适用的场景,并没有绝对的好坏,这也是这三种协议经久不衰的原因。只有了解并掌握它们,我们才能在具体的应用场景里选择最合适的协议。当然在嵌入式世界里,还有其他很多协议,小编以后再介绍吧。如果对这三种协议的底层感兴趣的朋友,也可以自己再去深入了解。



Top