ESP32S3系列--SPI主机驱动详解(一)

news2024/11/28 20:35:10

一、目的

SPI是一种串行同步接口,可用于与外围设备进行通信。

ESP32S3自带4个SPI控制器外设(Master),其中SPI0/SPI1内部专用,共用一组信号线,通过一个仲裁器访问外部Flash和PSRAM;SPI2/3各自使用一组信号线;开发者可以使用SPI2/3控制外部SPI从设备(Slave device);其中SPI2有6个片选,数据线最多可以有八根,SPI3有3个片选,数据线最多可以有四根。

本篇主要介绍SPI主机驱动的基本知识,包括标准SPI(MISO/MOSI)/Dual SPI/Quad SPI以及Octal SPI的配置和使用。

关于标准SPI/Dual SPI/Quad SPI以及Octal SPI的区别请阅读《理解SPI/Dual SPI/Quad SPI/QPI之间的区别》。

为了方便开发者使用SPI外设,ESP-IDF SDK中将SPI外设抽象为BUS(总线),一条总线上只有一个主设备,但是可以挂接多个从设备,每个从设备各自有独立的一条片选线(CS),其他信号线共用;片选线用于选中设备进行通信。

总线框图

上图中slave A和B共用一组信号线,使用的是标准SPI模式;每个设备有自己独立的片选控制线;某个时刻只能控制一个设备,要么控制A,要么控制B;A和B分时使用SPI总线。

二、介绍

参考资料

SPI Master Driver - ESP32-S3 - — ESP-IDF Programming Guide latest documentation (espressif.com)


SPI外设框图

SPI即串行同步接口

其中FSPI即SPI2,SPI2可以通过IOMUX或者GPIO Matrix进行引脚配置,SPI3只能通过GPIO Matrix进行引脚配置。

SPI2/3都支持DMA传输。


全双工和半双工区别

模式

说明

全双工

主机与从机之间的发送线和接收线各自独立,发送数据和接收数据同时进行。

半双工

主机和从机只能有一方先发送数据,另一方接收数据。发送数据和接收数据不能同时进行。

四线全双工

四线包括:时钟线、片选线和两条数据线。其中,可使用两条数据线同时发送和接收数据。

四线半双工

四线包括:时钟线、片选线和两条数据线。其中,分时使用两条数据线,不可同时使用。

三线半双工

三线包括:时钟线、片选线和一条数据线。使用数据线分时发送和 接收数据。

专业术语

术语

描述

Host(主机)

芯片内部的SPI控制器外设,用于主动发起SPI传输

Device(设备)

SPI从设备,一条SPI总线可以连接多个从设备;每个设备分时共享信号线;每个设备都有一根独立的片选控制线;当主机需要控制某个设备时,选中对应的片选线即可(一般拉低CS线)

Bus(总线)

多个设备共享的SPI信号线

MOSI(主机输出从机输入)

主机从此信号线输出数据,从设备从此信号线接收数据;在四线/八线模式下作为Data0

MISO(主机输入从机输出)

主机从此信号线接收数据,从设备从此信号线发送数据;在四线/八线模式下作为Data1

SCLK(串行同步时钟)

串行同步时钟,数据的发送接收依赖此信号来同步

CS(片选)

片选信号,每个从设备都有一个片选线

QUADWP(写保护)

写保护信号;在四线/八线模式下作为Data2

QUADHD(保持)

保持信号;在四线/八线模式下作为Data3

DATA4

在八线模式下作为Data4

DATA5

在八线模式下作为Data5

DATA6

在八线模式下作为Data6

DATA7

在八线模式下作为Data7

Assertion

发起SPI传输(一般是拉低片选线),总线进入忙状态

De-assertion

SPI传输结束(一般是拉高片选线),总线进入空闲状态

Transaction(传输事务)

一次完整的传输事务:主机拉低从机的 CS 线,开始传输数据,然 后再拉高从机的 CS 线。传输事务为原子操作,即不可打断。

Transfer(传输)

SPI 主机与从机完成数据交换的一次完整过程。一次 SPI 传输可以 包含一个或多个 SPI 传输事务。

单次传输

在这种传输模式下,仅包含一次传输事务。

Launch edge(发射边沿)

数据源寄存器在此边沿将数据比特发送至信号线上

Latch edge(锁存边沿)

数据目的寄存器在此边沿将数据比特锁存下来


特性

  • 支持多线程环境使用

  • 支持CPU控制的传输模式以及DMA控制的传输模式

  • DMA读写过程用户无感知

  • 自动时分复用(不同时刻对不同设备进行读写)

  • 时钟最高80MHz(主机模式)


注意点

  • 同一个设备尽量在一个线程中操作

  • 同一个设备在不同线程中操作需要通过互斥锁进行保护


SPI事务描述

每次SPI传输可以包含五个阶段,包括命令、地址、空周期、写、读阶段,每个阶段都是可选的。

阶段

描述

Command(命令)

在此阶段主机可以发送命令字段,长度最多16bit

Address(地址)

在此阶段主机可以发送地址字段,长度最多32bit

Dummy(空周期)

此阶段用于适配时序要求

Write(写)

此阶段主机发送数据给从设备

Read(读)

此阶段主机读取从设备数据

     

传输线模式配置

模式

命令线宽度

地址线宽度

数据线宽度

Transaction Flag

Bus IO setting Flag

Normal SPI

1

1

1

0

0

Dual Output

(DOUT)

1

1

2

SPI_TRANS_MODE_DIO

SPICOMMON_BUSFLAG_DUAL

Dual I/O

(DIO)

1

2

2

SPI_TRANS_MODE_DIO | SPI_TRANS_MULTILINE_ADDR

Quad Output

(QOUT)

1

1

4

SPI_TRANS_MODE_QIO

SPICOMMON_BUSFLAG_QUAD

Quad I/O

(QIO)

1

4

4

SPI_TRANS_MODE_QIO | SPI_TRANS_MULTILINE_ADDR

Octal Output

1

1

8

SPI_TRANS_MODE_OCT

SPICOMMON_BUSFLAG_OCTAL

OPI

8

8

8

SPI_TRANS_MODE_OCT | SPI_TRANS_MULTILINE_ADDR | SPI_TRANS_MULTILINE_CMD

传输标记说明

#define SPI_TRANS_MODE_DIO            (1<<0)  ///< Transmit/receive data in 2-bit mode

收发数据阶段使用双线模式

#define SPI_TRANS_MODE_QIO            (1<<1)  ///< Transmit/receive data in 4-bit mode

收发数据阶段使用四线模式

#define SPI_TRANS_MODE_DIOQIO_ADDR    (1<<4)  ///< Also transmit address in mode selected by SPI_MODE_DIO/SPI_MODE_QIO
#define SPI_TRANS_MULTILINE_ADDR      SPI_TRANS_MODE_DIOQIO_ADDR ///< The data lines used at address phase is the same as data phase (otherwise, only one data line is used at address phase)

地址阶段使用和数据阶段一样的模式

#define SPI_TRANS_MULTILINE_CMD       (1<<9)  ///< The data lines used at command phase is the same as data phase (otherwise, only one data line is used at command phase)

命令阶段使用和数据阶段一样的模式

#define SPI_TRANS_MODE_OCT            (1<<10) ///< Transmit/receive data in 8-bit mode

收发数据阶段使用八线模式

总线的初始化

在使用SPI总线之前必须先初始化

/**
 * @brief Initialize a SPI bus
 *
 * @warning SPI0/1 is not supported
 *
 * @param host_id       SPI peripheral that controls this bus
 * @param bus_config    Pointer to a spi_bus_config_t struct specifying how the host should be initialized
 * @param 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.
 *
 * @warning If a DMA channel is selected, any transmit and receive buffer used should be allocated in
 *          DMA-capable memory.
 *
 * @warning 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.
 *
 * @return
 *         - 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_initialize(spi_host_device_t host_id, const spi_bus_config_t *bus_config, spi_dma_chan_t dma_chan);

函数参数的说明:

host_id:SPI外设索引号

bus_config:SPI总线参数

dma_chan:是否使能DMA传输并配置使用的传输通道


使能DMA传输的注意点

  • 传输buffer需要时dma-capable的内部RAM

  • 必须32bit对齐并且长度必须是4字节对齐

如果这两个条件不满足,驱动内部会自动分配内存然后进行拷贝后再传输,故效率会降低;所以建议外部传入的buffer尽可能是DMA属性的内存


SPI外设索引号

/**
 * @brief Enum with the three SPI peripherals that are software-accessible in it
 */
typedef enum {
//SPI1 can be used as GPSPI only on ESP32
    SPI1_HOST=0,    ///< SPI1
    SPI2_HOST=1,    ///< SPI2
    SPI3_HOST=2,    ///< SPI3
    SPI_HOST_MAX,   ///< invalid host value
} spi_host_device_t;

在ESP32S3上可以使用SPI2_HOST和SPI3_HOST


SPI总线参数说明

/**
 * @brief 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, allowing for >40MHz speeds.
 *
 * @note Be advised that the slave driver does not use the quadwp/quadhd lines and fields in spi_bus_config_t refering to these lines will be ignored and can thus safely be left uninitialized.
 */
typedef struct {
    union {
      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.
    };
    union {
      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.
    union {
      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.
    };
    union {
      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.
    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.
    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.
                           */
} spi_bus_config_t;

各个字段含义:

mosi_io_num/data0_io_num:主机输出从机输入或者data0信号线IO引脚编号

miso_io_num/data1_io_num:主机输入从机输出或者data1信号线IO引脚编号

sclk_io_num:时钟信号IO引脚编号

quadwp_io_num/data2_io_num:写保护信号或者data2信号线IO引脚编号

quadhd_io_num/data3_io_num:保持信号或者data3信号线IO引脚编号

data4_io_num:data4信号线IO引脚编号

data5_io_num:data5信号线IO引脚编号

data6_io_num:data6信号线IO引脚编号

data7_io_num:data7信号线IO引脚编号

max_transfer_size:一次SPI传输最大的长度;使能DMA传输时,如果设置为0,则默认限制为4092字节;未使能DMA时,如果设置为0,则默认限制为SOC_SPI_MAXIMUM_BUFFER_SIZE(64字节)。

flags:总线特征标志,具体说明请看下文

intr_flags:中断特征标志

其中数据线根据实际硬件需要设置,如果某些引脚不需要,设置为-1即可。

关于flags字段的说明

#define SPICOMMON_BUSFLAG_SLAVE         0          ///< Initialize I/O in slave mode

设置从设备模式,内部使用;通过spi_slave_initialize接口初始化默认为从设备模式

#define SPICOMMON_BUSFLAG_MASTER        (1<<0)     ///< Initialize I/O in master mode

设置主设备模式,内部使用;通过spi_bus_initialize接口初始化默认为主设备模式

#define SPICOMMON_BUSFLAG_IOMUX_PINS    (1<<1)     ///< Check using iomux pins. Or indicates the pins are configured through the IO mux rather than GPIO matrix.

检查是否使用IOMUX作为IO输入输出或者指示IO引脚是否是使用IOMUX进行配置的

#define SPICOMMON_BUSFLAG_GPIO_PINS     (1<<2)     ///< Force the signals to be routed through GPIO matrix. Or indicates the pins are routed through the GPIO matrix.

强制使用GPIO Matrix作为IO输入输出或者指示IO引脚是否是使用GPIO Matrix进行配置的

默认情况下,SPI主机驱动会使用GPIO矩阵来配置外设引脚;但是如果所有的信号都可以通过IOMUX进行配置(或者某些信号不需要使用,设置为-1),那么就会使用IOMUX进行引脚配置,优点是传输时钟频率可以大于40MHz。

IO引脚配置说明

如果在初始化时设置了SPICOMMON_BUSFLAG_GPIO_PINS标记,那么内部就使用IO矩阵进行IO配置(即使需要的IO引脚可以通过IOMUX配置)如果设置了SPICOMMON_BUSFLAG_IOMUX_PINS,则内部会检查设置的IO引脚编号是否可以通过IOMUX配置,如果可以则初始化成功,否则初始化失败如果两个标记都没有设置,那么内部会检查设置的IO引脚编号是否可以通过IOMUX配置,如果可以则使用IOMUX,否则使用IO Matrix

一般情况下SPICOMMON_BUSFLAG_GPIO_PINS和SPICOMMON_BUSFLAG_IOMUX_PINS都不需要设置,只要我们设置的IO引脚是可以通过IOMUX配置的,默认就会使用IOMUX配置;否则都是通过IO Matrix配置。

通过IOMUX配置的引脚配置

Pin Name

SPI2

SPI3

GPIO Number

CS0*

10

N/A

SCLK

12

N/A

MISO

13

N/A

MOSI

11

N/A

QUADWP

14

N/A

QUADHD

9

N/A


#define SPICOMMON_BUSFLAG_SCLK          (1<<3)     ///< Check existing of SCLK pin. Or indicates CLK line initialized.

检查是否配置SCLK引脚,一般不使用此标记;如果设置了此标志,但是sclk_io_num字段为-1,则初始化出错。

#define SPICOMMON_BUSFLAG_MISO          (1<<4)     ///< Check existing of MISO pin. Or indicates MISO line initialized.
#define SPICOMMON_BUSFLAG_MOSI          (1<<5)     ///< Check existing of MOSI pin. Or indicates MOSI line initialized.

检查是否配置MOSI/MISO引脚,一般不使用此标记;如果设置了这些标志,但是mosi_io_num/mosi_io_num字段为-1,则初始化出错。

#define SPICOMMON_BUSFLAG_DUAL          (1<<6)     ///< Check MOSI and MISO pins can output. Or indicates bus able to work under DIO mode.

检查MOSI和MISO引脚可以输出或者指示总线能够工作在DIO模式,一般不使用此标记;如果设置了这些标记,但是mosi_io_num/mosi_io_num未都设置且合法,则初始化报错。

#define SPICOMMON_BUSFLAG_WPHD          (1<<7)     ///< Check existing of WP and HD pins. Or indicates WP & HD pins initialized.

检查WP/HD信号的存在或者指示WP/HD引脚已经初始化,一般不使用此标记;如果设置了这些标记,但是quadwp_io_num/quadhd_io_num未都设置且合法,则初始化报错。

#define SPICOMMON_BUSFLAG_QUAD          (SPICOMMON_BUSFLAG_DUAL|SPICOMMON_BUSFLAG_WPHD)     ///< Check existing of MOSI/MISO/WP/HD pins as output. Or indicates bus able to work under QIO mode.

检查MOSI/MISO/WP/HD引脚可以输出或者指示总线能够工作在QIO模式

#define SPICOMMON_BUSFLAG_IO4_IO7       (1<<8)     ///< Check existing of IO4~IO7 pins. Or indicates IO4~IO7 pins initialized.

检查IO4-IO7引脚存在或者指示IO4-IO7引脚已经初始化

#define SPICOMMON_BUSFLAG_OCTAL         (SPICOMMON_BUSFLAG_QUAD|SPICOMMON_BUSFLAG_IO4_IO7)  ///< Check existing of MOSI/MISO/WP/HD/SPIIO4/SPIIO5/SPIIO6/SPIIO7 pins as output. Or indicates bus able to work under octal mode.

检查MOSI/MISO/WP/HD/SPIIO4/SPIIO5/SPIIO6/SPIIO7引脚可以输出或者指示可以工作在八线模式


SPI DMA通道说明

/**
 * @brief SPI DMA channels
 */
typedef enum {
  SPI_DMA_DISABLED = 0,     ///< Do not enable DMA for SPI
#if CONFIG_IDF_TARGET_ESP32
  SPI_DMA_CH1      = 1,     ///< Enable DMA, select DMA Channel 1
  SPI_DMA_CH2      = 2,     ///< Enable DMA, select DMA Channel 2
#endif
  SPI_DMA_CH_AUTO  = 3,     ///< Enable DMA, channel is automatically selected by driver
} spi_common_dma_t;

使能DMA传输时传输的数据量不限制,默认为4092字节;禁用DMA传输时会限制最大可传输的数据量,默认为64字节。

往总线添加设备

总线初始化成功后,就可以往总线上添加从设备

typedef struct spi_device_t *spi_device_handle_t;  ///< Handle for a device on a SPI bus
/**
 * @brief 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.
 *
 * @note While in general, speeds up to 80MHz on the dedicated SPI pins and 40MHz on GPIO-matrix-routed pins are
 *       supported, full-duplex transfers routed over the GPIO matrix only support speeds up to 26MHz.
 *
 * @param host_id SPI peripheral to allocate device on
 * @param dev_config SPI interface protocol config for the device
 * @param handle Pointer to variable to hold the device handle
 * @return
 *         - ESP_ERR_INVALID_ARG   if parameter is invalid
 *         - 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_add_device(spi_host_device_t host_id, const spi_device_interface_config_t *dev_config, spi_device_handle_t *handle);

各个参数说明:

host_id:SPI外设索引号

dev_config:从设备配置信息

handle:从设备句柄

注意点

通过IOMUX配置外设引脚时钟最大可以为80MHz;通过GPIO Matrix的时钟只能40MHz,并且全双工通信时只能设置为26MHz.

设备配置信息

/**
 * @brief This is a configuration for a SPI slave device that is connected to one of the SPI buses.
 */
typedef struct {
    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)
                                     */
    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;             ///< Clock speed, divisors of 80MHz, in Hz. See ``SPI_MASTER_FREQ_*``.
    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.
        */
    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.
                                 */
} spi_device_interface_config_t;

各个字段含义:

command_bits:命令阶段传输的bit数(可以通过SPI_TRANS_VARIABLE_CMD进行覆盖)

address_bits:地址阶段传输的bit数(可以通过SPI_TRANS_VARIABLE_ADDR进行覆盖)

dummy_bits:地址阶段和数据阶段的空周期数(可以通过SPI_TRANS_VARIABLE_DUMMY进行覆盖)

由于并不是每一个传输都需要命令、地址、空周期这些阶段,一般情况下在添加从设备时这三个字段都是设置为0,在真正发起SPI传输时通过SPI_TRANS_VARIABLE_CMD、SPI_TRANS_VARIABLE_ADDR、SPI_TRANS_VARIABLE_DUMMY这些标志位进行设置。

mode:SPI时钟的极性和相位关系,请阅读《理解SPI/Dual SPI/Quad SPI/QPI之间的区别》中的时钟极性和时钟相位章节

/**< SPI mode, representing a pair of (CPOL, CPHA) configuration:
                                         - 0: (0, 0)
                                         - 1: (0, 1)
                                         - 2: (1, 0)
                                         - 3: (1, 1)
                                     */

duty_cycle_pos:时钟占空比,默认50%

cs_ena_pretrans:传输开始前CS信号提前有效的时间,只用在半双工传输中(0-16)

cs_ena_posttrans:传输结束后CS保持有效的时间(0-16)

clock_speed_hz:时钟频率

input_delay_ns:

spics_io_num:片选IO引脚编号,未使用时设置-1即可

flags:设备标志

queue_size:传输队列大小,使用spi_device_queue_trans接口可以将传输请求进行排队处理

pre_cb:传输开始前的回调函数,在中断中执行

post_cb:传输结束后的回调函数,在中断中执行

设备标志说明

#define SPI_DEVICE_TXBIT_LSBFIRST          (1<<0)  ///< Transmit command/address/data LSB first instead of the default MSB first

地址/命令/数据阶段发送的比特位的顺序为先发送低比特位(默认是先发送高比特位)

#define SPI_DEVICE_RXBIT_LSBFIRST          (1<<1)  ///< Receive data LSB first instead of the default MSB first

接收数据的比特位顺序为先接收低比特位(默认是先接收高比特位)

#define SPI_DEVICE_BIT_LSBFIRST            (SPI_DEVICE_TXBIT_LSBFIRST|SPI_DEVICE_RXBIT_LSBFIRST) ///< Transmit and receive LSB first

收发都使用LSB

#define SPI_DEVICE_POSITIVE_CS             (1<<3)  ///< Make CS positive during a transaction instead of negative

默认片段信号拉低有效,此标记设置为拉高为选中设备

#define SPI_DEVICE_HALFDUPLEX              (1<<4)  ///< Transmit data before receiving it, instead of simultaneously

设置为半双工模式,如果需要使用多线进行数据收发,必须在添加从设备时设置此字段


总线获取

有些情况下需要独占的使用总线,这个时候可以使用spi_device_acquire_bus()独占总线;通过spi_device_release_bus()释放总线


中断传输和轮询传输

通过spi_device_transmit()发起的传输是中断传输,会阻塞当前线程直到传输完成,此时CPU调度其他线程执行;

通过spi_device_queue_trans可以发起多个传输,每个传输进行排队,中断中一个接一个的处理。

调用此接口后,当前线程可以继续执行其他任务,通过spi_device_get_trans_result()获取传输结果。

示例代码

    esp_err_t ret;
    int x;
    //Transaction descriptors. Declared static so they're not allocated on the stack; we need this memory even when this
    //function is finished because the SPI driver needs access to it even while we're already calculating the next line.
    static spi_transaction_t trans[6];

    //In theory, it's better to initialize trans and data only once and hang on to the initialized
    //variables. We allocate them on the stack, so we need to re-init them each call.
    for (x=0; x<6; x++) {
        memset(&trans[x], 0, sizeof(spi_transaction_t));
        if ((x&1)==0) {
            //Even transfers are commands
            trans[x].length=8;
            trans[x].user=(void*)0;
        } else {
            //Odd transfers are data
            trans[x].length=8*4;
            trans[x].user=(void*)1;
        }
        trans[x].flags=SPI_TRANS_USE_TXDATA;
    }
    trans[0].tx_data[0]=0x2A;           //Column Address Set
    trans[1].tx_data[0]=0;              //Start Col High
    trans[1].tx_data[1]=0;              //Start Col Low
    trans[1].tx_data[2]=(320)>>8;       //End Col High
    trans[1].tx_data[3]=(320)&0xff;     //End Col Low
    trans[2].tx_data[0]=0x2B;           //Page address set
    trans[3].tx_data[0]=ypos>>8;        //Start page high
    trans[3].tx_data[1]=ypos&0xff;      //start page low
    trans[3].tx_data[2]=(ypos+PARALLEL_LINES)>>8;    //end page high
    trans[3].tx_data[3]=(ypos+PARALLEL_LINES)&0xff;  //end page low
    trans[4].tx_data[0]=0x2C;           //memory write
    trans[5].tx_buffer=linedata;        //finally send the line data
    trans[5].length=320*2*8*PARALLEL_LINES;          //Data length, in bits
    trans[5].flags=0; //undo SPI_TRANS_USE_TXDATA flag

    //Queue all transactions.
    for (x=0; x<6; x++) {
        ret=spi_device_queue_trans(spi, &trans[x], portMAX_DELAY);
        assert(ret==ESP_OK);
    }

上述代码片段采用排队方式的中断传输,一次发起6个SPI传输事务。

轮询传输通过查询标志位判断传输是否完成,通过spi_device_polling_transmit()进行

示例代码

    spi_transaction_t t;
    memset(&t, 0, sizeof(t));
    t.length=8*3;
    t.flags = SPI_TRANS_USE_RXDATA;
    t.user = (void*)1;

    esp_err_t ret = spi_device_polling_transmit(spi, &t);
    assert( ret == ESP_OK );

至此我们介绍了SPI主机驱动的基本知识,关于spi_transaction_t结构体的具体使用细节下篇讲解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/331360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】二叉树的前序中序后序非递归实现

文章目录二叉树的前序遍历二叉树的中序遍历二叉树的后序遍历总结二叉树的前序遍历 前序遍历的顺序是根、左、右。任何一颗树都可以认为分为左路节点&#xff0c;左路节点的右子树。先访问左路节点&#xff0c;再来访问左路节点的右子树。把访问左路节点的右子树看成一个子问题…

VUE3 插件的开发和使用

在构建 Vue 项目的过程中&#xff0c;离不开各种开箱即用的插件支持&#xff0c;用以快速完成需求&#xff0c;避免自己造轮子。 在 Vue 项目里&#xff0c;可以使用针对 Vue 定制开发的专属插件&#xff0c;也可以使用无框架依赖的通用 JS 插件&#xff0c;插件的表现形式也是…

51单片机学习笔记_11 蜂鸣器,识简谱,根据简谱编写蜂鸣器代码

蜂鸣器实验 蜂鸣器简单地说&#xff0c;就是电磁线圈和磁铁对振动膜的作用。 单片机的是无源蜂鸣器&#xff0c;不能一直充电&#xff0c;需要外部控制器发送震荡信号&#xff0c;可以改变频率产生不同的音色、音调。 大多数有源蜂鸣器则没有这个效果&#xff0c;有源蜂鸣器…

JavaScript(四)-全面详解(学习总结---从入门到深化)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主,Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 &#x1f4e7;如果文章…

JAVA开发(Redis的主从与集群)

现在web项目无处不在使用缓存技术&#xff0c;redis的身影可谓无处不在。但是又有多少项目使用到的是redis的集群&#xff1f;大概很多项目只是用到单机版的redis吧。作为缓存的一块&#xff0c;set &#xff0c;get数据。用的不亦乐乎。但是对于高可用系统来说&#xff0c;数据…

Tomcat简介

目录 一、Tomcat简介 二、下载安装Tomcat 三、利用Tomcat部署静态页面 一、Tomcat简介 Tomcat是一个HTTP服务器&#xff0c;可以按照HTTP的格式来解析请求来调用用户指定的相关代码然后按照HTTP的格式来构造返回数据。 二、下载安装Tomcat 进入Tomcat官网选择与自己电脑…

电子科技大学人工智能期末复习笔记(二):MDP与强化学习

目录 前言 期望最大搜索&#xff08;Expectimax Search&#xff09; ⭐马尔科夫决策&#xff08;MDP&#xff09;——offline&#xff08;超重点&#xff09; 先来看一个例子 基本概念 政策&#xff08;Policy&#xff09; 折扣&#xff08;Discounting&#xff09; 如…

Mysql中的事务

1. MyIsam是不支持事务的&#xff0c; InnoDB支持 2.事务的四大特性ACID 原子性&#xff08;Atomicity&#xff09;&#xff1a;一个事务中的所有操作&#xff0c;要么全部完成&#xff0c;要么全部不完成&#xff0c;不会结束在中间某个环节&#xff0c;而且事务在执行过程中…

PythonWeb开发基础(四 完)Response使用及wsgify装饰器

课程地址&#xff1a;Python 工程师进阶技术图谱 文章目录&#x1f33e; Response使用及wsgify装饰器1、Response的使用2、wsgify装饰器&#x1f33e; Response使用及wsgify装饰器 1、Response的使用 前面一节我们知道了&#xff0c;使用webob的Request模块可以很方便地对请求…

若依框架---PageHelper分页(十五)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是小童&#xff0c;Java开发工程师&#xff0c;CSDN博客博主&#xff0c;Java领域新星创作者 &#x1f4d5;系列专栏&#xff1a;前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 &#x1f4…

STM32开发(7)----CubeMX配置串口通讯(轮询方式)

CubeMX配置串口通讯&#xff08;轮询方式&#xff09;前言一、串口的介绍二、实验过程1.实验材料2.STM32CubeMX配置PWM3.代码实现重载printf轮询接收4.编译烧录5.硬件连接6.实验结果重载printf结果串口轮询接收结果总结前言 本章介绍使用STM32CubeMX对串口进行配置的方法&…

​力扣解法汇总1797. 设计一个验证系统

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 你需要设计一个包含验证码的验证系统。每一次验证中&#xff0c;用户会收到一个…

vue:自定义组件如何使用v-model

问题&#xff1a; 在使用自定义组件时&#xff0c;我们有时候需要使用 v-model 来双向绑定。方法&#xff1a; 在vue中&#xff0c;v-model 的值相当于默认传递了一个名为 value 的 prop 和一个名为 input 的事件&#xff08;注意&#xff0c;这个value的prop是需要在自定义组件…

《皮格马利翁效应》-期待效应

“ 人生没有白走的路&#xff0c;没有白读的书&#xff0c;你触碰过的那些文字会在不知不觉中帮你认知这个世界 #每天读本书 #关注我每天解读一本书”《皮格马利翁效应》01关于作者朱瑟琳•乔塞尔森&#xff0c;女性心理学家&#xff0c;是国际 公认的精神医学大师欧文•亚隆的…

刚刚,微软推出支持chatGPT的必应

文章目录刚刚&#xff0c;微软推出支持chatGPT的必应什么是新的必应&#xff1f;如何体验页面初体验结语&#xff1a;搜索引擎的新时代刚刚&#xff0c;微软推出支持chatGPT的必应 这款新的必应功能允许用户通过自然语言方式与chatgpt进行交流&#xff0c;以获得快速、准确的信…

华为HCIE学习之openstack基础

文章目录一、Openstack各种文件位置二、Openstack命令操作1.使用帮助三、用命令发放云主机1、创建租户2、创建用户并与租户绑定3、注册镜像4、创建规格5、创建公有网络及其子网&#xff08;做弹性IP用&#xff09;6、创建私有网络及其子网7、创建路由并设置网关与端口8、创建安…

C++类与对象(中)

✅<1>主页&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;C &#x1f525;<3>创作者&#xff1a;我的代码爱吃辣 ☂️<4>开发环境&#xff1a;Visual Studio 2022 &#x1f4ac;<5>前言&#xff1a;C类中一共有六个默认成员函…

当ChatGPT遇到网络安全

ChatGPT&#xff1a;是人工智能技术驱动的自然语言处理工具&#xff0c;它能够通过学习和理解人类的语言来进行对话&#xff0c;还能根据聊天的上下文进行互动&#xff0c;真正像人类一样来聊天交流&#xff0c;甚至能完成撰写邮件、视频脚本、文案、翻译、代码等任务。GPT 是 …

活动星投票文艺巡演活动免费投票程序制作网页投票网站

“文艺巡演活动”网络评选投票_线上小程序的投票方式_视频投票的功能_在线投票程序用户在使用微信投票的时候&#xff0c;需要功能齐全&#xff0c;又快捷方便的投票小程序。而“活动星投票”这款软件使用非常的方便&#xff0c;用户可以随时使用手机微信小程序获得线上投票服务…

ConcurrentHashMap底层原理介绍

概述: ConcurrentHashMap相对于HashMap的性能较差一些,但相比于Hashtable而言性能要高很多,因为Hashtable内部的所有方法都是同步方法,加了synchronized锁,所以性能上比较差,但在多线程环境下是具有很强的安全性的ConcurrentHashMap避免了对全局加锁改成了局部加锁操作&#xf…