串行外设接口
SPI全称Serial Peripheral Interface,即串行外设接口。由Motorola公司提出的一种同步串行数据传输标准。
- 同步:数据收发双方共用一个时钟;
- 串行:待传输的数据排成一行,一位一位地传送出去。
主要用于微控制器与其他外围设备,如EEPROM、Flash、AD转换器等之间的短距离传输,当然也可实现微控制器与微控制器间的数据传输。
特点:相比于其它通信协议,SPI采用四线制的硬件连接方式,结合四种信号间的时序关系,共同构成了SPI通信的语法!
SPI协议决定了可以有多个仆从设备,但只能存在一个主设备,主设备通过仆从设备选择线来确定当前要通信的仆从设备。
SPI通信的连接
Stduino UNO/Nano借助SS、MOSI、MISO、SCK四个接口实现SPI通信,四个接口的定义如下:
信号线 | 全称 | 中文名称 | 功能说明 |
---|---|---|---|
SCK | Serial CLock | 串行时钟 | 同步数据传输 |
MOSI | Master Out Slave In | 主出从进 | 主机输出从机输入数据线 |
MISO | Master In Slave Out | 主进从出 | 主机输入从机输出数据线 |
SS | Slave Select | 从机选择 | 从机此引脚设置为低电平 |
一主一从
这是最简单的SPI通信方式,由于主设备和仆从设备的角色是固定不变的,可以将主设备的SS端接高电平,将pu’ho的SS端固定接地。其它信号一一对应连接即可。
一主多从
主机选用独立的IO分别连接到从机的SS引脚,当需要与某个从机通信时,拉低相应的IO口即可;主设备与仆从设备SCK、MOSI、MISO相连。
Stduino对SPI通信的实现
Stduino对SPI进行了封装,并对用户开放了SPI对象用于操作SPI。
SPI的常用操作方法如下:
- SPI.begin()-完成主机的初始化工作,包括:四线的输入输出配置、开启SPI的工作使能。从机的四线输入输出、工作使能需要手工配置,可以参见下面的例程。
- SPI.setClockDivider(分频器) -相对于系统时钟设置SPI时钟分频器。在基于AVR的面板,可用的分频器为2,4,8,16,32,64或128。 SPI_CLOCK_DIV4,则为SPI时钟设置为系统时钟的四分之一(对于20 MHz的电路板为5 Mhz)。
- SPI.transfer()-主机传送字节,并返回从从机接收的字节。注意:主机是通过轮询的方式等待发送完成(也即接收完成)。
- SPI.attachInterrupt()-从机开启传输完成中断。注意:主机不要使用,因为固定为了轮询方式。
示例
两块Stduino之间通过SPI通信,并用串口打印传输数据,方便用户查看。
使用两块Stduino UNO,一主一从。
Stduino UNO A: SPI 主设备
Stduino UNO B: SPI 仆从设备
连线方式
Stduino UNO A | Stduino UNO B |
---|---|
MOSI | MOSI |
MISO | MISO |
SCLK | SLK |
SS | SS |
主设备代码
#include <Arduino.h>
#include <SPI.h>
void setup (void)
{
// 开始串口通讯
//注意:此串口与SPI通信没有任何关系,只是为了程序演示输出SPI接收到的字节。
Serial.begin(115200);
digitalWrite(SS, HIGH); //SPI内部逻辑复位
SPI.begin (); // SPI通讯初始化配置
}
void loop (void)
{
char c;
// 使能从机
digitalWrite(SS, LOW); // SS - pin 10
// 循环发送字节,实现字符串的发送
for (const char * p = "Hello,world!\n" ; c = *p; p++) {
SPI.transfer (c);//主机SPI发送
Serial.print(c);//串口显示发送的字节
}
// 复位从机
digitalWrite(SS, HIGH);
delay (1000);
}
仆从设备代码(轮询方式)
#include <Arduino.h>
#include <SPI.h>
char buf [100];
volatile byte pos;
volatile booleanprocess_it;
void setup (void)
{
Serial.begin (115200);
//从机的MISO要配置为输出模式
pinMode(MISO, OUTPUT);
//使能SPI,SPI可以正常工作了
SPCR |= _BV(SPE);
pos = 0;
}
charSPI_SlaveReceive(void){
while(!(SPSR & (1<<SPIF)));
return SPDR;
}
void loop(void){
buf[pos++] = SPI_SlaveReceive();
if(buf[pos-1]=='\n'){
buf[pos] = 0;
pos = 0;
Serial.print(buf);
}
}
文档更新时间: 2021-02-14 14:27 作者:admin