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:
Angus Gratton 2018-07-03 14:32:41 +08:00
commit 52f4ff6220
3 changed files with 89 additions and 35 deletions

View file

@ -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

View file

@ -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) {

View file

@ -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:
+--------+------------------+----------------------+-------------------+