SPI 协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速全双工的通信总线。广泛地应用在 ADC、LCD 等设备与 MCU 间通讯的场合。
SPI信号线
SPI 总线包含 4 条总线,分别为NSS,SCK、MOSI、MISO。它们的作用介绍如下:
- NSS:片选信号线,当有多个 SPI 设备与MCU 相连时,每个设备的这个片选信号线是与 MCU 单独的引脚相连的,而其它的 SCK、MOSI、MISO 线则为多个设备并联到相同的 SPI 总线上,当NSS信号线为低电平时,片选有效,开始 SPI 通讯。
- SCK:时钟信号线,由主通讯设备产生,不同的设备支持的时钟频率不一样,如 STM32 的 SPI 时钟频率最大为 f pclk /2。
- MOSI:主设备输出/从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入数据,即这条线上数据的方向为主机到从机。
- MISO:主设备输入/从设备输出引脚。主机从这条信号线读入数据,从机的数据则由这条信号线输出,即在这条线上数据的方向为从机到主机。
SPI 模式
根据 SPI 时钟极性(CPOL)和时钟相位(CPHA) 配置的不同,分为四种 SPI 模式。
时钟极性是指 SPI 通讯设备处于空闲状态时(也可以认为这是 SPI 通讯开始时,即NSS线为低电平时),SCK 信号线的电平信号。CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。
时钟相位是指数据的采样的时刻,当 CPHA=0 时,MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的奇数边沿被采样。当 CPHA=1 时,数据线在 SCK的偶数边沿采样。
STM32 的 SPI特性
单次传输可选择为 8 或 16 位。
波特率预分频系数(最大为 f PCLK /2) 。
时钟极性(CPOL)和相位(CPHA)可编程设置 。
数据顺序的传输顺序可进行编程选择,MSB 在前或 LSB 在前 。
可触发中断的专用发送和接收标志。
可以使用 DMA 进行数据传输操作。
STM32的SPI架构分析
可以看到 MISO 数据线接收到的信号经移位寄存器处理后把数据转移到接收缓冲区,然后这个数据就可以由我们的软件从接收缓冲区读出了。当要发送数据时,我们把数据写入到发送缓冲区,硬件将会把它用移位寄存器处理后输出到 MOSI 数据线。
SCK 的时钟信号则由波特率发生器产生,我们可以通过波特率控制位(BR)来控制它输出的波特率。
控制寄存器 CR1 掌管着主控制电路,STM32 的 SPI 模块的协议设置(时钟极性、相位等)就是由它来制定的。而控制寄存器 CR2 则用于设置各种中断使能。
最后的为 NSS 引脚,NSS片选信号线的角色,如果我们把 NSS 引脚配置为硬件自动控制,SPI 模块能够自动判别它能否成为 SPI 的主机,或自动进入 SPI 从机模式。但实际上我们用得更多的是由软件控制某些 GPIO 引脚单独作为NSS信号,这个 GPIO 引脚可以随便选择。
SPI接口读取 FLASH实例分析
首先,SPI初始化。SPI_FLASH_Init()函数初始化了 SPI1 复用到的 GPIO 引脚,启动了 GPIO及 SPI1 外设的时钟,并初始化了 SPI 的模式。
void SPI1_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC |
RCC_APB2Periph_SPI1,ENABLE);
GPIO_InitStructure.GPIO_Pin = SpiFlash_nCs;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SpiFlash_CLK | SpiFlash_MOSI;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SpiFlash_MISO;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStructure);
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_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1,&SPI_InitStructure);
SPI_Cmd(SPI1,ENABLE);
}
对spi的配置很简单,重要的是理解整个通信的过程。
下图是SPI的GPIO引脚复用设置:
SPI模式初始化
(1) SPI_Mode
STM32 的 SPI 设备可以工作在主机模式(SPI_Mode_Master)或从机模式(SPI_Mode_Slave ),这两个模式的最大区别为 SPI 的 SCK 信号线的时序,SCK 的时序是由通讯中的主机产生的。若被配置为从机模式,STM32 的 SPI 模块将接受外来的 SCK 信号。
(2) SPI_DataSize
这个成员可以选择 SPI 每次通讯的数据大小(称为数据帧)为 8 位还是 16位。
(3) SPI_CPOL 和 SPI_CPHA
这两个成员就是配置 SPI 的时钟极性(CPOL)和时钟相位(CPHA),这两个配置影响到 SPI 的通讯模式,要设置成符合将要互相通讯的设备的要求。CPOL 分别可以取 SPI_CPOL_High(SPI 通讯空闲时 SCK 为高电平)和SPI_CPOL_Low(SPI 通讯空闲时 SCK 为低电平) 。CPHA 则可以取 SPI_CPHA_1Edge(在 SCK 的奇数边沿采集数据) 和SPI_CPHA_2Edge (在 SCK 的偶数边沿采集数据)。
(4) SPI_NSS
本成员配置 NSS 引脚的使用模式,可以选择为硬件模式(SPI_NSS_Hard )与软件模式(SPI_NSS_Soft ),在硬件模式中的 SPI 片选信号由硬件自动产生,而软件模式则需要我们亲自把相应的 GPIO 端口拉高或置低产生非片选和片选信号。如果外界条件允许,硬件模式还会自动将 STM32 的 SPI 设置为主机。
(5) SPI_BaudRatePrescaler
本成员设置波特率分频值,分频后的时钟即为 SPI 的 SCK 信号线的时钟频率。这个成员参数可设置为 fpclk 的 2、4、6、8、16、32、64、128、256 分频。
(6) SPI_FirstBit
所有串行的通讯协议都会有 MSB 先行(高位数据在前)还是 LSB 先行(低位数据在前)的问题,而 STM32 的 SPI 模块可以通过这个结构体成员,对这个特性编程控制。
(7) SPI_CRCPolynomial
这是 SPI 的 CRC 校验中的多项式,若我们使用 CRC 校验时,就使用这个成员的参数(多项式),来计算 CRC 的值。
配置完这些结构体成员后,我们要调用 SPI_Init()函数把这些参数写入到寄存器中,实现 SPI 的初始化,然后调用 SPI_Cmd()来使能 SPI1 外设。
以上实验主要讲述SPI的配置以及通信实例,工程已上传至网盘,有需要的自行下载,链接:https://pan.baidu.com/s/1wfykkxyhZooELFWRcCM7Kw
提取码:rswp。