mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/spi_little_endien' into 'master'
feature(spi): provide macro to write multi-byte data straightly See merge request idf/esp-idf!2634
This commit is contained in:
commit
52f4ff6220
@ -31,6 +31,34 @@ extern "C"
|
||||
//Maximum amount of bytes that can be put in one DMA descriptor
|
||||
#define SPI_MAX_DMA_LEN (4096-4)
|
||||
|
||||
/**
|
||||
* 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``.
|
||||
*
|
||||
* @param data Data to be sent, can be uint8_t, uint16_t or uint32_t. @param
|
||||
* len Length of data to be sent, since the SPI peripheral sends from the MSB,
|
||||
* this helps to shift the data to the MSB.
|
||||
*/
|
||||
#define SPI_SWAP_DATA_TX(data, len) __builtin_bswap32((uint32_t)data<<(32-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);
|
||||
*
|
||||
* @param data Data to be rearranged, can be uint8_t, uint16_t or uint32_t.
|
||||
* @param len Length of data received, since the SPI peripheral writes from
|
||||
* the MSB, this helps to shift the data to the LSB.
|
||||
*/
|
||||
#define SPI_SWAP_DATA_RX(data, len) (__builtin_bswap32(data)>>(32-len))
|
||||
|
||||
/**
|
||||
* @brief Enum with the three SPI peripherals that are software-accessible in it
|
||||
@ -45,7 +73,7 @@ typedef enum {
|
||||
* @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
|
||||
* 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.
|
||||
@ -81,20 +109,20 @@ bool spicommon_periph_free(spi_host_device_t host);
|
||||
|
||||
/**
|
||||
* @brief Try to claim a SPI DMA channel
|
||||
*
|
||||
*
|
||||
* Call this if your driver wants to use SPI with a DMA channnel.
|
||||
*
|
||||
*
|
||||
* @param dma_chan channel to claim
|
||||
*
|
||||
*
|
||||
* @return True if success; false otherwise.
|
||||
*/
|
||||
bool spicommon_dma_chan_claim(int dma_chan);
|
||||
|
||||
/**
|
||||
* @brief Return the SPI DMA channel so other driver can claim it, or just to power down DMA.
|
||||
*
|
||||
*
|
||||
* @param dma_chan channel to return
|
||||
*
|
||||
*
|
||||
* @return True if success; false otherwise.
|
||||
*/
|
||||
bool spicommon_dma_chan_free(int dma_chan);
|
||||
@ -113,7 +141,7 @@ bool spicommon_dma_chan_free(int dma_chan);
|
||||
* @brief Connect a SPI peripheral to GPIO pins
|
||||
*
|
||||
* This routine is used to connect a SPI peripheral to the IO-pads and DMA channel given in
|
||||
* the arguments. Depending on the IO-pads requested, the routing is done either using the
|
||||
* the arguments. Depending on the IO-pads requested, the routing is done either using the
|
||||
* IO_mux or using the GPIO matrix.
|
||||
*
|
||||
* @param host SPI peripheral to be routed
|
||||
@ -123,7 +151,7 @@ bool spicommon_dma_chan_free(int dma_chan);
|
||||
* - ``SPICOMMON_BUSFLAG_MASTER``: Initialize I/O in master mode
|
||||
* - ``SPICOMMON_BUSFLAG_SLAVE``: Initialize I/O in slave mode
|
||||
* - ``SPICOMMON_BUSFLAG_NATIVE_PINS``: Pins set should match the iomux pins of the controller.
|
||||
* - ``SPICOMMON_BUSFLAG_SCLK``, ``SPICOMMON_BUSFLAG_MISO``, ``SPICOMMON_BUSFLAG_MOSI``:
|
||||
* - ``SPICOMMON_BUSFLAG_SCLK``, ``SPICOMMON_BUSFLAG_MISO``, ``SPICOMMON_BUSFLAG_MOSI``:
|
||||
* Make sure SCLK/MISO/MOSI is/are set to a valid GPIO. Also check output capability according to the mode.
|
||||
* - ``SPICOMMON_BUSFLAG_DUAL``: Make sure both MISO and MOSI are output capable so that DIO mode is capable.
|
||||
* - ``SPICOMMON_BUSFLAG_WPHD`` Make sure WP and HD are set to valid output GPIOs.
|
||||
@ -136,7 +164,7 @@ bool spicommon_dma_chan_free(int dma_chan);
|
||||
* - ``SPICOMMON_BUSFLAG_DUAL``: The bus is capable with DIO mode.
|
||||
* - ``SPICOMMON_BUSFLAG_WPHD`` The bus has WP and HD connected.
|
||||
* - ``SPICOMMON_BUSFLAG_QUAD``: Combination of ``SPICOMMON_BUSFLAG_DUAL`` and ``SPICOMMON_BUSFLAG_WPHD``.
|
||||
* @return
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
@ -145,10 +173,10 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
|
||||
/**
|
||||
* @brief Free the IO used by a SPI peripheral
|
||||
* @deprecated Use spicommon_bus_free_io_cfg instead.
|
||||
*
|
||||
*
|
||||
* @param host SPI peripheral to be freed
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
@ -158,8 +186,8 @@ esp_err_t spicommon_bus_free_io(spi_host_device_t host) __attribute__((deprecate
|
||||
* @brief Free the IO used by a SPI peripheral
|
||||
*
|
||||
* @param bus_cfg Bus config struct which defines which pins to be used.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @return
|
||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
@ -240,10 +268,10 @@ typedef void(*dmaworkaround_cb_t)(void *arg);
|
||||
* @note In some (well-defined) cases in the ESP32 (at least rev v.0 and v.1), a SPI DMA channel will get confused. This can be remedied
|
||||
* by resetting the SPI DMA hardware in case this happens. Unfortunately, the reset knob used for thsi will reset _both_ DMA channels, and
|
||||
* as such can only done safely when both DMA channels are idle. These functions coordinate this.
|
||||
*
|
||||
*
|
||||
* Essentially, when a reset is needed, a driver can request this using spicommon_dmaworkaround_req_reset. This is supposed to be called
|
||||
* with an user-supplied function as an argument. If both DMA channels are idle, this call will reset the DMA subsystem and return true.
|
||||
* If the other DMA channel is still busy, it will return false; as soon as the other DMA channel is done, however, it will reset the
|
||||
* with an user-supplied function as an argument. If both DMA channels are idle, this call will reset the DMA subsystem and return true.
|
||||
* If the other DMA channel is still busy, it will return false; as soon as the other DMA channel is done, however, it will reset the
|
||||
* DMA subsystem and call the callback. The callback is then supposed to be used to continue the SPI drivers activity.
|
||||
*
|
||||
* @param dmachan DMA channel associated with the SPI host that needs a reset
|
||||
|
@ -414,7 +414,7 @@ esp_err_t spi_bus_remove_device(spi_device_handle_t handle)
|
||||
SPI_CHECK(handle->host->cur_cs == NO_CS || handle->host->device[handle->host->cur_cs]!=handle, "Have unfinished transactions", ESP_ERR_INVALID_STATE);
|
||||
SPI_CHECK(uxQueueMessagesWaiting(handle->ret_queue)==0, "Have unfinished transactions", ESP_ERR_INVALID_STATE);
|
||||
|
||||
//return
|
||||
//return
|
||||
int spics_io_num = handle->cfg.spics_io_num;
|
||||
if (spics_io_num >= 0) spicommon_cs_free_io(spics_io_num);
|
||||
|
||||
@ -727,9 +727,12 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg)
|
||||
host->hw->user.usr_addr=addrlen?1:0;
|
||||
host->hw->user.usr_command=cmdlen?1:0;
|
||||
|
||||
// output command will be sent from bit 7 to 0 of command_value, and then bit 15 to 8 of the same register field.
|
||||
uint16_t command = trans->cmd << (16-cmdlen); //shift to MSB
|
||||
host->hw->user2.usr_command_value = (command>>8)|(command<<8); //swap the first and second byte
|
||||
/* Output command will be sent from bit 7 to 0 of command_value, and
|
||||
* then bit 15 to 8 of the same register field. Shift and swap to send
|
||||
* more straightly.
|
||||
*/
|
||||
host->hw->user2.usr_command_value = SPI_SWAP_DATA_TX(trans->cmd, cmdlen);
|
||||
|
||||
// shift the address to MSB of addr (and maybe slv_wr_status) register.
|
||||
// output address will be sent from MSB to LSB of addr register, then comes the MSB to LSB of slv_wr_status register.
|
||||
if (addrlen>32) {
|
||||
|
@ -167,12 +167,35 @@ memcpy of temporary buffers.
|
||||
.. note:: Half duplex transactions with both read and write phases are not supported when using DMA. See
|
||||
:ref:`spi_known_issues` for details and workarounds.
|
||||
|
||||
Sometimes, the amount of data is very small making it less than optimal allocating a separate buffer
|
||||
for it. If the data to be transferred is 32 bits or less, it can be stored in the transaction struct
|
||||
itself. For transmitted data, use the ``tx_data`` member for this and set the ``SPI_USE_TXDATA`` flag
|
||||
on the transmission. For received data, use ``rx_data`` and set ``SPI_USE_RXDATA``. In both cases, do
|
||||
not touch the ``tx_buffer`` or ``rx_buffer`` members, because they use the same memory locations
|
||||
as ``tx_data`` and ``rx_data``.
|
||||
Tips
|
||||
""""
|
||||
|
||||
1. Transactions with small amount of data:
|
||||
Sometimes, the amount of data is very small making it less than optimal allocating a separate buffer
|
||||
for it. If the data to be transferred is 32 bits or less, it can be stored in the transaction struct
|
||||
itself. For transmitted data, use the ``tx_data`` member for this and set the ``SPI_USE_TXDATA`` flag
|
||||
on the transmission. For received data, use ``rx_data`` and set ``SPI_USE_RXDATA``. In both cases, do
|
||||
not touch the ``tx_buffer`` or ``rx_buffer`` members, because they use the same memory locations
|
||||
as ``tx_data`` and ``rx_data``.
|
||||
|
||||
2. Transactions with integers other than uint8_t
|
||||
The SPI peripheral reads and writes the memory byte-by-byte. By default,
|
||||
the SPI works at MSB first mode, each bytes are sent or received from the
|
||||
MSB to the LSB. However, if you want to send data with length which is
|
||||
not multiples of 8 bits, unused bits are sent.
|
||||
|
||||
E.g. you write ``uint8_t data = 0x15`` (00010101B), and set length to
|
||||
only 5 bits, the sent data is ``00010B`` rather than expected ``10101B``.
|
||||
|
||||
Moreover, ESP32 is a little-endian chip whose lowest byte is stored at
|
||||
the very beginning address for uint16_t and uint32_t variables. Hence if
|
||||
a uint16_t is stored in the memory, it's bit 7 is first sent, then bit 6
|
||||
to 0, then comes its bit 15 to bit 8.
|
||||
|
||||
To send data other than uint8_t arrays, macros ``SPI_SWAP_DATA_TX`` is
|
||||
provided to shift your data to the MSB and swap the MSB to the lowest
|
||||
address; while ``SPI_SWAP_DATA_RX`` can be used to swap received data
|
||||
from the MSB to it's correct place.
|
||||
|
||||
Speed and Timing Considerations
|
||||
-------------------------------
|
||||
@ -286,10 +309,10 @@ And if the host only writes, the *dummy bit workaround* is not used and the freq
|
||||
| 40 | 80 |
|
||||
+-------------------+------------------+
|
||||
|
||||
The spi master driver can work even if the *input delay* in the ``spi_device_interface_config_t`` is set to 0.
|
||||
However, setting a accurate value helps to: (1) calculate the frequency limit in full duplex mode, and (2) compensate
|
||||
the timing correctly by dummy bits in half duplex mode. You may find the maximum data valid time after the launch edge
|
||||
of SPI clocks in the AC characteristics chapter of the device specifications, or measure the time on a oscilloscope or
|
||||
The spi master driver can work even if the *input delay* in the ``spi_device_interface_config_t`` is set to 0.
|
||||
However, setting a accurate value helps to: (1) calculate the frequency limit in full duplex mode, and (2) compensate
|
||||
the timing correctly by dummy bits in half duplex mode. You may find the maximum data valid time after the launch edge
|
||||
of SPI clocks in the AC characteristics chapter of the device specifications, or measure the time on a oscilloscope or
|
||||
logic analyzer.
|
||||
|
||||
.. wavedrom don't support rendering pdflatex till now(1.3.1), so we use the png here
|
||||
@ -327,18 +350,18 @@ Some typical delays are shown in the following table:
|
||||
| chip, 12.5ns sample delay included. |
|
||||
+---------------------------------------+
|
||||
|
||||
The MISO path delay(tv), consists of slave *input delay* and master *GPIO matrix delay*, finally determines the
|
||||
frequency limit, above which the full duplex mode will not work, or dummy bits are used in the half duplex mode. The
|
||||
The MISO path delay(tv), consists of slave *input delay* and master *GPIO matrix delay*, finally determines the
|
||||
frequency limit, above which the full duplex mode will not work, or dummy bits are used in the half duplex mode. The
|
||||
frequency limit is:
|
||||
|
||||
*Freq limit[MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)*
|
||||
|
||||
The figure below shows the relations of frequency limit against the input delay. 2 extra apb clocks should be counted
|
||||
The figure below shows the relations of frequency limit against the input delay. 2 extra apb clocks should be counted
|
||||
into the MISO delay if the GPIO matrix in the master is used.
|
||||
|
||||
.. image:: /../_static/spi_master_freq_tv.png
|
||||
|
||||
Corresponding frequency limit for different devices with different *input delay* are shown in the following
|
||||
Corresponding frequency limit for different devices with different *input delay* are shown in the following
|
||||
table:
|
||||
|
||||
+--------+------------------+----------------------+-------------------+
|
||||
|
Loading…
Reference in New Issue
Block a user