SPI(Serial Peripheral Interface)是一种高速、全双工、同步的通信协议,它广泛应用于微控制器与外设或其他微控制器之间的通信。SPI通过至少四条线路进行通信:一条串行时钟(SCLK)、一条主设备输出/从设备输入(MOSI, Master Out, Slave In)、一条主设备输入/从设备输出(MISO, Master In, Slave Out)以及一条片选信号(SS, Slave Select)。
工作原理
基本架构: SPI通信协议至少需要四条线路:
SCLK (Serial Clock): 由主设备生成的时钟信号,用于同步数据传输。
MOSI (Master Out, Slave In): 数据传输线,从主设备到从设备。
MISO (Master In, Slave Out): 数据传输线,从从设备到主设备。
SS (Slave Select): 由主设备控制,用于激活目标从设备。
通信过程:
初始化: 主设备通过配置串行时钟频率、数据位大小、时钟极性与相位,以及激活目标从设备的SS线来初始化SPI会话。
数据交换: 主设备通过MOSI线向从设备发送数据,同时从设备通过MISO线向主设备发送数据。这个过程由SCLK时钟同步,即在每个时钟周期,数据位逐一传输。
时钟极性(CPOL)与相位(CPHA):
SPI协议允许通过改变时钟信号的极性和相位来适应不同外设的通信需求,形成四种基本模式:
模式0 (CPOL = 0, CPHA = 0): SCLK在空闲时为低电平,数据在SCLK的上升沿采样。
模式1 (CPOL = 0, CPHA = 1): SCLK在空闲时为低电平,数据在SCLK的下降沿采样。
模式2 (CPOL = 1, CPHA = 0): SCLK在空闲时为高电平,数据在SCLK的上升沿采样。
模式3 (CPOL = 1, CPHA = 1): SCLK在空闲时为高电平,数据在SCLK的下降沿采样。
全双工通信:
SPI是一种全双工通信协议,这意味着主设备与从设备可以同时进行数据发送和接收。
多从设备:
虽然SPI总线原理上支持多从设备,但它需要为每个从设备配置一个单独的SS控制线。这可
能会随着从设备数量的增加而迅速消耗可用的GPIO资源。
程序编写
在STM32中配置SPI作为主设备进行通信时,通常需要经过以下几个步骤:配置SPI相关的GPIO引脚为复用功能模式、使能SPI时钟、配置SPI为主设备模式以及配置相关的DMA或中断(如果需要)。以下代码示例演示了如何使用STM32标准外设库配置SPI1作为主设备进行通信:
第1步:配置SPI引脚
SPI引脚需要配置为复用推挽输出模式。
// 假设SPI1位于GPIOA 5(SCK), GPIOA 6(MISO), GPIOA 7(MOSI)
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; // SCK, MOSI
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // MISO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
第2步:使能SPI时钟
需要为SPI接口使能时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
第3步:配置SPI为主设备模式
通过配置SPI_InitTypeDef结构体并使用SPI_Init()函数,设置SPI为主设备模式。
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主设备模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8位数据帧格式
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // 时钟极性为低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // 第1个边沿采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 软件控制NSS信号
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; // 波特率预分频,此处为系统时钟的16分频
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 高位在前
SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC值计算的多项式
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE); // 使能SPI1
SPI_InitTypeDef结构体参数设置的介绍
在STM32标准外设库中,SPI_InitTypeDef结构体用于配置SPI模块的初始化参数。这些参数设置涵盖了SPI通信方式的多个关键方面,包括数据方向、主从模式、数据大小、时钟极性和相位、NSS信号管理方式、波特率预分频、数据传输顺序和CRC校验多项式。以下是SPI_InitTypeDef结构体中各个成员的作用:
SPI_Direction: 设置SPI数据通信方向。
SPI_Direction_2Lines_FullDuplex:全双工,同时使用MISO和MOSI线进行数据接收和发送。
SPI_Direction_2Lines_RxOnly:仅接收模式,使用MISO线接收数据,常用于节约引脚。
SPI_Direction_1Line_Rx:单线接收模式。
SPI_Direction_1Line_Tx:单线发送模式。
SPI_Mode: 指定SPI的工作模式。
SPI_Mode_Master:主设备模式。
SPI_Mode_Slave:从设备模式。
SPI_DataSize: 配置SPI传输的数据大小。
SPI_DataSize_8b:8位数据帧。
SPI_DataSize_16b:16位数据帧。
SPI_CPOL: 配置时钟极性。
SPI_CPOL_Low:时钟空闲状态为低电平。
SPI_CPOL_High:时钟空闲状态为高电平。
SPI_CPHA: 配置时钟相位。
SPI_CPHA_1Edge:第一个时钟沿采样(上升或下降沿,取决于CPOL)。
SPI_CPHA_2Edge:第二个时钟沿采样。
SPI_NSS: 设置NSS信号的管理方式。
SPI_NSS_Soft:软件控制NSS信号。
SPI_NSS_Hard:硬件自动控制NSS信号。
SPI_BaudRatePrescaler: 设置波特率预分频,决定了SPI通信的速率。值从SPI_BaudRatePrescaler_2到SPI_BaudRatePrescaler_256不等,数值越大,通信速率越慢。
SPI_FirstBit: 指定数据传输的首位。
SPI_FirstBit_MSB:最高位(MSB)首先发送。
SPI_FirstBit_LSB:最低位(LSB)首先发送。
SPI_CRCPolynomial: 设置CRC校验多项式的值。仅在使能了SPI的CRC校验功能时使用。
通过这些参数的配置,SPI接口可以灵活地适用于多种通信需求,使得STM32微控制器能够与各种SPI设备进行高效的数据交换。在实际程序中,根据连接的外设具体要求来设置这些参数,以确保SPI接口的正确通信。
数据发送与接收
void SPI1_Transmit(uint8_t *data, uint16_t size) {
uint16_t i = 0;
for(i = 0; i < size; i++) {
// 等待发送缓冲区为空
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, data); // 发送数据
// 等待接收缓冲区非空
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
SPI_I2S_ReceiveData(SPI1); // 读取接收到的数据,即使不需要也要读取以清除接收缓冲区
}
}
请注意,SPI接口的具体配置参数(如频率、数据大小、时钟极性和相位等)需要根据所连接的外部设备的规格手册来确定,确保双方能够正确地进行通信。在实际应用中,还需考虑错误处理机制以及通信安全性等问题。
注意
在实际应用中,确保SPI通信双方(主设备和从设备)的配置参数相匹配,特别是时钟极性、时钟相位和数据帧格式。
配置从设备时,除了SPI配置,还需要考虑如何响应主设备的控制信号,例如NSS信号,进而正确管理通信时序。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_40345245/article/details/139068776
|