diff --git a/components/driver/include/driver/spi_common.h b/components/driver/include/driver/spi_common.h index cc029db2b..ce1229b19 100644 --- a/components/driver/include/driver/spi_common.h +++ b/components/driver/include/driver/spi_common.h @@ -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 diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index 4641e4bca..80e7d8743 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -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) { diff --git a/docs/en/api-reference/peripherals/spi_master.rst b/docs/en/api-reference/peripherals/spi_master.rst index 3a526fbd5..534bf8b9d 100644 --- a/docs/en/api-reference/peripherals/spi_master.rst +++ b/docs/en/api-reference/peripherals/spi_master.rst @@ -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: +--------+------------------+----------------------+-------------------+