电子说
下面我们还是用两块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选择从机 | 高 | |||
一主多从 | |||||
它们各自都有自己的优缺点和适用的场景,并没有绝对的好坏,这也是这三种协议经久不衰的原因。只有了解并掌握它们,我们才能在具体的应用场景里选择最合适的协议。当然在嵌入式世界里,还有其他很多协议,小编以后再介绍吧。如果对这三种协议的底层感兴趣的朋友,也可以自己再去深入了解。
全部0条评论
快来发表一下你的评论吧 !