Merge branch 'bugfix/spi_master_add_dummy_check' into 'master'

spi_master: add dummy check when both mosi and miso are set

Closes IDF-1872 and IDF-266

See merge request espressif/esp-idf!9406
This commit is contained in:
Michael (XIAO Xufeng)
2020-08-23 12:47:18 +08:00
4 changed files with 23 additions and 17 deletions

View File

@@ -352,7 +352,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
spi_hal_timing_conf_t temp_timing_conf; spi_hal_timing_conf_t temp_timing_conf;
esp_err_t ret = spi_hal_get_clock_conf(hal, dev_config->clock_speed_hz, duty_cycle, esp_err_t ret = spi_hal_cal_clock_conf(hal, dev_config->clock_speed_hz, duty_cycle,
!(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS), !(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS),
dev_config->input_delay_ns, &freq, dev_config->input_delay_ns, &freq,
&temp_timing_conf); &temp_timing_conf);
@@ -663,32 +663,37 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG); SPI_CHECK(handle!=NULL, "invalid dev handle", ESP_ERR_INVALID_ARG);
spi_host_t *host = handle->host; spi_host_t *host = handle->host;
const spi_bus_attr_t* bus_attr = host->bus_attr; const spi_bus_attr_t* bus_attr = host->bus_attr;
bool tx_enabled = (trans_desc->flags & SPI_TRANS_USE_TXDATA) || (trans_desc->tx_buffer);
bool rx_enabled = (trans_desc->flags & SPI_TRANS_USE_RXDATA) || (trans_desc->rx_buffer);
spi_transaction_ext_t *t_ext = (spi_transaction_ext_t *)trans_desc;
bool dummy_enabled = (((trans_desc->flags & SPI_TRANS_VARIABLE_DUMMY)? t_ext->dummy_bits: handle->cfg.dummy_bits) != 0);
bool extra_dummy_enabled = handle->timing_conf.timing_dummy;
bool is_half_duplex = ((handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) != 0);
//check transmission length //check transmission length
SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_RXDATA)==0 ||trans_desc->rxlength <= 32, "rxdata transfer > 32 bits without configured DMA", ESP_ERR_INVALID_ARG); SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_RXDATA)==0 ||trans_desc->rxlength <= 32, "rxdata transfer > 32 bits without configured DMA", ESP_ERR_INVALID_ARG);
SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_TXDATA)==0 ||trans_desc->length <= 32, "txdata transfer > 32 bits without configured DMA", ESP_ERR_INVALID_ARG); SPI_CHECK((trans_desc->flags & SPI_TRANS_USE_TXDATA)==0 ||trans_desc->length <= 32, "txdata transfer > 32 bits without configured DMA", ESP_ERR_INVALID_ARG);
SPI_CHECK(trans_desc->length <= bus_attr->max_transfer_sz*8, "txdata transfer > host maximum", ESP_ERR_INVALID_ARG); SPI_CHECK(trans_desc->length <= bus_attr->max_transfer_sz*8, "txdata transfer > host maximum", ESP_ERR_INVALID_ARG);
SPI_CHECK(trans_desc->rxlength <= bus_attr->max_transfer_sz*8, "rxdata transfer > host maximum", ESP_ERR_INVALID_ARG); SPI_CHECK(trans_desc->rxlength <= bus_attr->max_transfer_sz*8, "rxdata transfer > host maximum", ESP_ERR_INVALID_ARG);
SPI_CHECK((handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) || trans_desc->rxlength <= trans_desc->length, "rx length > tx length in full duplex mode", ESP_ERR_INVALID_ARG); SPI_CHECK(is_half_duplex || trans_desc->rxlength <= trans_desc->length, "rx length > tx length in full duplex mode", ESP_ERR_INVALID_ARG);
//check working mode //check working mode
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible iface params", ESP_ERR_INVALID_ARG); SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible iface params", ESP_ERR_INVALID_ARG);
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (!(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX))), "incompatible iface params", ESP_ERR_INVALID_ARG); SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && !is_half_duplex), "incompatible iface params", ESP_ERR_INVALID_ARG);
#ifdef CONFIG_IDF_TARGET_ESP32 #ifdef CONFIG_IDF_TARGET_ESP32
SPI_CHECK( !(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) || bus_attr->dma_chan == 0 || !(trans_desc->flags & SPI_TRANS_USE_RXDATA || trans_desc->rx_buffer != NULL) SPI_CHECK(!is_half_duplex || bus_attr->dma_chan == 0 || !rx_enabled || !tx_enabled, "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG );
|| !(trans_desc->flags & SPI_TRANS_USE_TXDATA || trans_desc->tx_buffer!=NULL), "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG );
#endif #endif
//MOSI phase is skipped only when both tx_buffer and SPI_TRANS_USE_TXDATA are not set. //MOSI phase is skipped only when both tx_buffer and SPI_TRANS_USE_TXDATA are not set.
SPI_CHECK(trans_desc->length != 0 || (trans_desc->tx_buffer == NULL && !(trans_desc->flags & SPI_TRANS_USE_TXDATA)), SPI_CHECK(trans_desc->length != 0 || !tx_enabled, "trans tx_buffer should be NULL and SPI_TRANS_USE_TXDATA should be cleared to skip MOSI phase.", ESP_ERR_INVALID_ARG);
"trans tx_buffer should be NULL and SPI_TRANS_USE_TXDATA should be cleared to skip MOSI phase.", ESP_ERR_INVALID_ARG);
//MISO phase is skipped only when both rx_buffer and SPI_TRANS_USE_RXDATA are not set. //MISO phase is skipped only when both rx_buffer and SPI_TRANS_USE_RXDATA are not set.
//If set rxlength=0 in full_duplex mode, it will be automatically set to length //If set rxlength=0 in full_duplex mode, it will be automatically set to length
SPI_CHECK(!(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) || trans_desc->rxlength != 0 || SPI_CHECK(!is_half_duplex || trans_desc->rxlength != 0 || !rx_enabled, "trans rx_buffer should be NULL and SPI_TRANS_USE_RXDATA should be cleared to skip MISO phase.", ESP_ERR_INVALID_ARG);
(trans_desc->rx_buffer == NULL && ((trans_desc->flags & SPI_TRANS_USE_RXDATA)==0)),
"trans rx_buffer should be NULL and SPI_TRANS_USE_RXDATA should be cleared to skip MISO phase.", ESP_ERR_INVALID_ARG);
//In Full duplex mode, default rxlength to be the same as length, if not filled in. //In Full duplex mode, default rxlength to be the same as length, if not filled in.
// set rxlength to length is ok, even when rx buffer=NULL // set rxlength to length is ok, even when rx buffer=NULL
if (trans_desc->rxlength==0 && !(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX)) { if (trans_desc->rxlength==0 && !is_half_duplex) {
trans_desc->rxlength=trans_desc->length; trans_desc->rxlength=trans_desc->length;
} }
//Dummy phase is not available when both data out and in are enabled, regardless of FD or HD mode.
SPI_CHECK(!tx_enabled || !rx_enabled || !dummy_enabled || !extra_dummy_enabled, "Dummy phase is not available when both data out and in are enabled", ESP_ERR_INVALID_ARG);
return ESP_OK; return ESP_OK;
} }

View File

@@ -169,7 +169,7 @@ void spi_hal_fetch_result(const spi_hal_context_t *hal);
* Utils * Utils
* ---------------------------------------------------------*/ * ---------------------------------------------------------*/
/** /**
* Get the configuration of clock and timing. The configuration will be used when ``spi_hal_setup_device``. * Calculate the configuration of clock and timing. The configuration will be used when ``spi_hal_setup_device``.
* *
* It is highly suggested to do this at initialization, since it takes long time. * It is highly suggested to do this at initialization, since it takes long time.
* *
@@ -185,7 +185,7 @@ void spi_hal_fetch_result(const spi_hal_context_t *hal);
* *
* @return ESP_OK if desired is available, otherwise fail. * @return ESP_OK if desired is available, otherwise fail.
*/ */
esp_err_t spi_hal_get_clock_conf(const spi_hal_context_t *hal, int speed_hz, int duty_cycle, bool use_gpio, int input_delay_ns, int *out_freq, spi_hal_timing_conf_t *timing_conf); esp_err_t spi_hal_cal_clock_conf(const spi_hal_context_t *hal, int speed_hz, int duty_cycle, bool use_gpio, int input_delay_ns, int *out_freq, spi_hal_timing_conf_t *timing_conf);
/** /**
* Get the frequency actual used. * Get the frequency actual used.

View File

@@ -49,7 +49,7 @@ void spi_hal_deinit(spi_hal_context_t *hal)
} }
} }
esp_err_t spi_hal_get_clock_conf(const spi_hal_context_t *hal, int speed_hz, int duty_cycle, bool use_gpio, int input_delay_ns, int *out_freq, spi_hal_timing_conf_t *timing_conf) esp_err_t spi_hal_cal_clock_conf(const spi_hal_context_t *hal, int speed_hz, int duty_cycle, bool use_gpio, int input_delay_ns, int *out_freq, spi_hal_timing_conf_t *timing_conf)
{ {
spi_hal_timing_conf_t temp_conf; spi_hal_timing_conf_t temp_conf;

View File

@@ -520,10 +520,11 @@ Known Issues
This can prohibit you from transmitting and receiving data longer than 64 bytes. This can prohibit you from transmitting and receiving data longer than 64 bytes.
3. Try using the command and address fields to replace the write phase. 3. Try using the command and address fields to replace the write phase.
2. Full-duplex transactions are not compatible with the *dummy bit workaround*, hence the frequency is limited. See :ref:`dummy 2. Full-duplex transactions are not compatible with the *dummy bit workaround*, hence the frequency is limited. See :ref:`dummy bit speed-up workaround <dummy_bit_workaround>`.
bit speed-up workaround <dummy_bit_workaround>`.
3. ``cs_ena_pretrans`` is not compatible with the command and address phases of full-duplex transactions. 3. ``dummy_bits`` in :cpp:type:`spi_device_interface_config_t` and :cpp:type:`spi_transaction_ext_t` are not available when SPI read and write phases are both enabled (regardless of full duplex or half duplex mode).
4. ``cs_ena_pretrans`` is not compatible with the command and address phases of full-duplex transactions.
Application Example Application Example