一、概述
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的使用。