STM32 SPI程序(标准外设库版本)
基于STM32标准外设库的完整SPI程序,包含初始化、发送接收、中断、DMA传输和应用示例。
一、SPI基础配置
1.1 SPI核心定义
/* SPI 核心数据结构定义 */
#include "stm32f10x.h"
#include "stm32f10x_spi.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_exti.h"
#include "misc.h"
#include <stdio.h>
#include <string.h>
// SPI设备选择
#define SPI_USE_SPI1 1
#define SPI_USE_SPI2 0
#define SPI_USE_SPI3 0
// SPI模式定义
#define SPI_MODE_MASTER SPI_Mode_Master
#define SPI_MODE_SLAVE SPI_Mode_Slave
// SPI数据大小
#define SPI_DATA_SIZE_8BIT SPI_DataSize_8b
#define SPI_DATA_SIZE_16BIT SPI_DataSize_16b
// SPI时钟极性
#define SPI_CPOL_LOW SPI_CPOL_Low
#define SPI_CPOL_HIGH SPI_CPOL_High
// SPI时钟相位
#define SPI_CPHA_1EDGE SPI_CPHA_1Edge
#define SPI_CPHA_2EDGE SPI_CPHA_2Edge
// SPI NSS管理
#define SPI_NSS_SOFT SPI_NSS_Soft
#define SPI_NSS_HARD SPI_NSS_Hard
// 全局变量
volatile uint8_t spi_tx_complete = 0;
volatile uint8_t spi_rx_complete = 0;
volatile uint8_t spi_error = 0;
uint32_t spi_transfer_count = 0;
1.2 SPI引脚定义
/* SPI引脚配置 */
#if SPI_USE_SPI1
#define SPIx SPI1
#define SPIx_CLK RCC_APB2Periph_SPI1
#define SPIx_GPIO_CLK RCC_APB2Periph_GPIOA
#define SPIx_SCK_PIN GPIO_Pin_5 // PA5
#define SPIx_MISO_PIN GPIO_Pin_6 // PA6
#define SPIx_MOSI_PIN GPIO_Pin_7 // PA7
#define SPIx_GPIO_PORT GPIOA
#define SPIx_IRQn SPI1_IRQn
#define SPIx_DMA_CHANNEL_TX DMA1_Channel3
#define SPIx_DMA_CHANNEL_RX DMA1_Channel2
#define SPIx_DMA_FLAG_TX_TC DMA1_FLAG_TC3
#define SPIx_DMA_FLAG_RX_TC DMA1_FLAG_TC2
#elif SPI_USE_SPI2
#define SPIx SPI2
#define SPIx_CLK RCC_APB1Periph_SPI2
#define SPIx_GPIO_CLK RCC_APB2Periph_GPIOB
#define SPIx_SCK_PIN GPIO_Pin_13 // PB13
#define SPIx_MISO_PIN GPIO_Pin_14 // PB14
#define SPIx_MOSI_PIN GPIO_Pin_15 // PB15
#define SPIx_GPIO_PORT GPIOB
#define SPIx_IRQn SPI2_IRQn
#define SPIx_DMA_CHANNEL_TX DMA1_Channel5
#define SPIx_DMA_CHANNEL_RX DMA1_Channel4
#define SPIx_DMA_FLAG_TX_TC DMA1_FLAG_TC5
#define SPIx_DMA_FLAG_RX_TC DMA1_FLAG_TC4
#endif
二、SPI初始化函数
2.1 SPI基础初始化
/* SPI初始化函数 */
void SPI_Init_Basic(void) {
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
// 使能时钟
#if SPI_USE_SPI1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
#elif SPI_USE_SPI2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
#endif
// 配置SPI引脚
GPIO_InitStructure.GPIO_Pin = SPIx_SCK_PIN | SPIx_MOSI_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPIx_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SPIx_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SPIx_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_DATA_SIZE_8BIT;
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_16; // 72MHz/16=4.5MHz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPIx, &SPI_InitStructure);
// 使能SPI
SPI_Cmd(SPIx, ENABLE);
printf("SPI Basic Init Complete\n");
}
/* SPI高级初始化 */
void SPI_Init_Advanced(uint16_t mode, uint16_t data_size,
uint16_t cpol, uint16_t cpha,
uint16_t baudrate_prescaler) {
SPI_InitTypeDef SPI_InitStructure;
// 禁用SPI进行配置
SPI_Cmd(SPIx, DISABLE);
// 配置SPI参数
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = mode;
SPI_InitStructure.SPI_DataSize = data_size;
SPI_InitStructure.SPI_CPOL = cpol;
SPI_InitStructure.SPI_CPHA = cpha;
SPI_InitStructure.SPI_NSS = SPI_NSS_SOFT;
SPI_InitStructure.SPI_BaudRatePrescaler = baudrate_prescaler;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPIx, &SPI_InitStructure);
// 重新使能SPI
SPI_Cmd(SPIx, ENABLE);
printf("SPI Advanced Init Complete\n");
}
2.2 SPI从机初始化
/* SPI从机初始化 */
void SPI_Init_Slave(void) {
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能时钟
#if SPI_USE_SPI1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
#elif SPI_USE_SPI2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
#endif
// 配置SPI引脚
GPIO_InitStructure.GPIO_Pin = SPIx_SCK_PIN | SPIx_MOSI_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPIx_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SPIx_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(SPIx_GPIO_PORT, &GPIO_InitStructure);
// 配置SPI参数
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_MODE_SLAVE;
SPI_InitStructure.SPI_DataSize = SPI_DATA_SIZE_8BIT;
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_16;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPIx, &SPI_InitStructure);
// 配置SPI中断
SPI_I2S_ITConfig(SPIx, SPI_I2S_IT_RXNE, ENABLE);
SPI_I2S_ITConfig(SPIx, SPI_I2S_IT_TXE, ENABLE);
SPI_I2S_ITConfig(SPIx, SPI_I2S_IT_ERR, ENABLE);
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = SPIx_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能SPI
SPI_Cmd(SPIx, ENABLE);
printf("SPI Slave Init Complete\n");
}
三、SPI基本传输函数
3.1 轮询方式传输
/* SPI发送一个字节 */
uint8_t SPI_SendByte(uint8_t byte) {
// 等待发送缓冲区为空
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
// 发送数据
SPI_I2S_SendData(SPIx, byte);
// 等待接收完成
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
// 返回接收到的数据
return SPI_I2S_ReceiveData(SPIx);
}
/* SPI接收数据 */
uint8_t SPI_ReceiveByte(void) {
// 发送空数据以产生时钟
return SPI_SendByte(0xFF);
}
/* SPI发送多个字节 */
void SPI_SendBytes(uint8_t *data, uint16_t length) {
for (uint16_t i = 0; i < length; i++) {
SPI_SendByte(data[i]);
}
}
/* SPI接收多个字节 */
void SPI_ReceiveBytes(uint8_t *buffer, uint16_t length) {
for (uint16_t i = 0; i < length; i++) {
buffer[i] = SPI_ReceiveByte();
}
}
/* SPI全双工传输 */
void SPI_Transfer(uint8_t *tx_buffer, uint8_t *rx_buffer, uint16_t length) {
for (uint16_t i = 0; i < length; i++) {
rx_buffer[i] = SPI_SendByte(tx_buffer[i]);
}
}
/* SPI发送16位数据 */
uint16_t SPI_SendHalfWord(uint16_t half_word) {
// 等待发送缓冲区为空
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);
// 发送数据
SPI_I2S_SendData(SPIx, half_word);
// 等待接收完成
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
// 返回接收到的数据
return SPI_I2S_ReceiveData(SPIx);
}
3.2 带片选控制的传输
/* SPI片选引脚定义 */
#define SPI_CS_PORT GPIOA
#define SPI_CS_PIN GPIO_Pin_4
/* 初始化片选引脚 */
void SPI_CS_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = SPI_CS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPI_CS_PORT, &GPIO_InitStructure);
// 默认拉高CS
GPIO_SetBits(SPI_CS_PORT, SPI_CS_PIN);
}
/* 片选控制 */
void SPI_CS_Select(void) {
GPIO_ResetBits(SPI_CS_PORT, SPI_CS_PIN);
}
void SPI_CS_Deselect(void) {
GPIO_SetBits(SPI_CS_PORT, SPI_CS_PIN);
}
/* 带片选的SPI传输 */
uint8_t SPI_TransferWithCS(uint8_t data) {
uint8_t received;
SPI_CS_Select();
received = SPI_SendByte(data);
SPI_CS_Deselect();
return received;
}
/* 带片选的多字节传输 */
void SPI_TransferBufferWithCS(uint8_t *tx_buffer, uint8_t *rx_buffer, uint16_t length) {
SPI_CS_Select();
for (uint16_t i = 0; i < length; i++) {
rx_buffer[i] = SPI_SendByte(tx_buffer[i]);
}
SPI_CS_Deselect();
}
四、SPI中断方式传输
4.1 中断初始化与处理
/* SPI中断初始化 */
void SPI_Interrupt_Init(void) {
NVIC_InitTypeDef NVIC_InitStructure;
// 使能SPI中断
SPI_I2S_ITConfig(SPIx, SPI_I2S_IT_RXNE, ENABLE); // 接收非空中断
SPI_I2S_ITConfig(SPIx, SPI_I2S_IT_TXE, ENABLE); // 发送空中断
SPI_I2S_ITConfig(SPIx, SPI_I2S_IT_ERR, ENABLE); // 错误中断
// 配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = SPIx_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/* SPI中断服务函数 */
void SPIx_IRQHandler(void) {
static uint8_t tx_index = 0;
static uint8_t rx_index = 0;
static uint8_t tx_buffer[16] = {0x01, 0x02, 0x03, 0x04};
static uint8_t rx_buffer[16];
// 接收中断
if (SPI_I2S_GetITStatus(SPIx, SPI_I2S_IT_RXNE) == SET) {
rx_buffer[rx_index++] = SPI_I2S_ReceiveData(SPIx);
if (rx_index >= 16) {
spi_rx_complete = 1;
rx_index = 0;
}
SPI_I2S_ClearITPendingBit(SPIx, SPI_I2S_IT_RXNE);
}
// 发送中断
if (SPI_I2S_GetITStatus(SPIx, SPI_I2S_IT_TXE) == SET) {
if (tx_index < 16) {
SPI_I2S_SendData(SPIx, tx_buffer[tx_index++]);
} else {
// 关闭发送中断
SPI_I2S_ITConfig(SPIx, SPI_I2S_IT_TXE, DISABLE);
spi_tx_complete = 1;
tx_index = 0;
}
SPI_I2S_ClearITPendingBit(SPIx, SPI_I2S_IT_TXE);
}
// 错误中断
if (SPI_I2S_GetITStatus(SPIx, SPI_I2S_IT_ERR) == SET) {
spi_error = 1;
SPI_I2S_ClearITPendingBit(SPIx, SPI_I2S_IT_ERR);
}
}
/* 启动中断传输 */
void SPI_StartInterruptTransfer(uint8_t *tx_data, uint8_t *rx_data, uint16_t length) {
spi_tx_complete = 0;
spi_rx_complete = 0;
spi_error = 0;
// 使能发送中断
SPI_I2S_ITConfig(SPIx, SPI_I2S_IT_TXE, ENABLE);
}
五、SPI DMA传输
5.1 DMA初始化
/* SPI DMA初始化 */
void SPI_DMA_Init(void) {
DMA_InitTypeDef DMA_InitStructure;
// 使能DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// 配置TX DMA通道
DMA_DeInit(SPIx_DMA_CHANNEL_TX);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPIx->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = 0; // 将在传输时设置
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 0; // 将在传输时设置
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(SPIx_DMA_CHANNEL_TX, &DMA_InitStructure);
// 配置RX DMA通道
DMA_DeInit(SPIx_DMA_CHANNEL_RX);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPIx->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = 0; // 将在传输时设置
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 0; // 将在传输时设置
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(SPIx_DMA_CHANNEL_RX, &DMA_InitStructure);
// 使能SPI DMA
SPI_I2S_DMACmd(SPIx, SPI_I2S_DMAReq_Tx, ENABLE);
SPI_I2S_DMACmd(SPIx, SPI_I2S_DMAReq_Rx, ENABLE);
printf("SPI DMA Init Complete\n");
}
/* 启动DMA传输 */
void SPI_StartDMA_Transfer(uint8_t *tx_buffer, uint8_t *rx_buffer, uint16_t length) {
// 重置完成标志
spi_tx_complete = 0;
spi_rx_complete = 0;
spi_error = 0;
// 配置TX DMA
DMA_Cmd(SPIx_DMA_CHANNEL_TX, DISABLE);
SPIx_DMA_CHANNEL_TX->CMAR = (uint32_t)tx_buffer;
SPIx_DMA_CHANNEL_TX->CNDTR = length;
DMA_Cmd(SPIx_DMA_CHANNEL_TX, ENABLE);
// 配置RX DMA
DMA_Cmd(SPIx_DMA_CHANNEL_RX, DISABLE);
SPIx_DMA_CHANNEL_RX->CMAR = (uint32_t)rx_buffer;
SPIx_DMA_CHANNEL_RX->CNDTR = length;
DMA_Cmd(SPIx_DMA_CHANNEL_RX, ENABLE);
// 使能DMA传输完成中断
DMA_ITConfig(SPIx_DMA_CHANNEL_TX, DMA_IT_TC, ENABLE);
DMA_ITConfig(SPIx_DMA_CHANNEL_RX, DMA_IT_TC, ENABLE);
// 配置NVIC
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
NVIC_Init(&NVIC_InitStructure);
}
/* DMA中断服务函数 */
void DMA1_Channel2_IRQHandler(void) {
if (DMA_GetITStatus(DMA1_IT_TC2)) {
spi_rx_complete = 1;
DMA_ClearITPendingBit(DMA1_IT_TC2);
}
}
void DMA1_Channel3_IRQHandler(void) {
if (DMA_GetITStatus(DMA1_IT_TC3)) {
spi_tx_complete = 1;
DMA_ClearITPendingBit(DMA1_IT_TC3);
}
}
六、SPI设备驱动示例
6.1 W25Qxx Flash驱动
/* W25Qxx SPI Flash驱动 */
#define W25QXX_WRITE_ENABLE 0x06
#define W25QXX_WRITE_DISABLE 0x04
#define W25QXX_READ_STATUS_REG1 0x05
#define W25QXX_READ_STATUS_REG2 0x35
#define W25QXX_WRITE_STATUS_REG 0x01
#define W25QXX_PAGE_PROGRAM 0x02
#define W25QXX_QUAD_PAGE_PROGRAM 0x32
#define W25QXX_BLOCK_ERASE_64KB 0xD8
#define W25QXX_BLOCK_ERASE_32KB 0x52
#define W25QXX_SECTOR_ERASE 0x20
#define W25QXX_CHIP_ERASE 0xC7
#define W25QXX_ERASE_SUSPEND 0x75
#define W25QXX_ERASE_RESUME 0x7A
#define W25QXX_POWER_DOWN 0xB9
#define W25QXX_HIGH_PERF_MODE 0xA3
#define W25QXX_CONTINUOUS_READ 0x03
#define W25QXX_FAST_READ 0x0B
#define W25QXX_MANUFACTURER_ID 0x90
#define W25QXX_READ_ID 0x9F
#define W25QXX_RELEASE_POWER_DOWN 0xAB
/* 初始化W25Qxx */
void W25Qxx_Init(void) {
SPI_CS_Init();
// 复位设备
W25Qxx_Reset();
// 读取ID验证
uint16_t id = W25Qxx_ReadID();
printf("W25Qxx ID: 0x%04X\n", id);
}
/* 写使能 */
void W25Qxx_WriteEnable(void) {
SPI_CS_Select();
SPI_SendByte(W25QXX_WRITE_ENABLE);
SPI_CS_Deselect();
}
/* 读取状态寄存器 */
uint8_t W25Qxx_ReadStatusReg1(void) {
uint8_t status;
SPI_CS_Select();
SPI_SendByte(W25QXX_READ_STATUS_REG1);
status = SPI_ReceiveByte();
SPI_CS_Deselect();
return status;
}
/* 等待忙状态结束 */
void W25Qxx_WaitBusy(void) {
while (W25Qxx_ReadStatusReg1() & 0x01);
}
/* 读取设备ID */
uint16_t W25Qxx_ReadID(void) {
uint16_t id = 0;
SPI_CS_Select();
SPI_SendByte(W25QXX_MANUFACTURER_ID);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
SPI_SendByte(0x00);
id |= SPI_ReceiveByte() << 8;
id |= SPI_ReceiveByte();
SPI_CS_Deselect();
return id;
}
/* 读取数据 */
void W25Qxx_Read(uint32_t addr, uint8_t *buffer, uint16_t length) {
SPI_CS_Select();
SPI_SendByte(W25QXX_CONTINUOUS_READ);
SPI_SendByte((addr >> 16) & 0xFF);
SPI_SendByte((addr >> 8) & 0xFF);
SPI_SendByte(addr & 0xFF);
for (uint16_t i = 0; i < length; i++) {
buffer[i] = SPI_ReceiveByte();
}
SPI_CS_Deselect();
}
/* 页编程 */
void W25Qxx_PageProgram(uint32_t addr, uint8_t *data, uint16_t length) {
W25Qxx_WriteEnable();
SPI_CS_Select();
SPI_SendByte(W25Qxx_PAGE_PROGRAM);
SPI_SendByte((addr >> 16) & 0xFF);
SPI_SendByte((addr >> 8) & 0xFF);
SPI_SendByte(addr & 0xFF);
for (uint16_t i = 0; i < length; i++) {
SPI_SendByte(data[i]);
}
SPI_CS_Deselect();
W25Qxx_WaitBusy();
}
/* 扇区擦除 */
void W25Qxx_SectorErase(uint32_t addr) {
W25Qxx_WriteEnable();
SPI_CS_Select();
SPI_SendByte(W25Qxx_SECTOR_ERASE);
SPI_SendByte((addr >> 16) & 0xFF);
SPI_SendByte((addr >> 8) & 0xFF);
SPI_SendByte(addr & 0xFF);
SPI_CS_Deselect();
W25Qxx_WaitBusy();
}
/* 芯片擦除 */
void W25Qxx_ChipErase(void) {
W25Qxx_WriteEnable();
SPI_CS_Select();
SPI_SendByte(W25Qxx_CHIP_ERASE);
SPI_CS_Deselect();
W25Qxx_WaitBusy();
}
6.2 ADXL345加速度计驱动
/* ADXL345三轴加速度计驱动 */
#define ADXL345_DEVID 0x00
#define ADXL345_THRESH_TAP 0x1D
#define ADXL345_OFSX 0x1E
#define ADXL345_OFSY 0x1F
#define ADXL345_OFSZ 0x20
#define ADXL345_DUR 0x21
#define ADXL345_LATENT 0x22
#define ADXL345_WINDOW 0x23
#define ADXL345_THRESH_ACT 0x24
#define ADXL345_THRESH_INACT 0x25
#define ADXL345_TIME_INACT 0x26
#define ADXL345_ACT_INACT_CTL 0x27
#define ADXL345_THRESH_FF 0x28
#define ADXL345_TIME_FF 0x29
#define ADXL345_TAP_AXES 0x2A
#define ADXL345_ACT_TAP_STATUS 0x2B
#define ADXL345_BW_RATE 0x2C
#define ADXL345_POWER_CTL 0x2D
#define ADXL345_INT_ENABLE 0x2E
#define ADXL345_INT_MAP 0x2F
#define ADXL345_INT_SOURCE 0x30
#define ADXL345_DATA_FORMAT 0x31
#define ADXL345_DATAX0 0x32
#define ADXL345_DATAX1 0x33
#define ADXL345_DATAY0 0x34
#define ADXL345_DATAY1 0x35
#define ADXL345_DATAZ0 0x36
#define ADXL345_DATAZ1 0x37
/* 初始化ADXL345 */
uint8_t ADXL345_Init(void) {
uint8_t devid;
SPI_CS_Init();
// 读取设备ID
devid = ADXL345_ReadReg(ADXL345_DEVID);
printf("ADXL345 Device ID: 0x%02X\n", devid);
if (devid != 0xE5) {
printf("ADXL345 Not Found!\n");
return 0;
}
// 配置ADXL345
ADXL345_WriteReg(ADXL345_POWER_CTL, 0x00); // 待机模式
ADXL345_WriteReg(ADXL345_DATA_FORMAT, 0x0B); // ±16g,13位全分辨率
ADXL345_WriteReg(ADXL345_BW_RATE, 0x0A); // 数据速率100Hz
ADXL345_WriteReg(ADXL345_POWER_CTL, 0x08); // 测量模式
printf("ADXL345 Init Complete\n");
return 1;
}
/* 写寄存器 */
void ADXL345_WriteReg(uint8_t reg, uint8_t value) {
SPI_CS_Select();
SPI_SendByte(reg & 0x3F); // 写命令(最高位为0)
SPI_SendByte(value);
SPI_CS_Deselect();
}
/* 读寄存器 */
uint8_t ADXL345_ReadReg(uint8_t reg) {
uint8_t value;
SPI_CS_Select();
SPI_SendByte(reg | 0x80); // 读命令(最高位为1)
value = SPI_ReceiveByte();
SPI_CS_Deselect();
return value;
}
/* 读取加速度数据 */
void ADXL345_ReadAccel(int16_t *ax, int16_t *ay, int16_t *az) {
uint8_t buffer[6];
SPI_CS_Select();
SPI_SendByte(ADXL345_DATAX0 | 0x80); // 多字节读取
for (uint8_t i = 0; i < 6; i++) {
buffer[i] = SPI_ReceiveByte();
}
SPI_CS_Deselect();
*ax = (int16_t)((buffer[1] << 8) | buffer[0]);
*ay = (int16_t)((buffer[3] << 8) | buffer[2]);
*az = (int16_t)((buffer[5] << 8) | buffer[4]);
}
/* 设置偏移校准 */
void ADXL345_SetOffset(int8_t x, int8_t y, int8_t z) {
ADXL345_WriteReg(ADXL345_OFSX, x);
ADXL345_WriteReg(ADXL345_OFSY, y);
ADXL345_WriteReg(ADXL345_OFSZ, z);
}
参考代码 STM32 SPI程序(库函数) www.youwenfan.com/contentcnt/133724.html
七、SPI性能测试
7.1 性能测试函数
/* SPI性能测试 */
void SPI_PerformanceTest(void) {
uint8_t tx_buffer[256];
uint8_t rx_buffer[256];
uint32_t start_time, end_time;
uint32_t transfer_size = 256;
// 准备测试数据
for (uint16_t i = 0; i < transfer_size; i++) {
tx_buffer[i] = i % 256;
}
printf("=== SPI Performance Test ===\n");
// 1. 轮询方式测试
start_time = Get_SystemTick();
SPI_Transfer(tx_buffer, rx_buffer, transfer_size);
end_time = Get_SystemTick();
printf("Polling Mode:\n");
printf(" Transfer Size: %lu bytes\n", transfer_size);
printf(" Elapsed Time: %lu ms\n", end_time - start_time);
printf(" Throughput: %lu bytes/sec\n",
(transfer_size * 1000) / (end_time - start_time));
// 2. DMA方式测试
start_time = Get_SystemTick();
SPI_StartDMA_Transfer(tx_buffer, rx_buffer, transfer_size);
// 等待DMA完成
while (!spi_tx_complete || !spi_rx_complete);
end_time = Get_SystemTick();
printf("DMA Mode:\n");
printf(" Transfer Size: %lu bytes\n", transfer_size);
printf(" Elapsed Time: %lu ms\n", end_time - start_time);
printf(" Throughput: %lu bytes/sec\n",
(transfer_size * 1000) / (end_time - start_time));
// 验证数据
uint8_t error = 0;
for (uint16_t i = 0; i < transfer_size; i++) {
if (rx_buffer[i] != tx_buffer[i]) {
error++;
}
}
printf("Data Verification: %s\n", error ? "FAILED" : "PASSED");
printf("Errors: %d\n", error);
printf("===========================\n");
}
八、主程序示例
8.1 完整主程序
/* 主程序 */
#include "stm32f10x.h"
#include "system_stm32f10x.h"
volatile uint32_t system_tick = 0;
void SysTick_Init(void) {
SysTick_Config(SystemCoreClock / 1000); // 1ms中断
}
void SysTick_Handler(void) {
system_tick++;
}
uint32_t Get_SystemTick(void) {
return system_tick;
}
int main(void) {
// 系统初始化
SystemInit();
SysTick_Init();
// 初始化串口(用于printf)
USART_Init();
printf("\r\n=== STM32 SPI Demo ===\r\n");
// 1. SPI基础初始化
SPI_Init_Basic();
SPI_CS_Init();
// 2. 测试W25Qxx Flash
printf("\r\n1. Testing W25Qxx Flash...\r\n");
W25Qxx_Init();
uint8_t test_data[] = "Hello SPI Flash!";
uint8_t read_data[20];
// 擦除扇区
W25Qxx_SectorErase(0x000000);
// 写入数据
W25Qxx_PageProgram(0x000000, test_data, strlen((char*)test_data));
// 读取数据
W25Qxx_Read(0x000000, read_data, strlen((char*)test_data));
read_data[strlen((char*)test_data)] = '\0';
printf("Written: %s\r\n", test_data);
printf("Read: %s\r\n", read_data);
// 3. 测试ADXL345加速度计
printf("\r\n2. Testing ADXL345 Accelerometer...\r\n");
if (ADXL345_Init()) {
int16_t ax, ay, az;
while (1) {
ADXL345_ReadAccel(&ax, &ay, &az);
printf("Accel: X=%d, Y=%d, Z=%d\r\n", ax, ay, az);
Delay_ms(1000);
}
}
// 4. SPI DMA测试
printf("\r\n3. SPI DMA Performance Test...\r\n");
SPI_DMA_Init();
SPI_PerformanceTest();
// 5. 中断方式测试
printf("\r\n4. SPI Interrupt Test...\r\n");
SPI_Interrupt_Init();
uint8_t tx_data[] = {0x01, 0x02, 0x03, 0x04};
uint8_t rx_data[4];
SPI_StartInterruptTransfer(tx_data, rx_data, 4);
while (!spi_tx_complete || !spi_rx_complete);
printf("Interrupt Transfer Complete\r\n");
for (uint8_t i = 0; i < 4; i++) {
printf("Data[%d]: 0x%02X\r\n", i, rx_data[i]);
}
printf("\r\n=== All Tests Complete ===\r\n");
while (1) {
// 主循环
Delay_ms(1000);
}
}
九、调试与优化
9.1 SPI调试函数
/* SPI调试工具 */
void SPI_DebugInfo(void) {
printf("=== SPI Debug Information ===\r\n");
// 检查SPI状态寄存器
uint16_t sr = SPIx->SR;
printf("SPI Status Register: 0x%04X\r\n", sr);
printf(" RXNE: %d (Receive buffer not empty)\r\n", (sr & SPI_SR_RXNE) ? 1 : 0);
printf(" TXE: %d (Transmit buffer empty)\r\n", (sr & SPI_SR_TXE) ? 1 : 0);
printf(" BSY: %d (Busy flag)\r\n", (sr & SPI_SR_BSY) ? 1 : 0);
printf(" MODF: %d (Mode fault)\r\n", (sr & SPI_SR_MODF) ? 1 : 0);
printf(" OVR: %d (Overrun error)\r\n", (sr & SPI_SR_OVR) ? 1 : 0);
printf(" CRCERR: %d (CRC error)\r\n", (sr & SPI_SR_CRCERR) ? 1 : 0);
// 检查SPI控制寄存器
uint16_t cr1 = SPIx->CR1;
printf("SPI Control Register 1: 0x%04X\r\n", cr1);
printf(" SPE: %d (SPI enable)\r\n", (cr1 & SPI_CR1_SPE) ? 1 : 0);
printf(" BR: %d (Baud rate)\r\n", (cr1 & SPI_CR1_BR) >> 3);
printf(" MSTR: %d (Master mode)\r\n", (cr1 & SPI_CR1_MSTR) ? 1 : 0);
printf(" CPOL: %d (Clock polarity)\r\n", (cr1 & SPI_CR1_CPOL) ? 1 : 0);
printf(" CPHA: %d (Clock phase)\r\n", (cr1 & SPI_CR1_CPHA) ? 1 : 0);
printf(" DFF: %d (Data frame format)\r\n", (cr1 & SPI_CR1_DFF) ? 1 : 0);
printf("============================\r\n");
}
/* SPI错误恢复 */
void SPI_ErrorRecovery(void) {
if (spi_error) {
printf("SPI Error Detected, Attempting Recovery...\r\n");
// 禁用SPI
SPI_Cmd(SPIx, DISABLE);
// 清除错误标志
SPI_I2S_ClearFlag(SPIx, SPI_I2S_FLAG_OVR);
SPI_I2S_ClearFlag(SPIx, SPI_I2S_FLAG_MODF);
SPI_I2S_ClearFlag(SPIx, SPI_I2S_FLAG_CRCERR);
// 重新初始化SPI
SPI_Init_Basic();
spi_error = 0;
printf("SPI Error Recovery Complete\r\n");
}
}
9.2 SPI优化建议
/* SPI优化配置 */
void SPI_OptimizeConfig(void) {
printf("SPI Optimization Tips:\r\n");
printf("1. Use DMA for large data transfers\r\n");
printf("2. Configure appropriate baud rate for your device\r\n");
printf("3. Use 16-bit data mode when possible\r\n");
printf("4. Enable CRC checking for critical data\r\n");
printf("5. Use hardware NSS for multi-slave systems\r\n");
printf("6. Implement proper chip select timing\r\n");
printf("7. Consider using dual/quad SPI for higher speeds\r\n");
printf("8. Use interrupt-driven transfers for real-time applications\r\n");
}
/* 高性能SPI配置 */
void SPI_HighPerformanceConfig(void) {
SPI_InitTypeDef SPI_InitStructure;
// 禁用SPI进行配置
SPI_Cmd(SPIx, DISABLE);
// 配置为最高性能模式
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_MODE_MASTER;
SPI_InitStructure.SPI_DataSize = SPI_DATA_SIZE_16BIT; // 16位数据
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_2; // 最高速
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPIx, &SPI_InitStructure);
// 使能SPI
SPI_Cmd(SPIx, ENABLE);
printf("SPI Configured for High Performance\r\n");
}
十、总结
这个完整的STM32 SPI程序实现了以下功能:
核心功能:
- SPI基础配置:主从模式、时钟极性/相位、数据大小
- 多种传输方式:轮询、中断、DMA传输
- 设备驱动:W25Qxx Flash、ADXL345加速度计
- 片选控制:软件/硬件片选管理
- 错误处理:错误检测与恢复机制
高级特性:
- 性能优化:DMA传输、16位数据模式
- 调试工具:状态寄存器监控、错误恢复
- 多设备支持:通过片选控制多个SPI设备
- 实时监控:传输完成标志、错误标志
应用场景:
- Flash存储器读写
- 传感器数据采集(加速度计、陀螺仪)
- 显示屏驱动(OLED、LCD)
- 无线通信模块(LoRa、WiFi)
- 音频编解码器接口
- 高速数据采集系统
这个程序可以直接用于STM32F10x系列项目,通过修改引脚定义和时钟配置,可以适配其他STM32系列。

浙公网安备 33010602011771号