SPI 是Serial Peripheral Interface的缩写,直译为串行外围设备接口,SPI是Motorola公司推出的一种同步串行通讯方式,是一种四线同步总线,因其硬件功能很强,与SPI有关的软件就相当简单,使MCU有更多的时间处理其他事务。这里要说明一下,专利在电子行业还是很关键的,因此,部分其它厂商将SPI通讯协议更名以规避高昂的专利费,但其硬件处理方式是一样的,只是换了一个名称而已,例如德仪单片机里的SSI通讯。
常用的SPI通讯方式是标准四线制,如下图电路示意图所示:
MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
SCK:串口时钟,作为主设备的输出,从设备的输入
NSS:从设备选择。这是一个可选的引脚,用来选择主/从设备。
MOSI脚相互连接,MISO脚相互连接。这样,数据在主和从之间串行地传输(MSB位在前)。通信总是由主设备发起。主设备通过MOSI脚把数据发送给从设备,从设备通过MISO引脚回传数据。这意味全双工通信的数据输出和数据输入是用同一个时钟信号同步的;时钟信号由主设备通过SCK脚提供。
比较复杂的是这个从选择(NSS)脚。其有两种模式:软件NSS模式与硬件NSS模式。
软件NSS模式下:在该模式下说得简单一些就是此引脚当作普通的GPIO来使用。其输入/输出的功能与操作GPIO是一样的。我们通过STM32来操作片外设备时多采用此模式。
硬件NSS模式下:此模式又下分两种情况:情况一、NSS输出被使能:当STM32工作为主SPI,并且NSS输出已经使能,这时NSS引脚被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将自动变成从SPI设备;情况二、NSS输出被关闭:允许操作于多主环境。
硬件的连接我们说完了,下面我再来介绍时钟线与信号线。
在学习数字逻辑电路时,我们都听老师讲过数据的锁存方式,例如上升沿锁存等。我们的SPI通讯方式在硬件上非常灵活的处理数据锁存方式,通过两个参数的配置提供了四种不同的数据传输模式,如下图所示:
从上图我们可以看出,当CPHA置高时,其数据锁存在第二个时钟边沿;CPHA清零时,数据锁存在第一个时钟边沿。而CPOL参数置高时,数据锁存在时钟信号的下降沿,时钟线空闲状态为常高,反之,数据锁存在时钟信号的上升沿,空闲状态为常低。
对于数据的发送过程,帧格式也是可以修改的,例如可以选择MSB方式(最高位先发送)或是LSB方式(最低位先发送),还可以选择插入CRC校验的方式等,这里对于这些高级的应用,由于本文片幅有限就不再详细讲解了。
接下来,我们通过STM32单片机对于SPI外设的初始化过程再来看一下SPI的硬件标准。
voidSPI_init(void)
{
RCC_APB2PeriphClockCmd(sFLASH_CS_GPIO_CLK | sFLASH_SPI_MOSI_GPIO_CLK | sFLASH_SPI_MISO_GPIO_CLK |
sFLASH_SPI_SCK_GPIO_CLK, ENABLE);
/*!< 配置SPI的外设时钟,并使能 */
RCC_APB2PeriphClockCmd(sFLASH_SPI_CLK, ENABLE);
/*!< 配置SCK引脚 */
GPIO_InitStructure.GPIO_Pin = sFLASH_SPI_SCK_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //此处根据具体应用而设置,例如可配置为开漏输出
GPIO_Init(sFLASH_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置MOSI引脚 */
GPIO_InitStructure.GPIO_Pin = sFLASH_SPI_MOSI_PIN;
GPIO_Init(sFLASH_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置MISO引脚 */
GPIO_InitStructure.GPIO_Pin = sFLASH_SPI_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(sFLASH_SPI_MISO_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置NSS引脚为GPIO输出 */
GPIO_InitStructure.GPIO_Pin = sFLASH_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(sFLASH_CS_GPIO_PORT, &GPIO_InitStructure);
/*!< SPI配置 */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //数据线两线,双向全双半
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //CPOL置高,时钟线在闲时常高,下降沿锁存数据
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //CPHA置高,则第二个时钟沿锁存数据
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //从引脚为软件配置方式
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //SPI时钟频率为4分频
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //MSB最高位优先发送
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC检验公式选择第7项
SPI_Init(sFLASH_SPI, &SPI_InitStructure);
/*!< 使能SPI */
SPI_Cmd(sFLASH_SPI, ENABLE);
}
上面的源代码是示例是ST公司操作SPIflash的Demo示例。我们再以74HC595芯片的硬件操作操作来配置,初始化SPI外设。
我们先来看一下74HC595的硬件操作时序图:
从上图,我们可以看出,时钟线(SH_CP)在空闲状态为常低,并且为第一个时钟沿的上升沿锁存数据。因此,我们需要将上面配置初始化的两个参数修改为如下:
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //CPOL置高,时钟线在闲时常低,上降沿锁存数据
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //CPHA清零,则第一个时钟沿锁存数据
其它参数不做修改即可。上述源代码已经通过STM32F103与8片74HC595串联实验通过,示例完整工程源代码可以到电子产品世界论坛片自行查找、下载。
标准四线的SPI通讯不仅为我们节省了宝贵的单片机引脚数,而且其规范的硬件协议也为我们嵌入式软件编程提供了极大的便利。丰富的外围器件支持,例如SPI的flash存储,SPI接口的SD读卡器,SPI接口的网络通讯模块都已经非常普及,可以看到应用好外设SPI通讯已经成为一名工程师必要的技能之一。