风筝
发表于: 2018-12-20 17:06:29 | 显示全部楼层

微控制器使用许多不同的协议与各种传感器和外围设备进行通信。有许多用于无线和有线通信的协议,并且最常用的通信技术是串行通信。串行通信是通过通信信道或总线一次一位地发送数据的过程。有许多类型的串行通信,如UART、CAN、USB、I2C和SPI通信。


在本篇文章中,我们将了解SPI协议以及如何在Arduino中使用它。我们将使用SPI协议在两个Arduino开发板之间进行通信。在这里,一个Arduino开发板将充当主机Master,另一个将充当从机Slave,分别有两个LED和按钮连接到两个arduino开发板。为了演示SPI通信,我们将使用SPI串行通信协议通过从站侧的按钮控制主机的LED,反之亦然。


什么是SPI?

SPI(串行外设接口)是一种串行通信协议。摩托罗拉在1970年发明了SPI接口。SPI具有全双工连接,这意味着数据可以同时发送和接收。即主设备可以将数据发送到从设备,从设备可以同时向主设备发送数据。 SPI是同步串行通信意味着通信需要时钟。


SPI的工作过程

SPI使用四条线进行主/从通信。 SPI只能有一个主站,并且可以有多个从站。主设备通常是微控制器,从设备可以是微控制器、传感器、ADC、DAC、LCD等。


下面是SPI主机带单个从机的框图表示。

SPI-communication-circuit-between-a-master-and-slave.png

SPI有四条线MISO、MOSI、SS和CLK

●    MISO(主进从出) - 用于向主设备发送数据的从设备线。

●    MOSI(主出从入) - 用于向外设发送数据的主线。

●    SCK(串行时钟) - 同步主机产生的数据传输的时钟脉冲。

●    SS(从机选择)-Master可以使用此引脚来启用和禁用特定设备。


SPI主站带多个从站的框图

SPI-communication.png

要启动主机和从机之间的通信,我们需要将所需设备的从机选择(SS)引脚设置为低电平,以便它可以与主机通信。当该引脚为高电平时,它会忽略主机。这允许您让多个SPI设备共享相同的MISO、MOSI和CLK主线。如上图所示,有四个从器件,其中SCLK、MISO,MOSI与主器件共用,每个器件的SS分别连接到主器件的各个SS引脚(SS1、SS2、SS3)。通过将所需的SS引脚设置为低电平,主器件可以与该从器件通信。


Arduino UNO中的SPI引脚

下图显示了Arduino UNO中的SPI引脚(红色框中)。

SPI-Pins-in-Arduino-UNO.png

SPI线
Arduino引脚
MOSI
11或ICSP-4
MISO
12或ICSP-1
SCK
13或ICSP-3
SS
10

在Arduino中使用SPI

在开始编程两个Arduinos之间的SPI通信之前。我们需要了解Arduino IDE中使用的SPI库。


库<SPI.h>包含在程序中,用于使用以下SPI通信函数。

1. SPI.begin()

用途:通过将SCK、MOSI和SS设置为输出来初始化SPI总线,将SCK和MOSI拉低,SS为高电平。


2. SPI.setClockDivider(divider)

用途:设置相对于系统时钟的SPI时钟分频器。可用的分频器为2,4,8,16,32,64或128。

divider参数:

         ◾    SPI_CLOCK_DIV2

         ◾    SPI_CLOCK_DIV4

         ◾    SPI_CLOCK_DIV8

         ◾    SPI_CLOCK_DIV16

         ◾    SPI_CLOCK_DIV32

         ◾    SPI_CLOCK_DIV64

         ◾    SPI_CLOCK_DIV128


3. SPI.attachInterrupt(handler)

用途:当从设备从主设备接收数据时,将调用此函数。


4. SPI.transfer(val)

用途:此函数用于在主站和从站之间同时发送和接收数据。


现在让我们从Arduino中SPI协议的实际演示开始。在本篇文章中,我们将使用两个arduino中的一个作为master,另一个作为slave。两个Arduino都分别带有一个LED和一个按钮。主LED可以通过使用从机Arduino的按钮来控制,从机Arduino的LED可以通过主Arduino的按钮使用arduino中的SPI通信协议来控制。


需要的组件

●    Arduino UNO开发板

●    LED指示灯

●    按钮

●    电阻10k

●    电阻2.2k

●    面包板

●    连接导线


Arduino SPI通信电路图

Circuit-Diagram-for-SPI-Communication-between-Two-Arduinos.png


Circuit-Hardware-for-SPI-Communication-between-Two-Arduinos.jpg


编程说明

本教程有两个程序,一个用于主arduino,另一个用于从机arduino。在本文的末尾处,提供了两个Arduino开发板的完整程序。


主机Arduino编程介绍

1.首先,我们需要包含SPI库以使用SPI通信函数。

  1. #include<SPI.h>                             
复制代码

2.在void setup()函数中,我们以波特率115200启动串行通信。

  1. Serial.begin(115200);   
复制代码

将LED连接至引脚7,将按钮连接至引脚2,并分别设置这些引脚OUTPUT和INPUT。

  1. pinMode(ipbutton,INPUT);               
  2. pinMode(LED,OUTPUT);   
复制代码

接下来我们开始SPI通信

  1. SPI.begin();  
复制代码

接下来,我们设置Clockdivider进行SPI通信。这里我们设置了分频系数8。

  1. SPI.setClockDivider(SPI_CLOCK_DIV8);   
复制代码

然后将SS引脚置为高电平,因为我们没有启动任何传输到从机arduino。

  1. digitalWrite(SS,HIGH);     
复制代码

3.在void loop()函数中:

我们读取了连接到pin2(Master Arduino)的按钮引脚的状态,用于将这些值发送到从Arduino。

  1. buttonvalue = digitalRead(ipbutton);   
复制代码

根据引脚2的输入设置逻辑以设置x值(发送到从机)

  1.   if(buttonvalue == HIGH)               
  2.   {
  3.     x = 1;
  4.   }
  5.   else
  6.   {
  7.     x = 0;
  8.   }
复制代码

在发送值之前,我们需要将从机选择值设置为LOW以开始从主设备传输到从设备。

  1. digitalWrite(SS, LOW);         
复制代码

以下是重要的一步,在下面的语句中,我们将存储在Mastersend变量中的按钮值发送到从机arduino,并从slave中接收将存储在Mastereceive变量中的值。

  1. Mastereceive=SPI.transfer(Mastersend);
复制代码

之后,根据Mastereceive值,我们将点亮或熄灭主机Arduino上的LED灯。

  1. if(Mastereceive == 1)                  
  2.   {
  3.     digitalWrite(LED,HIGH);        //Sets pin 7 HIGH
  4.     Serial.println("Master LED ON");
  5.   }
  6.   else
  7.   {
  8.    digitalWrite(LED,LOW);         //Sets pin 7 LOW
  9.    Serial.println("Master LED OFF");
  10.   }
复制代码

注意:我们使用serial.println()在Arduino IDE的串行监视器中查看结果。


从机Arduino编程代码介绍

1.首先,我们需要包含SPI库以使用SPI通信函数。

  1. #include<SPI.h>
复制代码

2.在void setup()函数中,我们以波特率115200启动串行通信。

  1. Serial.begin(115200);
复制代码

将LED连接至引脚7,将按钮连接至引脚2,并分别设置这些引脚OUTPUT和INPUT。

  1. pinMode(ipbutton,INPUT);
  2. pinMode(LED,OUTPUT);
复制代码

这里重要的一步是

  1. pinMode(MISO,OUTPUT);                  
复制代码

以上语句将MISO设置为OUTPUT(必须将数据发送到主IN)。所以数据是通过Slave Arduino的MISO发送的。


现在使用SPI控制寄存器在从模式下打开SPI

  1. SPCR |= _BV(SPE);         
复制代码

然后打开SPI中断的中断。如果从主站接收数据,则调用中断例程,并从SPDR(SPI数据寄存器)获取接收值

  1. SPI.attachInterrupt();         
复制代码

来自master的值取自SPDR并存储在Slavereceived变量中。这发生在以下中断例程函数中。

  1. ISR (SPI_STC_vect)
  2. {
  3.   Slavereceived = SPDR;                  
  4.   received = true;                       
  5. }
复制代码

3.接下来在void loop()函数中,我们根据Slavereceived值将从机arduino上的LED灯设置为ON或OFF。

  1. if (Slavereceived==1)
  2.    {
  3.    digitalWrite(LEDpin,HIGH); //Sets pin 7 as HIGH LED ON
  4.                                      Serial.println("Slave LED ON");
  5.      }
  6. else
  7.       {
  8.   digitalWrite(LEDpin,LOW);     //Sets pin 7 as LOW LED OFF
  9.   Serial.println("Slave LED OFF");
  10.       }
复制代码

接下来,我们读取从机Arduino按钮的状态,并将值存储在Slavesend中,通过给SPDR寄存器赋值将值发送给主机Arduino。

  1.   buttonvalue = digitalRead(buttonpin);
  2.   if (buttonvalue == HIGH)              
  3.       {
  4.         x=1;
  5.       }
  6. else
  7.       {
  8.        x=0;
  9.       }
  10.             Slavesend=x;                             
  11.             SPDR = Slavesend;  
复制代码

注意:我们使用serial.println()在Arduino IDE的串行监视器中查看结果。


测试硬件

下面是两个Arduino开发板之间SPI通信的最终设置图。


按下主侧的按钮时,从机侧的白色LED变为ON。

SPI-Communication-between-Two-Arduinos.jpg

当按下从属侧的按钮时,主侧的红色LED亮。

Testing-SPI-Communication-between-Two-Arduinos.jpg


代码

以下是主机侧和从机侧Arduino开发板的完整代码: main.rar (1.28 KB, 下载次数: 148)

跳转到指定楼层
阳间一阴魂
发表于: 2019-12-7 01:16:13 | 显示全部楼层

有两个问题想请教以下,首先是通信所用库是自带的吗?第二是如果使用多个从机,主机上的SS接口是从10往下递减吗?谢谢
回复

使用道具 举报

tzhxkd
发表于: 2020-2-17 23:03:11 | 显示全部楼层

阳间一阴魂 发表于 2019-12-7 01:16
有两个问题想请教以下,首先是通信所用库是自带的吗?第二是如果使用多个从机,主机上的SS接口是从10往下递 ...

得去下载添加到库文件里面就可以,不然编译不通过,ss端子是每个从机的片选,给那个通讯就拉低哪一个的ss端子吧,我看硬件就是这么接的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 714 | 回复: 1501



手机版|

GMT+8, 2024-12-22 21:31 , Processed in 0.046128 second(s), 6 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

快速回复 返回顶部 返回列表