# SPI 主机驱动程序

[English]

SPI 主机驱动程序是一个软件程序，用于在 ESP32 的通用 SPI (GP-SPI) 外设工作在主控模式时，对其进行控制。

备注

SPI1 不属于 GP-SPI。SPI 主机驱动程序也支持 SPI1。但在 SPI1 总线上使用 SPI 主机驱动程序存在诸多限制，请参阅 在 SPI1 总线上使用 SPI 主机驱动程序的注意事项。

有关 GP-SPI 硬件相关信息，请参考 ESP32 技术参考手册 > SPI 控制器 [PDF]。

## 术语

下表为 SPI 主机驱动的相关术语。

术语 | 定义
--- | ---
主机 (Host) | ESP32 内置的 SPI 控制器外设。用作 SPI 主机，在总线上发起 SPI 传输。
设备 (Device) | SPI 从机设备。一条 SPI 总线与一或多个设备连接。每个设备共享 MOSI、MISO 和 SCLK 信号，但只有当主机向设备的专属 CS 线发出信号时，设备才会在总线上处于激活状态。
总线 (Bus) | 信号总线，由连接到同一主机的所有设备共用。一般来说，每条总线包括以下线：MISO、MOSI、SCLK、一条或多条 CS 线，以及可选的 QUADWP 和 QUADHD。因此，除每个设备都有单独的 CS 线外，所有设备都连接在相同的线下。多个设备也可以菊花链的方式共享一条 CS 线。
MOSI | 主机输出，从机输入，也写作 D。数据从主机发送至设备。在 Octal/OPI 模式下也表示为 data0 信号。
MISO | 主机输入，从机输出，也写作 Q。数据从设备发送至主机。在 Octal/OPI 模式下也表示为 data1 信号。
SCLK | 串行时钟。由主机产生的振荡信号，使数据位的传输保持同步。
CS | 片选。允许主机选择连接到总线上的单个设备，以便发送或接收数据。
QUADWP | 写保护信号。只用于 4 位 (qio/qout) 传输。在 Octal/OPI 模式下也表示为 data2 信号。
QUADHD | 保持信号。只用于 4 位 (qio/qout) 传输。在 Octal/OPI 模式下也表示为 data3 信号。
DATA4 | 在 Octal/OPI 模式下表示为 data4 信号。
DATA5 | 在 Octal/OPI 模式下表示为 data5 信号。
DATA6 | 在 Octal/OPI 模式下表示为 data6 信号。
DATA7 | 在 Octal/OPI 模式下表示为 data7 信号。
断言 (Assertion) | 指激活一条线路的操作。
去断言 (De-assertion) | 指将线路恢复到非活动状态（回到空闲状态）的操作。
传输事务 (Transaction) | 即主机断言设备的 CS 线，向设备发送数据/从设备读取数据，接着去断言 CS 线的过程。传输事务为原子操作，不可打断。
发射沿 (Launch Edge) | 源寄存器将信号 发射 到线路上的时钟边沿。
锁存沿 (Latch Edge) | 目的寄存器 锁存 信号的时钟边沿。

## 主机驱动特性

SPI 主机驱动程序负责管理主机与设备间的通信，具有以下特性：

- 支持多线程环境使用

- 读写数据过程中 DMA 透明传输

- 同一信号总线上不同设备的数据可自动时分复用，请参阅 SPI 总线锁。

警告

SPI 主机驱动允许总线上连接多个设备（共享单个 ESP32 SPI 外设）。每个设备仅由一个任务访问时，驱动程序线程安全。反之，若多个任务尝试访问同一 SPI 设备，则驱动程序 非线程安全。此时，建议执行以下任一操作：

- 重构应用程序，确保每个 SPI 外设在同一时间仅由一个任务访问。使用 `spi_bus_config_t::isr_cpu_id` 将 SPI ISR 注册到与 SPI 外设相关任务相同的内核，以确保线程安全。

- 使用 `xSemaphoreCreateMutex` 为共享设备添加互斥锁。

## SPI 传输事务

SPI 总线传输事务由五个阶段构成，详见下表（任意阶段均可跳过）。

阶段名称 | 描述
--- | ---
命令阶段 (Command) | 在此阶段，主机向总线发送命令字段，长度为 0-16 位。
地址阶段 (Address) | 在此阶段，主机向总线发送地址字段，长度为 0-64 位。
Dummy 阶段 | 此阶段可自行配置，用于适配时序要求。
写入阶段 (Write) | 此阶段主机向设备传输数据，这些数据在紧随命令阶段（可选）和地址阶段（可选）之后。从电平的角度来看，数据与命令没有区别。
读取阶段 (Read) | 此阶段主机读取设备数据。

传输事务属性由总线配置结构体 `spi_bus_config_t`、设备配置结构体 `spi_device_interface_config_t` 和传输事务配置结构体 `spi_transaction_t` 共同决定。

一个 SPI 主机可以发送全双工传输事务，此时读取和写入阶段同步进行。传输事务总长度取决于以下结构体成员长度总和：

- `spi_device_interface_config_t::command_bits`

- `spi_device_interface_config_t::address_bits`

- `spi_transaction_t::length`

而 `spi_transaction_t::rxlength` 则决定了接收到的数据包长度。

在半双工传输事务中，读取和写入阶段独立进行（一次一个方向）。写入和读取阶段的长度由 `spi_transaction_t::length` 和 `spi_transaction_t::rxlength` 分别决定。

并非每个 SPI 设备都要求命令和/或地址，因此命令阶段和地址阶段为可选项。这反映在设备的配置中：如果 `spi_device_interface_config_t::command_bits` 和/或 `spi_device_interface_config_t::address_bits` 被设置为零，则不会唤起命令或地址阶段。

并非每个传输事务都需要写入和读取数据，因此读取和写入阶段也是可选项。如果将 `spi_transaction_t::rx_buffer` 设置为 `NULL`，且未设置 `SPI_TRANS_USE_RXDATA`，读取阶段将被跳过。如果将 `spi_transaction_t::tx_buffer` 设置为 `NULL`，且未设置 `SPI_TRANS_USE_TXDATA`，写入阶段将被跳过。

主机驱动程序支持两种类型的传输事务：中断传输事务和轮询传输事务。用户可以选择在不同设备上使用不同的传输事务类型。若设备需要同时使用两种传输事务类型，请参阅 向同一设备发送混合传输事务的注意事项。

### 中断传输事务

中断传输事务将阻塞传输事务程序，直至传输事务完成，以使 CPU 运行其他任务程序。

应用任务中可以将多个传输事务加入到队列中，驱动程序将在中断服务程序 (ISR) 中自动逐一发送队列中的数据。在所有传输事务完成以前，任务可切换到其他程序中。

### 轮询传输事务

轮询传输事务不依赖于中断，程序将不断轮询 SPI 主机的状态位，直到传输事务完成。

所有执行中断传输事务的任务都可能被队列阻塞。在此情况下，用户需要等待 ISR 和传输事务传输完成。轮询传输事务节省了原本用于队列处理和上下文切换的时间，减少了传输事务持续时间。传输事务类型的缺点是在这些事务进行期间，CPU 将被占用而处于忙碌状态。

传输事务完成后，`spi_device_polling_end()` 程序需要至少 1 µs 的时间来解除阻塞其他任务。此处强烈建议调用函数 `spi_device_acquire_bus()` 和 `spi_device_release_bus()` 来打包一系列轮询传输事务以避免开销。详情请参阅 获取总线。

### 传输线模式配置

ESP32 支持的线路模式如下。要使用这些模式，请在结构体 `spi_transaction_t` 中设置 `flags`，如 `传输事务标志信号` 一栏所示。要检查相应的 IO 管脚是否被设置，请在 `spi_bus_config_t` 中设置 `flags`，如 `总线 IO 设置标志信号` 一栏所示。

模式 | 命令位宽 | 地址位宽 | 数据位宽 | 传输事务标志信号 | 总线 IO 设置标志信号
--- | --- | --- | --- | --- | ---
普通 SPI 模式 | 1 | 1 | 1 | 0 | 0
双线输出模式 | 1 | 1 | 2 | SPI_TRANS_MODE_DIO | SPICOMMON_BUSFLAG_DUAL
双线 I/O 模式 | 1 | 2 | 2 | SPI_TRANS_MODE_DIO SPI_TRANS_MULTILINE_ADDR | SPICOMMON_BUSFLAG_DUAL
四线输出模式 | 1 | 1 | 4 | SPI_TRANS_MODE_QIO | SPICOMMON_BUSFLAG_QUAD
四线 I/O 模式 | 1 | 4 | 4 | SPI_TRANS_MODE_QIO SPI_TRANS_MULTILINE_ADDR | SPICOMMON_BUSFLAG_QUAD

### 命令阶段和地址阶段

在命令阶段和地址阶段，`spi_transaction_t::cmd` 和 `spi_transaction_t::addr` 将被发送到总线，该过程中无数据读取。命令阶段和地址阶段的默认长度通过调用 `spi_bus_add_device()` 在 `spi_device_interface_config_t` 中设置。如果 `spi_transaction_t::flags` 中的标志信号 `SPI_TRANS_VARIABLE_CMD` 和 `SPI_TRANS_VARIABLE_ADDR` 未设置，则驱动程序将在设备初始化期间自动将这些阶段的长度设置为默认值。

如需更改命令阶段和地址阶段的长度，可通过以下步骤实现：声明结构体 `spi_transaction_ext_t`，在 `spi_transaction_ext_t::base` 中设置标志信号 `SPI_TRANS_VARIABLE_CMD` 和/或 `SPI_TRANS_VARIABLE_ADDR`，随后按正常步骤完成余下配置。这样一来，各阶段的长度将等于结构体 `spi_transaction_ext_t` 中设置的 `spi_transaction_ext_t::command_bits` 和 `spi_transaction_ext_t::address_bits` 长度。

如果需要命令阶段和地址阶段的线数与数据阶段保持一致，则应在结构体 `spi_transaction_t` 中将 `SPI_TRANS_MULTILINE_CMD` 和/或 `SPI_TRANS_MULTILINE_ADDR` 设置进该结构体的 `flags` 成员变量。请参阅 传输线模式配置。

### 写入和读取阶段

一般而言，需要传输到设备或由设备读取的数据将由 `spi_transaction_t::rx_buffer` 和 `spi_transaction_t::tx_buffer` 指向的内存块中读取或写入。如果传输时启用了 DMA，则缓冲区应：

> 1. 申请支持 DMA 的内存。具体操作请参阅 支持 DMA 的内存。

> 2. 32 位对齐（从 32 位边界开始，长度为 4 字节的倍数）。

若未满足以上要求，传输事务效率将受到临时缓冲区分配和复制的影响。

如果使用多条数据线传输，请在结构体 `spi_device_interface_config_t` 中的 `flags` 设置 `SPI_DEVICE_HALFDUPLEX` 标志信号。结构体 `spi_transaction_t` 中的 `flags` 应按照 传输线模式配置 中的描述设置。

备注

当启用 DMA 时，不支持同时具有读取阶段和写入阶段的半双工传输事务。有关细节和解决方法，请参阅 已知问题。

### 获取总线

若需连续发送专门的 SPI 传输事务以提高效率，可采用获取总线的方式。获取总线后，与其他设备间的传输事务（包括轮询传输事务或中断传输事务）将处于待处理状态，直到总线被释放。要获取和释放总线，请调用函数 `spi_device_acquire_bus()` 和 `spi_device_release_bus()`。

## 使用驱动程序

- 通过调用函数 `spi_bus_initialize()` 初始化 SPI 总线。确保在结构体 `spi_bus_config_t` 中设置正确的 I/O 管脚。不需要的信号设置为 `-1`。

- 通过调用函数 `spi_bus_add_device()` 注册连接到总线的设备。确保用参数 dev_config 配置设备可能需要的任何时序要求。完成上述操作后可获得设备句柄，以便在向设备发送传输事务时使用。

- 要与设备交互，请在一个或多个 `spi_transaction_t` 结构体中填充所需的传输事务参数，随后使用轮询传输事务或中断传输事务发送这些结构体：

  > - 中断传输事务

  >     调用函数 `spi_device_queue_trans()` 将传输事务添加到队列中，随后使用函数 `spi_device_get_trans_result()` 查询结果，或将所有请求输入 `spi_device_transmit()` 实现同步处理。

  > - 轮询传输事务

  >     调用函数 `spi_device_polling_transmit()` 发送轮询传输事务。若有插入内容的需要，也可使用 `spi_device_polling_start()` 和 `spi_device_polling_end()` 发送传输事务。

- （可选）若要向设备发送背对背传输事务，请在发送传输事务前调用函数 `spi_device_acquire_bus()`，并在发送传输事务后调用函数 `spi_device_release_bus()`。

- （可选）要从总线上移除特定设备，请以设备句柄为参数调用函数 `spi_bus_remove_device()`。

- （可选）要复位（或重置）总线，请确保总线上未连接其他设备，并调用函数 `spi_bus_free()`。

SPI 主机驱动程序的示例代码存放在 ESP-IDF 示例项目的 peripherals/spi_master 目录下。

### 传输数据小于 32 位的传输事务

当传输事务数据等于或小于 32 位时，为数据分配一个缓冲区将是次优的选择。实际上，数据可以直接存储于传输事务结构体中。对已传输的数据，可通过调用函数 `spi_transaction_t::tx_data` 并在传输时设置 `SPI_TRANS_USE_TXDATA` 标志信号来实现。对已接收的数据，可通过调用函数 `spi_transaction_t::rx_data` 并设置 `SPI_TRANS_USE_RXDATA` 来实现。在这两种情况下，请勿修改 `spi_transaction_t::tx_buffer` 或 `spi_transaction_t::rx_buffer`，因为它们与 `spi_transaction_t::tx_data` 和 `spi_transaction_t::rx_data` 的内存位置相同。

### 非 `uint8_t` 的整数传输事务

SPI 主机逐字节地将数据读入和写入内存。默认情况下，数据优先以最高有效位 (MSB) 发送，极少数情况下会优先使用最低有效位 (LSB)。如果需要发送一个小于 8 位的值，这些位应以 MSB 优先的方式写入内存。

例如，如果需要发送 `0b00010`，则应将其写成 `uint8_t` 变量，读取长度设置为 5 位。此时，设备仍然会收到 8 位数据，并另有 3 个“随机”位，所以读取过程必须准确。

此外，ESP32 属于小端芯片，即 `uint16_t` 和 `uint32_t` 变量的最低有效位存储在最小的地址。因此，如果 `uint16_t` 存储在内存中，则首先发送位 [7:0]，其次是位 [15:8]。

在某些情况下，要传输的数据大小与 `uint8_t` 数组不同，可使用以下宏将数据转换为可由 SPI 驱动直接发送的格式：

- 需传输的数据，使用 `SPI_SWAP_DATA_TX`

- 接收到的数据，使用 `SPI_SWAP_DATA_RX`

### 向同一设备发送混合传输事务的注意事项

为避免代码过于复杂，请一次只向单个设备发送一种类型的传输事务（中断或轮询）。如有需要，可交替发送中断传输事务和轮询传输事务，详见下文。

所有的轮询传输事务和中断传输事务完成后，方可发送轮询传输事务。

由于未完成的轮询传输事务将阻塞其他传输事务，请务必在 `spi_device_polling_start()` 之后调用函数 `spi_device_polling_end()` 以允许其他事务或其他设备使用总线。如果在轮询过程中无需切换到其他任务，可调用函数 `spi_device_polling_transmit()` 启动传输事务，则该传输事务将自动结束。

ISR 会干扰飞行中的轮询传输事务，以适应中断传输事务。在调用 `spi_device_polling_start()` 前，需确保所有发送到 ISR 的中断传输事务已经完成。为此可持续调用 `spi_device_get_trans_result()`，直至全部传输事务返回。

为更好地控制函数的调用顺序，只在单个任务中向同一设备发送混合传输事务。

### 在 SPI1 总线上使用 SPI 主机驱动程序的注意事项

备注

因具备 SPI 总线锁 特性，SPI 主机驱动程序可在 SPI1 总线上运行，但该过程十分棘手，且需要许多特殊处理。这是一个适合高级开发者的功能。

要在 SPI1 总线上运行 SPI 主机驱动程序，需注意以下两个问题：

1. 当驱动在 SPI1 上运行时，代码和数据应在内部存储器中。

   总线由设备、flash 中的数据（代码）缓存以及 PSRAM 共享。当其他驱动在 SPI1 总线上运行时，缓存应被禁用。此时，flash 上的数据（代码）以及 PSRAM 就不会在驱动获取 SPI 总线时被抓取。驱动可经由以下方式获取 SPI1 总线：

   - 在 `spi_device_acquire_bus()` 和 `spi_device_release_bus()` 之间显式获取总线。

   - 在 `spi_device_polling_start()` 和 `spi_device_polling_end()` 之间隐式获取总线（或在 `spi_device_polling_transmit()` 中）。

   上述时间内，所有的其他任务和大多数 ISR 将被禁用（见 IRAM 安全中断处理程序）。当前任务所使用的应用程序代码和数据应放置于内存（DRAM 或 IRAM）中，或者已置于 ROM 中。访问外部存储器（flash 代码、flash 中的常量数据和 PSRAM 中的静态/堆积数据）将导致 `缓存禁用情况下访问缓存内存区域` 异常。关于 IRAM、DRAM、和 flash 缓存之间的区别，请参阅 应用程序内存布局。

   要将函数置入 IRAM，请选择：

   1. 将 `IRAM_ATTR` （包括 `esp_attr.h`）添加到函数中，如：

      > IRAM_ATTR void foo(void) { }

      函数内联时将跟随其调用者的段，且属性无法生效，使用 `NOLINE_ATTR` 可避免此类情况。注意，编译器可能会将部分代码转化为常量数据中的查找表，所以 `noflash_text` 并不安全。

   2. 或使用 `linker.lf` 中的 `noflash` 位置，请参阅 链接器脚本生成机制。注意，编译器可能会将部分代码转化为常量数据中的查找表，所以 `noflash_text` 并不安全。

   请注意，优化水平可能会影响编译器的内联行为，或将一些代码转化为常量数据中的查找表等。

   要将数据置入 DRAM，请选择：

   1. 在数据定义中加入 `DRAM_ATTR` （包括 `esp_attr.h`），如：

      > DRAM_ATTR int g_foo = 3;

   2. 或使用 `linker.lf` 中的 `noflash` 位置，请参阅 链接器脚本生成机制。

查看更多示例，请参阅 peripherals/spi_master/hd_eeprom。

### GPIO 矩阵与 IO_MUX 管脚

ESP32 的多数外设信号都直接与之专用的 IO_MUX 管脚连接，但这些信号也可以通过 GPIO 交换矩阵传输到任何其他可用的管脚。只要有一个信号通过 GPIO 交换矩阵传输，那么所有的信号都将通由其传输。

GPIO 交换矩阵提高了信号传输的灵活性，但也存在以下缺陷：

- 加大了 MISO 信号的输入延迟，增加了 MISO 设置时序违反的可能性。如果需要高速运行 SPI，请使用专用的 IO_MUX 管脚。

- 只能传输最大时钟频率为 40 MHz 的信号，而使用 IO_MUX 管脚时可以传输的最大时钟频率为 80 MHz。

备注

了解有关 MISO 输入延迟对最大时钟频率影响的更多信息，请参阅 时序影响因素。

SPI 总线的 IO_MUX 管脚如下表所示。

管脚名称 | GPIO 编号 (SPI2) | GPIO 编号 (SPI3)
--- | --- | ---
CS0 [1] | 15 | 5
SCLK | 14 | 18
MISO | 12 | 19
MOSI | 13 | 23
QUADWP | 2 | 22
QUADHD | 4 | 21

[1]

只有连接到总线的第一台设备可以使用 CS0 管脚。

## 传输速度的影响因素

传输速度主要有以下三个限制因素

- 传输事务间隔时间

- SPI 时钟频率

- 缓存缺失的 SPI 函数，包括回调

影响大传输事务传输速度的主要参数是时钟频率。而多个小传输事务的传输速度主要由传输事务间隔时长决定。

### 传输事务持续时间

传输事务持续时间包括设置 SPI 外设寄存器，将数据复制到 FIFO 或设置 DMA 链接，以及 SPI 传输事务时间。

中断传输事务允许附加额外的开销，以适应 FreeRTOS 队列的成本以及任务与 ISR 切换所需的时间。

对于 中断传输事务，CPU 可以在传输事务进行过程中切换到其他任务，这能够节约 CPU 时间，会延长传输事务持续时间，请参阅 中断传输事务。对于 轮询传输事务，它不会阻塞任务，但允许在传输事务进行时轮询。详情请参阅 轮询传输事务。

如果 DMA 被启用，每个传输事务设置链接列表需要约 2 µs。当主机传输数据时，它会自动从链接列表中读取数据。如果不启用 DMA，CPU 必须自己从 FIFO 中写入和读取每个字节。这一过程时长通常不到 2 µs，但写入和读取的传输事务长度都被限制在 64 字节。

单个字节数据的典型传输事务持续时间如下。

- 使用 DMA 的中断传输事务：28 µs。

- 使用 CPU 的中断传输事务：25 µs。

- 使用 DMA 的轮询传输事务：10 µs。

- 使用 CPU 的轮询传输事务：8 µs。

请注意，以上数据测试时，CONFIG_SPI_MASTER_ISR_IN_IRAM 选项处于启用状态，SPI 传输事务相关的代码放置在 IRAM 中。若关闭此选项（例如为了节省 IRAM），可能影响传输事务持续时间。

### SPI 时钟频率

GPSPI 外设的时钟源可以通过设置 `spi_device_interface_config_t::clock_source` 选择，可用的时钟源请参阅 `spi_clock_source_t`。

默认情况下，驱动程序将把时钟源设置为 `SPI_CLK_SRC_DEFAULT`。这往往代表 GPSPI 可选时钟源中的最高频率，在不同的芯片上这一数值会有所不同。

设备的实际时钟频率可能不完全等于所设置的数字，驱动会将其重新计算为与硬件兼容的最接近的数字，并且不超过时钟源的时钟频率。调用函数 `spi_device_get_actual_freq()` 以了解驱动计算的实际频率。

设备的时钟频率可在传输过程中实时更改，可以通过设置 `spi_transaction_t::override_freq_hz` 实现，此操作将为该设备的该次及以后的传输使用新的时钟频率。若某次期望设置的时钟频率无法实现，驱动将打印警告并继续使用之前的时钟频率进行传输。

写入或读取阶段的理论最大传输速度可根据下表计算：

写入/读取阶段的线宽 | 速度 (Bps)
--- | ---
1-Line | SPI 频率 / 8
2-Line | SPI 频率 / 4
4-Line | SPI 频率 / 2

其他阶段（命令阶段、地址阶段、Dummy 阶段）的传输速度计算与此类似。

时钟频率过高可能会限制部分功能的使用，请参阅 时序影响因素。

### 缓存缺失

默认配置只将 ISR 置于 IRAM 中。其他 SPI 相关功能，包括驱动本身和回调都可能发生缓存缺失，需等待代码从 flash 中读取。为避免缓存缺失，可参考 CONFIG_SPI_MASTER_IN_IRAM，将整个 SPI 驱动置入 IRAM，并将整个回调及其 callee 函数一起置入 IRAM。

备注

SPI 驱动是基于 FreeRTOS 的 API 实现的，在使用 CONFIG_SPI_MASTER_IN_IRAM 时，不得启用 CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH。

单个中断传输事务传输 n 字节的总成本为 20+8n/Fspi[MHz] [µs]，故传输速度为 n/(20+8n/Fspi)。8 MHz 时钟速度的传输速度见下表。

频率 (MHz) | 传输事务间隔 (µs) | 传输事务长度 (bytes) | 传输时长 (µs) | 传输速度 (KBps)
--- | --- | --- | --- | ---
8 | 25 | 1 | 26 | 38.5
8 | 25 | 8 | 33 | 242.4
8 | 25 | 16 | 41 | 490.2
8 | 25 | 64 | 89 | 719.1
8 | 25 | 128 | 153 | 836.6

传输事务长度较短时将提高传输事务间隔成本，因此应尽可能将几个短传输事务压缩成一个传输事务，以提升传输速度。

注意，ISR 在 flash 操作期间默认处于禁用状态。要在 flash 操作期间继续发送传输事务，请启用 CONFIG_SPI_MASTER_ISR_IN_IRAM，并在 `spi_bus_config_t::intr_flags` 中设置 `ESP_INTR_FLAG_IRAM`。此时，flash 操作前列队的传输事务将由 ISR 并行处理。此外，每个设备的回调和它们的 `callee` 函数都应该在 IRAM 中，避免回调因缓存丢失而崩溃。详情请参阅 IRAM 安全中断处理程序。

## 时序影响因素

如图所示， SCLK 发射沿之后、信号被内部寄存器锁存之前， MISO 线存在延迟。因此， MISO 管脚的设置时间是 SPI 时钟速度的限制因素。当延迟过长时，设置松弛度 < 0 ，违反了设置时序要求，读取可能有误。

最大有效频率取决于：

- `spi_device_interface_config_t::input_delay_ns` - SCLK 上的一个时钟周期开始后 MISO 总线上的最大数据有效时间

- 是否使用了 IO_MUX 管脚或 GPIO 矩阵

使用 GPIO 矩阵时，最大有效频率降至现有 输入延迟 的 33 ~ 77%。使用 IO_MUX 管脚或 dummy 位 可以保留较高的频率，调用函数 `spi_get_freq_limit()` 能够获取主机的最大读取频率。

Dummy 位：在读取阶段开始之前，可以插入 Dummy 时钟，在此期间，主机不读取数据。设备能看到 Dummy 时钟并发出数据，但主机在读取阶段之前不会读取。这就弥补了主机 MISO 设置时间不足的问题，并使主机达到更高的读取频率。

理想状态下，如果设备的速度足以使输入延迟控制在一个 APB 时钟周期 (12.5 ns) 以内，则在不同的条件下，主机读取（或读取和写入）的最大频率如下表所示：

频率限制 (MHz) | 频率限制 (MHz) | 驱动是否使用 Dummy 位 | 备注
--- | --- | --- | ---
GPIO 矩阵 | IO_MUX 管脚 |  |
26.6 | 80 | 否 |
40 | -- | 是 | 半双工，禁用 DMA

如果主机仅写入数据，可以通过设置 `spi_device_interface_config_t::flags` 中的 `SPI_DEVICE_NO_DUMMY` 位来禁用 Dummy 位 和频率检查机制。禁用 Dummy 位 和频率检查机制后，即便在使用 GPIO 矩阵的情况下，输出频率也可达 80 MHz。

`spi_device_interface_config_t::flags`

即使 `spi_device_interface_config_t` 结构体中的 `spi_device_interface_config_t::input_delay_ns` 设置为 0，SPI 主机驱动程序仍会运作，但设置精确值有助于：

- 计算全双工传输事务的频率限制

- 用半双工传输事务的 Dummy 位正确补偿时序

可根据设备规格说明中 AC 特性章节的数据估计 SPI 时钟发射沿后的最大数据有效时间，或者使用示波器或逻辑分析仪测量该时间。

请注意，实际 PCB 布局设计和过度负载可能会增加输入延迟。这意味着非最佳布线和/或总线上的负载电容很可能导致输入延迟值超过设备规格说明中给出的数值或在总线浮动时测量的数值。

典型的延迟值如下表所示，以下数据由从机设备使用不同芯片测试所得：

设备 | 输入延迟 (ns)
--- | ---
理想设备 | 0
使用 IO_MUX 的 ESP32 从机设备 | 50
使用 GPIO_MATRIX 的 ESP32 从机设备 | 75

MISO 路径延迟（有效时间）由从机的 输入延迟 与主机的 GPIO 矩阵延迟 组成。该延迟决定了频率限制，超过这个频率的全双工传输将无法如半双工交易中使用的 Dummy 位一样工作。该频率限制的计算方法为：

> Freq limit [MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)

频率限制与输入延迟间的关系如图所示。如果主机使用 GPIO 矩阵，应在 MISO 延迟上额外增加两个 APB 时钟周期。

不同设备在不同 输入延迟 时间下对应的频率限制如下表所示。

当主机驱动为 IO_MUX (0 ns) 时：

输入延迟 (ns) | MISO 通道延迟 (ns) | 频率限制 (MHz)
--- | --- | ---
0 | 0 | 80
50 | 50 | 16
75 | 75 | 11.43

当主机驱动为 GPIO_MATRIX (25 ns) 时：

输入延迟 (ns) | MISO 通道延迟 (ns) | 频率限制 (MHz)
--- | --- | ---
0 | 25 | 26.67
50 | 75 | 11.43
75 | 100 | 8.89

## 已知问题

1. 写入和读取阶段同时进行时，半双工传输事务与 DMA 不兼容。

   > 如需执行此类传输事务，可使用以下替代方案：

   > 1. 执行全双工传输事务。

   > 2. 将总线初始化函数的最后一个参数设置为 0 以禁用 DMA，即：

   >      `ret=spi_bus_initialize(SPI3_HOST, &buscfg, 0);`

   > 此举可避免传输和接收超过 64 字节的数据。 1. 尝试用命令和地址字段代替写入阶段。

2. 全双工传输事务与 Dummy 位 不兼容，因此存在频率限制，请参阅 Dummy 位加速方案。

3. SPI 读取和写入阶段同时进行时，全双工模式和半双工模式下，`spi_device_interface_config_t` 和 `spi_transaction_ext_t` 中的 `dummy_bits` 均会失效。

4. 全双工传输事务模式中，命令阶段和地址阶段与 `cs_ena_pretrans` 不兼容。

## 应用示例

- peripherals/spi_master/hd_eeprom 演示了如何使用 SPI 主机驱动程序的半双工模式来读写 AT93C46D EEPROM（8 位模式）。

peripherals/spi_master/lcd 演示了如何使用 SPI 主驱动程序在 LCD 上显示动画。借助 DMA，可以实现并行渲染和刷新。该示例还说明了如何使用 SPI 事务钩子函数来驱动 D/C 信号电平。

## API 参考 - SPI Common

### Header File

- components/hal/include/hal/spi_types.h

- This header file can be included with:

  >     #include "hal/spi_types.h"

### Structures

struct spi_line_mode_t

  Line mode of SPI transaction phases: CMD, ADDR, DOUT/DIN.

  Public Members

  uint8_t cmd_lines

    The line width of command phase, e.g. 2-line-cmd-phase.

  uint8_t addr_lines

    The line width of address phase, e.g. 1-line-addr-phase.

  uint8_t data_lines

    The line width of data phase, e.g. 4-line-data-phase.

### Type Definitions

typedef soc_periph_spi_clk_src_t spi_clock_source_t

  Type of SPI clock source.

### Enumerations

enum spi_host_device_t

  General purpose SPI Host Controller ID.

  Values:

  enumerator SPI1_HOST

    SPI1.

  enumerator SPI2_HOST

    SPI2.

  enumerator SPI3_HOST

    SPI3.

  enumerator SPI_HOST_MAX

    invalid host value

enum spi_event_t

  SPI Events.

  Values:

  enumerator SPI_EV_BUF_TX

    The buffer has sent data to master.

  enumerator SPI_EV_BUF_RX

    The buffer has received data from master.

  enumerator SPI_EV_SEND_DMA_READY

    Slave has loaded its TX data buffer to the hardware (DMA).

  enumerator SPI_EV_SEND

    Master has received certain number of the data, the number is determined by Master.

  enumerator SPI_EV_RECV_DMA_READY

    Slave has loaded its RX data buffer to the hardware (DMA).

  enumerator SPI_EV_RECV

    Slave has received certain number of data from master, the number is determined by Master.

  enumerator SPI_EV_CMD9

    Received CMD9 from master.

  enumerator SPI_EV_CMDA

    Received CMDA from master.

  enumerator SPI_EV_TRANS

    A transaction has done.

enum spi_command_t

  SPI command.

  Values:

  enumerator SPI_CMD_HD_WRBUF
  enumerator SPI_CMD_HD_RDBUF
  enumerator SPI_CMD_HD_WRDMA
  enumerator SPI_CMD_HD_RDDMA
  enumerator SPI_CMD_HD_SEG_END
  enumerator SPI_CMD_HD_EN_QPI
  enumerator SPI_CMD_HD_WR_END
  enumerator SPI_CMD_HD_INT0
  enumerator SPI_CMD_HD_INT1
  enumerator SPI_CMD_HD_INT2

enum spi_sampling_point_t

  SPI master RX sample point mode configuration.

  Values:

  enumerator SPI_SAMPLING_POINT_PHASE_0

    Data sampling point at 50% cycle delayed then standard timing, (default).

  enumerator SPI_SAMPLING_POINT_PHASE_1

    Data sampling point follows standard SPI timing in master mode.

### Header File

- components/esp_driver_spi/include/driver/spi_common.h

- This header file can be included with:

  >     #include "driver/spi_common.h"

- This header file is a part of the API provided by the `esp_driver_spi` component. To declare that your component depends on `esp_driver_spi`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_spi

  > or

  >     PRIV_REQUIRES esp_driver_spi

### Functions

esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t *bus_config, spi_dma_chan_t dma_chan)

  Initialize a SPI bus.

  警告

  SPI0/1 is not supported

  警告

  If a DMA channel is selected, any transmit and receive buffer used should be allocated in DMA-capable memory.

  警告

  The ISR of SPI is always executed on the core which calls this function. Never starve the ISR on this core or the SPI transactions will not be handled.

  参数:

    - host_id -- SPI peripheral that controls this bus

    - bus_config -- Pointer to a spi_bus_config_t struct specifying how the host should be initialized

    - dma_chan -- - Selecting a DMA channel for an SPI bus allows transactions on the bus with size only limited by the amount of internal memory.

      - Selecting SPI_DMA_DISABLED limits the size of transactions.

      - Set to SPI_DMA_DISABLED if only the SPI flash uses this bus.

      - Set to SPI_DMA_CH_AUTO to let the driver to allocate the DMA channel.

  返回:

    - ESP_ERR_INVALID_ARG if configuration is invalid

    - ESP_ERR_INVALID_STATE if host already is in use

    - ESP_ERR_NOT_FOUND if there is no available DMA channel

    - ESP_ERR_NO_MEM if out of memory

    - ESP_OK on success

esp_err_t spi_bus_free(spi_host_device_t host_id)

  Free a SPI bus.

  警告

  In order for this to succeed, all devices have to be removed first.

  参数:

    host_id -- SPI peripheral to free

  返回:

    - ESP_ERR_INVALID_ARG if parameter is invalid

    - ESP_ERR_INVALID_STATE if bus hasn't been initialized before, or not all devices on the bus are freed

    - ESP_OK on success

void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t extra_heap_caps)

  Helper function for malloc DMA capable memory for SPI driver.

  备注

  This API will take care of the cache and hardware alignment internally. To free/release memory allocated by this helper function, simply calling `free()`

  参数:

    - host_id -- [in] SPI peripheral who will using the memory

    - size -- [in] Size in bytes, the amount of memory to allocate

    - extra_heap_caps -- [in] Extra heap caps based on MALLOC_CAP_DMA

  返回:

    Pointer to the memory if allocated successfully

### Structures

struct spi_bus_config_t

  This is a configuration structure for a SPI bus.

  You can use this structure to specify the GPIO pins of the bus. Normally, the driver will use the GPIO matrix to route the signals. An exception is made when all signals either can be routed through the IO_MUX or are -1. In that case, the IO_MUX is used. On ESP32, using GPIO matrix will bring about 25ns of input delay, which may cause incorrect read for >40MHz speeds.

  备注

  Be advised that the slave driver does not use the quadwp/quadhd lines and fields in spi_bus_config_t referring to these lines will be ignored and can thus safely be left uninitialized.

  Public Members

  int mosi_io_num

    GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used.

  int data0_io_num

    GPIO pin for spi data0 signal in quad/octal mode, or -1 if not used.

  int miso_io_num

    GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used.

  int data1_io_num

    GPIO pin for spi data1 signal in quad/octal mode, or -1 if not used.

  int sclk_io_num

    GPIO pin for SPI Clock signal, or -1 if not used.

  int quadwp_io_num

    GPIO pin for WP (Write Protect) signal, or -1 if not used.

  int data2_io_num

    GPIO pin for spi data2 signal in quad/octal mode, or -1 if not used.

  int quadhd_io_num

    GPIO pin for HD (Hold) signal, or -1 if not used.

  int data3_io_num

    GPIO pin for spi data3 signal in quad/octal mode, or -1 if not used.

  int data4_io_num

    GPIO pin for spi data4 signal in octal mode, or -1 if not used.

  int data5_io_num

    GPIO pin for spi data5 signal in octal mode, or -1 if not used.

  int data6_io_num

    GPIO pin for spi data6 signal in octal mode, or -1 if not used.

  int data7_io_num

    GPIO pin for spi data7 signal in octal mode, or -1 if not used.

  bool data_io_default_level

    Output data IO default level when no transaction.

  int max_transfer_sz

    Maximum transfer size, in bytes. Defaults to 4092 if 0 when DMA enabled, or to `SOC_SPI_MAXIMUM_BUFFER_SIZE` if DMA is disabled.

  uint32_t flags

    Abilities of bus to be checked by the driver. Or-ed value of `SPICOMMON_BUSFLAG_*` flags.

  esp_intr_cpu_affinity_t isr_cpu_id

    Select cpu core to register SPI ISR.

  int intr_flags

    Interrupt flag for the bus to set the priority, and IRAM attribute, see `esp_intr_alloc.h`. Note that the EDGE, INTRDISABLED attribute are ignored by the driver. Note that if ESP_INTR_FLAG_IRAM is set, ALL the callbacks of the driver, and their callee functions, should be put in the IRAM.

### Macros

SPI_MAX_DMA_LEN

SPI_SWAP_DATA_TX(DATA, LEN)

  Transform unsigned integer of length <= 32 bits to the format which can be sent by the SPI driver directly.

  E.g. to send 9 bits of data, you can:

       uint16_t data = SPI_SWAP_DATA_TX(0x145, 9);

  Then points tx_buffer to `&data`.
  参数:

    - DATA -- Data to be sent, can be uint8_t, uint16_t or uint32_t.

    - LEN -- Length of data to be sent, since the SPI peripheral sends from the MSB, this helps to shift the data to the MSB.

SPI_SWAP_DATA_RX(DATA, LEN)

  Transform received data of length <= 32 bits to the format of an unsigned integer.

  E.g. to transform the data of 15 bits placed in a 4-byte array to integer:

       uint16_t data = SPI_SWAP_DATA_RX(*(uint32_t*)t->rx_data, 15);

  参数:

    - DATA -- Data to be rearranged, can be uint8_t, uint16_t or uint32_t.

    - LEN -- Length of data received, since the SPI peripheral writes from the MSB, this helps to shift the data to the LSB.

SPICOMMON_BUSFLAG_SLAVE

  Initialize I/O in slave mode.

SPICOMMON_BUSFLAG_MASTER

  Initialize I/O in master mode.

SPICOMMON_BUSFLAG_IOMUX_PINS

  Check using iomux pins. Or indicates the pins are configured through the IO mux rather than GPIO matrix.

SPICOMMON_BUSFLAG_GPIO_PINS

  Force the signals to be routed through GPIO matrix. Or indicates the pins are routed through the GPIO matrix.

SPICOMMON_BUSFLAG_SCLK

  Check existing of SCLK pin. Or indicates CLK line initialized.

SPICOMMON_BUSFLAG_MISO

  Check existing of MISO pin. Or indicates MISO line initialized.

SPICOMMON_BUSFLAG_MOSI

  Check existing of MOSI pin. Or indicates MOSI line initialized.

SPICOMMON_BUSFLAG_DUAL

  Check MOSI and MISO pins can output. Or indicates bus able to work under DIO mode.

SPICOMMON_BUSFLAG_WPHD

  Check existing of WP and HD pins. Or indicates WP & HD pins initialized.

SPICOMMON_BUSFLAG_QUAD

  Check existing of MOSI/MISO/WP/HD pins as output. Or indicates bus able to work under QIO mode.

SPICOMMON_BUSFLAG_IO4_IO7

  Check existing of IO4~IO7 pins. Or indicates IO4~IO7 pins initialized.

SPICOMMON_BUSFLAG_OCTAL

  Check existing of MOSI/MISO/WP/HD/SPIIO4/SPIIO5/SPIIO6/SPIIO7 pins as output. Or indicates bus able to work under octal mode.

SPICOMMON_BUSFLAG_NATIVE_PINS

SPICOMMON_BUSFLAG_SLP_ALLOW_PD

  Allow to power down the peripheral during light sleep, and auto recover then.

### Type Definitions

typedef spi_common_dma_t spi_dma_chan_t

### Enumerations

enum spi_common_dma_t

  SPI DMA channels.

  Values:

  enumerator SPI_DMA_DISABLED

    Do not enable DMA for SPI.

  enumerator SPI_DMA_CH1

    Enable DMA, select DMA Channel 1.

  enumerator SPI_DMA_CH2

    Enable DMA, select DMA Channel 2.

  enumerator SPI_DMA_CH_AUTO

    Enable DMA, channel is automatically selected by driver.

## API 参考 - SPI Master

### Header File

- components/esp_driver_spi/include/driver/spi_master.h

- This header file can be included with:

  >     #include "driver/spi_master.h"

- This header file is a part of the API provided by the `esp_driver_spi` component. To declare that your component depends on `esp_driver_spi`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_driver_spi

  > or

  >     PRIV_REQUIRES esp_driver_spi

### Functions

esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interface_config_t *dev_config, spi_device_handle_t *handle)

  Allocate a device on a SPI bus.

  This initializes the internal structures for a device, plus allocates a CS pin on the indicated SPI master peripheral and routes it to the indicated GPIO. All SPI master devices have three CS pins and can thus control up to three devices.

  There's no notable delay on chips other than ESP32.

  备注

  On ESP32, due to the delay of GPIO matrix, the maximum frequency SPI Master can correctly samples the slave's output is lower than the case using IOMUX. Typical maximum frequency communicating with an ideal slave without data output delay: 80MHz (IOMUX pins) and 26MHz (GPIO matrix pins). With the help of extra dummy cycles in half-duplex mode, the delay can be compensated by setting `input_delay_ns` in `dev_config` structure correctly.

  参数:

    - host_id -- SPI peripheral to allocate device on

    - dev_config -- SPI interface protocol config for the device

    - handle -- Pointer to variable to hold the device handle

  返回:

    - ESP_ERR_INVALID_ARG if parameter is invalid or configuration combination is not supported (e.g. `dev_config->post_cb` isn't set while flag `SPI_DEVICE_NO_RETURN_RESULT` is enabled)

    - ESP_ERR_INVALID_STATE if selected clock source is unavailable or spi bus not initialized

    - ESP_ERR_NOT_FOUND if host doesn't have any free CS slots

    - ESP_ERR_NO_MEM if out of memory

    - ESP_OK on success

esp_err_t spi_bus_remove_device(spi_device_handle_t handle)

  Remove a device from the SPI bus.

  参数:

    handle -- Device handle to free

  返回:

    - ESP_ERR_INVALID_ARG if parameter is invalid

    - ESP_ERR_INVALID_STATE if device already is freed

    - ESP_OK on success

esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait)

  Queue a SPI transaction for interrupt transaction execution. Get the result by `spi_device_get_trans_result`.

  备注

  Normally a device cannot start (queue) polling and interrupt transactions simultaneously.

  参数:

    - handle -- Device handle obtained using spi_bus_add_device()

    - trans_desc -- Description of transaction to execute

    - ticks_to_wait -- Ticks to wait until there's room in the queue; use portMAX_DELAY to never time out.

  返回:

    - ESP_ERR_INVALID_ARG if parameter is invalid. This can happen if SPI_TRANS_CS_KEEP_ACTIVE flag is specified while the bus was not acquired (`spi_device_acquire_bus()` should be called first) or set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but tx or rx buffer not DMA-capable, or addr&len not align to cache line size

    - ESP_ERR_TIMEOUT if there was no room in the queue before ticks_to_wait expired

    - ESP_ERR_NO_MEM if allocating DMA-capable temporary buffer failed

    - ESP_ERR_INVALID_STATE if previous transactions are not finished

    - ESP_OK on success

esp_err_t spi_device_get_trans_result(spi_device_handle_t handle, spi_transaction_t **trans_desc, TickType_t ticks_to_wait)

  Get the result of a SPI transaction queued earlier by `spi_device_queue_trans`.

  This routine will wait until a transaction to the given device successfully completed. It will then return the description of the completed transaction so software can inspect the result and e.g. free the memory or reuse the buffers.

  参数:

    - handle -- Device handle obtained using spi_bus_add_device()

    - trans_desc -- Pointer to variable able to contain a pointer to the description of the transaction that is executed. The descriptor should not be modified until the descriptor is returned by spi_device_get_trans_result.

    - ticks_to_wait -- Ticks to wait until there's a returned item; use portMAX_DELAY to never time out.

  返回:

    - ESP_ERR_INVALID_ARG if parameter is invalid

    - ESP_ERR_NOT_SUPPORTED if flag `SPI_DEVICE_NO_RETURN_RESULT` is set

    - ESP_ERR_TIMEOUT if there was no completed transaction before ticks_to_wait expired

    - ESP_OK on success

esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *trans_desc)

  Send a SPI transaction, wait for it to complete, and return the result.

  This function is the equivalent of calling spi_device_queue_trans() followed by spi_device_get_trans_result(). Do not use this when there is still a transaction separately queued (started) from spi_device_queue_trans() or polling_start/transmit that hasn't been finalized.

  备注

  This function is not thread safe when multiple tasks access the same SPI device. Normally a device cannot start (queue) polling and interrupt transactions simutanuously.

  参数:

    - handle -- Device handle obtained using spi_bus_add_device()

    - trans_desc -- Description of transaction to execute

  返回:

    - ESP_ERR_INVALID_ARG if parameter is invalid

    - ESP_OK on success

esp_err_t spi_device_polling_start(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait)

  Immediately start a polling transaction.

  备注

  Normally a device cannot start (queue) polling and interrupt transactions simutanuously. Moreover, a device cannot start a new polling transaction if another polling transaction is not finished.

  参数:

    - handle -- Device handle obtained using spi_bus_add_device()

    - trans_desc -- Description of transaction to execute

    - ticks_to_wait -- Ticks to wait until there's room in the queue; currently only portMAX_DELAY is supported.

  返回:

    - ESP_ERR_INVALID_ARG if parameter is invalid. This can happen if SPI_TRANS_CS_KEEP_ACTIVE flag is specified while the bus was not acquired (`spi_device_acquire_bus()` should be called first) or set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but tx or rx buffer not DMA-capable, or addr&len not align to cache line size

    - ESP_ERR_TIMEOUT if the device cannot get control of the bus before `ticks_to_wait` expired

    - ESP_ERR_NO_MEM if allocating DMA-capable temporary buffer failed

    - ESP_ERR_INVALID_STATE if previous transactions are not finished

    - ESP_OK on success

esp_err_t spi_device_polling_end(spi_device_handle_t handle, TickType_t ticks_to_wait)

  Poll until the polling transaction ends.

  This routine will not return until the transaction to the given device has successfully completed. The task is not blocked, but actively busy-spins for the transaction to be completed.

  参数:

    - handle -- Device handle obtained using spi_bus_add_device()

    - ticks_to_wait -- Ticks to wait until there's a returned item; use portMAX_DELAY to never time out.

  返回:

    - ESP_ERR_INVALID_ARG if parameter is invalid

    - ESP_ERR_TIMEOUT if the transaction cannot finish before ticks_to_wait expired

    - ESP_OK on success

esp_err_t spi_device_polling_transmit(spi_device_handle_t handle, spi_transaction_t *trans_desc)

  Send a polling transaction, wait for it to complete, and return the result.

  This function is the equivalent of calling spi_device_polling_start() followed by spi_device_polling_end(). Do not use this when there is still a transaction that hasn't been finalized.

  备注

  This function is not thread safe when multiple tasks access the same SPI device. Normally a device cannot start (queue) polling and interrupt transactions simutanuously.

  参数:

    - handle -- Device handle obtained using spi_bus_add_device()

    - trans_desc -- Description of transaction to execute

  返回:

    - ESP_ERR_INVALID_ARG if parameter is invalid

    - ESP_ERR_TIMEOUT if the device cannot get control of the bus

    - ESP_ERR_NO_MEM if allocating DMA-capable temporary buffer failed

    - ESP_ERR_INVALID_STATE if previous transactions of same device are not finished

    - ESP_OK on success

esp_err_t spi_device_acquire_bus(spi_device_handle_t device, TickType_t wait)

  Occupy the SPI bus for a device to do continuous transactions.

  Transactions to all other devices will be put off until `spi_device_release_bus` is called.

  备注

  The function will wait until all the existing transactions have been sent.

  参数:

    - device -- The device to occupy the bus.

    - wait -- Time to wait before the the bus is occupied by the device. Currently MUST set to portMAX_DELAY.

  返回:

    - ESP_ERR_INVALID_ARG : `wait` is not set to portMAX_DELAY.

    - ESP_OK : Success.

void spi_device_release_bus(spi_device_handle_t dev)

  Release the SPI bus occupied by the device. All other devices can start sending transactions.

  参数:

    dev -- The device to release the bus.

esp_err_t spi_device_get_actual_freq(spi_device_handle_t handle, int *freq_khz)

  Calculate working frequency for specific device.

  参数:

    - handle -- SPI device handle

    - freq_khz -- [out] output parameter to hold calculated frequency in kHz

  返回:

    - ESP_ERR_INVALID_ARG : `handle` or `freq_khz` parameter is NULL

    - ESP_OK : Success

int spi_get_actual_clock(int fapb, int hz, int duty_cycle)

  Calculate the working frequency that is most close to desired frequency.

  参数:

    - fapb -- The frequency of apb clock, should be `APB_CLK_FREQ`.

    - hz -- Desired working frequency

    - duty_cycle -- Duty cycle of the spi clock

  返回:

    Actual working frequency that most fit.

void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int *dummy_o, int *cycles_remain_o)

  Calculate the timing settings of specified frequency and settings.

  备注

  If **dummy_o* is not zero, it means dummy bits should be applied in half duplex mode, and full duplex mode may not work.

  参数:

    - gpio_is_used -- True if using GPIO matrix, or False if iomux pins are used.

    - input_delay_ns -- Input delay from SCLK launch edge to MISO data valid.

    - eff_clk -- Effective clock frequency (in Hz) from `spi_get_actual_clock()`.

    - dummy_o -- Address of dummy bits used output. Set to NULL if not needed.

    - cycles_remain_o -- Address of cycles remaining (after dummy bits are used) output.

      - -1 If too many cycles remaining, suggest to compensate half a clock.

      - 0 If no remaining cycles or dummy bits are not used.

      - positive value: cycles suggest to compensate.

int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns)

  Get the frequency limit of current configurations. SPI master working at this limit is OK, while above the limit, full duplex mode and DMA will not work, and dummy bits will be applied in the half duplex mode.

  参数:

    - gpio_is_used -- True if using GPIO matrix, or False if native pins are used.

    - input_delay_ns -- Input delay from SCLK launch edge to MISO data valid.

  返回:

    Frequency limit of current configurations.

esp_err_t spi_bus_get_max_transaction_len(spi_host_device_t host_id, size_t *max_bytes)

  Get max length (in bytes) of one transaction.

  参数:

    - host_id -- SPI peripheral

    - max_bytes -- [out] Max length of one transaction, in bytes

  返回:

    - ESP_OK: On success

    - ESP_ERR_INVALID_ARG: Invalid argument

### Structures

struct spi_device_interface_config_t

  This is a configuration for a SPI slave device that is connected to one of the SPI buses.

  Public Members

  uint8_t command_bits

    Default amount of bits in command phase (0-16), used when `SPI_TRANS_VARIABLE_CMD` is not used, otherwise ignored.

  uint8_t address_bits

    Default amount of bits in address phase (0-64), used when `SPI_TRANS_VARIABLE_ADDR` is not used, otherwise ignored.

  uint8_t dummy_bits

    Amount of dummy bits to insert between address and data phase.

  uint8_t mode

    SPI mode, representing a pair of (CPOL, CPHA) configuration:

    - 0: (0, 0)

    - 1: (0, 1)

    - 2: (1, 0)

    - 3: (1, 1)

  spi_clock_source_t clock_source

    Select SPI clock source, `SPI_CLK_SRC_DEFAULT` by default.

  uint16_t duty_cycle_pos

    Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128.

  uint16_t cs_ena_pretrans

    Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions.

  uint8_t cs_ena_posttrans

    Amount of SPI bit-cycles the cs should stay active after the transmission (0-16)

  int clock_speed_hz

    SPI clock speed in Hz. Derived from `clock_source`.

  int input_delay_ns

    Maximum data valid time of slave. The time required between SCLK and MISO valid, including the possible clock delay from slave to master. The driver uses this value to give an extra delay before the MISO is ready on the line. Leave at 0 unless you know you need a delay. For better timing performance at high frequency (over 8MHz), it's suggest to have the right value.

  spi_sampling_point_t sample_point

    Sample point tuning of spi master receiving bit.

  int spics_io_num

    CS GPIO pin for this device, or -1 if not used.

  uint32_t flags

    Bitwise OR of SPI_DEVICE_* flags.

  int queue_size

    Transaction queue size. This sets how many transactions can be 'in the air' (queued using spi_device_queue_trans but not yet finished using spi_device_get_trans_result) at the same time.

  transaction_cb_t pre_cb

    Callback to be called before a transmission is started.

    This callback is called within interrupt context should be in IRAM for best performance, see "Transferring Speed" section in the SPI Master documentation for full details. If not, the callback may crash during flash operation when the driver is initialized with ESP_INTR_FLAG_IRAM.

  transaction_cb_t post_cb

    Callback to be called after a transmission has completed.

    This callback is called within interrupt context should be in IRAM for best performance, see "Transferring Speed" section in the SPI Master documentation for full details. If not, the callback may crash during flash operation when the driver is initialized with ESP_INTR_FLAG_IRAM.

struct spi_transaction_t

  This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes.

  Public Members

  uint32_t flags

    Bitwise OR of SPI_TRANS_* flags.

  uint16_t cmd

    Command data, of which the length is set in the `command_bits` of spi_device_interface_config_t.

    NOTE: this field, used to be "command" in ESP-IDF 2.1 and before, is re-written to be used in a new way in ESP-IDF 3.0.

    Example: write 0x0123 and command_bits=12 to send command 0x12, 0x3_ (in previous version, you may have to write 0x3_12).

  uint64_t addr

    Address data, of which the length is set in the `address_bits` of spi_device_interface_config_t.

    NOTE: this field, used to be "address" in ESP-IDF 2.1 and before, is re-written to be used in a new way in ESP-IDF3.0.

    Example: write 0x123400 and address_bits=24 to send address of 0x12, 0x34, 0x00 (in previous version, you may have to write 0x12340000).

  size_t length

    Total data length, in bits.

  size_t rxlength

    Total data length received, should be not greater than `length` in full-duplex mode (0 defaults this to the value of `length`).

  uint32_t override_freq_hz

    New freq speed value will override for current device, remain `0` to skip update. Need about 30us each time.

  void *user

    User-defined variable. Can be used to store eg transaction ID.

  const void *tx_buffer

    Pointer to transmit buffer, or NULL for no MOSI phase.

  uint8_t tx_data[4]

    If SPI_TRANS_USE_TXDATA is set, data set here is sent directly from this variable.

  void *rx_buffer

    Pointer to receive buffer, or NULL for no MISO phase. Written by 4 bytes-unit if DMA is used.

  uint8_t rx_data[4]

    If SPI_TRANS_USE_RXDATA is set, data is received directly to this variable.

struct spi_transaction_ext_t

  This struct is for SPI transactions which may change their address and command length. Please do set the flags in base to `SPI_TRANS_VARIABLE_CMD_ADR` to use the bit length here.

  Public Members

  struct spi_transaction_t base

    Transaction data, so that pointer to spi_transaction_t can be converted into spi_transaction_ext_t.

  uint8_t command_bits

    The command length in this transaction, in bits.

  uint8_t address_bits

    The address length in this transaction, in bits.

  uint8_t dummy_bits

    The dummy length in this transaction, in bits.

### Macros

SPI_MASTER_FREQ_8M

  SPI common used frequency (in Hz)

  备注

  SPI peripheral only has an integer divider, and the default clock source can be different on other targets, so the actual frequency may be slightly different from the desired frequency. 8MHz

SPI_MASTER_FREQ_9M

  8.89MHz

SPI_MASTER_FREQ_10M

  10MHz

SPI_MASTER_FREQ_11M

  11.43MHz

SPI_MASTER_FREQ_13M

  13.33MHz

SPI_MASTER_FREQ_16M

  16MHz

SPI_MASTER_FREQ_20M

  20MHz

SPI_MASTER_FREQ_26M

  26.67MHz

SPI_MASTER_FREQ_40M

  40MHz

SPI_MASTER_FREQ_80M

  80MHz

SPI_DEVICE_TXBIT_LSBFIRST

  Transmit command/address/data LSB first instead of the default MSB first.

SPI_DEVICE_RXBIT_LSBFIRST

  Receive data LSB first instead of the default MSB first.

SPI_DEVICE_BIT_LSBFIRST

  Transmit and receive LSB first.

SPI_DEVICE_3WIRE

  Use MOSI (=spid) for both sending and receiving data.

SPI_DEVICE_POSITIVE_CS

  Make CS positive during a transaction instead of negative.

SPI_DEVICE_HALFDUPLEX

  Transmit data before receiving it, instead of simultaneously.

SPI_DEVICE_CLK_AS_CS

  Output clock on CS line if CS is active.

SPI_DEVICE_NO_DUMMY

  There are timing issue when reading at high frequency (the frequency is related to whether iomux pins are used, valid time after slave sees the clock).

  - In half-duplex mode, the driver automatically inserts dummy bits before reading phase to fix the timing issue. Set this flag to disable this feature.

  - In full-duplex mode, however, the hardware cannot use dummy bits, so there is no way to prevent data being read from getting corrupted. Set this flag to confirm that you're going to work with output only, or read without dummy bits at your own risk.

SPI_DEVICE_DDRCLK

SPI_DEVICE_NO_RETURN_RESULT

  Don't return the descriptor to the host on completion (use post_cb to notify instead)

SPI_TRANS_MODE_DIO

  Transmit/receive data in 2-bit mode.

SPI_TRANS_MODE_QIO

  Transmit/receive data in 4-bit mode.

SPI_TRANS_USE_RXDATA

  Receive into rx_data member of spi_transaction_t instead into memory at rx_buffer.

SPI_TRANS_USE_TXDATA

  Transmit tx_data member of spi_transaction_t instead of data at tx_buffer. Do not set tx_buffer when using this.

SPI_TRANS_MODE_DIOQIO_ADDR

  Also transmit address in mode selected by SPI_MODE_DIO/SPI_MODE_QIO.

SPI_TRANS_VARIABLE_CMD

  Use the `command_bits` in `spi_transaction_ext_t` rather than default value in `spi_device_interface_config_t`.

SPI_TRANS_VARIABLE_ADDR

  Use the `address_bits` in `spi_transaction_ext_t` rather than default value in `spi_device_interface_config_t`.

SPI_TRANS_VARIABLE_DUMMY

  Use the `dummy_bits` in `spi_transaction_ext_t` rather than default value in `spi_device_interface_config_t`.

SPI_TRANS_CS_KEEP_ACTIVE

  Keep CS active after data transfer.

SPI_TRANS_MULTILINE_CMD

  The data lines used at command phase is the same as data phase (otherwise, only one data line is used at command phase)

SPI_TRANS_MODE_OCT

  Transmit/receive data in 8-bit mode.

SPI_TRANS_MULTILINE_ADDR

  The data lines used at address phase is the same as data phase (otherwise, only one data line is used at address phase)

SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL

  By default driver will automatically re-alloc dma buffer if it doesn't meet hardware alignment or dma_capable requirements, this flag is for you to disable this feature, you will need to take care of the alignment otherwise driver will return you error ESP_ERR_INVALID_ARG.

### Type Definitions

typedef void (*transaction_cb_t)(spi_transaction_t *trans)

typedef struct spi_device_t *spi_device_handle_t

  Handle for a device on a SPI bus.

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。