diff --git a/components/driver/include/driver/spi_master.h b/components/driver/include/driver/spi_master.h index da5f5b5214..8582a33276 100644 --- a/components/driver/include/driver/spi_master.h +++ b/components/driver/include/driver/spi_master.h @@ -105,7 +105,7 @@ typedef struct { #define SPI_TRANS_VARIABLE_ADDR (1<<6) ///< Use the ``address_bits`` in ``spi_transaction_ext_t`` rather than default value in ``spi_device_interface_config_t``. #define SPI_TRANS_VARIABLE_DUMMY (1<<7) ///< Use the ``dummy_bits`` in ``spi_transaction_ext_t`` rather than default value in ``spi_device_interface_config_t``. #define SPI_TRANS_SET_CD (1<<7) ///< Set the CD pin - +#define SPI_TRANS_CS_KEEP_ACTIVE (1<<8) ///< Keep CS active after data transfer /** * This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes. */ @@ -194,7 +194,8 @@ esp_err_t spi_bus_remove_device(spi_device_handle_t handle); * @param ticks_to_wait Ticks to wait until there's room in the queue; use portMAX_DELAY to * never time out. * @return - * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_INVALID_ARG if parameter is invalid. This can happen if SPI_DEVICE_CS_KEEP_LOW flag is specified while + * the bus was not acquired (`spi_device_acquire_bus()` should be called first) * - ESP_ERR_TIMEOUT if there was no room in the queue before ticks_to_wait expired * - ESP_ERR_NO_MEM if allocating DMA-capable temporary buffer failed * - ESP_ERR_INVALID_STATE if previous transactions are not finished @@ -257,7 +258,8 @@ esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *tra * currently only portMAX_DELAY is supported. * * @return - * - ESP_ERR_INVALID_ARG if parameter is invalid + * - ESP_ERR_INVALID_ARG if parameter is invalid. This can happen if SPI_DEVICE_CS_KEEP_LOW flag is specified while + * the bus was not acquired (`spi_device_acquire_bus()` should be called first) * - ESP_ERR_TIMEOUT if the device cannot get control of the bus before ``ticks_to_wait`` expired * - ESP_ERR_NO_MEM if allocating DMA-capable temporary buffer failed * - ESP_ERR_INVALID_STATE if previous transactions are not finished diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index 84674f26df..5efe7993cd 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -466,14 +466,13 @@ int spi_get_actual_clock(int fapb, int hz, int duty_cycle) static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev) { spi_bus_lock_dev_handle_t dev_lock = dev->dev_lock; - - if (!spi_bus_lock_touch(dev_lock)) { - //if the configuration is already applied, skip the following. - return; - } spi_hal_context_t *hal = &dev->host->hal; spi_hal_dev_config_t *hal_dev = &(dev->hal_dev); - spi_hal_setup_device(hal, hal_dev); + + if (spi_bus_lock_touch(dev_lock)) { + /* Configuration has not been applied yet. */ + spi_hal_setup_device(hal, hal_dev); + } } static SPI_MASTER_ISR_ATTR spi_device_t *get_acquiring_dev(spi_host_t *host) @@ -512,12 +511,11 @@ static void spi_bus_intr_disable(void *host) // Setup the transaction-specified registers and linked-list used by the DMA (or FIFO if DMA is not used) static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_t *trans_buf) { - spi_transaction_t *trans = NULL; + spi_transaction_t *trans = trans_buf->trans; spi_host_t *host = dev->host; spi_hal_context_t *hal = &(host->hal); spi_hal_dev_config_t *hal_dev = &(dev->hal_dev); - trans = trans_buf->trans; host->cur_cs = dev->id; //Reconfigure according to device settings, the function only has effect when the dev_id is changed. @@ -531,6 +529,7 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_ hal_trans.send_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_send; hal_trans.cmd = trans->cmd; hal_trans.addr = trans->addr; + hal_trans.cs_keep_active = (trans->flags & SPI_TRANS_CS_KEEP_ACTIVE) ? 1 : 0; //Set up QIO/DIO if needed hal_trans.io_mode = (trans->flags & SPI_TRANS_MODE_DIO ? (trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR ? SPI_LL_IO_MODE_DIO : SPI_LL_IO_MODE_DUAL) : @@ -784,6 +783,12 @@ esp_err_t SPI_MASTER_ATTR spi_device_queue_trans(spi_device_handle_t handle, spi SPI_CHECK(!spi_bus_device_is_polling(handle), "Cannot queue new transaction while previous polling transaction is not terminated.", ESP_ERR_INVALID_STATE ); + /* Even when using interrupt transfer, the CS can only be kept activated if the bus has been + * acquired with `spi_device_acquire_bus()` first. */ + if (host->device_acquiring_lock != handle && (trans_desc->flags & SPI_TRANS_CS_KEEP_ACTIVE)) { + return ESP_ERR_INVALID_ARG; + } + spi_trans_priv_t trans_buf; ret = setup_priv_desc(trans_desc, &trans_buf, (host->bus_attr->dma_enabled)); if (ret != ESP_OK) return ret; @@ -921,8 +926,16 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_start(spi_device_handle_t handl SPI_CHECK(!spi_bus_device_is_polling(handle), "Cannot send polling transaction while the previous polling transaction is not terminated.", ESP_ERR_INVALID_STATE ); + /* If device_acquiring_lock is set to handle, it means that the user has already + * acquired the bus thanks to the function `spi_device_acquire_bus()`. + * In that case, we don't need to take the lock again. */ if (host->device_acquiring_lock != handle) { - ret = spi_bus_lock_acquire_start(handle->dev_lock, ticks_to_wait); + /* The user cannot ask for the CS to keep active has the bus is not locked/acquired. */ + if ((trans_desc->flags & SPI_TRANS_CS_KEEP_ACTIVE) != 0) { + ret = ESP_ERR_INVALID_ARG; + } else { + ret = spi_bus_lock_acquire_start(handle->dev_lock, ticks_to_wait); + } } else { ret = spi_bus_lock_wait_bg_done(handle->dev_lock, ticks_to_wait); } @@ -963,6 +976,9 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle, uninstall_priv_desc(&host->cur_trans_buf); host->polling = false; + /* Once again here, if device_acquiring_lock is set to `handle`, it means that the user has already + * acquired the bus thanks to the function `spi_device_acquire_bus()`. + * In that case, the lock must not be released now because . */ if (host->device_acquiring_lock != handle) { assert(host->device_acquiring_lock == NULL); spi_bus_lock_acquire_end(handle->dev_lock); diff --git a/components/hal/esp32/include/hal/spi_ll.h b/components/hal/esp32/include/hal/spi_ll.h index efd5170309..426d720843 100644 --- a/components/hal/esp32/include/hal/spi_ll.h +++ b/components/hal/esp32/include/hal/spi_ll.h @@ -496,6 +496,16 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id) hw->pin.cs2_dis = (cs_id == 2) ? 0 : 1; } +/** + * Keep Chip Select activated after the current transaction. + * + * @param hw Beginning address of the peripheral registers. + * @param keep_active if 0 don't keep CS activated, else keep CS activated + */ +static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) { + hw->pin.cs_keep_active = (keep_active != 0) ? 1 : 0; +} + /*------------------------------------------------------------------------------ * Configs: parameters *----------------------------------------------------------------------------*/ diff --git a/components/hal/esp32c3/include/hal/spi_ll.h b/components/hal/esp32c3/include/hal/spi_ll.h index cf773da678..8d2c1079f5 100644 --- a/components/hal/esp32c3/include/hal/spi_ll.h +++ b/components/hal/esp32c3/include/hal/spi_ll.h @@ -604,6 +604,16 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id) hw->misc.cs5_dis = (cs_id == 5) ? 0 : 1; } +/** + * Keep Chip Select activated after the current transaction. + * + * @param hw Beginning address of the peripheral registers. + * @param keep_active if 0 don't keep CS activated, else keep CS activated + */ +static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) { + hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0; +} + /*------------------------------------------------------------------------------ * Configs: parameters *----------------------------------------------------------------------------*/ diff --git a/components/hal/esp32h2/include/hal/spi_ll.h b/components/hal/esp32h2/include/hal/spi_ll.h index e4f462dc11..1f68acac7b 100644 --- a/components/hal/esp32h2/include/hal/spi_ll.h +++ b/components/hal/esp32h2/include/hal/spi_ll.h @@ -603,6 +603,16 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id) hw->misc.cs5_dis = (cs_id == 5) ? 0 : 1; } +/** + * Keep Chip Select activated after the current transaction. + * + * @param hw Beginning address of the peripheral registers. + * @param keep_active if 0 don't keep CS activated, else keep CS activated + */ +static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) { + hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0; +} + /*------------------------------------------------------------------------------ * Configs: parameters *----------------------------------------------------------------------------*/ diff --git a/components/hal/esp32s2/include/hal/spi_ll.h b/components/hal/esp32s2/include/hal/spi_ll.h index 16cf0b99eb..855036d05d 100644 --- a/components/hal/esp32s2/include/hal/spi_ll.h +++ b/components/hal/esp32s2/include/hal/spi_ll.h @@ -566,6 +566,16 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id) hw->misc.cs5_dis = (cs_id == 5) ? 0 : 1; } +/** + * Keep Chip Select activated after the current transaction. + * + * @param hw Beginning address of the peripheral registers. + * @param keep_active if 0 don't keep CS activated, else keep CS activated + */ +static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) { + hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0; +} + /*------------------------------------------------------------------------------ * Configs: parameters *----------------------------------------------------------------------------*/ diff --git a/components/hal/esp32s3/include/hal/spi_ll.h b/components/hal/esp32s3/include/hal/spi_ll.h index 8c85823b4e..1748105bff 100644 --- a/components/hal/esp32s3/include/hal/spi_ll.h +++ b/components/hal/esp32s3/include/hal/spi_ll.h @@ -612,6 +612,16 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id) } } +/** + * Keep Chip Select activated after the current transaction. + * + * @param hw Beginning address of the peripheral registers. + * @param keep_active if 0 don't keep CS activated, else keep CS activated + */ +static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) { + hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0; +} + /*------------------------------------------------------------------------------ * Configs: parameters *----------------------------------------------------------------------------*/ diff --git a/components/hal/include/hal/spi_hal.h b/components/hal/include/hal/spi_hal.h index 1c4dc3a049..b37c7c9250 100644 --- a/components/hal/include/hal/spi_hal.h +++ b/components/hal/include/hal/spi_hal.h @@ -101,6 +101,7 @@ typedef struct { uint8_t *send_buffer; ///< Data to be sent uint8_t *rcv_buffer; ///< Buffer to hold the receive data. spi_ll_io_mode_t io_mode; ///< IO mode of the master + int cs_keep_active; ///< Keep CS active after transaction } spi_hal_trans_config_t; /** diff --git a/components/hal/spi_hal_iram.c b/components/hal/spi_hal_iram.c index 7c609b8e18..456fb942f5 100644 --- a/components/hal/spi_hal_iram.c +++ b/components/hal/spi_hal_iram.c @@ -131,6 +131,9 @@ void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev spi_ll_set_command(hw, trans->cmd, cmdlen, dev->tx_lsbfirst); spi_ll_set_address(hw, trans->addr, addrlen, dev->tx_lsbfirst); + //Configure keep active CS + spi_ll_master_keep_cs(hw, trans->cs_keep_active); + //Save the transaction attributes for internal usage. memcpy(&hal->trans_config, trans, sizeof(spi_hal_trans_config_t)); }