SPI通信协议深度解析:从理论到实践

摘要

SPI(Serial Peripheral Interface)是一种高速、全双工、同步的串行通信协议,广泛应用于嵌入式系统和各种外设之间的通信。本文将深入探讨SPI通信的基本原理、工作模式、硬件实现、软件编程、性能优化及实际应用案例,通过丰富的代码示例和实践经验,帮助读者全面掌握SPI通信技术。

第一章:SPI通信协议基础

1.1 SPI通信概述

SPI(Serial Peripheral Interface)是由摩托罗拉公司在1980年代开发的一种同步串行通信接口。它具有以下主要特点:

  • 全双工通信:数据可以同时发送和接收
  • 高速传输:通常支持高达10Mbps以上的传输速率
  • 主从架构:由一个主设备和一个或多个从设备组成
  • 同步通信:基于时钟信号进行数据同步
  • 简单硬件实现:通常只需要4根信号线

1.2 SPI通信的优势与局限性

优势:
  1. 高速传输:相比I2C和UART,SPI支持更高的时钟频率
  2. 全双工操作:可以同时进行数据发送和接收
  3. 简单的硬件设计:接口简单,易于实现
  4. 灵活的时钟配置:时钟极性和相位可调
  5. 无寻址机制:通过片选信号选择从设备
局限性:
  1. 需要更多引脚:至少需要4根线(全双工)
  2. 无错误检测:协议本身不包含错误检测机制
  3. 无应答机制:发送方无法确认接收方是否正确接收
  4. 距离受限:通常只适合板级通信

1.3 SPI与其他通信协议对比

特性 SPI I2C UART
通信方式 全双工 半双工 全双工
时钟 同步 同步 异步
数据线数量 3-4 2 2
最大速率 10+ Mbps 3.4 Mbps 1 Mbps
寻址方式 硬件片选 软件地址
拓扑结构 点对点/菊花链 多主多从 点对点
复杂度 中等

第二章:SPI通信硬件原理

2.1 SPI信号线详解

SPI通信通常使用以下4根信号线:

  1. SCLK(Serial Clock):时钟信号,由主设备产生
  2. MOSI(Master Out Slave In):主设备输出,从设备输入
  3. MISO(Master In Slave Out):主设备输入,从设备输出
  4. SS/CS(Slave Select/Chip Select):从设备选择信号,低电平有效
扩展信号线:

在某些应用中,可能会使用更多信号线:

  • WP(Write Protect):写保护
  • HOLD:保持信号,暂停传输
  • INT:中断信号

2.2 SPI硬件连接方式

2.2.1 标准单从机连接
/*
 * 标准SPI连接示意图
 * 
 * 主设备                从设备
 * MOSI  ------------->  SDI
 * MISO  <-------------  SDO
 * SCLK  ------------->  SCK
 * CS    ------------->  CS
 */
2.2.2 多从机连接方式
/*
 * 多从机独立片选连接
 * 
 * 主设备      从设备1      从设备2
 * MOSI ------> MOSI ------> MOSI
 * MISO <------ MISO <------ MISO
 * SCLK ------> SCLK ------> SCLK
 * CS1  ------> CS
 * CS2  -------------------> CS
 */
2.2.3 菊花链连接
/*
 * 菊花链连接方式
 * 
 * 主设备      从设备1      从设备2
 * MOSI ------> SDI
 * MISO <------ SDO -------> SDI
 * SCLK ------> SCK -------> SCK
 * CS   ------> CS  -------> CS
 *              SDO <------- SDO
 */

2.3 SPI硬件接口电路设计

// SPI接口电平转换电路设计示例
/*
 * 3.3V设备与5V设备SPI通信电平转换
 * 
 * 3.3V侧       电平转换芯片       5V侧
 * MOSI  ------> A1  B1 ------> MOSI
 * MISO  <------ A2  B2 <------ MISO
 * SCLK  ------> A3  B3 ------> SCLK
 * CS    ------> A4  B4 ------> CS
 * 3.3V  ------> VCCA
 * 5V    ------> VCCB
 * GND   ------> GND
 * 
 * 推荐芯片:TXS0108E, SN74LVC8T245
 */

第三章:SPI通信协议详解

3.1 SPI时钟模式

SPI有4种不同的时钟模式,由CPOL(Clock Polarity)和CPHA(Clock Phase)两个参数决定:

模式 CPOL CPHA 描述
0 0 0 时钟空闲低电平,数据在第一个边沿采样
1 0 1 时钟空闲低电平,数据在第二个边沿采样
2 1 0 时钟空闲高电平,数据在第一个边沿采样
3 1 1 时钟空闲高电平,数据在第二个边沿采样

3.2 SPI数据传输时序

// SPI模式0时序详解
/*
 * 模式0时序图 (CPOL=0, CPHA=0)
 * 
 * CS    : ______/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\______
 * SCLK  : ______/‾\___/‾\___/‾\___/‾\___/‾\______
 * MOSI  : ______/‾‾‾\___/‾‾‾\___/‾‾‾\___/‾‾‾\______
 * MISO  : ______/‾‾‾\___/‾‾‾\___/‾‾‾\___/‾‾‾\______
 * 
 * 说明:
 * 1. CS下降沿表示通信开始
 * 2. SCLK空闲时为低电平
 * 3. 数据在SCLK上升沿采样
 * 4. 数据在SCLK下降沿变化
 */

3.3 SPI数据帧格式

SPI数据帧通常由8位、16位或32位组成,传输时高位(MSB)在前或低位(LSB)在前。

// 8位数据传输示例 (MSB在前)
/*
 * 字节数据:0x53 (二进制:01010011)
 * 
 * 传输顺序:bit7 -> bit0
 * MOSI: 0  1  0  1  0  0  1  1
 *       │  │  │  │  │  │  │  │
 * SCLK: \_/‾\_/‾\_/‾\_/‾\_/‾\_/‾\_/‾
 * 
 * 时间:t0 t1 t2 t3 t4 t5 t6 t7
 */

3.4 SPI通信流程

  1. 初始化阶段

    • 配置时钟极性和相位
    • 设置时钟频率
    • 配置数据位宽
    • 初始化GPIO
  2. 通信开始

    • 拉低片选信号
    • 等待从设备准备就绪
  3. 数据传输

    • 主设备发送数据到MOSI
    • 主设备同时接收MISO数据
    • 每个时钟周期传输一位数据
  4. 通信结束

    • 拉高片选信号
    • 释放总线

第四章:STM32 SPI接口编程

4.1 STM32 SPI硬件架构

STM32系列微控制器通常包含多个SPI接口,每个SPI接口具有以下特性:

  • 支持全双工/半双工通信
  • 支持主/从模式
  • 可配置的时钟极性和相位
  • 可编程的数据位宽(4-16位)
  • 硬件CRC计算
  • DMA支持

4.2 STM32 HAL库SPI编程

4.2.1 SPI初始化配置
#include "stm32f4xx_hal.h"

SPI_HandleTypeDef hspi1;

void SPI1_Init(void)
{
    // SPI实例配置
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;              // 主模式
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;    // 全双工
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;        // 8位数据
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;      // CPOL = 0
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;          // CPHA = 0
    hspi1.Init.NSS = SPI_NSS_SOFT;                  // 软件片选
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; // 时钟分频
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;         // MSB在前
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;         // TI模式禁用
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // CRC禁用
    hspi1.Init.CRCPolynomial = 7;                   // CRC多项式
    
    // 初始化SPI
    if (HAL_SPI_Init(&hspi1) != HAL_OK)
    {
        Error_Handler();
    }
}

// GPIO初始化
void SPI1_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 使能时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_SPI1_CLK_ENABLE();
    
    // PA5: SPI1_SCK
    // PA6: SPI1_MISO
    // PA7: SPI1_MOSI
    GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;        // 复用推挽输出
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;     // 复用功能SPI1
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 片选引脚配置
    GPIO_InitStruct.Pin = GPIO_PIN_4;              // PA4作为片选
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 初始时片选无效
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}
4.2.2 基本SPI数据传输函数
// SPI字节传输函数
uint8_t SPI_TransmitReceiveByte(uint8_t txData)
{
    uint8_t rxData = 0;
    
    // 拉低片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    
    // 传输一个字节
    HAL_SPI_TransmitReceive(&hspi1, &txData, &rxData, 1, HAL_MAX_DELAY);
    
    // 拉高片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    
    return rxData;
}

// SPI多字节传输函数
void SPI_TransmitReceiveBuffer(uint8_t *txBuffer, uint8_t *rxBuffer, uint16_t size)
{
    // 拉低片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    
    // 传输数据
    HAL_SPI_TransmitReceive(&hspi1, txBuffer, rxBuffer, size, HAL_MAX_DELAY);
    
    // 拉高片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}

// SPI只发送函数
void SPI_TransmitBuffer(uint8_t *buffer, uint16_t size)
{
    // 拉低片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    
    // 发送数据
    HAL_SPI_Transmit(&hspi1, buffer, size, HAL_MAX_DELAY);
    
    // 拉高片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}

// SPI只接收函数
void SPI_ReceiveBuffer(uint8_t *buffer, uint16_t size)
{
    // 拉低片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    
    // 接收数据
    HAL_SPI_Receive(&hspi1, buffer, size, HAL_MAX_DELAY);
    
    // 拉高片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}

4.3 SPI中断方式编程

// SPI中断回调函数
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
    if (hspi->Instance == SPI1)
    {
        // SPI1传输完成处理
        SPI_TransferComplete = 1;
    }
}

// 中断方式传输
void SPI_TransmitReceive_IT(uint8_t *txBuffer, uint8_t *rxBuffer, uint16_t size)
{
    // 清除完成标志
    SPI_TransferComplete = 0;
    
    // 拉低片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    
    // 启动中断传输
    HAL_SPI_TransmitReceive_IT(&hspi1, txBuffer, rxBuffer, size);
}

// 等待传输完成
void SPI_WaitForTransferComplete(void)
{
    while (!SPI_TransferComplete)
    {
        // 可以在这里执行其他任务
        __NOP();
    }
    
    // 拉高片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}

4.4 SPI DMA方式编程

// DMA传输配置
void SPI1_DMA_Init(void)
{
    // DMA控制器时钟使能
    __HAL_RCC_DMA2_CLK_ENABLE();
    
    // DMA发送流配置
    hdma_tx.Instance = DMA2_Stream3;
    hdma_tx.Init.Channel = DMA_CHANNEL_3;
    hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_tx.Init.Mode = DMA_NORMAL;
    hdma_tx.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    
    HAL_DMA_Init(&hdma_tx);
    __HAL_LINKDMA(&hspi1, hdmatx, hdma_tx);
    
    // DMA接收流配置
    hdma_rx.Instance = DMA2_Stream0;
    hdma_rx.Init.Channel = DMA_CHANNEL_3;
    hdma_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_rx.Init.Mode = DMA_NORMAL;
    hdma_rx.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    
    HAL_DMA_Init(&hdma_rx);
    __HAL_LINKDMA(&hspi1, hdmarx, hdma_rx);
}

// DMA传输函数
void SPI_TransmitReceive_DMA(uint8_t *txBuffer, uint8_t *rxBuffer, uint16_t size)
{
    // 清除完成标志
    SPI_DMA_TransferComplete = 0;
    
    // 拉低片选
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    
    // 启动DMA传输
    HAL_SPI_TransmitReceive_DMA(&hspi1, txBuffer, rxBuffer, size);
}

// DMA传输完成回调
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
    if (hspi->Instance == SPI1)
    {
        SPI_DMA_TransferComplete = 1;
        // 拉高片选
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    }
}

第五章:Linux SPI驱动开发

5.1 Linux SPI子系统架构

Linux内核SPI子系统分为三层:

  1. SPI核心层:提供SPI总线驱动模型
  2. SPI控制器驱动:平台相关的SPI主机控制器驱动
  3. SPI设备驱动:具体的SPI外设驱动

5.2 SPI设备树配置

// SPI设备树配置示例
/*
 * arch/arm/boot/dts/myboard.dts
 */

&spi1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&spi1_pins>;
    cs-gpios = <&gpioa 4 GPIO_ACTIVE_LOW>;
    
    // SPI Flash设备
    spiflash: w25q128@0 {
        compatible = "winbond,w25q128";
        reg = <0>;                      // 片选0
        spi-max-frequency = <50000000>; // 最大50MHz
        spi-cpol;                       // CPOL = 1
        spi-cpha;                       // CPHA = 1
        
        // MTD分区
        partitions {
            compatible = "fixed-partitions";
            #address-cells = <1>;
            #size-cells = <1>;
            
            partition@0 {
                label = "bootloader";
                reg = <0x00000000 0x00040000>; // 256KB
            };
            
            partition@40000 {
                label = "kernel";
                reg = <0x00040000 0x00200000>; // 2MB
            };
            
            partition@240000 {
                label = "rootfs";
                reg = <0x00240000 0x00dc0000>; // 14MB
            };
        };
    };
    
    // 另一个SPI设备
    adc@1 {
        compatible = "ti,ads8341";
        reg = <1>;                      // 片选1
        spi-max-frequency = <1000000>;  // 最大1MHz
        vref-supply = <&vref_reg>;
    };
};

5.3 SPI字符设备驱动开发

// SPI设备驱动示例 - 头文件
#ifndef SPI_DEVICE_DRIVER_H
#define SPI_DEVICE_DRIVER_H

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/uaccess.h>
#include <linux/delay.h>

#define DEVICE_NAME "spi_example"
#define CLASS_NAME "spi_class"
#define MAX_DEVICES 1

// 设备数据结构
struct spi_example_dev {
    struct spi_device *spi;
    struct cdev cdev;
    dev_t devno;
    struct class *class;
    struct device *device;
    uint8_t *buffer;
    size_t buffer_size;
    struct mutex lock;
};

// 设备操作函数
static int spi_example_open(struct inode *inode, struct file *filp);
static int spi_example_release(struct inode *inode, struct file *filp);
static ssize_t spi_example_read(struct file *filp, char __user *buf, 
                               size_t count, loff_t *f_pos);
static ssize_t spi_example_write(struct file *filp, const char __user *buf,
                                size_t count, loff_t *f_pos);
static long spi_example_ioctl(struct file *filp, unsigned int cmd,
                             unsigned long arg);

// SPI传输函数
static int spi_example_transfer(struct spi_device *spi, uint8_t *tx_buf,
                               uint8_t *rx_buf, size_t len);

#endif // SPI_DEVICE_DRIVER_H
// SPI设备驱动示例 - 源文件
#include "spi_device_driver.h"

// 文件操作结构
static const struct file_operations spi_example_fops = {
    .owner = THIS_MODULE,
    .open = spi_example_open,
    .release = spi_example_release,
    .read = spi_example_read,
    .write = spi_example_write,
    .unlocked_ioctl = spi_example_ioctl,
};

// 全局设备结构
static struct spi_example_dev *spi_example_devices[MAX_DEVICES];
static int spi_example_major = 0;
static struct class *spi_example_class = NULL;

// 打开设备
static int spi_example_open(struct inode *inode, struct file *filp)
{
    struct spi_example_dev *dev;
    int minor = iminor(inode);
    
    if (minor >= MAX_DEVICES) {
        return -ENODEV;
    }
    
    dev = spi_example_devices[minor];
    if (!dev) {
        return -ENODEV;
    }
    
    filp->private_data = dev;
    
    return 0;
}

// 关闭设备
static int spi_example_release(struct inode *inode, struct file *filp)
{
    return 0;
}

// 读取数据
static ssize_t spi_example_read(struct file *filp, char __user *buf,
                               size_t count, loff_t *f_pos)
{
    struct spi_example_dev *dev = filp->private_data;
    uint8_t *rx_buffer;
    ssize_t retval = 0;
    
    if (!dev || !dev->spi) {
        return -ENODEV;
    }
    
    // 分配接收缓冲区
    rx_buffer = kmalloc(count, GFP_KERNEL);
    if (!rx_buffer) {
        return -ENOMEM;
    }
    
    mutex_lock(&dev->lock);
    
    // 执行SPI读取
    retval = spi_example_transfer(dev->spi, NULL, rx_buffer, count);
    if (retval < 0) {
        goto out;
    }
    
    // 复制数据到用户空间
    if (copy_to_user(buf, rx_buffer, count)) {
        retval = -EFAULT;
        goto out;
    }
    
    retval = count;
    *f_pos += count;
    
out:
    mutex_unlock(&dev->lock);
    kfree(rx_buffer);
    return retval;
}

// 写入数据
static ssize_t spi_example_write(struct file *filp, const char __user *buf,
                                size_t count, loff_t *f_pos)
{
    struct spi_example_dev *dev = filp->private_data;
    uint8_t *tx_buffer;
    ssize_t retval = 0;
    
    if (!dev || !dev->spi) {
        return -ENODEV;
    }
    
    // 分配发送缓冲区
    tx_buffer = kmalloc(count, GFP_KERNEL);
    if (!tx_buffer) {
        return -ENOMEM;
    }
    
    // 从用户空间复制数据
    if (copy_from_user(tx_buffer, buf, count)) {
        kfree(tx_buffer);
        return -EFAULT;
    }
    
    mutex_lock(&dev->lock);
    
    // 执行SPI写入
    retval = spi_example_transfer(dev->spi, tx_buffer, NULL, count);
    if (retval < 0) {
        goto out;
    }
    
    retval = count;
    *f_pos += count;
    
out:
    mutex_unlock(&dev->lock);
    kfree(tx_buffer);
    return retval;
}

// IOCTL命令处理
static long spi_example_ioctl(struct file *filp, unsigned int cmd,
                             unsigned long arg)
{
    struct spi_example_dev *dev = filp->private_data;
    struct spi_ioc_transfer xfer;
    uint8_t *tx_buf = NULL, *rx_buf = NULL;
    int retval = 0;
    
    if (!dev || !dev->spi) {
        return -ENODEV;
    }
    
    switch (cmd) {
    case SPI_EXAMPLE_IOCTL_XFER:
        if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) {
            return -EFAULT;
        }
        
        // 分配缓冲区
        if (xfer.tx_buf) {
            tx_buf = kmalloc(xfer.len, GFP_KERNEL);
            if (!tx_buf) {
                return -ENOMEM;
            }
            if (copy_from_user(tx_buf, (void __user *)xfer.tx_buf, xfer.len)) {
                kfree(tx_buf);
                return -EFAULT;
            }
        }
        
        if (xfer.rx_buf) {
            rx_buf = kmalloc(xfer.len, GFP_KERNEL);
            if (!rx_buf) {
                kfree(tx_buf);
                return -ENOMEM;
            }
        }
        
        // 执行传输
        mutex_lock(&dev->lock);
        retval = spi_example_transfer(dev->spi, tx_buf, rx_buf, xfer.len);
        mutex_unlock(&dev->lock);
        
        // 复制接收数据回用户空间
        if (rx_buf && xfer.rx_buf) {
            if (copy_to_user((void __user *)xfer.rx_buf, rx_buf, xfer.len)) {
                retval = -EFAULT;
            }
        }
        
        kfree(tx_buf);
        kfree(rx_buf);
        break;
        
    default:
        return -ENOTTY;
    }
    
    return retval;
}

// SPI传输函数
static int spi_example_transfer(struct spi_device *spi, uint8_t *tx_buf,
                               uint8_t *rx_buf, size_t len)
{
    struct spi_transfer t = {
        .tx_buf = tx_buf,
        .rx_buf = rx_buf,
        .len = len,
        .delay_usecs = 10,
        .speed_hz = spi->max_speed_hz,
    };
    
    struct spi_message m;
    
    spi_message_init(&m);
    spi_message_add_tail(&t, &m);
    
    return spi_sync(spi, &m);
}

// SPI设备探测函数
static int spi_example_probe(struct spi_device *spi)
{
    struct spi_example_dev *dev;
    int retval, minor;
    
    // 分配设备结构
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev) {
        return -ENOMEM;
    }
    
    // 初始化设备
    dev->spi = spi;
    mutex_init(&dev->lock);
    
    // 配置SPI设备
    spi->mode = SPI_MODE_0;
    spi->bits_per_word = 8;
    spi->max_speed_hz = 1000000; // 1MHz
    
    retval = spi_setup(spi);
    if (retval < 0) {
        dev_err(&spi->dev, "Failed to setup SPI device\n");
        goto error;
    }
    
    // 分配设备号
    for (minor = 0; minor < MAX_DEVICES; minor++) {
        if (!spi_example_devices[minor]) {
            break;
        }
    }
    
    if (minor == MAX_DEVICES) {
        retval = -ENODEV;
        goto error;
    }
    
    dev->devno = MKDEV(spi_example_major, minor);
    
    // 初始化字符设备
    cdev_init(&dev->cdev, &spi_example_fops);
    dev->cdev.owner = THIS_MODULE;
    
    retval = cdev_add(&dev->cdev, dev->devno, 1);
    if (retval) {
        dev_err(&spi->dev, "Failed to add character device\n");
        goto error;
    }
    
    // 创建设备节点
    dev->device = device_create(spi_example_class, &spi->dev,
                               dev->devno, NULL, "spi_example%d", minor);
    if (IS_ERR(dev->device)) {
        retval = PTR_ERR(dev->device);
        goto error_cdev;
    }
    
    spi_example_devices[minor] = dev;
    spi_set_drvdata(spi, dev);
    
    dev_info(&spi->dev, "SPI example device registered\n");
    return 0;
    
error_cdev:
    cdev_del(&dev->cdev);
error:
    kfree(dev);
    return retval;
}

// SPI设备移除函数
static int spi_example_remove(struct spi_device *spi)
{
    struct spi_example_dev *dev = spi_get_drvdata(spi);
    int minor;
    
    if (!dev) {
        return -ENODEV;
    }
    
    // 查找设备索引
    for (minor = 0; minor < MAX_DEVICES; minor++) {
        if (spi_example_devices[minor] == dev) {
            break;
        }
    }
    
    if (minor < MAX_DEVICES) {
        spi_example_devices[minor] = NULL;
    }
    
    // 销毁设备
    if (dev->device) {
        device_destroy(spi_example_class, dev->devno);
    }
    
    cdev_del(&dev->cdev);
    kfree(dev);
    
    dev_info(&spi->dev, "SPI example device removed\n");
    return 0;
}

// SPI设备ID表
static const struct spi_device_id spi_example_ids[] = {
    { "spi-example", 0 },
    { }
};
MODULE_DEVICE_TABLE(spi, spi_example_ids);

// SPI设备匹配表
static const struct of_device_id spi_example_of_match[] = {
    { .compatible = "example,spi-device" },
    { }
};
MODULE_DEVICE_TABLE(of, spi_example_of_match);

// SPI驱动结构
static struct spi_driver spi_example_driver = {
    .driver = {
        .name = "spi_example",
        .owner = THIS_MODULE,
        .of_match_table = spi_example_of_match,
    },
    .probe = spi_example_probe,
    .remove = spi_example_remove,
    .id_table = spi_example_ids,
};

// 模块初始化
static int __init spi_example_init(void)
{
    int retval;
    
    // 分配主设备号
    retval = alloc_chrdev_region(&spi_example_devices[0]->devno, 0,
                                MAX_DEVICES, DEVICE_NAME);
    if (retval < 0) {
        pr_err("Failed to allocate device numbers\n");
        return retval;
    }
    
    spi_example_major = MAJOR(spi_example_devices[0]->devno);
    
    // 创建设备类
    spi_example_class = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(spi_example_class)) {
        retval = PTR_ERR(spi_example_class);
        unregister_chrdev_region(spi_example_devices[0]->devno, MAX_DEVICES);
        return retval;
    }
    
    // 注册SPI驱动
    retval = spi_register_driver(&spi_example_driver);
    if (retval < 0) {
        class_destroy(spi_example_class);
        unregister_chrdev_region(spi_example_devices[0]->devno, MAX_DEVICES);
        return retval;
    }
    
    pr_info("SPI example driver loaded\n");
    return 0;
}

// 模块清理
static void __exit spi_example_exit(void)
{
    spi_unregister_driver(&spi_example_driver);
    class_destroy(spi_example_class);
    unregister_chrdev_region(spi_example_devices[0]->devno, MAX_DEVICES);
    pr_info("SPI example driver unloaded\n");
}

module_init(spi_example_init);
module_exit(spi_example_exit);

MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("SPI Example Device Driver");
MODULE_LICENSE("GPL");

5.4 SPI用户空间编程

// SPI用户空间测试程序
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

// SPI设备路径
#define SPI_DEVICE "/dev/spidev0.0"

// SPI配置结构
static uint8_t mode = SPI_MODE_0;
static uint8_t bits = 8;
static uint32_t speed = 1000000; // 1MHz
static uint16_t delay = 10;

// SPI传输函数
static int spi_transfer(int fd, uint8_t *tx, uint8_t *rx, size_t len)
{
    struct spi_ioc_transfer tr = {
        .tx_buf = (unsigned long)tx,
        .rx_buf = (unsigned long)rx,
        .len = len,
        .delay_usecs = delay,
        .speed_hz = speed,
        .bits_per_word = bits,
    };
    
    return ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
}

// 配置SPI参数
static int spi_config(int fd)
{
    int ret;
    
    // 设置SPI模式
    ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
    if (ret == -1) {
        perror("Can't set SPI mode");
        return -1;
    }
    
    // 设置数据位宽
    ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret == -1) {
        perror("Can't set bits per word");
        return -1;
    }
    
    // 设置时钟速度
    ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret == -1) {
        perror("Can't set max speed");
        return -1;
    }
    
    return 0;
}

// 读取SPI参数
static void spi_print_config(int fd)
{
    uint8_t mode_r, bits_r;
    uint32_t speed_r;
    
    ioctl(fd, SPI_IOC_RD_MODE, &mode_r);
    ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits_r);
    ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed_r);
    
    printf("SPI configuration:\n");
    printf("  Mode: %d\n", mode_r);
    printf("  Bits per word: %d\n", bits_r);
    printf("  Max speed: %d Hz\n", speed_r);
}

// 主函数
int main(int argc, char *argv[])
{
    int fd;
    uint8_t tx_buffer[32], rx_buffer[32];
    int ret, i;
    
    // 打开SPI设备
    fd = open(SPI_DEVICE, O_RDWR);
    if (fd < 0) {
        perror("Can't open SPI device");
        return -1;
    }
    
    // 配置SPI
    if (spi_config(fd) < 0) {
        close(fd);
        return -1;
    }
    
    // 打印配置
    spi_print_config(fd);
    
    // 准备测试数据
    memset(tx_buffer, 0, sizeof(tx_buffer));
    memset(rx_buffer, 0, sizeof(rx_buffer));
    
    for (i = 0; i < sizeof(tx_buffer); i++) {
        tx_buffer[i] = i;
    }
    
    // 执行SPI传输
    printf("\nSending data: ");
    for (i = 0; i < 16; i++) {
        printf("%02X ", tx_buffer[i]);
    }
    printf("\n");
    
    ret = spi_transfer(fd, tx_buffer, rx_buffer, sizeof(tx_buffer));
    if (ret < 0) {
        perror("SPI transfer failed");
        close(fd);
        return -1;
    }
    
    // 打印接收到的数据
    printf("Received data: ");
    for (i = 0; i < 16; i++) {
        printf("%02X ", rx_buffer[i]);
    }
    printf("\n");
    
    // 关闭设备
    close(fd);
    
    return 0;
}

第六章:SPI高级应用与优化

6.1 SPI性能优化技巧

6.1.1 时钟频率优化
// 动态时钟调整
void optimize_spi_clock(SPI_HandleTypeDef *hspi, uint32_t desired_freq)
{
    uint32_t apb_clock = HAL_RCC_GetPCLK2Freq(); // 获取APB时钟
    uint32_t prescaler = 0;
    
    // 计算最佳分频系数
    if (desired_freq >= apb_clock / 2) {
        prescaler = SPI_BAUDRATEPRESCALER_2;
    } else if (desired_freq >= apb_clock / 4) {
        prescaler = SPI_BAUDRATEPRESCALER_4;
    } else if (desired_freq >= apb_clock / 8) {
        prescaler = SPI_BAUDRATEPRESCALER_8;
    } else if (desired_freq >= apb_clock / 16) {
        prescaler = SPI_BAUDRATEPRESCALER_16;
    } else if (desired_freq >= apb_clock / 32) {
        prescaler = SPI_BAUDRATEPRESCALER_32;
    } else if (desired_freq >= apb_clock / 64) {
        prescaler = SPI_BAUDRATEPRESCALER_64;
    } else if (desired_freq >= apb_clock / 128) {
        prescaler = SPI_BAUDRATEPRESCALER_128;
    } else {
        prescaler = SPI_BAUDRATEPRESCALER_256;
    }
    
    // 更新SPI配置
    hspi->Init.BaudRatePrescaler = prescaler;
    HAL_SPI_Init(hspi);
    
    // 计算实际频率
    uint32_t actual_freq = apb_clock / (1 << (prescaler >> 3));
    printf("SPI Clock: Desired=%luHz, Actual=%luHz\n", 
           desired_freq, actual_freq);
}
6.1.2 DMA双缓冲技术
// DMA双缓冲实现
#define BUFFER_SIZE 1024

typedef struct {
    uint8_t buffer1[BUFFER_SIZE];
    uint8_t buffer2[BUFFER_SIZE];
    uint8_t *current_tx;
    uint8_t *current_rx;
    volatile uint8_t buffer_ready;
    size_t data_size;
} DoubleBuffer;

DoubleBuffer dma_buffer;

void init_double_buffer(void)
{
    memset(&dma_buffer, 0, sizeof(DoubleBuffer));
    dma_buffer.current_tx = dma_buffer.buffer1;
    dma_buffer.current_rx = dma_buffer.buffer1;
    dma_buffer.buffer_ready = 0;
}

void spi_dma_double_buffer_transfer(SPI_HandleTypeDef *hspi, 
                                   uint8_t *data, size_t size)
{
    static uint8_t active_buffer = 0;
    
    // 等待当前传输完成
    while (dma_buffer.buffer_ready == 0) {
        // 可以在这里处理其他任务
    }
    
    // 准备下一个缓冲区
    uint8_t *next_tx = (active_buffer == 0) ? 
                      dma_buffer.buffer1 : dma_buffer.buffer2;
    
    memcpy(next_tx, data, size);
    dma_buffer.data_size = size;
    dma_buffer.buffer_ready = 0;
    
    // 启动DMA传输
    if (active_buffer == 0) {
        HAL_SPI_TransmitReceive_DMA(hspi, 
                                   dma_buffer.buffer1,
                                   dma_buffer.buffer1,
                                   size);
        active_buffer = 1;
    } else {
        HAL_SPI_TransmitReceive_DMA(hspi,
                                   dma_buffer.buffer2,
                                   dma_buffer.buffer2,
                                   size);
        active_buffer = 0;
    }
}

// DMA传输完成回调
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
    dma_buffer.buffer_ready = 1;
    
    // 处理接收到的数据
    process_received_data(dma_buffer.current_rx, dma_buffer.data_size);
    
    // 切换缓冲区
    dma_buffer.current_rx = (dma_buffer.current_rx == dma_buffer.buffer1) ?
                           dma_buffer.buffer2 : dma_buffer.buffer1;
}

6.2 SPI错误处理与调试

6.2.1 错误检测与恢复
// SPI错误处理
typedef enum {
    SPI_ERROR_NONE = 0,
    SPI_ERROR_TIMEOUT,
    SPI_ERROR_CRC,
    SPI_ERROR_OVERRUN,
    SPI_ERROR_FRAME,
    SPI_ERROR_DMA,
    SPI_ERROR_BUSY
} SPI_ErrorType;

typedef struct {
    uint32_t total_transfers;
    uint32_t error_count;
    uint32_t timeout_count;
    uint32_t crc_error_count;
    SPI_ErrorType last_error;
} SPI_ErrorStats;

SPI_ErrorStats spi_stats;

void spi_error_handler(SPI_HandleTypeDef *hspi, SPI_ErrorType error)
{
    spi_stats.error_count++;
    spi_stats.last_error = error;
    
    switch (error) {
    case SPI_ERROR_TIMEOUT:
        spi_stats.timeout_count++;
        printf("SPI Timeout Error\n");
        break;
        
    case SPI_ERROR_CRC:
        spi_stats.crc_error_count++;
        printf("SPI CRC Error\n");
        break;
        
    case SPI_ERROR_OVERRUN:
        printf("SPI Overrun Error\n");
        HAL_SPI_Abort(hspi);
        HAL_SPI_Init(hspi);
        break;
        
    case SPI_ERROR_FRAME:
        printf("SPI Frame Error\n");
        break;
        
    default:
        printf("SPI Unknown Error\n");
        break;
    }
    
    // 记录调试信息
    log_spi_error(hspi, error);
    
    // 尝试恢复
    spi_recover(hspi);
}

void spi_recover(SPI_HandleTypeDef *hspi)
{
    // 1. 停止当前传输
    HAL_SPI_Abort(hspi);
    
    // 2. 重新初始化SPI
    HAL_SPI_DeInit(hspi);
    HAL_SPI_Init(hspi);
    
    // 3. 重置DMA(如果使用)
    if (hspi->hdmatx) {
        HAL_DMA_DeInit(hspi->hdmatx);
        HAL_DMA_Init(hspi->hdmatx);
    }
    
    if (hspi->hdmarx) {
        HAL_DMA_DeInit(hspi->hdmarx);
        HAL_DMA_Init(hspi->hdmarx);
    }
    
    // 4. 重新配置SPI
    __HAL_SPI_ENABLE(hspi);
    
    printf("SPI recovered successfully\n");
}

// 带错误处理的SPI传输
HAL_StatusTypeDef spi_transfer_with_retry(SPI_HandleTypeDef *hspi,
                                         uint8_t *tx_data,
                                         uint8_t *rx_data,
                                         uint16_t size,
                                         uint32_t timeout,
                                         uint8_t max_retries)
{
    HAL_StatusTypeDef status;
    uint8_t retry_count = 0;
    
    do {
        status = HAL_SPI_TransmitReceive(hspi, tx_data, rx_data, size, timeout);
        
        if (status == HAL_OK) {
            spi_stats.total_transfers++;
            return HAL_OK;
        }
        
        retry_count++;
        
        // 处理特定错误
        if (status == HAL_TIMEOUT) {
            spi_error_handler(hspi, SPI_ERROR_TIMEOUT);
        } else if (status == HAL_ERROR) {
            // 检查具体错误标志
            if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_CRCERR)) {
                spi_error_handler(hspi, SPI_ERROR_CRC);
                __HAL_SPI_CLEAR_CRCERRFLAG(hspi);
            } else if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_OVR)) {
                spi_error_handler(hspi, SPI_ERROR_OVERRUN);
                __HAL_SPI_CLEAR_OVRFLAG(hspi);
            }
        }
        
        // 短暂延时后重试
        HAL_Delay(10);
        
    } while (retry_count < max_retries);
    
    return status;
}
6.2.2 SPI调试工具
// SPI调试信息收集
typedef struct {
    uint32_t timestamp;
    uint8_t tx_data[16];
    uint8_t rx_data[16];
    uint16_t data_size;
    SPI_ErrorType error;
    uint32_t clock_freq;
    uint8_t mode;
} SPI_DebugEntry;

#define DEBUG_BUFFER_SIZE 100
SPI_DebugEntry debug_buffer[DEBUG_BUFFER_SIZE];
uint32_t debug_index = 0;

void log_spi_transaction(uint8_t *tx_data, uint8_t *rx_data,
                        uint16_t size, SPI_ErrorType error)
{
    uint32_t index = debug_index % DEBUG_BUFFER_SIZE;
    
    debug_buffer[index].timestamp = HAL_GetTick();
    debug_buffer[index].data_size = size;
    debug_buffer[index].error = error;
    debug_buffer[index].clock_freq = hspi1.Instance->CR1 & SPI_CR1_BR;
    debug_buffer[index].mode = (hspi1.Instance->CR1 & (SPI_CR1_CPOL | SPI_CR1_CPHA)) >> 1;
    
    // 记录数据(最多16字节)
    uint16_t copy_size = (size > 16) ? 16 : size;
    memcpy(debug_buffer[index].tx_data, tx_data, copy_size);
    memcpy(debug_buffer[index].rx_data, rx_data, copy_size);
    
    debug_index++;
}

void print_spi_debug_info(void)
{
    printf("=== SPI Debug Information ===\n");
    printf("Total transfers: %lu\n", spi_stats.total_transfers);
    printf("Error count: %lu\n", spi_stats.error_count);
    printf("Timeout count: %lu\n", spi_stats.timeout_count);
    printf("CRC error count: %lu\n", spi_stats.crc_error_count);
    
    printf("\nLast %d transactions:\n", 
           (debug_index > DEBUG_BUFFER_SIZE) ? DEBUG_BUFFER_SIZE : debug_index);
    
    uint32_t start = (debug_index > DEBUG_BUFFER_SIZE) ? 
                    debug_index - DEBUG_BUFFER_SIZE : 0;
    
    for (uint32_t i = start; i < debug_index; i++) {
        uint32_t idx = i % DEBUG_BUFFER_SIZE;
        printf("[%lu] Time: %lums, Size: %u, Error: %d\n",
               i, debug_buffer[idx].timestamp,
               debug_buffer[idx].data_size,
               debug_buffer[idx].error);
        
        if (debug_buffer[idx].data_size > 0) {
            printf("  TX: ");
            for (uint16_t j = 0; j < debug_buffer[idx].data_size && j < 16; j++) {
                printf("%02X ", debug_buffer[idx].tx_data[j]);
            }
            printf("\n  RX: ");
            for (uint16_t j = 0; j < debug_buffer[idx].data_size && j < 16; j++) {
                printf("%02X ", debug_buffer[idx].rx_data[j]);
            }
            printf("\n");
        }
    }
}

// 实时SPI信号监控(需要逻辑分析仪或示波器接口)
void monitor_spi_signals(void)
{
    // 配置GPIO为输入模式,用于监控
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 监控引脚配置
    GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; // SCK, MISO, MOSI
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 采样SPI信号
    uint32_t samples[1000];
    for (int i = 0; i < 1000; i++) {
        samples[i] = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) << 2 |  // SCK
                     HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) << 1 |  // MISO
                     HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7);        // MOSI
        HAL_Delay(1); // 1ms采样间隔
    }
    
    // 分析信号
    analyze_spi_waveform(samples, 1000);
}

6.3 SPI在物联网中的应用

6.3.1 SPI连接传感器
// 温湿度传感器DHT12 SPI接口
#define DHT12_READ_CMD  0x00
#define DHT12_WRITE_CMD 0x80

typedef struct {
    float temperature;
    float humidity;
    uint8_t checksum;
} DHT12_Data;

HAL_StatusTypeDef dht12_read_sensor(SPI_HandleTypeDef *hspi, DHT12_Data *data)
{
    uint8_t tx_buffer[5] = {0};
    uint8_t rx_buffer[5] = {0};
    
    // 发送读取命令
    tx_buffer[0] = DHT12_READ_CMD;
    
    // 选择传感器
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    
    // 传输数据
    HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(hspi, tx_buffer, 
                                                      rx_buffer, 5, 100);
    
    // 取消选择
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    
    if (status == HAL_OK) {
        // 解析数据
        data->humidity = rx_buffer[1] + rx_buffer[2] * 0.1;
        data->temperature = rx_buffer[3] + rx_buffer[4] * 0.1;
        
        // 验证校验和
        uint8_t checksum = rx_buffer[1] + rx_buffer[2] + rx_buffer[3] + rx_buffer[4];
        data->checksum = (checksum == rx_buffer[0]);
        
        if (!data->checksum) {
            return HAL_ERROR;
        }
    }
    
    return status;
}

// 批量读取传感器数据
void read_multiple_sensors(void)
{
    DHT12_Data sensor_data[10];
    uint32_t timestamps[10];
    
    for (int i = 0; i < 10; i++) {
        timestamps[i] = HAL_GetTick();
        
        if (dht12_read_sensor(&hspi1, &sensor_data[i]) == HAL_OK) {
            printf("Sensor %d: Temp=%.1fC, Hum=%.1f%%\n", 
                   i, sensor_data[i].temperature, sensor_data[i].humidity);
            
            // 上传到云平台
            upload_to_cloud(sensor_data[i], timestamps[i]);
        } else {
            printf("Sensor %d: Read failed\n", i);
        }
        
        HAL_Delay(2000); // 2秒间隔
    }
    
    // 数据分析
    analyze_sensor_data(sensor_data, 10);
}
6.3.2 SPI连接无线模块
// NRF24L01+无线模块SPI驱动
#define NRF24_CMD_R_REGISTER    0x00
#define NRF24_CMD_W_REGISTER    0x20
#define NRF24_CMD_R_RX_PAYLOAD  0x61
#define NRF24_CMD_W_TX_PAYLOAD  0xA0
#define NRF24_CMD_FLUSH_TX      0xE1
#define NRF24_CMD_FLUSH_RX      0xE2

typedef struct {
    SPI_HandleTypeDef *spi;
    GPIO_TypeDef *ce_port;
    uint16_t ce_pin;
    GPIO_TypeDef *csn_port;
    uint16_t csn_pin;
    uint8_t channel;
    uint8_t payload_size;
} NRF24_Handle;

// NRF24L01+初始化
HAL_StatusTypeDef nrf24_init(NRF24_Handle *nrf)
{
    uint8_t config = 0;
    
    // 1. 配置CE和CSN引脚
    nrf24_csn_low(nrf);
    
    // 2. 写入配置寄存器
    config = (1 << 1) |  // PRX: 接收模式
             (1 << 0);   // PWR_UP: 上电
    nrf24_write_register(nrf, 0x00, &config, 1);
    
    // 3. 启用自动应答
    uint8_t en_aa = 0x01; // 启用数据通道0自动应答
    nrf24_write_register(nrf, 0x01, &en_aa, 1);
    
    // 4. 启用接收地址
    uint8_t en_rxaddr = 0x01; // 启用数据通道0
    nrf24_write_register(nrf, 0x02, &en_rxaddr, 1);
    
    // 5. 设置地址宽度
    uint8_t setup_aw = 0x03; // 5字节地址
    nrf24_write_register(nrf, 0x03, &setup_aw, 1);
    
    // 6. 设置重传
    uint8_t setup_retr = (0x01 << 4) | 0x0F; // 重传延迟250us,重试15次
    nrf24_write_register(nrf, 0x04, &setup_retr, 1);
    
    // 7. 设置射频通道
    nrf24_write_register(nrf, 0x05, &nrf->channel, 1);
    
    // 8. 设置射频参数
    uint8_t rf_setup = (0x01 << 5) |  // RF_PWR: 0dBm
                       (0x01 << 3) |  // RF_DR_HIGH: 1Mbps
                       (0x00 << 2);   // LNA_HCURR: 默认
    nrf24_write_register(nrf, 0x06, &rf_setup, 1);
    
    nrf24_csn_high(nrf);
    
    // 9. 清空中断
    uint8_t status = 0;
    nrf24_get_status(nrf, &status);
    
    return HAL_OK;
}

// SPI读写函数
uint8_t nrf24_read_register(NRF24_Handle *nrf, uint8_t reg, 
                           uint8_t *data, uint8_t len)
{
    uint8_t status;
    uint8_t tx_buffer[32], rx_buffer[32];
    
    tx_buffer[0] = NRF24_CMD_R_REGISTER | reg;
    memset(&tx_buffer[1], 0xFF, len);
    
    nrf24_csn_low(nrf);
    HAL_SPI_TransmitReceive(nrf->spi, tx_buffer, rx_buffer, len + 1, 100);
    nrf24_csn_high(nrf);
    
    status = rx_buffer[0];
    memcpy(data, &rx_buffer[1], len);
    
    return status;
}

uint8_t nrf24_write_register(NRF24_Handle *nrf, uint8_t reg,
                            uint8_t *data, uint8_t len)
{
    uint8_t status;
    uint8_t tx_buffer[32], rx_buffer[32];
    
    tx_buffer[0] = NRF24_CMD_W_REGISTER | reg;
    memcpy(&tx_buffer[1], data, len);
    
    nrf24_csn_low(nrf);
    HAL_SPI_TransmitReceive(nrf->spi, tx_buffer, rx_buffer, len + 1, 100);
    nrf24_csn_high(nrf);
    
    status = rx_buffer[0];
    return status;
}

// 发送数据
uint8_t nrf24_send_packet(NRF24_Handle *nrf, uint8_t *data, uint8_t len)
{
    uint8_t status;
    uint8_t tx_buffer[32];
    
    // 切换到发送模式
    nrf24_ce_low(nrf);
    
    // 写入发送数据
    tx_buffer[0] = NRF24_CMD_W_TX_PAYLOAD;
    memcpy(&tx_buffer[1], data, len);
    
    nrf24_csn_low(nrf);
    HAL_SPI_Transmit(nrf->spi, tx_buffer, len + 1, 100);
    nrf24_csn_high(nrf);
    
    // 启动发送
    nrf24_ce_high(nrf);
    HAL_Delay(1); // 至少10us
    nrf24_ce_low(nrf);
    
    // 等待发送完成
    HAL_Delay(10);
    
    // 获取状态
    nrf24_get_status(nrf, &status);
    
    // 清空中断标志
    if (status & (1 << 5)) { // TX_DS
        nrf24_write_register(nrf, 0x07, &status, 1);
    }
    
    return status;
}

// 接收数据
uint8_t nrf24_receive_packet(NRF24_Handle *nrf, uint8_t *data, uint8_t *len)
{
    uint8_t status;
    uint8_t rx_buffer[32];
    
    // 获取状态
    nrf24_get_status(nrf, &status);
    
    if (status & (1 << 6)) { // RX_DR
        // 读取数据
        rx_buffer[0] = NRF24_CMD_R_RX_PAYLOAD;
        
        nrf24_csn_low(nrf);
        HAL_SPI_TransmitReceive(nrf->spi, rx_buffer, rx_buffer, 
                               nrf->payload_size + 1, 100);
        nrf24_csn_high(nrf);
        
        memcpy(data, &rx_buffer[1], nrf->payload_size);
        *len = nrf->payload_size;
        
        // 清空中断标志
        nrf24_write_register(nrf, 0x07, &status, 1);
        
        return 1;
    }
    
    return 0;
}

第七章:SPI通信实战案例

7.1 基于SPI的OLED显示屏驱动

// SSD1306 OLED显示屏SPI驱动
#define OLED_WIDTH   128
#define OLED_HEIGHT  64
#define OLED_PAGES   (OLED_HEIGHT / 8)

// SSD1306命令定义
#define OLED_CMD_SET_CONTRAST       0x81
#define OLED_CMD_DISPLAY_ALL_ON_RESUME 0xA4
#define OLED_CMD_DISPLAY_ALL_ON     0xA5
#define OLED_CMD_NORMAL_DISPLAY     0xA6
#define OLED_CMD_INVERT_DISPLAY     0xA7
#define OLED_CMD_DISPLAY_OFF        0xAE
#define OLED_CMD_DISPLAY_ON         0xAF
#define OLED_CMD_SET_DISPLAY_OFFSET 0xD3
#define OLED_CMD_SET_COM_PINS       0xDA
#define OLED_CMD_SET_VCOM_DETECT    0xDB
#define OLED_CMD_SET_DISPLAY_CLOCK_DIV 0xD5
#define OLED_CMD_SET_PRECHARGE      0xD9
#define OLED_CMD_SET_MULTIPLEX      0xA8
#define OLED_CMD_SET_LOW_COLUMN     0x00
#define OLED_CMD_SET_HIGH_COLUMN    0x10
#define OLED_CMD_SET_START_LINE     0x40
#define OLED_CMD_MEMORY_MODE        0x20
#define OLED_CMD_COLUMN_ADDR        0x21
#define OLED_CMD_PAGE_ADDR          0x22
#define OLED_CMD_COM_SCAN_INC       0xC0
#define OLED_CMD_COM_SCAN_DEC       0xC8
#define OLED_CMD_SEG_REMAP          0xA0
#define OLED_CMD_CHARGE_PUMP        0x8D

typedef struct {
    SPI_HandleTypeDef *spi;
    GPIO_TypeDef *dc_port;   // 数据/命令选择
    uint16_t dc_pin;
    GPIO_TypeDef *res_port;  // 复位
    uint16_t res_pin;
    GPIO_TypeDef *cs_port;   // 片选
    uint16_t cs_pin;
    uint8_t buffer[OLED_PAGES][OLED_WIDTH];
} OLED_Handle;

// OLED初始化
void oled_init(OLED_Handle *oled)
{
    // 硬件复位
    HAL_GPIO_WritePin(oled->res_port, oled->res_pin, GPIO_PIN_RESET);
    HAL_Delay(10);
    HAL_GPIO_WritePin(oled->res_port, oled->res_pin, GPIO_PIN_SET);
    HAL_Delay(10);
    
    // 发送初始化命令序列
    oled_send_command(oled, OLED_CMD_DISPLAY_OFF);
    
    oled_send_command(oled, OLED_CMD_SET_DISPLAY_CLOCK_DIV);
    oled_send_command(oled, 0x80); // 建议值
    
    oled_send_command(oled, OLED_CMD_SET_MULTIPLEX);
    oled_send_command(oled, 0x3F); // 64-1
    
    oled_send_command(oled, OLED_CMD_SET_DISPLAY_OFFSET);
    oled_send_command(oled, 0x00); // 无偏移
    
    oled_send_command(oled, OLED_CMD_SET_START_LINE | 0x00);
    
    oled_send_command(oled, OLED_CMD_CHARGE_PUMP);
    oled_send_command(oled, 0x14); // 启用电荷泵
    
    oled_send_command(oled, OLED_CMD_MEMORY_MODE);
    oled_send_command(oled, 0x00); // 水平寻址模式
    
    oled_send_command(oled, OLED_CMD_SEG_REMAP | 0x01); // 列地址127映射到SEG0
    oled_send_command(oled, OLED_CMD_COM_SCAN_DEC); // 扫描方向
    
    oled_send_command(oled, OLED_CMD_SET_COM_PINS);
    oled_send_command(oled, 0x12); // 引脚配置
    
    oled_send_command(oled, OLED_CMD_SET_CONTRAST);
    oled_send_command(oled, 0x7F); // 对比度
    
    oled_send_command(oled, OLED_CMD_SET_PRECHARGE);
    oled_send_command(oled, 0xF1); // 预充电周期
    
    oled_send_command(oled, OLED_CMD_SET_VCOM_DETECT);
    oled_send_command(oled, 0x40); // VCOMH电平
    
    oled_send_command(oled, OLED_CMD_DISPLAY_ALL_ON_RESUME);
    oled_send_command(oled, OLED_CMD_NORMAL_DISPLAY);
    
    oled_send_command(oled, OLED_CMD_DISPLAY_ON);
    
    // 清屏
    oled_clear(oled);
    oled_update(oled);
}

// 发送命令
void oled_send_command(OLED_Handle *oled, uint8_t cmd)
{
    HAL_GPIO_WritePin(oled->dc_port, oled->dc_pin, GPIO_PIN_RESET); // 命令模式
    HAL_GPIO_WritePin(oled->cs_port, oled->cs_pin, GPIO_PIN_RESET); // 选择OLED
    
    HAL_SPI_Transmit(oled->spi, &cmd, 1, 100);
    
    HAL_GPIO_WritePin(oled->cs_port, oled->cs_pin, GPIO_PIN_SET); // 取消选择
}

// 发送数据
void oled_send_data(OLED_Handle *oled, uint8_t *data, uint16_t size)
{
    HAL_GPIO_WritePin(oled->dc_port, oled->dc_pin, GPIO_PIN_SET); // 数据模式
    HAL_GPIO_WritePin(oled->cs_port, oled->cs_pin, GPIO_PIN_RESET); // 选择OLED
    
    HAL_SPI_Transmit(oled->spi, data, size, 1000);
    
    HAL_GPIO_WritePin(oled->cs_port, oled->cs_pin, GPIO_PIN_SET); // 取消选择
}

// 清屏
void oled_clear(OLED_Handle *oled)
{
    memset(oled->buffer, 0, sizeof(oled->buffer));
}

// 更新显示
void oled_update(OLED_Handle *oled)
{
    for (uint8_t page = 0; page < OLED_PAGES; page++) {
        // 设置页地址
        oled_send_command(oled, 0xB0 + page); // 设置页起始地址
        oled_send_command(oled, OLED_CMD_SET_LOW_COLUMN); // 设置列低地址
        oled_send_command(oled, OLED_CMD_SET_HIGH_COLUMN); // 设置列高地址
        
        // 发送该页数据
        oled_send_data(oled, oled->buffer[page], OLED_WIDTH);
    }
}

// 绘制像素
void oled_draw_pixel(OLED_Handle *oled, uint8_t x, uint8_t y, uint8_t color)
{
    if (x >= OLED_WIDTH || y >= OLED_HEIGHT) {
        return;
    }
    
    uint8_t page = y / 8;
    uint8_t bit = y % 8;
    
    if (color) {
        oled->buffer[page][x] |= (1 << bit);
    } else {
        oled->buffer[page][x] &= ~(1 << bit);
    }
}

// 绘制直线
void oled_draw_line(OLED_Handle *oled, uint8_t x0, uint8_t y0,
                   uint8_t x1, uint8_t y1, uint8_t color)
{
    int16_t dx = abs(x1 - x0);
    int16_t dy = abs(y1 - y0);
    int16_t sx = (x0 < x1) ? 1 : -1;
    int16_t sy = (y0 < y1) ? 1 : -1;
    int16_t err = dx - dy;
    int16_t e2;
    
    while (1) {
        oled_draw_pixel(oled, x0, y0, color);
        
        if (x0 == x1 && y0 == y1) {
            break;
        }
        
        e2 = 2 * err;
        if (e2 > -dy) {
            err -= dy;
            x0 += sx;
        }
        if (e2 < dx) {
            err += dx;
            y0 += sy;
        }
    }
}

// 绘制矩形
void oled_draw_rectangle(OLED_Handle *oled, uint8_t x, uint8_t y,
                        uint8_t width, uint8_t height, uint8_t color)
{
    // 上边
    oled_draw_line(oled, x, y, x + width - 1, y, color);
    // 下边
    oled_draw_line(oled, x, y + height - 1, x + width - 1, y + height - 1, color);
    // 左边
    oled_draw_line(oled, x, y, x, y + height - 1, color);
    // 右边
    oled_draw_line(oled, x + width - 1, y, x + width - 1, y + height - 1, color);
}

// 绘制填充矩形
void oled_fill_rectangle(OLED_Handle *oled, uint8_t x, uint8_t y,
                        uint8_t width, uint8_t height, uint8_t color)
{
    for (uint8_t i = 0; i < height; i++) {
        oled_draw_line(oled, x, y + i, x + width - 1, y + i, color);
    }
}

// 绘制圆形
void oled_draw_circle(OLED_Handle *oled, uint8_t x0, uint8_t y0,
                     uint8_t radius, uint8_t color)
{
    int16_t f = 1 - radius;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * radius;
    int16_t x = 0;
    int16_t y = radius;
    
    oled_draw_pixel(oled, x0, y0 + radius, color);
    oled_draw_pixel(oled, x0, y0 - radius, color);
    oled_draw_pixel(oled, x0 + radius, y0, color);
    oled_draw_pixel(oled, x0 - radius, y0, color);
    
    while (x < y) {
        if (f >= 0) {
            y--;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;
        
        oled_draw_pixel(oled, x0 + x, y0 + y, color);
        oled_draw_pixel(oled, x0 - x, y0 + y, color);
        oled_draw_pixel(oled, x0 + x, y0 - y, color);
        oled_draw_pixel(oled, x0 - x, y0 - y, color);
        oled_draw_pixel(oled, x0 + y, y0 + x, color);
        oled_draw_pixel(oled, x0 - y, y0 + x, color);
        oled_draw_pixel(oled, x0 + y, y0 - x, color);
        oled_draw_pixel(oled, x0 - y, y0 - x, color);
    }
}

// 显示字符
void oled_draw_char(OLED_Handle *oled, uint8_t x, uint8_t y,
                   char ch, const uint8_t *font, uint8_t color)
{
    uint8_t font_width = font[0];
    uint8_t font_height = font[1];
    uint8_t bytes_per_char = font[2];
    
    // 计算字符在字体数据中的位置
    uint16_t index = 3 + (ch - 32) * bytes_per_char;
    
    for (uint8_t i = 0; i < font_width; i++) {
        uint8_t column_data = font[index + i];
        
        for (uint8_t j = 0; j < font_height; j++) {
            if (column_data & (1 << j)) {
                oled_draw_pixel(oled, x + i, y + j, color);
            }
        }
    }
}

// 显示字符串
void oled_draw_string(OLED_Handle *oled, uint8_t x, uint8_t y,
                     const char *str, const uint8_t *font, uint8_t color)
{
    uint8_t font_width = font[0];
    uint8_t cursor_x = x;
    
    while (*str) {
        oled_draw_char(oled, cursor_x, y, *str, font, color);
        cursor_x += font_width + 1; // 字符宽度加间距
        str++;
        
        // 检查是否超出屏幕宽度
        if (cursor_x + font_width >= OLED_WIDTH) {
            break;
        }
    }
}

// 显示位图
void oled_draw_bitmap(OLED_Handle *oled, uint8_t x, uint8_t y,
                     const uint8_t *bitmap, uint8_t width, uint8_t height,
                     uint8_t color)
{
    for (uint8_t j = 0; j < height; j++) {
        for (uint8_t i = 0; i < width; i++) {
            if (bitmap[j * width + i]) {
                oled_draw_pixel(oled, x + i, y + j, color);
            }
        }
    }
}

// 示例:创建简单UI
void oled_create_ui(OLED_Handle *oled)
{
    // 清屏
    oled_clear(oled);
    
    // 绘制边框
    oled_draw_rectangle(oled, 0, 0, OLED_WIDTH, OLED_HEIGHT, 1);
    
    // 绘制标题栏
    oled_fill_rectangle(oled, 1, 1, OLED_WIDTH - 2, 12, 1);
    
    // 显示标题(反色显示)
    const uint8_t font_6x8[] = {6, 8, 6, /* 字体数据 */};
    oled_draw_string(oled, 10, 3, "SPI OLED Demo", font_6x8, 0);
    
    // 绘制分割线
    oled_draw_line(oled, 1, 15, OLED_WIDTH - 2, 15, 1);
    
    // 显示传感器数据
    oled_draw_string(oled, 5, 20, "Temperature: 25.5C", font_6x8, 1);
    oled_draw_string(oled, 5, 30, "Humidity:    60.0%", font_6x8, 1);
    oled_draw_string(oled, 5, 40, "Pressure:  1013 hPa", font_6x8, 1);
    
    // 绘制电池图标
    oled_draw_rectangle(oled, OLED_WIDTH - 25, 3, 20, 8, 1);
    oled_fill_rectangle(oled, OLED_WIDTH - 24, 4, 15, 6, 1); // 电池电量75%
    oled_fill_rectangle(oled, OLED_WIDTH - 5, 5, 2, 4, 1);   // 电池正极
    
    // 绘制信号强度指示
    for (int i = 0; i < 4; i++) {
        oled_fill_rectangle(oled, OLED_WIDTH - 40 + i * 3, 
                          10 - i * 2, 2, i * 2 + 2, 1);
    }
    
    // 更新显示
    oled_update(oled);
}

// 动画示例:进度条
void oled_show_progress_bar(OLED_Handle *oled, uint8_t progress)
{
    uint8_t bar_width = 100;
    uint8_t bar_height = 8;
    uint8_t bar_x = (OLED_WIDTH - bar_width) / 2;
    uint8_t bar_y = (OLED_HEIGHT - bar_height) / 2;
    
    // 清除进度条区域
    oled_fill_rectangle(oled, bar_x - 1, bar_y - 1, 
                       bar_width + 2, bar_height + 2, 0);
    
    // 绘制外框
    oled_draw_rectangle(oled, bar_x, bar_y, bar_width, bar_height, 1);
    
    // 绘制进度
    uint8_t fill_width = (progress * bar_width) / 100;
    if (fill_width > 0) {
        oled_fill_rectangle(oled, bar_x + 1, bar_y + 1, 
                           fill_width - 2, bar_height - 2, 1);
    }
    
    // 显示百分比
    char progress_str[10];
    sprintf(progress_str, "%d%%", progress);
    
    const uint8_t font_6x8[] = {6, 8, 6, /* 字体数据 */};
    oled_draw_string(oled, bar_x + bar_width + 5, bar_y, 
                    progress_str, font_6x8, 1);
    
    oled_update(oled);
}

// 动画示例:滚动文本
void oled_scroll_text(OLED_Handle *oled, const char *text, 
                     uint8_t speed_ms)
{
    uint8_t text_width = strlen(text) * 7; // 假设每个字符6像素宽+1像素间距
    uint8_t start_x = OLED_WIDTH;
    
    while (start_x > -text_width) {
        // 清除显示区域
        oled_fill_rectangle(oled, 0, 20, OLED_WIDTH, 8, 0);
        
        // 绘制文本
        const uint8_t font_6x8[] = {6, 8, 6, /* 字体数据 */};
        oled_draw_string(oled, start_x, 20, text, font_6x8, 1);
        
        // 更新显示
        oled_update(oled);
        
        // 延迟
        HAL_Delay(speed_ms);
        
        // 移动位置
        start_x--;
    }
}

7.2 SPI Flash存储器操作

// W25Qxx系列SPI Flash驱动
#define W25Q_PAGE_SIZE     256
#define W25Q_SECTOR_SIZE   4096
#define W25Q_BLOCK_SIZE    65536

// W25Q指令集
#define W25Q_CMD_WRITE_ENABLE     0x06
#define W25Q_CMD_WRITE_DISABLE    0x04
#define W25Q_CMD_READ_STATUS_REG1 0x05
#define W25Q_CMD_WRITE_STATUS_REG1 0x01
#define W25Q_CMD_READ_DATA        0x03
#define W25Q_CMD_FAST_READ        0x0B
#define W25Q_CMD_PAGE_PROGRAM     0x02
#define W25Q_CMD_SECTOR_ERASE     0x20
#define W25Q_CMD_BLOCK_ERASE_32K  0x52
#define W25Q_CMD_BLOCK_ERASE_64K  0xD8
#define W25Q_CMD_CHIP_ERASE       0xC7
#define W25Q_CMD_POWER_DOWN       0xB9
#define W25Q_CMD_RELEASE_POWER_DOWN 0xAB
#define W25Q_CMD_READ_MANUFACTURER_ID 0x90
#define W25Q_CMD_READ_JEDEC_ID    0x9F

typedef struct {
    SPI_HandleTypeDef *spi;
    GPIO_TypeDef *cs_port;
    uint16_t cs_pin;
    uint32_t capacity;      // 容量(字节)
    uint16_t page_size;     // 页大小
    uint16_t sector_size;   // 扇区大小
    uint32_t sector_count;  // 扇区数量
    uint8_t manufacturer_id;
    uint8_t memory_type;
    uint8_t capacity_id;
} W25Q_Handle;

// 等待Flash就绪
static void w25q_wait_busy(W25Q_Handle *flash)
{
    uint8_t status;
    
    do {
        HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_RESET);
        uint8_t cmd = W25Q_CMD_READ_STATUS_REG1;
        HAL_SPI_Transmit(flash->spi, &cmd, 1, 100);
        HAL_SPI_Receive(flash->spi, &status, 1, 100);
        HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_SET);
        
        // 检查BUSY位(bit0)
    } while (status & 0x01);
}

// 写使能
static void w25q_write_enable(W25Q_Handle *flash)
{
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_RESET);
    uint8_t cmd = W25Q_CMD_WRITE_ENABLE;
    HAL_SPI_Transmit(flash->spi, &cmd, 1, 100);
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_SET);
}

// 读取制造商和设备ID
void w25q_read_id(W25Q_Handle *flash)
{
    uint8_t tx_buffer[4] = {W25Q_CMD_READ_JEDEC_ID, 0x00, 0x00, 0x00};
    uint8_t rx_buffer[4];
    
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_RESET);
    HAL_SPI_TransmitReceive(flash->spi, tx_buffer, rx_buffer, 4, 100);
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_SET);
    
    flash->manufacturer_id = rx_buffer[1];
    flash->memory_type = rx_buffer[2];
    flash->capacity_id = rx_buffer[3];
    
    // 根据ID确定容量
    switch (flash->capacity_id) {
    case 0x15: // W25Q16
        flash->capacity = 2 * 1024 * 1024; // 2MB
        break;
    case 0x16: // W25Q32
        flash->capacity = 4 * 1024 * 1024; // 4MB
        break;
    case 0x17: // W25Q64
        flash->capacity = 8 * 1024 * 1024; // 8MB
        break;
    case 0x18: // W25Q128
        flash->capacity = 16 * 1024 * 1024; // 16MB
        break;
    default:
        flash->capacity = 0;
        break;
    }
    
    flash->page_size = W25Q_PAGE_SIZE;
    flash->sector_size = W25Q_SECTOR_SIZE;
    flash->sector_count = flash->capacity / flash->sector_size;
}

// 读取数据
void w25q_read(W25Q_Handle *flash, uint32_t addr, uint8_t *data, uint32_t len)
{
    uint8_t tx_buffer[4];
    
    tx_buffer[0] = W25Q_CMD_READ_DATA;
    tx_buffer[1] = (addr >> 16) & 0xFF;
    tx_buffer[2] = (addr >> 8) & 0xFF;
    tx_buffer[3] = addr & 0xFF;
    
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(flash->spi, tx_buffer, 4, 100);
    HAL_SPI_Receive(flash->spi, data, len, 1000);
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_SET);
}

// 快速读取
void w25q_fast_read(W25Q_Handle *flash, uint32_t addr, uint8_t *data, uint32_t len)
{
    uint8_t tx_buffer[5];
    
    tx_buffer[0] = W25Q_CMD_FAST_READ;
    tx_buffer[1] = (addr >> 16) & 0xFF;
    tx_buffer[2] = (addr >> 8) & 0xFF;
    tx_buffer[3] = addr & 0xFF;
    tx_buffer[4] = 0x00; // 虚字节
    
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(flash->spi, tx_buffer, 5, 100);
    HAL_SPI_Receive(flash->spi, data, len, 1000);
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_SET);
}

// 页编程(写入)
void w25q_page_program(W25Q_Handle *flash, uint32_t addr, uint8_t *data, uint32_t len)
{
    uint8_t tx_buffer[4];
    
    // 检查地址是否页对齐
    if ((addr % flash->page_size) + len > flash->page_size) {
        // 跨页写入需要分多次
        uint32_t remaining = len;
        uint32_t current_addr = addr;
        uint8_t *current_data = data;
        
        while (remaining > 0) {
            uint32_t write_len = flash->page_size - (current_addr % flash->page_size);
            if (write_len > remaining) {
                write_len = remaining;
            }
            
            w25q_page_program(flash, current_addr, current_data, write_len);
            
            current_addr += write_len;
            current_data += write_len;
            remaining -= write_len;
        }
        return;
    }
    
    // 写使能
    w25q_write_enable(flash);
    
    // 发送页编程命令和地址
    tx_buffer[0] = W25Q_CMD_PAGE_PROGRAM;
    tx_buffer[1] = (addr >> 16) & 0xFF;
    tx_buffer[2] = (addr >> 8) & 0xFF;
    tx_buffer[3] = addr & 0xFF;
    
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(flash->spi, tx_buffer, 4, 100);
    HAL_SPI_Transmit(flash->spi, data, len, 1000);
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_SET);
    
    // 等待写入完成
    w25q_wait_busy(flash);
}

// 扇区擦除
void w25q_sector_erase(W25Q_Handle *flash, uint32_t sector)
{
    uint32_t addr = sector * flash->sector_size;
    uint8_t tx_buffer[4];
    
    // 写使能
    w25q_write_enable(flash);
    
    // 发送扇区擦除命令和地址
    tx_buffer[0] = W25Q_CMD_SECTOR_ERASE;
    tx_buffer[1] = (addr >> 16) & 0xFF;
    tx_buffer[2] = (addr >> 8) & 0xFF;
    tx_buffer[3] = addr & 0xFF;
    
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(flash->spi, tx_buffer, 4, 100);
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_SET);
    
    // 等待擦除完成
    w25q_wait_busy(flash);
}

// 块擦除
void w25q_block_erase(W25Q_Handle *flash, uint32_t block, uint8_t block_size)
{
    uint32_t addr = block * block_size;
    uint8_t tx_buffer[4];
    uint8_t cmd;
    
    // 选择擦除命令
    if (block_size == 32 * 1024) {
        cmd = W25Q_CMD_BLOCK_ERASE_32K;
    } else if (block_size == 64 * 1024) {
        cmd = W25Q_CMD_BLOCK_ERASE_64K;
    } else {
        return; // 不支持的块大小
    }
    
    // 写使能
    w25q_write_enable(flash);
    
    // 发送块擦除命令和地址
    tx_buffer[0] = cmd;
    tx_buffer[1] = (addr >> 16) & 0xFF;
    tx_buffer[2] = (addr >> 8) & 0xFF;
    tx_buffer[3] = addr & 0xFF;
    
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(flash->spi, tx_buffer, 4, 100);
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_SET);
    
    // 等待擦除完成
    w25q_wait_busy(flash);
}

// 芯片擦除
void w25q_chip_erase(W25Q_Handle *flash)
{
    // 写使能
    w25q_write_enable(flash);
    
    // 发送芯片擦除命令
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_RESET);
    uint8_t cmd = W25Q_CMD_CHIP_ERASE;
    HAL_SPI_Transmit(flash->spi, &cmd, 1, 100);
    HAL_GPIO_WritePin(flash->cs_port, flash->cs_pin, GPIO_PIN_SET);
    
    // 等待擦除完成(需要较长时间)
    w25q_wait_busy(flash);
}

// 文件系统支持
typedef struct {
    W25Q_Handle *flash;
    uint32_t fat_start_sector;
    uint32_t data_start_sector;
    uint32_t total_sectors;
    uint32_t sectors_per_cluster;
    uint8_t *fat_cache;
} W25Q_FAT;

// 初始化FAT文件系统
int w25q_fat_init(W25Q_FAT *fat, W25Q_Handle *flash)
{
    // 分配FAT缓存
    fat->fat_cache = malloc(flash->sector_size);
    if (!fat->fat_cache) {
        return -1;
    }
    
    fat->flash = flash;
    fat->total_sectors = flash->sector_count;
    
    // 简单FAT配置
    fat->sectors_per_cluster = 1; // 每簇1个扇区
    fat->fat_start_sector = 0;    // FAT从扇区0开始
    fat->data_start_sector = 2;   // 数据区从扇区2开始
    
    // 初始化FAT表
    w25q_read(flash, 0, fat->fat_cache, flash->sector_size);
    
    // 检查是否已经格式化
    if (fat->fat_cache[0] != 0xF8 || fat->fat_cache[1] != 0xFF) {
        // 未格式化,进行格式化
        w25q_fat_format(fat);
    }
    
    return 0;
}

// 格式化FAT
void w25q_fat_format(W25Q_FAT *fat)
{
    // 擦除前几个扇区
    for (uint32_t i = 0; i < 10; i++) {
        w25q_sector_erase(fat->flash, i);
    }
    
    // 创建引导扇区
    uint8_t boot_sector[512] = {0};
    boot_sector[0] = 0xEB; // 跳转指令
    boot_sector[1] = 0x3C;
    boot_sector[2] = 0x90;
    
    memcpy(boot_sector + 3, "MSDOS5.0", 8); // OEM名称
    
    // BPB参数
    boot_sector[11] = 0x00; // 每扇区字节数低字节
    boot_sector[12] = 0x02; // 每扇区512字节高字节
    
    boot_sector[13] = 0x01; // 每簇扇区数
    
    boot_sector[14] = 0x01; // 保留扇区数低字节
    boot_sector[15] = 0x00; // 保留扇区数高字节
    
    boot_sector[16] = 0x01; // FAT数量
    
    // 写入引导扇区
    w25q_page_program(fat->flash, 0, boot_sector, 512);
    
    // 初始化FAT表
    memset(fat->fat_cache, 0, fat->flash->sector_size);
    fat->fat_cache[0] = 0xF8; // 介质描述符
    fat->fat_cache[1] = 0xFF;
    fat->fat_cache[2] = 0xFF;
    fat->fat_cache[3] = 0xFF;
    
    // 写入FAT表
    w25q_page_program(fat->flash, 512, fat->fat_cache, fat->flash->sector_size);
}

// 文件操作
typedef struct {
    W25Q_FAT *fat;
    uint32_t first_cluster;
    uint32_t current_cluster;
    uint32_t file_size;
    uint32_t position;
    uint8_t mode; // 'r' 读, 'w' 写, 'a' 追加
} W25Q_File;

// 打开文件
int w25q_fopen(W25Q_File *file, W25Q_FAT *fat, const char *filename, const char *mode)
{
    // 简化的文件打开实现
    // 在实际应用中,需要搜索目录项
    
    file->fat = fat;
    file->mode = mode[0];
    file->position = 0;
    
    if (file->mode == 'r') {
        // 查找文件
        // 这里简化实现,假设文件从簇2开始
        file->first_cluster = 2;
        file->current_cluster = 2;
        
        // 读取文件大小等信息
        // 简化为固定大小
        file->file_size = 1024;
    } else if (file->mode == 'w' || file->mode == 'a') {
        // 创建新文件或追加
        // 分配起始簇
        file->first_cluster = 2; // 简化
        file->current_cluster = 2;
        file->file_size = 0;
        
        if (file->mode == 'a') {
            // 移动到文件末尾
            file->position = file->file_size;
        }
    }
    
    return 0;
}

// 读取文件
size_t w25q_fread(void *ptr, size_t size, size_t count, W25Q_File *file)
{
    size_t total_bytes = size * count;
    size_t bytes_to_read = total_bytes;
    
    if (file->position + bytes_to_read > file->file_size) {
        bytes_to_read = file->file_size - file->position;
    }
    
    if (bytes_to_read == 0) {
        return 0;
    }
    
    // 计算读取地址
    uint32_t sector = file->fat->data_start_sector + 
                     (file->current_cluster - 2) * file->fat->sectors_per_cluster;
    uint32_t addr = sector * file->fat->flash->sector_size + 
                   (file->position % (file->fat->sectors_per_cluster * file->fat->flash->sector_size));
    
    // 读取数据
    w25q_read(file->fat->flash, addr, ptr, bytes_to_read);
    
    // 更新位置
    file->position += bytes_to_read;
    
    return bytes_to_read / size;
}

// 写入文件
size_t w25q_fwrite(const void *ptr, size_t size, size_t count, W25Q_File *file)
{
    size_t total_bytes = size * count;
    
    // 计算写入地址
    uint32_t sector = file->fat->data_start_sector + 
                     (file->current_cluster - 2) * file->fat->sectors_per_cluster;
    uint32_t addr = sector * file->fat->flash->sector_size + 
                   (file->position % (file->fat->sectors_per_cluster * file->fat->flash->sector_size));
    
    // 写入数据
    w25q_page_program(file->fat->flash, addr, (uint8_t *)ptr, total_bytes);
    
    // 更新位置和文件大小
    file->position += total_bytes;
    if (file->position > file->file_size) {
        file->file_size = file->position;
    }
    
    return count;
}

// 关闭文件
int w25q_fclose(W25Q_File *file)
{
    // 更新文件大小等信息到目录项
    // 简化实现
    
    return 0;
}

// 示例:使用SPI Flash存储传感器数据
void store_sensor_data(W25Q_Handle *flash, float temperature, 
                      float humidity, uint32_t timestamp)
{
    static uint32_t write_address = 0x1000; // 起始地址
    static uint16_t record_count = 0;
    
    // 数据记录结构
    typedef struct {
        uint32_t timestamp;
        float temperature;
        float humidity;
        uint16_t checksum;
    } SensorRecord;
    
    SensorRecord record;
    record.timestamp = timestamp;
    record.temperature = temperature;
    record.humidity = humidity;
    
    // 计算校验和
    uint8_t *data = (uint8_t *)&record;
    uint16_t checksum = 0;
    for (int i = 0; i < sizeof(record) - sizeof(uint16_t); i++) {
        checksum += data[i];
    }
    record.checksum = checksum;
    
    // 检查是否需要擦除新扇区
    if ((write_address % flash->sector_size) == 0) {
        uint32_t sector = write_address / flash->sector_size;
        w25q_sector_erase(flash, sector);
    }
    
    // 写入数据
    w25q_page_program(flash, write_address, (uint8_t *)&record, sizeof(record));
    
    // 更新地址
    write_address += sizeof(record);
    record_count++;
    
    // 如果到达存储末尾,循环覆盖
    if (write_address >= 0x10000) { // 限制在64KB范围内
        write_address = 0x1000;
        record_count = 0;
    }
    
    printf("Stored record %d at 0x%08lX\n", record_count, write_address - sizeof(record));
}

// 示例:读取存储的传感器数据
void read_sensor_data(W25Q_Handle *flash, uint32_t start_address, uint16_t count)
{
    typedef struct {
        uint32_t timestamp;
        float temperature;
        float humidity;
        uint16_t checksum;
    } SensorRecord;
    
    SensorRecord record;
    uint32_t address = start_address;
    
    printf("\n=== Sensor Data Records ===\n");
    
    for (int i = 0; i < count; i++) {
        // 读取记录
        w25q_read(flash, address, (uint8_t *)&record, sizeof(record));
        
        // 验证校验和
        uint8_t *data = (uint8_t *)&record;
        uint16_t checksum = 0;
        for (int j = 0; j < sizeof(record) - sizeof(uint16_t); j++) {
            checksum += data[j];
        }
        
        if (checksum == record.checksum) {
            // 转换时间戳为可读格式
            time_t t = record.timestamp;
            struct tm *tm_info = localtime(&t);
            char time_str[20];
            strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
            
            printf("[%04d] %s - Temp: %.1fC, Hum: %.1f%%\n",
                   i, time_str, record.temperature, record.humidity);
        } else {
            printf("[%04d] Checksum error!\n", i);
        }
        
        address += sizeof(record);
        
        // 检查是否到达存储末尾
        if (address >= 0x10000) {
            address = 0x1000;
        }
    }
}

第八章:SPI通信的未来发展趋势

8.1 高速SPI协议演进

随着物联网、人工智能和5G技术的发展,SPI协议也在不断演进:

  1. Quad-SPI (QSPI) 和 Octal-SPI

    • 支持4线和8线并行传输
    • 时钟频率可达200MHz以上
    • 带宽提升4-8倍
  2. Serial Memory Interface (SMI)

    • 专门为存储器设计
    • 支持DDR(双倍数据率)
    • 更低的功耗
  3. SPI Express

    • 类似PCIe的包交换协议
    • 支持多设备并行通信
    • 高级错误检测和恢复

8.2 SPI在边缘计算中的应用

// 边缘AI加速器SPI接口示例
#define AI_ACCELERATOR_CMD_INFER   0x10
#define AI_ACCELERATOR_CMD_LOAD_MODEL 0x11
#define AI_ACCELERATOR_CMD_GET_RESULT 0x12

typedef struct {
    SPI_HandleTypeDef *spi;
    GPIO_TypeDef *cs_port;
    uint16_t cs_pin;
    uint8_t model_id;
    uint32_t inference_time;
} AI_Accelerator;

// AI推理请求
int ai_accelerator_infer(AI_Accelerator *accel, float *input_data, 
                        uint32_t input_size, float *output_data,
                        uint32_t output_size)
{
    uint8_t tx_buffer[256];
    uint8_t rx_buffer[256];
    uint32_t total_size = input_size * sizeof(float);
    uint32_t chunk_size = 64; // 每次传输64字节
    
    // 发送推理命令
    tx_buffer[0] = AI_ACCELERATOR_CMD_INFER;
    tx_buffer[1] = accel->model_id;
    tx_buffer[2] = (total_size >> 8) & 0xFF;
    tx_buffer[3] = total_size & 0xFF;
    
    HAL_GPIO_WritePin(accel->cs_port, accel->cs_pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(accel->spi, tx_buffer, 4, 100);
    HAL_GPIO_WritePin(accel->cs_port, accel->cs_pin, GPIO_PIN_SET);
    
    // 分块发送输入数据
    uint8_t *input_bytes = (uint8_t *)input_data;
    for (uint32_t i = 0; i < total_size; i += chunk_size) {
        uint32_t current_chunk = (total_size - i) < chunk_size ? 
                                 (total_size - i) : chunk_size;
        
        HAL_GPIO_WritePin(accel->cs_port, accel->cs_pin, GPIO_PIN_RESET);
        HAL_SPI_Transmit(accel->spi, &input_bytes[i], current_chunk, 100);
        HAL_GPIO_WritePin(accel->cs_port, accel->cs_pin, GPIO_PIN_SET);
        
        HAL_Delay(1); // 短延时
    }
    
    // 等待推理完成
    uint32_t start_time = HAL_GetTick();
    uint8_t status = 0;
    
    do {
        HAL_GPIO_WritePin(accel->cs_port, accel->cs_pin, GPIO_PIN_RESET);
        tx_buffer[0] = AI_ACCELERATOR_CMD_GET_RESULT;
        HAL_SPI_TransmitReceive(accel->spi, tx_buffer, rx_buffer, 2, 100);
        HAL_GPIO_WritePin(accel->cs_port, accel->cs_pin, GPIO_PIN_SET);
        
        status = rx_buffer[1];
        
        if (HAL_GetTick() - start_time > 1000) { // 1秒超时
            return -1; // 超时错误
        }
    } while (status != 0x01); // 等待推理完成标志
    
    // 读取推理结果
    uint32_t output_bytes = output_size * sizeof(float);
    
    HAL_GPIO_WritePin(accel->cs_port, accel->cs_pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(accel->spi, tx_buffer, 1, 100); // 发送读取结果命令
    HAL_SPI_Receive(accel->spi, (uint8_t *)output_data, output_bytes, 100);
    HAL_GPIO_WritePin(accel->cs_port, accel->cs_pin, GPIO_PIN_SET);
    
    // 记录推理时间
    accel->inference_time = HAL_GetTick() - start_time;
    
    return 0;
}

8.3 SPI安全增强

随着物联网安全需求的增加,SPI通信也需要加强安全防护:

  1. 加密SPI通信

    // SPI数据加密传输
    void spi_encrypted_transfer(SPI_HandleTypeDef *hspi, uint8_t *plaintext,
                               uint8_t *ciphertext, uint16_t size, uint8_t *key)
    {
        // 1. 加密数据
        aes128_encrypt(plaintext, ciphertext, size, key);
        
        // 2. 添加消息认证码(MAC)
        uint8_t mac[16];
        hmac_sha256(ciphertext, size, key, mac);
        
        // 3. 传输加密数据和MAC
        uint8_t tx_buffer[size + 16];
        memcpy(tx_buffer, ciphertext, size);
        memcpy(tx_buffer + size, mac, 16);
        
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
        HAL_SPI_Transmit(hspi, tx_buffer, size + 16, 1000);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    }
    
  2. 安全启动和固件验证

    // 安全启动验证
    int secure_boot_verify(SPI_HandleTypeDef *hspi, uint32_t firmware_address)
    {
        uint8_t signature[256];
        uint8_t public_key[256];
        uint8_t firmware_hash[32];
        
        // 1. 读取固件签名
        w25q_read(flash, firmware_address - 256, signature, 256);
        
        // 2. 计算固件哈希
        sha256_compute(firmware_address, firmware_size, firmware_hash);
        
        // 3. 使用公钥验证签名
        if (rsa_verify(firmware_hash, signature, public_key) != 0) {
            // 签名验证失败
            return -1;
        }
        
        // 4. 验证通过,执行固件
        return 0;
    }
    

第九章:总结与最佳实践

9.1 SPI通信最佳实践总结

  1. 硬件设计最佳实践

    • 保持信号线尽可能短
    • 使用适当的阻抗匹配
    • 添加适当的终端电阻
    • 避免信号线交叉
  2. 软件设计最佳实践

    • 实现完善的错误处理
    • 使用DMA提高效率
    • 添加超时机制
    • 实现重试逻辑
  3. 性能优化建议

    • 根据设备能力调整时钟频率
    • 使用批量传输减少开销
    • 合理使用中断和DMA
    • 优化缓冲区管理

9.2 常见问题与解决方案

问题 可能原因 解决方案
数据传输错误 时钟模式不匹配 检查CPOL和CPHA设置
通信速度慢 时钟频率设置过低 适当提高时钟频率
数据丢失 缓冲区溢出 增加缓冲区大小或使用DMA
设备无响应 片选信号问题 检查片选信号时序
干扰问题 信号线过长或未屏蔽 缩短信号线,增加屏蔽

9.3 未来学习建议

  1. 深入学习相关协议

    • I2C、UART、CAN等通信协议
    • USB、Ethernet等高速协议
  2. 掌握高级调试技巧

    • 逻辑分析仪使用
    • 示波器信号分析
    • 协议分析软件
  3. 了解行业最新发展

    • 关注芯片厂商最新SPI技术
    • 学习新兴通信标准
    • 参与开源硬件项目

附录

A. SPI相关资源

  1. 官方文档

    • STM32 SPI参考手册
    • Linux SPI子系统文档
    • 各芯片厂商数据手册
  2. 开发工具

    • STM32CubeMX
    • Logic Analyzer(Saleae)
    • Wireshark(网络协议分析)
  3. 开源项目

    • Linux内核SPI驱动
    • FreeRTOS SPI组件
    • Arduino SPI库

B. 术语表

术语 解释
CPOL 时钟极性
CPHA 时钟相位
MOSI 主出从入
MISO 主入从出
SCLK 串行时钟
SS/CS 从机选择/芯片选择
FIFO 先进先出缓冲区
DMA 直接内存访问

C. 常见SPI设备列表

设备类型 常见型号 通信速率
Flash存储器 W25Qxx, AT45DBxx 10-104MHz
显示屏 SSD1306, ILI9341 10-66MHz
传感器 MPU6050, BME280 1-10MHz
ADC/DAC MCP3008, MAX5216 1-20MHz
无线模块 NRF24L01, CC1101 0.25-10MHz

版权声明:本文为技术分享文章,欢迎转载,但请注明出处。文中代码示例仅供参考,实际使用时请根据具体需求进行修改和测试。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐