diff --git a/docs/en/api-reference/peripherals/spi_slave_hd.rst b/docs/en/api-reference/peripherals/spi_slave_hd.rst index 2f353ea2a1..6aeed965b2 100644 --- a/docs/en/api-reference/peripherals/spi_slave_hd.rst +++ b/docs/en/api-reference/peripherals/spi_slave_hd.rst @@ -1,24 +1,26 @@ SPI Slave Half Duplex ===================== +:link_to_translation:`zh_CN:[中文]` + Introduction ------------ -The half duplex (HD) mode is a special mode provided by ESP SPI Slave peripheral. Under this mode, the hardware provides more services than the full duplex (FD) mode (the mode for general purpose SPI transactions, see :doc:`spi_slave`). These services reduce the CPU load and the response time of SPI Slave, but the communication format is determined by the hardware. The communication format is always half duplex, so comes the name of Half Duplex Mode. +The Half Duplex (HD) Mode is a special mode provided by ESP SPI Slave peripheral. Under this mode, the hardware provides more services than the Full Duplex (FD) Mode (the mode for general-purpose SPI transactions, see :doc:`spi_slave`). These services reduce the CPU load and the response time of SPI Slave. However, it is important to note that the communication format is determined by the hardware and is always in a half-duplex configuration, allowing only one-way data transfer at any given time. Hence, the mode is named Half Duplex Mode due to this characteristic. -There are several different types of transactions, determined by the *command* phase of the transaction. Each transaction may consist of the following phases: command, address, dummy, data. The command phase is mandatory, while the other fields may be determined by the command field. During the command, address, dummy phases, the bus is always controlled by the master, while the direction of the data phase depends on the command. The data phase can be either an in phase, for the master to write data to the slave; or an out phase, for the master to read data from the slave. +When conducting an SPI transaction, transactions can be classified into several types based on the **command** phase of the transaction. Each transaction may consist of the following phases: command, address, dummy, and data. The command phase is mandatory, while the other phases may be determined by the command field. During the command, address, and dummy phases, the bus is always controlled by the master (usually the host), while the direction of the data phase depends on the command. The data phase can be either an input phase, where the master writes data to the slave (e.g., the host sends data to the slave), or an output phase, where the master reads data from the slave (e.g., the host receives data from the slave). Protocol ^^^^^^^^ About the details of how master should communicate with the SPI Slave, see :doc:`/api-reference/protocols/esp_spi_slave_protocol`. -By these different transactions, the slave provide these services to the master: +Through these different transactions, the slave provides these services to the master: - A DMA channel for the master to write a great amount of data to the slave. - A DMA channel for the master to read a great amount of data from the slave. -- Several general purpose registers, shard between the master and the slave. -- Several general purpose interrupts, for the master to interrupt the SW of slave. +- Several general purpose registers, shared between the master and the slave. +- Several general purpose interrupts, for the master to interrupt the SW of the slave. Terminology ----------- @@ -36,61 +38,68 @@ Driver Feature - Queues for data to send and received -Driver usage +Driver Usage ------------ -Slave initialization +Slave Initialization ^^^^^^^^^^^^^^^^^^^^ -Call :cpp:func:`spi_slave_hd_init` to initialize the SPI bus as well as the peripheral and the driver. The SPI slave will exclusively use the SPI peripheral, pins of the bus before it's deinitialized. Most configurations of the slave should be done as soon as the slave is being initialized. +Call :cpp:func:`spi_slave_hd_init` to initialize the SPI bus as well as the peripheral and the driver. The SPI Slave exclusively uses the SPI peripheral, pins of the bus before it is deinitialized, which means other devices are unable to use the above resources during initialization. Thus, to ensure SPI resources are correctly occupied and the connections work properly, most configurations of the slave should be done as soon as the slave is initialized. The :cpp:type:`spi_bus_config_t` specifies how the bus should be initialized, while :cpp:type:`spi_slave_hd_slot_config_t` specifies how the SPI Slave driver should work. -Deinitialization (optional) +Deinitialization (Optional) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Call :cpp:func:`spi_slave_hd_deinit` to uninstall the driver. The resources, including the pins, SPI peripheral, internal memory used by the driver, interrupt sources, will be released by the deinit function. +Call :cpp:func:`spi_slave_hd_deinit` to uninstall the driver. The resources, including the pins, SPI peripheral, internal memory used by the driver, and interrupt sources, are released by the ``deinit()`` function. Send/Receive Data by DMA Channels ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To send data to the master through the sending DMA channel, the application should properly wrap the data to send by a :cpp:type:`spi_slave_hd_data_t` descriptor structure before calling :cpp:func:`spi_slave_hd_queue_trans` with the data descriptor, and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. The pointers to descriptors are stored in the queue, and the data will be send to the master upon master's RDDMA command in the same order they are put into the queue by :cpp:func:`spi_slave_hd_queue_trans`. +To send data to the master through the sending DMA channel, the application should properly wrap the data in an :cpp:type:`spi_slave_hd_data_t` descriptor structure before calling :cpp:func:`spi_slave_hd_queue_trans` with the data descriptor and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. The pointers to descriptors are stored in the queue, and the data is sent to the master in the same order they are enqueued using :cpp:func:`spi_slave_hd_queue_trans`, upon receiving the master's ``Rd_DMA`` command. -The application should check the result of data sending by calling :cpp:func:`spi_slave_hd_get_trans_res` with the channel set as :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. This function will block until the transaction with command RDDMA from master successfully completes (or timeout). The ``out_trans`` argument of the function will output the pointer of the data descriptor which is just finished. +The application should check the result of data sending by calling :cpp:func:`spi_slave_hd_get_trans_res` with the channel set as :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. This function blocks until the transaction with the command ``Rd_DMA`` from the master successfully completes (or timeout). The ``out_trans`` argument of the function outputs the pointer of the data descriptor which is just finished, providing information about the sending. -Receiving data from the master through the receiving DMA channel is quite similar. The application calls :cpp:func:`spi_slave_hd_queue_trans` with proper data descriptor and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_RX`. And the application calls the :cpp:func:`spi_slave_hd_get_trans_res` later to get the descriptor to the receiving buffer, before it handles the data in the receiving buffer. +Receiving data from the master through the receiving DMA channel is quite similar. The application calls :cpp:func:`spi_slave_hd_queue_trans` with proper data descriptor and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_RX`. And the application calls the :cpp:func:`spi_slave_hd_get_trans_res` later to get the descriptor to the receiving buffer before it handles the data in the receiving buffer. .. note:: - This driver itself doesn't have internal buffer for the data to send, or just received. The application should provide data descriptors for the data buffer to send to master, or to receive data from the master. - The application will have to properly keep the data descriptor as well as the buffer it points to, after the descriptor is successfully sent into the driver internal queue by :cpp:func:`spi_slave_hd_queue_trans`, and before returned by :cpp:func:`spi_slave_hd_get_trans_res`. During this period, the hardware as well as the driver may read or write to the buffer and the descriptor when required at any time. + This driver itself does not have an internal buffer for the data to send or just received. The application should provide data buffer for driver via data descriptors to send to the master, or to receive data from the master. -Please note that the buffer doesn't have to be fully sent or filled before it's terminated. For example, in the segment transaction mode, the master has to send CMD7 to terminate a WRDMA transaction, or send CMD8 to terminate a RDDMA transaction (in segments), no matter the send (receive) buffer is used up (full) or not. + The application has to properly keep the data descriptor as well as the buffer it points, after the descriptor is successfully sent into the driver internal queue by :cpp:func:`spi_slave_hd_queue_trans`, and before returned by :cpp:func:`spi_slave_hd_get_trans_res`. During this period, the hardware as well as the driver may read or write to the buffer and the descriptor when required at any time. + +Please note that, when using this driver for data transfer, the buffer does not have to be fully sent or filled before it is terminated. For example, in the segment transaction mode, the master has to send ``CMD7`` to terminate a ``Wr_DMA`` transaction or send ``CMD8`` to terminate an ``Rd_DMA`` transaction (in segments), no matter whether the send (receive) buffer is used up (full) or not. .. _spi_slave_hd_data_arguments: -Using Data Arguments -^^^^^^^^^^^^^^^^^^^^ +Using Data Descriptor with Customized User Arguments +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Sometimes you may have initiator (sending data descriptor) and closure (handling returned descriptors) functions in different places. When you get the returned data descriptor in the closure, you may need some extra information when handle the finished data descriptor. For example, you may want to know which round it is for the returned descriptor, when you send the same piece of data for several times. +Sometimes you may have initiator (sending data descriptor) and closure (handling returned descriptors) functions in different places. When you get the returned data descriptor in the closure, you may need some extra information when handling the finished data descriptor. For example, you may want to know which round it is for the returned descriptor when you send the same piece of data several times. -Set the ``arg`` member in the data descriptor to an variable indicating the transaction (by force casting), or point it to a a structure which wraps all the information you may need when handling the sending/receiving data. Then you can get what you need in your closure. +Set the ``arg`` member in the data descriptor to a variable indicating the transaction by force casting, or point it to a structure that wraps all the information you may need when handling the sending/receiving data. Then you can get what you need in your closure. .. _spi_slave_hd_callbacks: -Using callbacks +Using Callbacks ^^^^^^^^^^^^^^^ .. note:: - These callbacks are called in the ISR, so that they are fast enough. However, you may need to be very careful to write the code in the ISR. The callback should return as soon as possible. No delay or blocking operations are allowed. -The :cpp:type:`spi_slave_hd_intr_config_t` member in the :cpp:type:`spi_slave_hd_slot_config_t` configuration structure passed when initialize the SPI Slave HD driver, allows you having callbacks for each events you may concern. + These callbacks are called in the ISR, so the required operations need to be processed quickly and returned as soon as possible to ensure that the system is functioning properly. You may need to be very careful to write the code in the ISR. -The corresponding interrupt for each callbacks that is not *NULL* will enabled, so that the callbacks can be called immediately when the events happen. You don't need to provide callbacks for the unconcerned events. + Since the interrupt handling is executed concurrently with the application, long delays or blocking may cause the system to respond slower or lead to unpredictable behavior. Therefore, when writing callback functions, avoid using operations that may cause delays or blocking, e.g., waiting, sleeping, resource locking, etc. -The ``arg`` member in the configuration structure can help you pass some context to the callback, or indicate which SPI Slave instance when you are using the same callbacks for several SPI Slave peripherals. Set the ``arg`` member to an variable indicating the SPI Slave instance (by force casting), or point it to a context structure. All the callbacks will be called with this ``arg`` argument you set when the callbacks are initialized. +The :cpp:type:`spi_slave_hd_callback_config_t` member in the :cpp:type:`spi_slave_hd_slot_config_t` configuration structure passed when initializing the SPI Slave HD driver, allows you to have callbacks for each event you may concern. -There are two other arguments: the ``event`` and the ``awoken``. The ``event`` passes the information of the current event to the callback. The :cpp:type:`spi_slave_hd_event_t` type contains the information of the event, for example, event type, the data descriptor just finished (The :ref:`data argument ` will be very useful in this case!). The ``awoken`` argument is an output one, telling the ISR there are tasks are awoken after this callback, and the ISR should call `portYIELD_FROM_ISR()` to do task scheduling. Just pass the ``awoken`` argument to all FreeRTOS APIs which may unblock tasks, and the awoken will be returned to the ISR. +The corresponding interrupt for each callback that is not **NULL** is enabled, so that the callbacks can be called immediately when the events happen. You do not need to provide callbacks for the unconcerned events. + +The ``arg`` member in the configuration structure can help you pass some context to the callback or indicate the specific SPI Slave instance when using the same callbacks for multiple SPI Slave peripherals. You can set the arg member to a variable that indicates the SPI Slave instance by performing a forced type casting or point it to a context structure. All the callbacks are called with this ``arg`` argument you set when the callbacks are initialized. + +There are two other arguments: the ``event`` and the ``awoken``. + + - The ``event`` passes the information of the current event to the callback. The :cpp:type:`spi_slave_hd_event_t` type contains the information of the event, for example, event type, the data descriptor just finished (The :ref:`data argument ` is very useful in this case!). + - The ``awoken`` argument serves as an output parameter. It informs the ISR that tasks have been awakened after the callback function, and the ISR should call `portYIELD_FROM_ISR()` to schedule these tasks. Simply pass the ``awoken`` argument to all FreeRTOS APIs that may unblock tasks, and the value of ``awoken`` will be returned to the ISR. Writing/Reading Shared Registers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -98,18 +107,19 @@ Writing/Reading Shared Registers Call :cpp:func:`spi_slave_hd_write_buffer` to write the shared buffer, and :cpp:func:`spi_slave_hd_read_buffer` to read the shared buffer. .. note:: - On {IDF_TARGET_NAME}, the shared registers are read/written in words by the application, but read/written in bytes by the master. There's no guarantee four continuous bytes read from the master are from the same word written by the slave's application. It's also possible that if the slave reads a word while the master is writing bytes of the word, the slave may get one word with half of them just written by the master, and the other half hasn't been written into. + + On {IDF_TARGET_NAME}, the shared registers are read/written in words by the application but read/written in bytes by the master. There is no guarantee four continuous bytes read from the master are from the same word written by the slave's application. It is also possible that if the slave reads a word while the master is writing bytes of the word, the slave may get one word with half of them just written by the master, and the other half has not been written into. The master can confirm that the word is not in transition by reading the word twice and comparing the values. - For the slave, it will be more difficult to ensure the word is not in transition because the process of master writing four bytes can be very long (32 SPI clocks). You can put some CRC in the last (largest address) byte of a word so that when the byte is written, the word is sure to be all written. + For the slave, it is more difficult to ensure the word is not in transition because the process of master writing four bytes can be very long (32 SPI clocks). You can put some CRC in the last (largest address) byte of a word so that when the byte is written, the word is sure to be all written. - Due to the conflicts there may be among read/write from SW (worse if there are multi cores) and master, it is suggested that a word is only used in one direction (only written by master or only written by the slave). + Due to the conflicts that may be among read/write from SW (worse if there are multi-cores) and master, it is suggested that a word is only used in one direction (only written by the master or only written by the slave). -Receiving General Purpose Interrupts From the Master +Receiving General Purpose Interrupts from the Master ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When the master sends CMD 0x08, 0x09 or 0x0A, the slave corresponding will be triggered. Currently the CMD8 is permanently used to indicate the termination of RDDMA segments. To receiving general purpose interrupts, register callbacks for CMD 0x09 and 0x0A when the slave is initialized, see :ref:`spi_slave_hd_callbacks`. +When the master sends ``CMD8``, ``CMD9`` or ``CMDA``, the slave corresponding is triggered. Currently the ``CMD8`` is permanently used to indicate the termination of ``Rd_DMA`` segments. To receive general-purpose interrupts, register callbacks for ``CMD9`` and ``CMDA`` when the slave is initialized, see :ref:`spi_slave_hd_callbacks`. Application Example @@ -118,7 +128,7 @@ Application Example The code example for Device/Host communication can be found in the :example:`peripherals/spi_slave_hd` directory of ESP-IDF examples. -API reference +API Reference ------------- .. include-build-file:: inc/spi_slave_hd.inc \ No newline at end of file diff --git a/docs/zh_CN/api-reference/peripherals/spi_slave_hd.rst b/docs/zh_CN/api-reference/peripherals/spi_slave_hd.rst index ae61c273d3..4a860a88b5 100644 --- a/docs/zh_CN/api-reference/peripherals/spi_slave_hd.rst +++ b/docs/zh_CN/api-reference/peripherals/spi_slave_hd.rst @@ -1 +1,134 @@ -.. include:: ../../../en/api-reference/peripherals/spi_slave_hd.rst \ No newline at end of file +SPI 从机半双工模式 +===================== + +:link_to_translation:`en:[English]` + +简介 +------------ + +半双工 (HD) 模式是 ESP SPI 从机外设提供的一种特殊模式。比起全双工 (FD) 模式(用于 GPSPI 事务,详情请参阅 :doc:`spi_slave`),在半双工模式下,硬件会提供更多服务。这些服务减轻了 CPU 负载,提高了 SPI 从机的响应速度。然而,通信格式由硬件确定,始终为半双工模式,即在同一时刻只能进行单向数据传输,半双工模式因此得名。 + +当进行 SPI 事务时,根据事务的 **命令** 阶段,可以将事务分为不同的类型。每个事务可能包含以下阶段:命令、地址、dummy、数据。命令阶段是必需的,其他阶段存在与否则取决于命令的需求。在命令、地址、dummy 阶段,总线的控制权始终由主设备(通常是主机)控制,数据阶段的方向则取决于命令。数据阶段可以是输入阶段,即主设备将数据写入从设备(例如,主机向从机发送数据);也可以是输出阶段,即主设备从从设备读取数据(例如,主机从从机接收数据)。 + +协议 +^^^^^^^^ + +有关主设备与 SPI 从机通信的详细信息,请参阅 :doc:`/api-reference/protocols/esp_spi_slave_protocol`。 + +通过不同类型的事务,从设备为主设备提供以下服务: + +- 一个 DMA 通道,支持主设备向从设备写入大量数据。 +- 一个 DMA 通道,支持主设备从从设备读取大量数据。 +- 一些通用寄存器,由主设备和从设备共享。 +- 一些通用中断,用于主设备打断从设备的软件执行。 + +术语 +----------- + +- 事务 (transaction) +- 通道 (channel) +- 发送 (sending) +- 接收 (receiving) +- 数据描述符 (data descriptor) + +驱动程序特性 +-------------- + +- 支持主设备以分段方式读写事务 + +- 支持发送数据队列和接收数据队列 + +驱动程序使用 +------------ + +从机初始化 +^^^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`spi_slave_hd_init` 初始化 SPI 总线、外设和驱动程序。在初始化之后,SPI 从机将独占使用 SPI 外设和总线上的管脚。这意味着在反初始化前,其他设备无法使用这些资源。因此,为了确保其他设备可以正确利用 SPI 资源并进行正常通信,从机的大部分配置应在其初始化过程中完成。 + +结构体 :cpp:type:`spi_bus_config_t` 指定了总线的初始化方式,结构体 :cpp:type:`spi_slave_hd_slot_config_t` 指定了 SPI 从机驱动程序的运行方式。 + +从机反初始化(可选) +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`spi_slave_hd_deinit` 卸载驱动程序。该反初始化函数会释放相关资源,包括管脚、SPI 外设、驱动程序所用内存、中断源等。 + +通过 DMA 通道发送/接收数据 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +要通过 DMA 通道向主设备发送数据,应用程序需要先将数据正确地封装在 :cpp:type:`spi_slave_hd_data_t` 描述符结构体中,然后再将数据描述符和通道参数 :cpp:enumerator:`SPI_SLAVE_CHAN_TX` 传递给 :cpp:func:`spi_slave_hd_queue_trans`。数据描述符的指针存储在队列中,一旦接收到主设备的 Rd_DMA 命令,就会按照调用 :cpp:func:`spi_slave_hd_queue_trans` 时数据进入队列的顺序,依次将数据发送给主设备。 + +应用程序需要检查数据发送的结果。为此,应用程序可以调用 :cpp:func:`spi_slave_hd_get_trans_res`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_TX`。该函数将阻塞程序,直到主设备发起的 Rd_DMA 命令事务成功完成或超时。函数中的参数 ``out_trans`` 将输出刚刚完成的数据描述符的指针,从而提供有关已完成的发送操作的信息。 + +通过 DMA 通道从主设备接收数据的操作与发送数据类似。应用程序需要使用正确的数据描述符调用 :cpp:func:`spi_slave_hd_queue_trans`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_RX`。随后,应用程序调用 :cpp:func:`spi_slave_hd_get_trans_res` 获取接收 buffer 的描述符,然后处理接收 buffer 中的数据。 + +.. note:: + + 驱动程序本身并没有用于发送或接收数据的内部 buffer。应用程序需要通过数据描述符为驱动程序提供 buffer,从而向主设备发送数据,或接收来自主设备的数据。 + + 在使用 :cpp:func:`spi_slave_hd_queue_trans` 将数据描述符成功发送到驱动程序的内部队列后、并由 :cpp:func:`spi_slave_hd_get_trans_res` 返回前,应用程序需要正确地维护数据描述符以及它所指向的 buffer。在此期间,根据需要,硬件和驱动程序可以随时读取或写入 buffer 和描述符。 + +注意,在使用该驱动程序进行数据传输时,可以根据实际需要提前终止数据传输,而不需要等待整个 buffer 填满或者完全发送完毕。例如,在分段事务模式下,无论发送/接收 buffer 是否已使用完(已满),主设备都需要发送 ``CMD7`` 终止 ``Wr_DMA`` 事务,或发送 ``CMD8`` 以分段方式终止 ``Rd_DMA`` 事务。 + +.. _spi_slave_hd_data_arguments: + +以自定义用户参数使用数据描述符 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +在某些情况下,发送包函数和回收包函数可能会分散在不同位置。发送包函数用于发送数据描述符,回收包函数用于处理返回的数据描述符。在回收包函数中获取返回的数据描述符时,可能需要一些额外信息,帮助处理数据传输完成后返回给应用程序的描述符。例如,多次发送相同数据时,你可能想知道返回的描述符来自哪一轮发送。 + +为此,可以通过强制类型转换,将数据描述符中的 ``arg`` 设置为变量,提供事务信息;或者将其指向一个包含处理发送/接收数据所需的所有信息的结构体。在回收包函数处理返回的描述符时,即可使用这个额外信息。 + +.. _spi_slave_hd_callbacks: + +使用回调函数 +^^^^^^^^^^^^^^^ + +.. note:: + + 这些回调函数在 ISR 中调用,因此需要迅速处理所需操作,并尽快返回,确保系统正常运行。因此,在编写 ISR 的代码时,需要十分谨慎。 + + 由于中断处理过程是与主程序并发执行的,长时间的延迟或阻塞操作可能会导致系统响应变慢,或导致不可预测的行为。因此,在编写回调函数时,应避免使用可能引起延迟或阻塞的操作,例如等待、睡眠、资源锁等。 + +在初始化 SPI 从机半双工驱动程序时,会传递结构体 :cpp:type:`spi_slave_hd_slot_config_t` 中的 :cpp:type:`spi_slave_hd_callback_config_t`,为任意事件设置回调函数。 + +每个不为 **NULL** 的回调函数都将使能对应的中断,所以回调函数会在对应的中断事件触发时立即调用。对于不感兴趣的事件,则无需为其提供回调函数。 + +配置结构体中的 ``arg`` 可以给回调函数传递部分上下文信息,或在使用相同的回调函数处理多个 SPI 从机外设时,指明特定的 SPI 从机实例。通过强制类型转换,可以将 ``arg`` 设置为表示 SPI 从机实例的变量,或者将其指向某个上下文结构体变量。所有回调函数都会使用在初始化回调函数时设置的 ``arg`` 参数。 + +配置结构体中的 ``event`` 和 ``awoken`` 参数也可以给回调函数传递上下文信息。 + + - 参数 ``event`` 向回调函数传递当前事件信息。:cpp:type:`spi_slave_hd_event_t` 包含事件类型和刚刚处理完的数据描述符等信息,此时,通常会使用 :ref:`data argument `。 + - 参数 ``awoken`` 是一个输出参数,用于告知 ISR,在回调函数后已有其他操作唤醒任务,ISR 应调用 `portYIELD_FROM_ISR()` 调度这些任务。只需将 ``awoken`` 参数传递给可能解除任务阻塞的 FreeRTOS API,ISR 即可接收 ``awoken`` 的返回值。 + +写入/读取共享寄存器 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`spi_slave_hd_write_buffer` 写入共享 buffer,调用 :cpp:func:`spi_slave_hd_read_buffer` 读取共享 buffer。 + +.. note:: + + 在 {IDF_TARGET_NAME} 上,应用程序以字为单位读取/写入共享寄存器,但主机以字节为单位读取/写入共享寄存器。这样一来,就无法确保从主机读取的四个连续字节是来自从机应用程序写入的同一个字。同时,如果从机在主机写入字节时读取了一个字,可能会得到这样的字:主机刚刚写入它的一半,另一半尚未写入。 + + 通过两次读取同个字,并对两次读取的值做比较,主机可以确保读取的字处于非过渡态。 + + 对从机而言,要确保读取的字处于非过渡态则更为困难,因为主机写入四个字节的过程可能会非常长,达到 32 个 SPI 时钟周期。为此,可以在写入的字的最后一个(最大地址)字节中添加一些冗余校验码 (CRC),确保在写入含有 CRC 的字节时,即代表整个字完全写入。 + + 从软件读取/写入和从主机读取/写入可能存在冲突,在多核心系统中尤为如此。因此,建议在数据传输过程中,一个字只在一个方向上使用,即要么只由主机写入,要么只由从机写入。 + +接收来自主机的通用中断 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +当主机发送 ``CMD8``、``CMD9`` 或 ``CMDA`` 时,从机会触发相应的动作。目前,``CMD8`` 固定用于指示 ``Rd_DMA`` 段的终止。要接收通用中断,可以在从机初始化时为 ``CMD9`` 和 ``CMDA`` 注册回调函数,详情请参阅 :ref:`spi_slave_hd_callbacks`。 + + +应用示例 +------------------- + +查看从机设备/主机通信的示例代码,请前往 ESP-IDF 示例的 :example:`peripherals/spi_slave_hd` 目录。 + + +API 参考 +------------- + +.. include-build-file:: inc/spi_slave_hd.inc \ No newline at end of file