SPI

Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.


功能概述

基础定义

SPI(Serial Peripheral Interface),即串行外设接口,是由摩托罗拉(现为NXP半导体)于20世纪80年代开发的一种高速、全双工、同步的串行通信协议,用于连接微控制器及其外围设备。SPI接口主要应用在EEPROM、Flash、实时时钟、A/D转换器、数字信号处理器和数字信号解码器之间。SPI采用主从架构:每次通信由一个主设备控制,可连接一个或多个从设备,主设备负责启动和结束通信会话。蜂窝模组SPI默认作为主设备,外部设备作为从设备。

SPI总线的连接

../../_images/image_LPLTbR761oJN1RxRG3bcWBgSnle.webp

通用的SPI接口一般包含4根通讯线:

  • SCK:主设备向从设备传输时钟信号,控制数据交换的时机以及速率;

  • SS/CS:用于主设备片选从设备,使被选中的从设备能够被主设备访问;

  • SDO/MOSI:在主设备上也被称为Tx-Channel,作为数据的出口,主要用于SPI设备发送数据;

  • SDI/MISO:在主设备上也被称为Rx-Channel,作为数据的入口,主要用于SPI设备接收数据。

SPI通信是全双工的,这意味着数据可以在两个方向上同时进行传输。对于SPI,这是通过MOSI(主设备输出,从设备输入)和MISO(主设备输入,从设备输出)线路实现的。

SPI总线的特点

  • 全双工通信:数据可以同时发送和接收。

  • 同步传输:使用时钟信号同步数据传输。

  • 高速传输:相比I2C等协议,SPI可以达到更高的传输速率。

  • 主从架构:由一个主设备控制通信,一个或多个从设备响应。

  • 硬件简单:接口信号线较少,硬件实现简单。

  • 无寻址机制:通过片选信号选择从设备。

SPI总线的数据传输速率可以从几千bps到几百Mbps甚至更高,具体取决于主设备和从设备的规格和性能。

SPI通信时序介绍

SPI的时序逻辑由时钟极性(CPOL)和时钟相位(CPHA)共同定义,决定了数据在时钟信号(SCLK)的哪个边沿被采样和更新。

时钟极性(CPOL)

定义SCLK在空闲状态(无数据传输时)的电平:

  • CPOL=0:SCLK空闲时为低电平

  • CPOL=1:SCLK空闲时为高电平

时钟相位(CPHA)

定义数据在时钟的哪个边沿被采样:

  • CPHA=0:数据在SCLK的第一个边沿(从空闲状态跳变的边沿)被采样

  • CPHA=1:数据在SCLK的第二个边沿(与第一个边沿相反的边沿)被采样

四种SPI模式

结合CPOL和CPHA的不同设置,SPI协议定义了四种工作模式:

模式0(CPOL=0,CPHA=0)

  • 空闲状态下SCLK为低电平

  • 数据在SCLK的上升沿采样

  • 数据变化通常发生在SCLK下降沿

  • 在很多低速外设中广泛应用

模式1(CPOL=0,CPHA=1)

  • 空闲状态下SCLK为低电平

  • 数据在SCLK的下降沿采样

  • 数据在第一个边沿发生变化,在第二个边沿采样

  • 适用于对数据稳定性要求较高的场合

模式2(CPOL=1,CPHA=0)

  • 空闲状态下SCLK为高电平

  • 数据在SCLK的下降沿采样

  • 数据通常在SCLK上升沿发生变化

  • 常用于某些特定硬件设备要求高时钟电平的场合

模式3(CPOL=1,CPHA=1)

  • 空闲状态下SCLK为高电平

  • 数据在SCLK的上升沿采样

  • 数据变化发生在SCLK下降沿

  • 适用于对时序要求较严谨的系统

数据传输时序

  • 片选控制:主设备通过拉低目标从设备的CS信号,通知从设备准备进入通信状态。

  • 时钟同步:主设备产生SCLK信号,作为所有数据传输的同步时钟。

  • 数据发送与接收:在每个时钟周期内,主设备在MOSI线上发送数据的同时,从设备在MISO线上传回数据。

  • 数据位序:通常情况下,每个数据帧为8位,数据从高位到低位传输(MSB优先)。

  • 结束通信:数据传输完成后,主设备将CS信号置为高电平,结束本次通信。

基本帧结构

  • 片选激活:主设备拉低CS信号,启动通信。

  • 命令字节:通常是第一个传输的字节,用于指定操作类型。

  • 地址字节:用于指定要访问的寄存器或存储单元地址。

  • 数据字节:实际要传输的数据内容。

  • 片选释放:主设备拉高CS信号,结束通信。

传输特性

  • 数据位宽:通常为8位,也有部分设备支持16位或更长的数据帧。

  • 传输顺序:高位优先(MSB)或低位优先(LSB),由硬件配置决定。

  • 全双工特性:在每个时钟周期内,数据在MOSI和MISO线上同时传输。

  • 无应答机制:SPI协议没有类似I2C那样的ACK机制,数据可靠性依赖严格的时序设计。

SPI完整通信过程

主设备写操作

写操作流程:

  1. 主设备拉低目标从设备的CS信号,激活从设备。

  2. 主设备产生SCLK时钟信号。

  3. 主设备通过MOSI线发送写命令字节。

  4. 主设备通过MOSI线发送目标地址字节。

  5. 主设备通过MOSI线发送要写入的数据字节。

  6. 从设备通过MISO线返回状态或数据(可选)。

  7. 主设备拉高CS信号,结束通信。

主设备读操作

读操作流程:

  1. 主设备拉低目标从设备的CS信号,激活从设备。

  2. 主设备产生SCLK时钟信号。

  3. 主设备通过MOSI线发送读命令字节。

  4. 主设备通过MOSI线发送目标地址字节。

  5. 主设备通过MOSI线发送空字节(用于产生时钟,触发从设备返回数据)。

  6. 主设备通过MISO线接收从设备返回的数据字节。

  7. 主设备拉高CS信号,结束通信。

全双工特性:

SPI的独特之处在于其全双工通信能力。在每个时钟周期内:

  • 主设备在MOSI线上发送数据的同时,从设备在MISO线上返回数据。

  • 这种同时收发的机制极大地提高了通信效率。

SPI的优缺点

优点

  • 高速传输:可达数十Mbps甚至更高,适合高速数据传输。

  • 全双工通信:数据可以同时发送和接收,效率高。

  • 简单灵活:协议简单,硬件实现成本低。

  • 多模式支持:四种工作模式,适应不同设备需求。

  • 信号隔离友好:单向信号,易于电隔离。

  • 无复杂仲裁:相对健壮,没有复杂的总线仲裁机制。

  • 硬件加速:现代控制器支持DMA、FIFO等高级特性。

  • 广泛支持:几乎所有微控制器都集成了SPI接口。

缺点

  • 引脚占用多:至少需要4根线,相比I2C更占引脚资源。

  • 无寻址机制:需要额外的片选信号选择从设备。

  • 多从设备支持差:每个从设备需独立CS线。

  • 无错误检测:没有内置的错误检测机制如CRC。

  • 无应答机制:无法确认数据是否被正确接收。

  • 短距离限制:主要用于板内短距离通信。

  • 时序要求严格:主从设备模式必须完全一致。

  • 无正式标准:事实上的标准,但没有统一的官方标准。

SPI与其他协议比较

特性

SPI

I2C

UART

通信方式

同步、全双工

同步、半双工

异步、全双工

信号线数量

4根(SCLK、MOSI、MISO、CS)

2根(SDA、SCL)

2根(TX、RX)

寻址方式

硬件片选(CS)

7/10位地址

无(点对点)

多设备支持

一主多从(需独立CS)

多主多从

点对点

速率

高(数十Mbps)

中(标准模式100 kbps)

低(通常低于1 Mbps)

应答机制

有(ACK/NACK)

硬件复杂度

中等

中等

典型应用

高速外设通信

低速多设备通信

点对点通信

SPI的选择建议

适合选择SPI的场景

  1. 高速数据传输:需要快速传输大量数据的应用。

  2. 全双工通信:需要同时收发数据的实时系统。

  3. 短距离板内通信:芯片间或板内模块通信。

  4. 简单的主从架构:一主多从的简单系统。

  5. 高速外设接口:Flash存储器、显示屏、ADC/DAC等。

  6. 实时性要求高:需要微秒级响应的控制系统。

  7. 硬件资源充足:引脚资源相对丰富的系统。

  8. 工业环境应用:需要电气隔离的工业产品。

不适合选择SPI的场景

  1. 引脚资源紧张:只有少量GPIO可用的系统。

  2. 大量设备连接:需要连接10个以上设备的系统。

  3. 长距离通信:超过1米的板间通信。

  4. 多主设备系统:需要多个主设备共享总线。

  5. 复杂错误处理:需要高级错误检测和恢复机制。

  6. 低成本要求:对硬件成本非常敏感的应用。

  7. 简单的低速通信:仅需要简单的低速数据传输。

SPI API

头文件

qosa_spi.h

函数概览

函数

说明

qosa_spi_init()

初始化指定的SPI通道,配置传输模式和时钟频率

qosa_spi_deinit()

去初始化指定的SPI通道,释放相关资源

qosa_spi_write()

通过指定的SPI通道发送数据

qosa_spi_read()

通过指定的SPI通道接收数据

qosa_spi_write_read()

执行SPI全双工读写操作,同时发送和接收数据

qosa_spi_ioctl()

动态配置SPI参数

函数详解

qosa_spi_init

  • 功能描述
    初始化指定的SPI通道,配置传输模式和时钟频率。

  • 函数原型

qosa_spi_errcode_e qosa_spi_init(qosa_spi_port_e port, qosa_spi_transmit_mode_e mode, qosa_spi_clk_e clk)
  • 参数说明

参数名

输入/输出

类型

说明

port

输入

qosa_spi_port_e

SPI通道;详见 qosa_spi_port_e

mode

输入

qosa_spi_transmit_mode_e

SPI传输模式;详见 qosa_spi_transmit_mode_e

clk

输入

qosa_spi_clk_e

SPI时钟频率;详见 qosa_spi_clk_e

  • 返回值说明
    QOSA_SPI_SUCCESS:函数执行成功
    其他值(详见 qosa_spi_errcode_e):函数执行失败

qosa_spi_deinit

  • 功能描述
    去初始化指定的SPI通道,释放相关资源。

  • 函数原型

qosa_spi_errcode_e qosa_spi_deinit(qosa_spi_port_e port)
  • 参数说明

参数名

输入/输出

类型

说明

port

输入

qosa_spi_port_e

需要去初始化的SPI通道;详见 qosa_spi_port_e

  • 返回值说明
    QOSA_SPI_SUCCESS:函数执行成功
    其他值(详见 qosa_spi_errcode_e):函数执行失败

qosa_spi_write

  • 功能描述
    通过指定的SPI通道发送数据。

  • 函数原型

qosa_spi_errcode_e qosa_spi_write(qosa_spi_port_e port, void *txData, qosa_uint32_t length)
  • 参数说明

参数名

输入/输出

类型

说明

port

输入

qosa_spi_port_e

SPI通道;详见 qosa_spi_port_e

txData

输入

void *

待发送数据的缓冲区指针

length

输入

qosa_uint32_t

要发送的数据长度;单位:字节

  • 返回值说明
    QOSA_SPI_SUCCESS:函数执行成功
    其他值(详见 qosa_spi_errcode_e):函数执行失败

qosa_spi_read

  • 功能描述
    通过指定的SPI通道接收数据。

  • 函数原型

qosa_spi_errcode_e qosa_spi_read(qosa_spi_port_e port, void *rxData, qosa_uint32_t length)
  • 参数说明

参数名

输入/输出

类型

说明

port

输入

qosa_spi_port_e

SPI通道;详见 qosa_spi_port_e

rxData

输出

void *

存放接收数据的缓冲区指针

length

输入

qosa_uint32_t

要接收的数据长度;单位:字节

  • 返回值说明
    QOSA_SPI_SUCCESS:函数执行成功
    其他值(详见 qosa_spi_errcode_e):函数执行失败

qosa_spi_write_read

  • 功能描述
    执行SPI全双工读写操作,同时发送和接收数据。

  • 函数原型

qosa_spi_errcode_e qosa_spi_write_read(qosa_spi_port_e port, void *rxData, void *txData, qosa_uint32_t length)
  • 参数说明

参数名

输入/输出

类型

说明

port

输入

qosa_spi_port_e

SPI通道;详见 qosa_spi_port_e

rxData

输出

void *

存放接收数据的缓冲区指针

txData

输入

void *

待发送数据的缓冲区指针

length

输入

qosa_uint32_t

收发数据的长度;单位:字节

  • 返回值说明
    QOSA_SPI_SUCCESS:函数执行成功
    其他值(详见 qosa_spi_errcode_e):函数执行失败

qosa_spi_ioctl

  • 功能描述
    动态配置SPI参数。

  • 函数原型

qosa_spi_errcode_e qosa_spi_ioctl(qosa_spi_port_e port, qosa_spi_ioctl_cmd_e cmd, void *arg)
  • 参数说明

参数名

输入/输出

类型

说明

port

输入

qosa_spi_port_e

SPI通道;详见 qosa_spi_port_e

cmd

输入

qosa_spi_ioctl_cmd_e

参数配置命令;详见 qosa_spi_ioctl_cmd_e

arg

输入

void *

命令参数指针,指向的数据类型由cmd决定;详见 qosa_spi_ioctl_cmd_e

  • 返回值说明
    QOSA_SPI_SUCCESS:函数执行成功
    其他值(详见 qosa_spi_errcode_e):函数执行失败

枚举定义

qosa_spi_errcode_e

SPI操作错误码枚举定义如下:

typedef enum
{
    QOSA_SPI_SUCCESS = 0,
    QOSA_SPI_EXECUTE_ERR = 1 | QOSA_SPI_ERRCODE_BASE,
    QOSA_SPI_MEM_ADDR_NULL_ERR,
    QOSA_SPI_INVALID_PARAM_ERR,
    QOSA_SPI_WRITE_READ_ERR,
} qosa_spi_errcode_e;

成员

说明

QOSA_SPI_SUCCESS

函数执行成功

QOSA_SPI_EXECUTE_ERR

执行错误

QOSA_SPI_MEM_ADDR_NULL_ERR

内存地址为空

QOSA_SPI_INVALID_PARAM_ERR

参数无效

QOSA_SPI_WRITE_READ_ERR

读写失败

qosa_spi_clk_e

SPI时钟频率配置枚举定义如下:

typedef enum
{
    QOSA_SPI_CLK_INVALID = -1,
    QOSA_SPI_CLK_812_5KHZ = 812500,
    QOSA_SPI_CLK_1_625MHZ = 1625000,
    QOSA_SPI_CLK_3_25MHZ = 3250000,
    QOSA_SPI_CLK_6_5MHZ = 6500000,
    QOSA_SPI_CLK_13MHZ = 13000000,
    QOSA_SPI_CLK_26MHZ = 26000000,
    QOSA_SPI_CLK_52MHZ = 52000000,
} qosa_spi_clk_e;

成员

说明

QOSA_SPI_CLK_INVALID

无效时钟频率

QOSA_SPI_CLK_812_5KHZ

时钟频率812.5 kHz

QOSA_SPI_CLK_1_625MHZ

时钟频率1.625 MHz

QOSA_SPI_CLK_3_25MHZ

时钟频率3.25 MHz

QOSA_SPI_CLK_6_5MHZ

时钟频率6.5 MHz

QOSA_SPI_CLK_13MHZ

时钟频率13 MHz

QOSA_SPI_CLK_26MHZ

时钟频率26 MHz

QOSA_SPI_CLK_52MHZ

时钟频率52 MHz

qosa_spi_transmit_mode_e

SPI传输模式枚举定义如下:

typedef enum
{
    QOSA_SPI_TRANSMIT_POLLING = 0,
    QOSA_SPI_TRANSMIT_DMA,
} qosa_spi_transmit_mode_e;

成员

说明

QOSA_SPI_TRANSMIT_POLLING

FIFO读写模式

QOSA_SPI_TRANSMIT_DMA

DMA读写模式

qosa_spi_port_e

SPI通道选择枚举定义如下:

typedef enum
{
    QOSA_SPI_PORT_NONE = -1,
    QOSA_SPI_PORT0,
    QOSA_SPI_PORT1,
    QOSA_SPI_PORT2,
    QOSA_SPI_PORT_MAX,
} qosa_spi_port_e;

成员

说明

QOSA_SPI_PORT_NONE

无效通道(占位值)

QOSA_SPI_PORT0

SPI0

QOSA_SPI_PORT1

SPI1

QOSA_SPI_PORT2

SPI2

QOSA_SPI_PORT_MAX

通道数量上限(无效通道)

qosa_spi_mode_e

SPI主从模式枚举定义如下:

typedef enum
{
    QOSA_SPI_MODE_MASTER = 0,
    QOSA_SPI_MODE_SLAVE,
} qosa_spi_mode_e;

成员

说明

QOSA_SPI_MODE_MASTER

主模式(默认)

QOSA_SPI_MODE_SLAVE

从模式

qosa_spi_frame_format_e

SPI时钟极性和相位配置枚举定义如下:

typedef enum
{
    QOSA_SPI_CLK_CPOL0_CPHA0 = 0,
    QOSA_SPI_CLK_CPOL0_CPHA1,
    QOSA_SPI_CLK_CPOL1_CPHA0,
    QOSA_SPI_CLK_CPOL1_CPHA1,
} qosa_spi_frame_format_e;

成员

说明

QOSA_SPI_CLK_CPOL0_CPHA0

时钟极性0/时钟相位0(默认)

QOSA_SPI_CLK_CPOL0_CPHA1

时钟极性0/时钟相位1

QOSA_SPI_CLK_CPOL1_CPHA0

时钟极性1/时钟相位0

QOSA_SPI_CLK_CPOL1_CPHA1

时钟极性1/时钟相位1

qosa_spi_nss_mode_e

SPI片选(NSS)信号控制模式枚举定义如下:

typedef enum
{
    QOSA_SPI_NSS_MASTER_HARDWARE = 0,
    QOSA_SPI_NSS_MASTER_SOFTWARE,
    QOSA_SPI_NSS_SLAVE_HARDWARE,
} qosa_spi_nss_mode_e;

成员

说明

QOSA_SPI_NSS_MASTER_HARDWARE

主模式硬件控制CS引脚(默认)

QOSA_SPI_NSS_MASTER_SOFTWARE

主模式软件控制CS引脚

QOSA_SPI_NSS_SLAVE_HARDWARE

从模式硬件控制CS引脚

qosa_spi_bit_order_e

SPI数据传输位顺序枚举定义如下:

typedef enum
{
    QOSA_SPI_MSB_FIRST = 0,
    QOSA_SPI_LSB_FIRST,
} qosa_spi_bit_order_e;

成员

说明

QOSA_SPI_MSB_FIRST

收发数据以MSB为起始位(默认)

QOSA_SPI_LSB_FIRST

收发数据以LSB为起始位

qosa_spi_data_width_e

SPI数据传输位宽度枚举定义如下:

typedef enum
{
    QOSA_SPI_WIDTH_1_BYTES = 0,
    QOSA_SPI_WIDTH_2_BYTES,
} qosa_spi_data_width_e;

成员

说明

QOSA_SPI_WIDTH_1_BYTES

数据位宽度为1字节(8位)(默认)

QOSA_SPI_WIDTH_2_BYTES

数据位宽度为2字节(16位)

qosa_spi_ioctl_cmd_e

SPI参数配置命令枚举定义如下:

typedef enum
{
    QOSA_SPI_IOCTL_NONE,
    QOSA_SPI_IOCTL_SET_MODE,
    QOSA_SPI_IOCTL_SET_CLK_POLARITY_PHASE,
    QOSA_SPI_IOCTL_SET_NSS_MODE,
    QOSA_SPI_IOCTL_SET_CLOCK_FREQUENCY,
    QOSA_SPI_IOCTL_SET_BIT_ORDER,
    QOSA_SPI_IOCTL_SET_DATA_WIDTH,
} qosa_spi_ioctl_cmd_e;

成员

说明

QOSA_SPI_IOCTL_NONE

无操作(占位值)

QOSA_SPI_IOCTL_SET_MODE

设置SPI主从模式,该命令取值类型为 qosa_spi_mode_e

QOSA_SPI_IOCTL_SET_CLK_POLARITY_PHASE

设置SPI时钟极性和相位,该命令取值类型为 qosa_spi_frame_format_e

QOSA_SPI_IOCTL_SET_NSS_MODE

设置片选(NSS)信号控制模式,该命令取值类型为 qosa_spi_nss_mode_e

QOSA_SPI_IOCTL_SET_CLOCK_FREQUENCY

设置SPI时钟频率

QOSA_SPI_IOCTL_SET_BIT_ORDER

设置数据传输位顺序,该命令取值类型为 qosa_spi_bit_order_e

QOSA_SPI_IOCTL_SET_DATA_WIDTH

设置数据传输位宽度,该命令取值类型为 qosa_spi_data_width_e

应用逻辑流程图

image

示例代码

完整示例代码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/peripheral/spi/spi_demo.c