feat(spi_master): add new feature allow use variable command and address field length for the same device.

Closes #654
This commit is contained in:
michael 2017-09-27 20:16:37 +08:00
parent 7c9b24b407
commit 0330ec270a
3 changed files with 60 additions and 15 deletions

View file

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

View file

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

View file

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