From 0330ec270a61a5348548cc413871f368b3116619 Mon Sep 17 00:00:00 2001 From: michael Date: Wed, 27 Sep 2017 20:16:37 +0800 Subject: [PATCH] feat(spi_master): add new feature allow use variable command and address field length for the same device. Closes #654 --- components/driver/include/driver/spi_master.h | 16 +++++++- components/driver/spi_master.c | 37 ++++++++++++++----- docs/api-reference/peripherals/spi_master.rst | 22 +++++++++-- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/components/driver/include/driver/spi_master.h b/components/driver/include/driver/spi_master.h index c501998c6..f43394d3c 100644 --- a/components/driver/include/driver/spi_master.h +++ b/components/driver/include/driver/spi_master.h @@ -44,8 +44,8 @@ typedef void(*transaction_cb_t)(spi_transaction_t *trans); * @brief This is a configuration for a SPI slave device that is connected to one of the SPI buses. */ typedef struct { - uint8_t command_bits; ///< Amount of bits in command phase (0-16) - uint8_t address_bits; ///< Amount of bits in address phase (0-64) + uint8_t command_bits; ///< Default amount of bits in command phase (0-16), used when ``SPI_TRANS_VARIABLE_CMD`` is not used, otherwise ignored. + uint8_t address_bits; ///< Default amount of bits in address phase (0-64), used when ``SPI_TRANS_VARIABLE_ADDR`` is not used, otherwise ignored. uint8_t dummy_bits; ///< Amount of dummy bits to insert between address and data phase uint8_t mode; ///< SPI mode (0-3) uint8_t duty_cycle_pos; ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128. @@ -65,6 +65,8 @@ typedef struct { #define SPI_TRANS_MODE_DIOQIO_ADDR (1<<4) ///< Also transmit address in mode selected by SPI_MODE_DIO/SPI_MODE_QIO #define SPI_TRANS_USE_RXDATA (1<<2) ///< Receive into rx_data member of spi_transaction_t instead into memory at rx_buffer. #define SPI_TRANS_USE_TXDATA (1<<3) ///< Transmit tx_data member of spi_transaction_t instead of data at tx_buffer. Do not set tx_buffer when using this. +#define SPI_TRANS_VARIABLE_CMD (1<<4) ///< Use the ``command_bits`` in ``spi_transaction_ext_t`` rather than default value in ``spi_device_interface_config_t``. +#define SPI_TRANS_VARIABLE_ADDR (1<<5) ///< Use the ``address_bits`` in ``spi_transaction_ext_t`` rather than default value in ``spi_device_interface_config_t``. /** * This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes. @@ -90,6 +92,16 @@ struct spi_transaction_t { }; } ; //the rx data should start from a 32-bit aligned address to get around dma issue. +/** + * This struct is for SPI transactions which may change their address and command length. + * Please do set the flags in base to ``SPI_TRANS_VARIABLE_CMD_ADR`` to use the bit length here. + */ +typedef struct { + struct spi_transaction_t base; ///< Transaction data, so that pointer to spi_transaction_t can be converted into spi_transaction_ext_t + uint8_t command_bits; ///< The command length in this transaction, in bits. + uint8_t address_bits; ///< The address length in this transaction, in bits. +} spi_transaction_ext_t ; + typedef struct spi_device_t* spi_device_handle_t; ///< Handle for a device on a SPI bus diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index 8809d2e04..85730d68b 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -489,13 +489,9 @@ static void IRAM_ATTR spi_intr(void *arg) host->hw->ctrl2.miso_delay_mode=nodelay?0:2; } - //Configure bit sizes, load addr and command + //configure dummy bits host->hw->user.usr_dummy=(dev->cfg.dummy_bits+extra_dummy)?1:0; - host->hw->user.usr_addr=(dev->cfg.address_bits)?1:0; - host->hw->user.usr_command=(dev->cfg.command_bits)?1:0; - host->hw->user1.usr_addr_bitlen=dev->cfg.address_bits-1; host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits+extra_dummy-1; - host->hw->user2.usr_command_bitlen=dev->cfg.command_bits-1; //Configure misc stuff host->hw->user.doutdin=(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1; host->hw->user.sio=(dev->cfg.flags & SPI_DEVICE_3WIRE)?1:0; @@ -587,17 +583,36 @@ static void IRAM_ATTR spi_intr(void *arg) host->hw->miso_dlen.usr_miso_dbitlen=trans->length-1; } + //Configure bit sizes, load addr and command + int cmdlen; + if ( trans->flags & SPI_TRANS_VARIABLE_CMD ) { + cmdlen = ((spi_transaction_ext_t*)trans)->command_bits; + } else { + cmdlen = dev->cfg.command_bits; + } + int addrlen; + if ( trans->flags & SPI_TRANS_VARIABLE_ADDR ) { + addrlen = ((spi_transaction_ext_t*)trans)->address_bits; + } else { + addrlen = dev->cfg.address_bits; + } + host->hw->user1.usr_addr_bitlen=addrlen-1; + host->hw->user2.usr_command_bitlen=cmdlen-1; + 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-dev->cfg.command_bits); //shift to MSB + 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 // 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 (dev->cfg.address_bits>32) { - host->hw->addr = trans->addr >> (dev->cfg.address_bits - 32); - host->hw->slv_wr_status = trans->addr << (64 - dev->cfg.address_bits); + if (addrlen>32) { + host->hw->addr = trans->addr >> (addrlen- 32); + host->hw->slv_wr_status = trans->addr << (64 - addrlen); } else { - host->hw->addr = trans->addr << (32 - dev->cfg.address_bits); + host->hw->addr = trans->addr << (32 - addrlen); } + host->hw->user.usr_mosi=( (!(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX) && trans_buf->buffer_to_rcv) || trans_buf->buffer_to_send)?1:0; host->hw->user.usr_miso=(trans_buf->buffer_to_rcv)?1:0; @@ -645,6 +660,7 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t * } if ( trans_buf.buffer_to_rcv && handle->host->dma_chan && (!esp_ptr_dma_capable( trans_buf.buffer_to_rcv ) || ((int)trans_buf.buffer_to_rcv%4!=0)) ) { //if rxbuf in the desc not DMA-capable, malloc a new one. The rx buffer need to be length of multiples of 32 bits to avoid heap corruption. + ESP_LOGV( SPI_TAG, "Allocate RX buffer for DMA" ); trans_buf.buffer_to_rcv = heap_caps_malloc((trans_desc->rxlength+31)/8, MALLOC_CAP_DMA); if ( trans_buf.buffer_to_rcv==NULL ) return ESP_ERR_NO_MEM; } @@ -659,6 +675,7 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t * } if ( txdata && handle->host->dma_chan && !esp_ptr_dma_capable( txdata )) { //if txbuf in the desc not DMA-capable, malloc a new one + ESP_LOGV( SPI_TAG, "Allocate TX buffer for DMA" ); trans_buf.buffer_to_send = heap_caps_malloc((trans_desc->length+7)/8, MALLOC_CAP_DMA); if ( trans_buf.buffer_to_send==NULL ) { // free malloc-ed buffer (if needed) before return. diff --git a/docs/api-reference/peripherals/spi_master.rst b/docs/api-reference/peripherals/spi_master.rst index e4b0347f8..e598f06e7 100644 --- a/docs/api-reference/peripherals/spi_master.rst +++ b/docs/api-reference/peripherals/spi_master.rst @@ -59,7 +59,7 @@ A transaction on the SPI bus consists of five phases, any of which may be skippe In full duplex, the read and write phases are combined, causing the SPI host to read and write data simultaneously. The total transaction length is decided by -``dev_conf.command_bits + dev_conf.address_bits + trans_conf.length``, while the ``trans_conf.rx_length`` +``command_bits + address_bits + trans_conf.length``, while the ``trans_conf.rx_length`` only determins length of data received into the buffer. In half duplex, the length of write phase and read phase are decided by ``trans_conf.length`` and @@ -103,9 +103,25 @@ Using the spi_master driver - Optional: to remove the driver for a bus, make sure no more drivers are attached and call ``spi_bus_free``. +Command and address phases +^^^^^^^^^^^^^^^^^^^^^^^^^^ -Transaction data -^^^^^^^^^^^^^^^^ +During the command and address phases, ``cmd`` and ``addr`` field in the +``spi_transaction_t`` struct are sent to the bus, while nothing is read at the +same time. The default length of command and address phase are set in the +``spi_device_interface_config_t`` and by ``spi_bus_add_device``. When the the +flag ``SPI_TRANS_VARIABLE_CMD`` and ``SPI_TRANS_VARIABLE_ADDR`` are not set in +the ``spi_transaction_t``,the driver automatically set the length of these +phases to the default value as set when the device is initialized respectively. + +If the length of command and address phases needs to be variable, declare a +``spi_transaction_ext_t`` descriptor, set the flag ``SPI_TRANS_VARIABLE_CMD`` +or/and ``SPI_TRANS_VARIABLE_ADDR`` in the ``flags`` of ``base`` member and +configure the rest part of ``base`` as usual. Then the length of each phases +will be ``command_bits`` and ``address_bits`` set in the ``spi_transaction_ext_t``. + +Write and read phases +^^^^^^^^^^^^^^^^^^^^^ Normally, data to be transferred to or from a device will be read from or written to a chunk of memory indicated by the ``rx_buffer`` and ``tx_buffer`` members of the transaction structure.