一、SPI主机驱动
SPI主控程序是一个控制ESP32的SPI外围设备的程序,同时它们作为主控设备发挥作用。
ESP32的SPI外围设备概述
ESP32集成了4个SPI外设。
- SPI0和SPI1在内部用于访问ESP32的附加闪存。两个控制器共享相同的SPI总线信号,并且有一个仲裁器来决定哪个控制器可以访问总线。
在SPI1总线上使用SPI主驱动器时有很多限制,请看在SPI1总线上使用SPI主驱动器的注意事项。
- SPI2和SPI3是通用的SPI控制器,有时分别被称为HSPI和VSPI。它们对用户是开放的。SPI2和SPI3有独立的总线信号,各自的名称相同。每条总线有三条CS线,可以驱动多达相同数量的SPI从机。
名词解释
下表给出了与SPI主驱动有关的名词解释。
术语 | 定义 |
---|---|
Host | ESP32内部的SPI控制器外设,在总线上发起SPI传输,并作为一个SPI主站。 |
Device | SPI从属设备。一个SPI总线可以连接到一个或多个器件。每个设备共享MOSI、MISO和SCLK信号,但只有当主机发出设备的单独CS线时,才在总线上处于激活状态。 |
Bus | 一个信号总线,是连接到一个主机的所有设备所共有的。一般来说,一个总线包括以下线路。MISO、MOSI、SCLK、一条或多条CS线,以及可选的QUADWP和QUADHD。所以设备都连接到相同的线路上,除了每个设备都有自己的CS线之外。如果以菊花链的方式连接,几个器件也可以共享一条CS线。 |
MOSI | 主出从进,又称从主机到设备的数据传输。也是Octal/OPI模式下的data0信号。 |
MISO | 主进从出,又称从设备到主机的数据传输。也是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外围设备一次只能被一个任务访问。
- 使用
xSemaphoreCreateMutex
在共享设备周围添加一个互斥锁。
SPI交易
一个SPI总线交易包括五个阶段,可以在下表中找到。这些阶段中的任何一个都可以被跳过。
阶段 | 描述 |
---|---|
Command | 在这个阶段,一个命令(0-16位)由主机写到总线上。 |
Address | 在这个阶段,一个地址(0-64位)由主机在总线上传输。 |
Write | 主机向设备发送数据。这种数据紧随可选的命令和地址阶段,在电气层面上与它们没有区别。 |
Dummy | 这个阶段是可配置的,用于满足时间要求。 |
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
只决定了接收到缓冲区的数据长度。
在半双工事务中,读和写阶段不是同时进行的(一次一个方向)。写和读阶段的长度分别由struct spi_transaction_t
的length
和rxlength
成员决定。
命令和地址阶段是可选的,因为不是每个SPI设备都需要命令和/或地址。这反映在从机的配置中:如果command_bits
和/或address_bits
被设置为0,就不会发生命令或地址阶段。
读和写阶段也可以是可选的,因为不是每个交易都需要同时写入和读取数据。如果rx_buffer
是NULL,并且SPI_TRANS_USE_RXDATA
没有被设置,那么读取阶段将被跳过。如果tx_buffer
是NULL,并且SPI_TRANS_USE_TXDATA
没有被设置,则写阶段被跳过。
该驱动支持两种类型的交易:中断交易和轮询交易。程序员可以选择在每个设备上使用不同的交易类型。如果你的设备需要两种交易类型,请看向同一设备发送混合交易的注意事项。
中断交易
中断事务将阻塞交易例程,直到交易完成,从而使CPU能够运行其他任务。
一个应用任务可以排队多个交易,驱动程序将在中断服务例程(ISR)中自动逐一处理这些交易。它允许任务切换到其他程序,直到所有交易完成。
轮询交易
轮询交易不使用中断。该例程一直轮询SPI主机的状态位,直到交易完成。
所有使用中断交易的任务都可以被队列阻断。这时,他们需要等待ISR运行两次,然后才完成交易。轮询交易节省了原本用于队列处理和上下文切换的时间,从而使交易持续时间更小。缺点是在这些交易进行时CPU很忙。
spi_device_polling_end()
例程需要至少1个us的开销,以便在交易完成时解除对其他任务的封锁。强烈建议使用函数spi_device_acquire_bus()
和spi_device_release_bus()
包装一系列的轮询交易,以避免开销。欲了解更多信息,请参见总线获取。
交易行模式
ESP32支持的线路模式如下,要使用这些模式,请在结构 spi_transaction_t
中设置成员标志,如交易标志栏所示。如果你想检查相应的IO引脚是否被设置,请设置spi_bus_config_t
中的成员标志,如总线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 | |
四路输出 | 1 | 1 | 4 | SPI_TRANS_MODE_QIO | SPICOMMON_BUSFLAG_QUAD |
四路 I/O | 1 | 4 | 4 | SPI_TRANS_MODE_QIO | SPI_TRANS_MULTILINE_ADDR |
命令和地址阶段
在命令和地址阶段,结构spi_transaction_t
中的成员cmd
和addr
被发送到总线上,此时没有任何东西被读取。命令和地址阶段的默认长度在spi_device_interface_config_t
中通过调用spi_bus_add_device()
设置。如果成员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
,并像平常一样配置base的其他部分。然后每个阶段的长度将等于spi_transaction_ext_t
结构中设定的command_bits
和address_bits
。
如果命令和地址阶段需要和数据阶段一样的行数,你需要在结构体 spi_transaction_t
的flags成员中设置SPI_TRANS_MULTILINE_CMD和/或SPI_TRANS_MULTILINE_ADDR。也请看交易行模式。
写和读阶段
通常情况下,需要传输到从机设备或从从机设备传输的数据将从结构spi_transaction_t
的成员rx_buffer
和tx_buffer
中读取或写入一个内存块。如果传输时启用了DMA,则要求缓冲区必须是。
- 分配在具有DMA功能的内部存储器中。如果外部PSRAM被启用,这意味着使用
pvPortMallocCaps(size, MALLOC_CAP_DMA)
。 - 32位对齐(从32位边界开始盯住,长度为4字节的倍数)。
如果不满足这些要求,由于临时缓冲区的分配和复制,事务效率将受到影响。
如果使用多条数据线传输,请在struct spi_device_interface_config_t
的成员标志中设置SPI_DEVICE_HALFDUPLEX标志。而结构体 spi_transaction_t
中的成员标志应该按照交易行模式中的描述进行设置。
在使用DMA时,不支持同时具有读和写阶段的半双工交易。有关细节和解决方法,请参见 “已知问题”。
总线获取
有时你可能想专门和连续地发送SPI事务,以便尽可能少地花费时间。为此,你可以使用总线获取,这有助于暂停对其他设备的交易(包括轮询或中断),直到总线被释放。要获取和释放总线,请使用函数spi_device_acquire_bus()
和spi_device_release_bus()
。
驱动程序的使用
-
通过调用函数
spi_bus_initialize()
来初始化一个SPI总线。确保在struct 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()
发送交易。
-
-
(可选)要对一个Device执行背对背的交易,在发送交易之前调用函数
spi_device_acquire_bus()
,在交易发送完毕后调用spi_device_release_bus()
。 -
(可选)要卸载某个设备的驱动程序,请调用
spi_bus_remove_device()
,并将设备句柄作为参数。 -
(可选)要移除总线的驱动,确保没有更多的驱动被连接,并调用
spi_bus_free()
。
查看例子:peripherals/spi_master。
数据不超过32位的交易
当交易数据大小等于或小于32位时,为数据分配一个缓冲区将是次优的。数据可以直接存储在事务结构中。对于传输的数据,可以通过使用tx_data
成员并在传输时设置SPI_TRANS_USE_TXDATA
标志来实现。对于接收的数据,使用rx_data
并设置SPI_TRANS_USE_RXDATA
。在这两种情况下,不要碰tx_buffer
或rx_buffer
成员,因为它们与tx_data
和rx_data
使用相同的内存位置。
与uint8_t
以外的整数进行交易
一个SPI主机逐个字节地将数据读入和写入内存。默认情况下,数据是以最有意义的位(MSB)为先发送的,LSB先用在极少数情况下。如果需要发送一个小于8位的值,应该以MSB优先的方式将这些位写进内存。
例如,如果需要发送0b00010
,应将其写入uint8_t变量中,读取的长度应设置为5位。设备仍然会收到8位,其中有3个额外的 "随机 "位,所以必须正确地进行读取。
除此之外,ESP32是一个little-endian芯片,这意味着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 Bus Lock功能使得在SPI1总线上使用SPI Master驱动成为可能,但它仍然很棘手,需要很多特殊处理。这是一个适合高级开发者的功能。
要在SPI1总线上使用SPI Master驱动,你必须注意两个问题。
-
在驱动器操作SPI1总线时,所需的代码和数据应该在内部存储器中。
SPI1总线是由设备和Flash以及PSRAM中的数据(代码)缓存共享的。在其他驱动器操作SPI1总线时,缓存应该被禁用。因此,在驱动程序获取SPI1总线的同时,闪存和PSRAM中的数据(代码)不能被获取。
-
在
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中的const数据,以及PSRAM中的静态/堆数据)将导致缓存禁用但缓存的存储器区域被访问的异常。关于IRAM、DRAM和闪存缓存之间的区别,请参考应用内存布局文档。
要把函数放到IRAM中,你可以这样做:
-
在函数中加入IRAM_ATTR(include “esp_attr.h”),比如:
IRAM_ATTR void foo(void) { }
请注意,当一个函数被内联时,它将跟随其调用者的段,而属性将不生效。你可能需要使用NOLINE_ATTR来避免这种情况。
-
在linker.lf中使用noflash位置。请看更多关于链接器脚本生成机制的内容。请注意,有些代码可能被编译器转化为常量数据中的查找表,所以noflash_text并不安全。
请注意,优化水平可能会影响编译器的内联行为,或将一些代码转化为常量数据中的查找表,等等。
为了将数据放入DRAM,你可以这样做:
-
在数据定义中加入DRAM_ATTR(include “esp_attr.h”),比如:
DRAM_ATTR int g_foo = 3;
-
在linker.lf中使用noflash放置。请看更多关于链接器脚本生成机制的内容。
查看例子:peripherals/spi_master/hd_eeprom。
GPIO矩阵和IO_MUX
ESP32的大多数外设信号都直接连接到其专用的IO_MUX引脚。然而,这些信号也可以通过不太直接的GPIO矩阵被路由到任何其他可用的引脚。如果至少有一个信号是通过GPIO矩阵路由的,那么所有的信号都将通过它路由。
GPIO矩阵引入了路由的灵活性,但也带来了以下的缺点。
增加了MISO信号的输入延迟,这使得MISO设置时间违反的可能性更大。如果SPI需要高速运行,请使用专用的IO_MUX引脚。
允许时钟频率只有40MHz的信号,而如果使用IO_MUX引脚则为80MHz。
关于MISO输入延迟对最大时钟频率的影响的更多细节,请参见 “时间考虑”。
用于SPI总线的IO_MUX引脚在下面给出。
SPI2 | SPI3 | |
---|---|---|
Pin Name | GPIO Number | GPIO Number |
CS0* | 15 | 5 |
SCLK | 14 | 18 |
MISO | 12 | 19 |
MOSI | 13 | 23 |
QUADWP | 2 | 22 |
QUADHD | 4 | 21 |
- 只有连接到总线上的第一个器件可以使用CS0引脚。
传输速度方面的考虑
有三个因素限制了传输速度:
- 交易间隔。
- SPI时钟频率。
- SPI函数的缓存缺失,包括回调。
决定大交易的传输速度的主要参数是时钟频率。对于多个小交易来说,传输速度主要由交易间隔的长度决定。
交易时间
交易持续时间包括设置SPI外围寄存器,将数据复制到FIFO或设置DMA链接,以及SPI交易的时间。
中断交易允许附加额外的开销,以适应FreeRTOS队列的成本以及在任务和ISR之间切换所需的时间。
对于中断交易,CPU可以在交易进行时切换到其他任务。这节省了CPU的时间,但增加了交易的持续时间。参见中断交易。对于轮询交易,它不会阻塞任务,但允许在交易进行中进行轮询。更多信息,请看轮询交易。
如果启用了DMA,设置链接列表需要每个交易约2us。当一个主站在传输数据时,它会自动从链接列表中读取数据。如果没有启用DMA,CPU必须自己从FIFO中写和读每个字节。通常情况下,这要比2个小时快,但是写和读的交易长度都被限制在64字节。
一个字节的数据的典型交易时间如下:
-
通过DMA的中断交易:28µs。
-
通过CPU的中断交易:25µs。
-
通过DMA的轮询交易:10µs。
-
通过CPU的轮询交易:8µs。
SPI时钟频率
传输每个字节需要8倍的时钟周期8/fspi。
如果时钟频率太高,一些功能的使用可能会受到限制。请看时序考虑因素。
缓存缺失
默认配置只将ISR放入IRAM。其他与SPI相关的功能,包括驱动本身和回调,可能会遭受缓存缺失,需要等到代码从flash中读取。选择CONFIG_SPI_MASTER_IN_IRAM可以将整个SPI驱动放入IRAM中,并将整个回调(s)及其被调用函数放入IRAM中以防止缓存丢失。
对于一个中断交易,在一个交易中传输n个字节,总体成本是20+8n/Fspi[MHz] [us]。因此,传输速度是:n/(20+8n/Fspi)。下表给出了8MHz时钟速度下的传输速度的例子。
频率(MHz) | 交易间隔(us) | 交易长度(bytes) | 总时间(us) | 总速度(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操作期间继续发送交易,请启用CONFIG_SPI_MASTER_ISR_IN_IRAM并在成员spi_bus_config_t::intr_flags
中设置ESP_INTR_FLAG_IRAM
。在这种情况下,所有在开始闪存操作前排队的事务都将由ISR并行处理。还要注意的是,每个Device的回调和它们的callee函数都应该在IRAM中,否则你的回调会因为缓存丢失而崩溃。更多细节,请看IRAM安全中断处理程序。
时序考虑因素
如下图所示,在SCLK启动边缘之后和信号被内部寄存器锁定之前,MISO线有一个延迟。因此,MISO引脚的设置时间是SPI时钟速度的限制性因素。当延迟过长时,设置松弛度<0,这意味着违反了设置时间要求,读数可能不正确。
允许的最大频率取决于:
input_delay_ns
- SCLK上一个时钟周期开始后MISO总线上的最大数据有效时间- 如果使用IO_MUX引脚或GPIO矩阵
当使用GPIO矩阵时,与现有的输入延迟相比,最大允许的频率降低到约33~77%。为了保留更高的频率,你必须使用IO_MUX引脚或假位的工作方法。你可以通过使用函数spi_get_freq_limit()
获得主站的最大读取频率。
虚位的解决方法:在读取阶段开始之前,可以插入假时钟,在此期间主机不读取数据。器件仍然可以看到假时钟并发出数据,但主机在读阶段到来之前不会读。这就弥补了主机所需的MISO设置时间的不足,并允许主机以更高的频率进行读取。
在理想的情况下,如果器件的速度非常快,以至于输入延迟短于一个APB时钟周期–12.5ns–那么在不同的条件下,主机可以读(或读和写)的最大频率如下:
GPIO 矩阵 | IO_MUX 引脚 | 驱动器使用的虚拟比特 | 意见 |
---|---|---|---|
26.6 | 80 | No | |
40 | – | Yes | Half-duplex, no DMA allowed |
如果主机只写数据,可以通过设置成员spi_device_interface_config_t::flags
中的位SPI_DEVICE_NO_DUMMY来禁用假位工作法和频率检查。当禁用时,输出频率可以是80MHz,即使使用了GPIO矩阵。
spi_device_interface_config_t::flags
即使结构spi_device_interface_config_t
中的input_delay_ns
被设置为0,SPI主驱动仍然可以工作。 然而,设置一个准确的值有助于:
-
计算全双工交易的频率限制
-
对于半双工事务,用虚拟比特正确补偿时间
你可以通过检查你的Device规格书的AC特性一章中的统计资料,或者使用示波器或逻辑分析仪测量时间,来近似计算出SPI时钟启动边缘后的最大数据有效时间。
请注意,实际的PCB布局设计和过度的负载可能会增加输入延迟。这意味着非最佳布线和/或总线上的负载电容很可能导致输入延迟值超过器件规范中给出的数值或在总线浮动时测量的数值。
一些典型的延迟值显示在下表:(这些数据是在从属设备在不同的物理芯片上时检索的)
设备 | 输入延迟 (ns) |
---|---|
Ideal Device | 0 |
ESP32 slave using IO_MUX* | 50 |
ESP32 slave using GPIO_MUX* | 75 |
MISO路径延迟(有效时间)由从机的输入延迟加上主机的GPIO矩阵延迟组成。这个延迟决定了频率限制,超过这个频率的全双工传输将不能像半双工交易中使用的假比特一样工作。该频率限制为:
F
r
e
q
l
i
m
i
t
[
M
H
z
]
=
80
/
(
f
l
o
o
r
(
M
I
S
O
d
e
l
a
y
[
n
s
]
/
12.5
)
+
1
)
Freq limit [MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)
Freqlimit[MHz]=80/(floor(MISOdelay[ns]/12.5)+1)
下图显示了频率限制和输入延迟之间的关系。如果主站使用GPIO矩阵,应在MISO延迟上增加两个额外的APB时钟周期。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UO1ihz6U-1639189816722)(https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/_images/spi_master_freq_tv.png)]
下表显示了不同设备在不同输入延迟时间下的对应频率限制。
主机 | 输入延迟 (ns) | MISO 路径延迟 (ns) | 频率限制(MHz) |
---|---|---|---|
IO_MUX (0ns) | 0 | 0 | 80 |
IO_MUX (0ns) | 50 | 50 | 16 |
IO_MUX (0ns) | 75 | 75 | 11.43 |
GPIO (25ns) | 0 | 25 | 26.67 |
GPIO (25ns) | 50 | 75 | 11.43 |
GPIO (25ns) | 75 | 100 | 8.89 |
已知问题
-
当同时使用写入和读取阶段时,半双工事务与DMA不兼容。
如果需要这样的交易,你必须使用其中一个替代解决方案:
-
使用全双工事务来代替。
-
通过将总线初始化函数的最后一个参数设置为0来禁用DMA:
ret=spi_bus_initialize(VSPI_HOST, &buscfg, 0)。
这可以禁止你发送和接收超过64字节的数据。
-
试着用命令和地址字段来代替写阶段。
-
-
全双工交易与虚位工作法不兼容,因此频率受到限制。见虚位的解决方法。
-
当SPI读和写阶段都被启用时,
spi_device_interface_config_t
和spi_transaction_ext_t
中的**dummy_bits
**不可用(无论全双工还是半双工模式)。 -
**
cs_ena_pretrans
**与全双工事务的命令和地址阶段不兼容。