1. SPI 简介
SPI(Serial Peripheral Interface)即串行外设接口,是一种高速、全双工、同步的通信总线,常用于微控制器与各种外设(如传感器、存储器等)之间的通信。STM32 系列微控制器提供了多个 SPI 接口,具有灵活的配置选项。
2. 相关函数解析
2.1 初始化相关函数
SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)
- 功能:根据
SPI_InitStruct
结构体中的参数初始化指定的 SPI 外设。 - 参数:
SPIx
:指定要初始化的 SPI 外设,如SPI1
、SPI2
等。SPI_InitStruct
:指向SPI_InitTypeDef
结构体的指针,该结构体包含了 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; // 时钟相位
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // 软件控制NSS
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 波特率预分频
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // 先发送高位
SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC多项式
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState)
- 功能:使能或禁用指定的 SPI 外设。
- 参数:
SPIx
:指定要操作的 SPI 外设。NewState
:可以是ENABLE
或DISABLE
。
示例代码:
SPI_Cmd(SPI1, ENABLE); // 使能SPI1
2.2 数据传输相关函数
SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data)
- 功能:向指定的 SPI 外设发送一个数据。
- 参数:
SPIx
:指定要操作的 SPI 外设。Data
:要发送的数据。
示例代码:
SPI_I2S_SendData(SPI1, 0x55); // 向SPI1发送数据0x55
SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)
- 功能:从指定的 SPI 外设接收一个数据。
- 参数:
SPIx
:指定要操作的 SPI 外设。
- 返回值:接收到的数据。
示例代码:
uint16_t receivedData = SPI_I2S_ReceiveData(SPI1); // 从SPI1接收数据
2.3 状态检查相关函数
SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG)
- 功能:检查指定 SPI 外设的指定标志位状态。
- 参数:
SPIx
:指定要操作的 SPI 外设。SPI_I2S_FLAG
:要检查的标志位,如SPI_FLAG_TXE
(发送缓冲区为空)、SPI_FLAG_RXNE
(接收缓冲区非空)等。
- 返回值:如果标志位被设置,返回
SET
;否则返回RESET
。
示例代码:
while (SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_TXE) == RESET); // 等待发送缓冲区为空
3. 完整示例代码
以下是一个简单的 SPI 主模式发送和接收数据的示例代码:
#include "stm32f10x.h"
void SPI1_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
// 使能SPI1和GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA, ENABLE);
// 配置SPI1引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// SPI1配置
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_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
// 使能SPI1
SPI_Cmd(SPI1, ENABLE);
}
void SPI1_SendByte(uint8_t data)
{
// 等待发送缓冲区为空
while (SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_TXE) == RESET);
// 发送数据
SPI_I2S_SendData(SPI1, data);
// 等待接收缓冲区非空
while (SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_RXNE) == RESET);
// 读取接收数据(清空缓冲区)
SPI_I2S_ReceiveData(SPI1);
}
uint8_t SPI1_ReceiveByte(void)
{
// 发送一个虚拟数据以触发接收
SPI_I2S_SendData(SPI1, 0xFF);
// 等待接收缓冲区非空
while (SPI_I2S_GetFlagStatus(SPI1, SPI_FLAG_RXNE) == RESET);
// 读取接收数据
return SPI_I2S_ReceiveData(SPI1);
}
int main(void)
{
uint8_t sendData = 0xAA;
uint8_t receivedData;
// 配置SPI1
SPI1_Configuration();
// 发送数据
SPI1_SendByte(sendData);
// 接收数据
receivedData = SPI1_ReceiveByte();
while (1)
{
// 主循环
}
}
4. 代码说明
SPI1_Configuration
函数:对 SPI1 进行初始化配置,包括 GPIO 引脚配置和 SPI 参数配置,并使能 SPI1。SPI1_SendByte
函数:向 SPI1 发送一个字节的数据,发送前等待发送缓冲区为空,发送后等待接收缓冲区非空并读取数据以清空缓冲区。SPI1_ReceiveByte
函数:从 SPI1 接收一个字节的数据,通过发送一个虚拟数据触发接收,然后等待接收缓冲区非空并读取数据。main
函数:调用初始化函数,发送一个数据并接收数据,最后进入主循环。