一、概述
SPI = Serial Peripheral Interface,是串行外围接口设备,是一种高速,全双工,同步的通信总线。常规只占用四根线,节约了芯片管脚,PCB的布局省空间。
- 优点:
支持全双工,push-pull的驱动性能相比open-drain信号完整性更好。
支持高速(100MHz以上)。
协议支持字节长不限于8bits,可根据应用特点灵活选择消息字长
硬件连接简单。
- 缺点:
相比I2C多两根线。
没有寻址机制,只能靠片选选择不同设备。
没有从设备接收ACK,主设备对于发送成功与否不得而知。
典型应用只支持单主控
相比RS232 RS485和CAN总线,SPI传输距离短
二、硬件结构
SPI总线定义两个及以上设备间的数据通信,提供时钟的设备为主设备Master,接收时钟的设备为从设备Slave;
信号定义如下:
SCK :Serial Clock 串行时钟
MOSI:Master Ouput,Slave Input 主发从收信号
MISO:Master Input,Slave Input主收从发信号
SS/CS:Slave Select片选信号
电路连接如下:
单个主设备和单个从设备:
单个主设备和多个从设备:
三、寄存器类型
摩托罗拉定义的SPI寄存器包括:
SPI Control Register 1 控制寄存器1
SPI Control Register 2 控制寄存器2
SPI Baud Rate Register 波特率寄存器
SPI Status Register(SPISR) 状态寄存器(只读 其余均可读可写)
SPI Data Register(SPIDR) 数据寄存器
通过往寄存器中写入不同的值,设置SPI模块的不同属性
四、SPI传输模式
SPI通信有四种模式,简单地讲就是设置SCLK时钟信号线的那种信号为有效信号
通过设置控制寄存器SPICR1中的CPOL和CPHA位,将SPI可以分成四种传输模式
时钟极性CPOL,即Clock Polarity,决定时钟空闲时状态电平。对于SPI数据传输格式没有显著影响
CPOL = 0 ,表示当SCLK=0时处于空闲状态,所以有效状态就是SCLK处于高电平状态时
CPOL = 1, 表示当SCLK=1时处于空闲状态,所以有效状态就是SCLK处于低电平状态时
CPHA,即Clock Phase,定义SPI数据传输的两种基本模式。
CPHA = 0 ,在时钟的第一个跳变沿(上升沿或下降沿)进行数据采样。,在第2个边沿发送数据
CPHA = 1, 在时钟的第二个跳变沿(上升沿或下降沿)进行数据采样。,在第1个边沿发送数据
四种模式如下图所示:
先看第一列两张图(CPHA = 0),采样发生在第一个时钟跳变沿,即数据采样发生在SCK奇数边沿;
再看第二列两张图(CPHA = 1),采样发生在第二个时钟跳变沿,即数据采样发生在SCK偶数边沿。
MODE0:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第一个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下沿
MODE1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第一个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿
MODE2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采样是在第一个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿
MODE3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第一个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿
五、读写操作
标准SPI读写为例
片选——读指令——地址——数据读出
片选——写指令——地址——数据写入
SPI通信协议
主从设备
必须使用相同的工作模式——SCLK、CPOL 和 CPHA,才能正常工作。如果有多个从设备
,并且它们使用了不同的工作模式,那么主设备
必须在读写不同从设备
时需要重新修改对应从设备的模式。以上SPI总线协议的主要内容。
是不是感觉,这就完了? SPI就是如此,他没有规定最大传输速率,没有地址方案,也没规定通信应答机制,没有规定流控制规则。
只要四根信号线连接正确,SPI模式相同,将CS/SS信号线拉低,即可以直接通信,一次一个字节的传输,读写数据同时操作,这就是SPI
一些通信控制都得通过SPI设备自行实现,SPI并不关心物理接口的电气特性,例如信号的标准电压。
STM32中SPI初始化配置
1.初始化GPIO口,配置相关引脚的复用功能,使能SPIx时钟。调用函数:void GPIO_Init();
2.使能SPI时钟总线:RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE)
3.配置SPI初始化的参数,设置SPI工作模式:SPI_Init(SPI1,&SPI_Initstructure)
4.使能SPI外设:SPI_Cmd(SPI1,ENABLE);
SPI配置设置
typedef struct { uint16_t SPI_Direction; /*!< 传输方向,两向全双工,单向接收等*/ uint16_t SPI_Mode; /*!< 模式选择,确定主机还是从机 */ uint16_t SPI_DataSize; /*!< 数据大小,8位还是16位 */ uint16_t SPI_CPOL; /*!< 时钟极性选择 */ uint16_t SPI_CPHA; /*!< 时钟相位选择 */ uint16_t SPI_NSS; /*!< 片选是硬件还是软件*/ uint16_t SPI_BaudRatePrescaler; /*!< 分频系数 */ uint16_t SPI_FirstBit; /*!< 指定数据传输是从MSB还是LSB位开始的。MSB就是二进制第一位,LSB就是最后一位 */ uint16_t SPI_CRCPolynomial; /*!< CRC校验 ,设置 CRC 校验多项式,提高通信可靠性,大于 1 即可*/ }SPI_InitTypeDef;
void SPI2_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能 RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );//SPI2时钟使能 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15上拉 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始 SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式 SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器 SPI_Cmd(SPI2, ENABLE); //使能SPI外设 SPI2_ReadWriteByte(0xff);//启动传输
}