当前位置: 首页 > news >正文

SPI入门(基于ESP-IDF-v5.4.1)

主要参考资料:
SPI 主机驱动程序: https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32s3/api-reference/peripherals/spi_master.html
ESP32S3系列–SPI主机驱动详解(一): https://blog.csdn.net/tianizimark/article/details/128917426?fromshare=blogdetail&sharetype=blogdetail&sharerId=128917426&sharerefer=PC&sharesource=qq_40773212&sharefrom=from_link

目录

  • spi_bus_initialize总线参数
  • spi_bus_add_device设备配置信息
  • 踩坑
    • 使用数据代替命令一直读不出数据
    • address_bits、dummy_bits默认值不是0,
    • spi_transaction_t 结构体 地址必须是 4-byte 对齐

spi_bus_initialize总线参数

 * @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:中断特征标志,如果设置为ESP_INTR_FLAG_IRAM则驱动中涉及到的回调以及回调调用的函数也要是IRAM属性的

flags

#define SPICOMMON_BUSFLAG_SLAVE         0          ///< Initialize I/O in slave mode
#define SPICOMMON_BUSFLAG_MASTER        (1<<0)     ///< Initialize I/O in master mode
#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.
#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.
#define SPICOMMON_BUSFLAG_SCLK          (1<<3)     ///< Check existing of SCLK pin. Or indicates CLK line initialized.
#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.
#define SPICOMMON_BUSFLAG_DUAL          (1<<6)     ///< Check MOSI and MISO pins can output. Or indicates bus able to work under DIO mode.
#define SPICOMMON_BUSFLAG_WPHD          (1<<7)     ///< Check existing of WP and HD pins. Or indicates WP & HD pins initialized.
#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.
#define SPICOMMON_BUSFLAG_IO4_IO7       (1<<8)     ///< Check existing of IO4~IO7 pins. Or indicates IO4~IO7 pins initialized.
#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.
#define SPICOMMON_BUSFLAG_NATIVE_PINS   SPICOMMON_BUSFLAG_IOMUX_PINS
#define SPICOMMON_BUSFLAG_SLP_ALLOW_PD  (1<<9)     ///< Allow to power down the peripheral during light sleep, and auto recover then.

spi_bus_add_device设备配置信息

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 phaseuint8_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 MISOvalid, including the possible clock delay from slave to master. The driver uses this value to give an extradelay before the MISO is ready on the line. Leave at 0 unless you know you need a delay. For better timingperformance 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 useduint32_t flags;                 ///< Bitwise OR of SPI_DEVICE_* flagsint 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 timetransaction_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数,最大16bit(可以通过SPI_TRANS_VARIABLE_CMD进行覆盖)
address_bits:地址阶段传输的bit数,最大64bit(可以通过SPI_TRANS_VARIABLE_ADDR进行覆盖)
dummy_bits:地址阶段和数据阶段的空周期数(可以通过SPI_TRANS_VARIABLE_DUMMY进行覆盖)
(由于并不是每一个传输都需要命令、地址、空周期这些阶段,一般情况下在添加从设备时这三个字段都是设置为0,在真正发起SPI传输时通过SPI_TRANS_VARIABLE_CMD、SPI_TRANS_VARIABLE_ADDR、SPI_TRANS_VARIABLE_DUMMY这些标志位进行设置。)
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:传输结束后的回调函数,在中断中执行

flags

#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
#define SPI_DEVICE_3WIRE                   (1<<2)  ///< Use MOSI (=spid) for both sending and receiving data
#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
#define SPI_DEVICE_CLK_AS_CS               (1<<5)  ///< Output clock on CS line if CS is active
/** 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.*/
#define SPI_DEVICE_NO_DUMMY                (1<<6)
#define SPI_DEVICE_DDRCLK                  (1<<7)
#define SPI_DEVICE_NO_RETURN_RESULT        (1<<8)  ///< Don't return the descriptor to the host on completion (use post_cb to notify instead)

踩坑

使用数据代替命令一直读不出数据

使用数据代替命令

spi_transaction_t t = {};
t.flags  = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;
t.length = 16;
t.tx_data[0] = (addr << 1) | 0x80;             // 第 1 字节:命令/地址 + 读标志
t.tx_data[1] = 0x00;                           // 第 2 字节:Dummy(只为产生时钟)

命令与数据分开,终于读出数据!

spi_transaction_t t = {};
t.cmd = (addr << 1) | 0x80;      // 读命令 (最高位=1)
t.length   = 8;      
t.rxlength = 8;      
t.flags  = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;       
t.tx_data[0] = 0x00; 

address_bits、dummy_bits默认值不是0,

spi_device_interface_config_t dev_config_;
dev_config_.command_bits = 8;   // 8位命令
dev_config_.address_bits = 0;
dev_config_.dummy_bits = 0;

spi_transaction_t 结构体 地址必须是 4-byte 对齐

起因:在读数据前初始化了一个大小为7的数组,导致读数据一直失败

//ESP-IDF 的 SPI 主机驱动规定:传给 spi_device_polling_transmit() 的 spi_transaction_t 结构体 地址必须是 4-byte 对齐spi_transaction_t t __attribute__((aligned(4))) = {}; t.cmd = (addr << 1) | 0x80;      // 读命令 (最高位=1)t.length   = 8;      t.rxlength = 8;      t.flags  = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;       t.tx_data[0] = 0x00; 
http://www.dtcms.com/a/313596.html

相关文章:

  • accept4系统调用及示例
  • ELECTRICAL靶场
  • 检索召回率优化探究三:基于LangChain0.3集成Milvu2.5向量数据库构建的智能问答系统
  • 思途JSP学习 0802(项目完整流程)
  • Fay数字人如何使用GPT-SOVITS进行TTS转换以及遇到的一些问题
  • 写作路上的迷茫与突破
  • 推荐系统学习笔记(八)其他召回通道
  • ssh服务器端口和本地端口映射
  • 基于Python 批量导入实体与关系到 Neo4j 数据库的完整实践
  • jconsole与jvisualvm监控
  • 数据结构基础 - 平衡二叉树
  • async/await和Promise之间的关系是什么?(补充)
  • NSA稀疏注意力深度解析:DeepSeek如何将Transformer复杂度从O(N²)降至线性,实现9倍训练加速
  • 能表示旋转的矩阵是一个流形吗?
  • 【大模型篇】:GPT-Llama-Qwen-Deepseek
  • 数据结构重点内容
  • Go语言实战案例:多协程并发下载网页内容
  • 《 ThreadLocal 工作机制深度解析:高并发场景的利与弊》
  • Mysql深入学习:InnoDB执行引擎篇
  • C++ : 反向迭代器的模拟实现
  • 【图像处理基石】如何使用deepseek进行图像质量的分析?
  • vllm0.8.5:思维链(Chain-of-Thought, CoT)微调模型的输出结果包括</think>,提供一种关闭思考过程的方法
  • MCP协议:CAD地图应用的AI智能化解决方案(唯杰地图MCP)
  • 【数据结构与算法】数据结构初阶:排序内容加餐(二)——文件归并排序思路详解(附代码实现)
  • 【C++】面向对象编程
  • C语言(长期更新)第8讲 函数递归
  • 网络通信与Socket套接字详解
  • C#模式匹配用法与总结
  • 网页 URL 转 Markdown API 接口
  • 大模型中的Token和Tokenizer:核心概念解析