SPI是串行外设接口(Serial Peripheral Interface)的缩写。SPI,是一种高速的,全双工,同步的通信总线,是Motorola最先在其的MC68HCXX芯片上使用,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议。
SPI接口主要应用在实时时钟、E2PROM、FLASH、AD转换器还有数字信号处理器和数字信号解码器之间,它的用途相当广泛。而且其传输速度较快一般可以达到10M以上。
二、基本原理
由图可知SPI一般由四条线组成:MISO(主机输入从机输出)、MOSI(主机输出从机输入)、SCLK(时钟线)、CS(片选信号)
四线模式的SPI支持全双工,如果把MISO和MOSI合为一条数据线,就变成了三线SPI,只支持半双工。
主机的数据通过移位寄存器传输给从机时,从机的数据也会通过它的移位寄存器传输给主机
示例:(从最低位开始)
主机 从机
00000000 11111111
第一个时钟周期 00000001 11111110
第二个时钟周期 00000011 11111100
......
第八个时钟周期 11111111 00000000
所以说每次的数据传输就相当于主机从机的数据进行交换
由此,如果要传输数据给从机,主机只要忽略掉从从机接收到的数据即可,如果要从从机读取数据,就可以发送无意义的数据给从机(如0x00)
注意:大多数情况下发送和接收并不会同时进行。
三、STM32上的SPI
STM32 上的SPI接口提供俩个功能,支持SPI协议和I2S音频协议,默认情况下,选择的是SPI功能。可通过软件将接口从SPI切换到I2S。
STM32上SPI的特性如下
SPI的框图如下:
这个框图并不复杂,主要要关注的就是MISO和MOSI那里。
数据总线中的数据写入发送缓冲区,通过移位寄存器一位一位的从MOSI发送,同时从机返回的数据从MISO进入移位寄存器,进入接收缓冲区。
下面的部分就是波特率发生器,主控制逻辑以及通信控制。
注意:CPOL(时钟极性)决定SPI接口传输时时钟空闲状态的电平(1,高电平;0,低电平)
CPHA(时钟相位)控制在时钟的第一个还是第二个边沿被采集(1,第二个边沿,0,第一个边沿)
四、STM32 上SPI的实现流程
1、配置相关引脚的复用功能,使能SPI时钟
2、设置SPI的工作模式
3、使能SPI
代码如下:
初始化函数
void SPI1_Init(void)
{
u16 tempreg=0;
RCC->AHB1ENR|=1<<0; //使能PORTA时钟
RCC->APB2ENR|=1<<12; //SPI1时钟使能
GPIO_Set(GPIOB,7<<3,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU); //PB3~5复用功能输出
GPIO_AF_Set(GPIOB,3,5); //PB3,AF5
GPIO_AF_Set(GPIOB,4,5); //PB4,AF5
GPIO_AF_Set(GPIOB,5,5); //PB5,AF5
//这里只针对SPI口初始化
RCC->APB2RSTR|=1<<12; //复位SPI1
RCC->APB2RSTR&=~(1<<12);//停止复位SPI1
tempreg|=0<<10; //全双工模式
tempreg|=1<<9; //软件nss管理
tempreg|=1<<8;
tempreg|=1<<2; //SPI主机
tempreg|=0<<11; //8位数据格式
tempreg|=1<<1; //空闲模式下SCK为1 CPOL=1
tempreg|=1<<0; //数据采样从第2个时间边沿开始,CPHA=1
//对SPI1属于APB2的外设.时钟频率最大为84Mhz频率.
tempreg|=7<<3; //Fsck=Fpclk1/256
tempreg|=0<<7; //MSB First
tempreg|=1<<6; //SPI启动
SPI1->CR1=tempreg; //设置CR1
SPI1->I2SCFGR&=~(1<<11);//选择SPI模式
SPI1_ReadWriteByte(0xff);//启动传输
}
这里对SPI1进行初始化,首先使能IO口的时钟,初始化IO口味复用功能,复用为SPI1,。首先先复位SPI1,设置为全双工模式,软件从器件管理,内部从器件选择,作为主机,八位数据格式,空闲状态高电平,第二个时间边沿开始采样,设置波特率256分频,从最高位开始传输,使能SPI。
再选择SPI模式(I2SCFGR寄存器的第11位)
发送一个字节启动传输
传输速度设置函数
//SPI1速度设置函数
//SpeedSet:0~7
//SPI速度=fAPB2/2^(SpeedSet+1)
//fAPB2时钟一般为84Mhz
void SPI1_SetSpeed(u8 SpeedSet)
{
SpeedSet&=0X07; //限制范围
SPI1->CR1&=0XFFC7;
SPI1->CR1|=SpeedSet<<3; //设置SPI1速度
SPI1->CR1|=1<<6; //SPI设备使能
}
通过改变分频系数来设置SPI的传输速度
读写一个字节
//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI1_ReadWriteByte(u8 TxData)
{
while((SPI1->SR&1<<1)==0); //等待发送区空
SPI1->DR=TxData; //发送一个byte
while((SPI1->SR&1<<0)==0); //等待接收完一个byte
return SPI1->DR; //返回收到的数据
}
TxDate为要发送的字节,函数的返回值为接收到的字节
五、使用SPI
在使用SPI时先将片选信号拉低选中从器件,然后发送或者接收数据,完成后,拉高片选。
随后的博客会通过W25Q128的使用来介绍SPI的使用。