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程序实现了以下功能:

核心功能:

  1. SPI基础配置:主从模式、时钟极性/相位、数据大小
  2. 多种传输方式:轮询、中断、DMA传输
  3. 设备驱动:W25Qxx Flash、ADXL345加速度计
  4. 片选控制:软件/硬件片选管理
  5. 错误处理:错误检测与恢复机制

高级特性:

  1. 性能优化:DMA传输、16位数据模式
  2. 调试工具:状态寄存器监控、错误恢复
  3. 多设备支持:通过片选控制多个SPI设备
  4. 实时监控:传输完成标志、错误标志

应用场景:

  • Flash存储器读写
  • 传感器数据采集(加速度计、陀螺仪)
  • 显示屏驱动(OLED、LCD)
  • 无线通信模块(LoRa、WiFi)
  • 音频编解码器接口
  • 高速数据采集系统

这个程序可以直接用于STM32F10x系列项目,通过修改引脚定义和时钟配置,可以适配其他STM32系列。

posted @ 2026-04-24 08:54  lingxingqi  阅读(2)  评论(0)    收藏  举报