diff --git a/components/driver/spi_master.c b/components/driver/spi_master.c index 95a6a3f3afb2..048b08642ed7 100644 --- a/components/driver/spi_master.c +++ b/components/driver/spi_master.c @@ -144,7 +144,7 @@ typedef struct { int id; spi_device_t* device[DEV_NUM_MAX]; intr_handle_t intr; - spi_hal_context_t hal; + spi_hal_context_t hal; spi_trans_priv_t cur_trans_buf; int cur_cs; //current device doing transaction const spi_bus_attr_t* bus_attr; @@ -164,9 +164,8 @@ struct spi_device_t { QueueHandle_t trans_queue; QueueHandle_t ret_queue; spi_device_interface_config_t cfg; - spi_hal_timing_conf_t timing_conf; + spi_hal_dev_config_t hal_dev; spi_host_t *host; - spi_bus_lock_dev_handle_t dev_lock; }; @@ -230,11 +229,18 @@ static esp_err_t spi_master_init_driver(spi_host_device_t host_id) } } - spi_hal_init(&host->hal, host_id); + //assign the SPI, RX DMA and TX DMA peripheral registers beginning address + spi_hal_dma_config_t hal_dma_config = { + //On ESP32-S2 and earlier chips, DMA registers are part of SPI registers. Pass the registers of SPI peripheral to control it. + .dma_in = SPI_LL_GET_HW(host_id), + .dma_out = SPI_LL_GET_HW(host_id), + .dmadesc_tx = bus_attr->dmadesc_tx, + .dmadesc_rx = bus_attr->dmadesc_rx, + .dmadesc_n = bus_attr->dma_desc_num + }; + + spi_hal_init(&host->hal, host_id, &hal_dma_config); host->hal.dma_enabled = (bus_attr->dma_chan != 0); - host->hal.dmadesc_tx = bus_attr->dmadesc_tx; - host->hal.dmadesc_rx = bus_attr->dmadesc_rx; - host->hal.dmadesc_n = bus_attr->dma_desc_num; if (host_id != SPI1_HOST) { //SPI1 attributes are already initialized at start up. @@ -293,7 +299,6 @@ void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int* dum int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns) { return spi_hal_get_freq_limit(gpio_is_used, input_delay_ns); - } /* @@ -302,7 +307,6 @@ int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns) */ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interface_config_t *dev_config, spi_device_handle_t *handle) { - int duty_cycle; spi_device_t *dev = NULL; esp_err_t err = ESP_OK; @@ -339,33 +343,32 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa int freecs = spi_bus_lock_get_dev_id(dev_handle); SPI_CHECK(freecs != -1, "no free cs pins for the host", ESP_ERR_NOT_FOUND); - duty_cycle = (dev_config->duty_cycle_pos==0) ? 128 : dev_config->duty_cycle_pos; + //input parameters to calculate timing configuration + int half_duplex = dev_config->flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0; + int no_compensate = dev_config->flags & SPI_DEVICE_NO_DUMMY ? 1 : 0; + int duty_cycle = (dev_config->duty_cycle_pos==0) ? 128 : dev_config->duty_cycle_pos; + int use_gpio = !(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS); + spi_hal_timing_param_t timing_param = { + .half_duplex = half_duplex, + .no_compensate = no_compensate, + .clock_speed_hz = dev_config->clock_speed_hz, + .duty_cycle = duty_cycle, + .input_delay_ns = dev_config->input_delay_ns, + .use_gpio = use_gpio + }; + //output values of timing configuration + spi_hal_timing_conf_t temp_timing_conf; int freq; - spi_hal_context_t *hal = &(host->hal); - hal->half_duplex = dev_config->flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0; -#ifdef SOC_SPI_SUPPORT_AS_CS - hal->as_cs = dev_config->flags & SPI_DEVICE_CLK_AS_CS ? 1 : 0; -#endif - hal->positive_cs = dev_config->flags & SPI_DEVICE_POSITIVE_CS ? 1 : 0; - hal->no_compensate = dev_config->flags & SPI_DEVICE_NO_DUMMY ? 1 : 0; - - spi_hal_timing_conf_t temp_timing_conf; - - esp_err_t ret = spi_hal_cal_clock_conf(hal, dev_config->clock_speed_hz, duty_cycle, - !(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS), - dev_config->input_delay_ns, &freq, - &temp_timing_conf); - + esp_err_t ret = spi_hal_cal_clock_conf(&timing_param, &freq, &temp_timing_conf); SPI_CHECK(ret==ESP_OK, "assigned clock speed not supported", ret); //Allocate memory for device - dev=malloc(sizeof(spi_device_t)); - if (dev==NULL) goto nomem; + dev = malloc(sizeof(spi_device_t)); + if (dev == NULL) goto nomem; memset(dev, 0, sizeof(spi_device_t)); - host->device[freecs] = dev; + dev->id = freecs; - dev->timing_conf = temp_timing_conf; dev->dev_lock = dev_handle; //Allocate queues, set defaults @@ -375,8 +378,6 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa goto nomem; } - dev->host= host; - //We want to save a copy of the dev config in the dev struct. memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t)); dev->cfg.duty_cycle_pos = duty_cycle; @@ -384,10 +385,37 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa //Set CS pin, CS options if (dev_config->spics_io_num >= 0) { - spicommon_cs_initialize(host_id, dev_config->spics_io_num, freecs, !(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS)); + spicommon_cs_initialize(host_id, dev_config->spics_io_num, freecs, use_gpio); } - *handle=dev; + //save a pointer to device in spi_host_t + host->device[freecs] = dev; + //save a pointer to host in spi_device_t + dev->host= host; + + //initialise the device specific configuration + spi_hal_dev_config_t *hal_dev = &(dev->hal_dev); + hal_dev->mode = dev_config->mode; + hal_dev->cs_setup = dev_config->cs_ena_pretrans; + hal_dev->cs_hold = dev_config->cs_ena_posttrans; + //set hold_time to 0 will not actually append delay to CS + //set it to 1 since we do need at least one clock of hold time in most cases + if (hal_dev->cs_hold == 0) { + hal_dev->cs_hold = 1; + } + hal_dev->cs_pin_id = dev->id; + hal_dev->timing_conf = temp_timing_conf; + hal_dev->sio = (dev_config->flags) & SPI_DEVICE_3WIRE ? 1 : 0; + hal_dev->half_duplex = dev_config->flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0; + hal_dev->tx_lsbfirst = dev_config->flags & SPI_DEVICE_TXBIT_LSBFIRST ? 1 : 0; + hal_dev->rx_lsbfirst = dev_config->flags & SPI_DEVICE_RXBIT_LSBFIRST ? 1 : 0; + hal_dev->no_compensate = dev_config->flags & SPI_DEVICE_NO_DUMMY ? 1 : 0; +#ifdef SOC_SPI_SUPPORT_AS_CS + hal_dev->as_cs = dev_config->flags& SPI_DEVICE_CLK_AS_CS ? 1 : 0; +#endif + hal_dev->positive_cs = dev_config->flags & SPI_DEVICE_POSITIVE_CS ? 1 : 0; + + *handle = dev; ESP_LOGD(SPI_TAG, "SPI%d: New device added to CS%d, effective clock: %dkHz", host_id+1, freecs, freq/1000); return ESP_OK; @@ -447,24 +475,9 @@ static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev) //if the configuration is already applied, skip the following. return; } - - spi_host_t* host = dev->host; - spi_hal_context_t *hal = &host->hal; - hal->mode = dev->cfg.mode; - hal->tx_lsbfirst = dev->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST ? 1 : 0; - hal->rx_lsbfirst = dev->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST ? 1 : 0; - hal->no_compensate = dev->cfg.flags & SPI_DEVICE_NO_DUMMY ? 1 : 0; - hal->sio = dev->cfg.flags & SPI_DEVICE_3WIRE ? 1 : 0; - hal->dummy_bits = dev->cfg.dummy_bits; - hal->cs_setup = dev->cfg.cs_ena_pretrans; - hal->cs_hold =dev->cfg.cs_ena_posttrans; - //set hold_time to 0 will not actually append delay to CS - //set it to 1 since we do need at least one clock of hold time in most cases - if (hal->cs_hold == 0) hal->cs_hold = 1; - hal->cs_pin_id = dev->id; - hal->timing_conf = &dev->timing_conf; - - spi_hal_setup_device(hal); + 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); } static SPI_MASTER_ISR_ATTR spi_device_t *get_acquiring_dev(spi_host_t *host) @@ -501,54 +514,53 @@ static void spi_bus_intr_disable(void *host) // The function is called to send a new transaction, in ISR or in the task. // 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_hal_context_t *hal) +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_host_t *host = dev->host; - int dev_id = dev->id; + 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; + host->cur_cs = dev->id; //Reconfigure according to device settings, the function only has effect when the dev_id is changed. - spi_setup_device(host->device[dev_id]); - - hal->tx_bitlen = trans->length; - hal->rx_bitlen = trans->rxlength; - hal->rcv_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_rcv; - hal->send_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_send; - hal->half_duplex = dev->cfg.flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0; - hal->cmd = trans->cmd; - hal->addr = trans->addr; + spi_setup_device(dev); + + //set the transaction specific configuration each time before a transaction setup + spi_hal_trans_config_t hal_trans = {}; + hal_trans.tx_bitlen = trans->length; + hal_trans.rx_bitlen = trans->rxlength; + hal_trans.rcv_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_rcv; + hal_trans.send_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_send; + hal_trans.cmd = trans->cmd; + hal_trans.addr = trans->addr; //Set up QIO/DIO if needed - hal->io_mode = (trans->flags & SPI_TRANS_MODE_DIO ? + 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) : (trans->flags & SPI_TRANS_MODE_QIO ? (trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR ? SPI_LL_IO_MODE_QIO : SPI_LL_IO_MODE_QUAD) : SPI_LL_IO_MODE_NORMAL )); - hal->tx_bitlen = trans->length; - hal->rx_bitlen = trans->rxlength; - if (trans->flags & SPI_TRANS_VARIABLE_CMD) { - hal->cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits; + hal_trans.cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits; } else { - hal->cmd_bits = dev->cfg.command_bits; + hal_trans.cmd_bits = dev->cfg.command_bits; } if (trans->flags & SPI_TRANS_VARIABLE_ADDR) { - hal->addr_bits = ((spi_transaction_ext_t *)trans)->address_bits; + hal_trans.addr_bits = ((spi_transaction_ext_t *)trans)->address_bits; } else { - hal->addr_bits = dev->cfg.address_bits; + hal_trans.addr_bits = dev->cfg.address_bits; } if (trans->flags & SPI_TRANS_VARIABLE_DUMMY) { - hal->dummy_bits = ((spi_transaction_ext_t *)trans)->dummy_bits; + hal_trans.dummy_bits = ((spi_transaction_ext_t *)trans)->dummy_bits; } else { - hal->dummy_bits = dev->cfg.dummy_bits; + hal_trans.dummy_bits = dev->cfg.dummy_bits; } - spi_hal_setup_trans(hal); - spi_hal_prepare_data(hal); + spi_hal_setup_trans(hal, hal_dev, &hal_trans); + spi_hal_prepare_data(hal, hal_dev, &hal_trans); //Call pre-transmission callback, if any if (dev->cfg.pre_cb) dev->cfg.pre_cb(trans); @@ -561,6 +573,7 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_ static void SPI_MASTER_ISR_ATTR spi_post_trans(spi_host_t *host) { spi_transaction_t *cur_trans = host->cur_trans_buf.trans; + spi_hal_fetch_result(&host->hal); //Call post-transaction callback, if any spi_device_t* dev = host->device[host->cur_cs]; @@ -569,7 +582,6 @@ static void SPI_MASTER_ISR_ATTR spi_post_trans(spi_host_t *host) host->cur_cs = DEV_NUM_MAX; } - // This is run in interrupt context. static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) { @@ -649,7 +661,7 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg) //mark channel as active, so that the DMA will not be reset by the slave spicommon_dmaworkaround_transfer_active(bus_attr->dma_chan); } - spi_new_trans(device_to_send, cur_trans_buf, (&host->hal)); + spi_new_trans(device_to_send, cur_trans_buf); } // Exit of the ISR, handle interrupt re-enable (if sending transaction), retry (if there's coming BG), // or resume acquiring device task (if quit due to bus acquiring). @@ -667,7 +679,7 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl 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 extra_dummy_enabled = handle->hal_dev.timing_conf.timing_dummy; bool is_half_duplex = ((handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) != 0); //check transmission length @@ -763,7 +775,6 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_transaction_t *trans_de return ESP_ERR_NO_MEM; } - esp_err_t SPI_MASTER_ATTR spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait) { esp_err_t ret = check_trans_valid(handle, trans_desc); @@ -841,7 +852,6 @@ esp_err_t SPI_MASTER_ATTR spi_device_transmit(spi_device_handle_t handle, spi_tr return ESP_OK; } - esp_err_t SPI_MASTER_ISR_ATTR spi_device_acquire_bus(spi_device_t *device, TickType_t wait) { spi_host_t *const host = device->host; @@ -897,7 +907,6 @@ void SPI_MASTER_ISR_ATTR spi_device_release_bus(spi_device_t *dev) assert(ret == ESP_OK); } - esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_start(spi_device_handle_t handle, spi_transaction_t *trans_desc, TickType_t ticks_to_wait) { esp_err_t ret; @@ -923,12 +932,11 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_start(spi_device_handle_t handl host->polling = true; ESP_LOGV(SPI_TAG, "polling trans"); - spi_new_trans(handle, &host->cur_trans_buf, (&host->hal)); + spi_new_trans(handle, &host->cur_trans_buf); return ESP_OK; } - esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle, TickType_t ticks_to_wait) { SPI_CHECK(handle != NULL, "invalid dev handle", ESP_ERR_INVALID_ARG); @@ -960,7 +968,6 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle, return ESP_OK; } - esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_transmit(spi_device_handle_t handle, spi_transaction_t* trans_desc) { esp_err_t ret; diff --git a/components/driver/spi_slave_hd.c b/components/driver/spi_slave_hd.c index 1b9183d692e4..c182e7983e7a 100644 --- a/components/driver/spi_slave_hd.c +++ b/components/driver/spi_slave_hd.c @@ -102,13 +102,14 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b spi_slave_hd_hal_config_t hal_config = { .host_id = host_id, + .dma_in = SPI_LL_GET_HW(host_id), + .dma_out = SPI_LL_GET_HW(host_id), .tx_lsbfirst = (config->flags & SPI_SLAVE_HD_RXBIT_LSBFIRST), .rx_lsbfirst = (config->flags & SPI_SLAVE_HD_TXBIT_LSBFIRST), .dma_chan = config->dma_chan, - .mode = config->mode, + .mode = config->mode }; - - slave_hd_hal_init(&host->hal, &hal_config); + spi_slave_hd_hal_init(&host->hal, &hal_config); if (config->dma_chan != 0) { //See how many dma descriptors we need and allocate them diff --git a/components/hal/esp32/include/hal/spi_ll.h b/components/hal/esp32/include/hal/spi_ll.h index 73b8b01a6ab9..bfeccf471dff 100644 --- a/components/hal/esp32/include/hal/spi_ll.h +++ b/components/hal/esp32/include/hal/spi_ll.h @@ -485,7 +485,7 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id) * @param hw Beginning address of the peripheral registers. * @param val stored clock configuration calculated before (by ``spi_ll_cal_clock``). */ -static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, spi_ll_clock_val_t *val) +static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_clock_val_t *val) { hw->clock.val = *(uint32_t *)val; } diff --git a/components/hal/esp32s2/include/hal/spi_ll.h b/components/hal/esp32s2/include/hal/spi_ll.h index ca8f685c7184..e4158e79084b 100644 --- a/components/hal/esp32s2/include/hal/spi_ll.h +++ b/components/hal/esp32s2/include/hal/spi_ll.h @@ -590,7 +590,7 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id) * @param hw Beginning address of the peripheral registers. * @param val stored clock configuration calculated before (by ``spi_ll_cal_clock``). */ -static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, spi_ll_clock_val_t *val) +static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_clock_val_t *val) { hw->clock.val = *(uint32_t *)val; } diff --git a/components/hal/include/hal/spi_hal.h b/components/hal/include/hal/spi_hal.h index 6f0aa024fb5b..dd5e01df07f6 100644 --- a/components/hal/include/hal/spi_hal.h +++ b/components/hal/include/hal/spi_hal.h @@ -38,83 +38,115 @@ #include #include "soc/lldesc.h" +/** + * Input parameters to the ``spi_hal_cal_clock_conf`` to calculate the timing configuration + */ +typedef struct { + uint32_t half_duplex; ///< Whether half duplex mode is used, device specific + uint32_t no_compensate; ///< No need to add dummy to compensate the timing, device specific + uint32_t clock_speed_hz; ///< Desired frequency. + uint32_t duty_cycle; ///< Desired duty cycle of SPI clock + uint32_t input_delay_ns; /**< Maximum delay between SPI launch clock and the data to be valid. + * This is used to compensate/calculate the maximum frequency allowed. + * Left 0 if not known. + */ + bool use_gpio; ///< True if the GPIO matrix is used, otherwise false +} spi_hal_timing_param_t; + /** * Timing configuration structure that should be calculated by - * ``spi_hal_setup_clock`` at initialization and hold. Filled into the + * ``spi_hal_cal_clock_conf`` at initialization and hold. Filled into the * ``timing_conf`` member of the context of HAL before setup a device. */ typedef struct { - spi_ll_clock_val_t clock_reg; ///< Register value used by the LL layer - int timing_dummy; ///< Extra dummy needed to compensate the timing - int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing + spi_ll_clock_val_t clock_reg; ///< Register value used by the LL layer + int timing_dummy; ///< Extra dummy needed to compensate the timing + int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing } spi_hal_timing_conf_t; +/** + * DMA configuration structure + * Should be set by driver at initialization + */ +typedef struct { + spi_dma_dev_t *dma_in; ///< Input DMA(DMA -> RAM) peripheral register address + spi_dma_dev_t *dma_out; ///< Output DMA(RAM -> DMA) peripheral register address + lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA. + * The amount should be larger than dmadesc_n. The driver should ensure that + * the data to be sent is shorter than the descriptors can hold. + */ + lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA. + * The amount should be larger than dmadesc_n. The driver should ensure that + * the data to be sent is shorter than the descriptors can hold. + */ + int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use. +} spi_hal_dma_config_t; + +/** + * Transaction configuration structure, this should be assigned by driver each time. + * All these parameters will be updated to the peripheral every transaction. + */ +typedef struct { + uint16_t cmd; ///< Command value to be sent + int cmd_bits; ///< Length (in bits) of the command phase + int addr_bits; ///< Length (in bits) of the address phase + int dummy_bits; ///< Base length (in bits) of the dummy phase. Note when the compensation is enabled, some extra dummy bits may be appended. + int tx_bitlen; ///< TX length, in bits + int rx_bitlen; ///< RX length, in bits + uint64_t addr; ///< Address value to be sent + 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 +} spi_hal_trans_config_t; + /** * Context that should be maintained by both the driver and the HAL. */ typedef struct { - /* configured by driver at initialization, don't touch */ - spi_dev_t *hw; ///< Beginning address of the peripheral registers. - /* should be configured by driver at initialization */ - lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA. - * The amount should be larger than dmadesc_n. The driver should ensure that - * the data to be sent is shorter than the descriptors can hold. - */ - lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA. - * The amount should be larger than dmadesc_n. The driver should ensure that - * the data to be sent is shorter than the descriptors can hold. - */ - int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use. - /* - * Device specific, all these parameters will be updated to the peripheral - * only when ``spi_hal_setup_device``. They may not get updated when - * ``spi_hal_setup_trans``. - */ + /* Configured by driver at initialization, don't touch */ + spi_dev_t *hw; ///< Beginning address of the peripheral registers. + spi_dma_dev_t *dma_in; ///< Address of the DMA peripheral registers which stores the data received from a peripheral into RAM (DMA -> RAM). + spi_dma_dev_t *dma_out; ///< Address of the DMA peripheral registers which transmits the data from RAM to a peripheral (RAM -> DMA). + bool dma_enabled; ///< Whether the DMA is enabled, do not update after initialization + spi_hal_dma_config_t dma_config; ///< DMA configuration + + /* Internal parameters, don't touch */ + spi_hal_trans_config_t trans_config; ///< Transaction configuration +} spi_hal_context_t; + +/** + * Device configuration structure, this should be initialised by driver based on different devices respectively. + * All these parameters will be updated to the peripheral only when ``spi_hal_setup_device``. + * They may not get updated when ``spi_hal_setup_trans``. + */ +typedef struct { int mode; ///< SPI mode, device specific int cs_setup; ///< Setup time of CS active edge before the first SPI clock, device specific int cs_hold; ///< Hold time of CS inactive edge after the last SPI clock, device specific int cs_pin_id; ///< CS pin to use, 0-2, otherwise all the CS pins are not used. Device specific - spi_hal_timing_conf_t *timing_conf; /**< Pointer to an structure holding - * the pre-calculated timing configuration for the device at initialization, - * device specific + spi_hal_timing_conf_t timing_conf; /**< This structure holds the pre-calculated timing configuration for the device + * at initialization, device specific */ struct { - uint32_t sio : 1; ///< Whether to use SIO mode, device specific - uint32_t half_duplex : 1; ///< Whether half duplex mode is used, device specific - uint32_t tx_lsbfirst : 1; ///< Whether LSB is sent first for TX data, device specific - uint32_t rx_lsbfirst : 1; ///< Whether LSB is received first for RX data, device specific - uint32_t dma_enabled : 1; ///< Whether the DMA is enabled, do not update after initialization - uint32_t no_compensate : 1; ///< No need to add dummy to compensate the timing, device specific + uint32_t sio : 1; ///< Whether to use SIO mode, device specific + uint32_t half_duplex : 1; ///< Whether half duplex mode is used, device specific + uint32_t tx_lsbfirst : 1; ///< Whether LSB is sent first for TX data, device specific + uint32_t rx_lsbfirst : 1; ///< Whether LSB is received first for RX data, device specific + uint32_t no_compensate : 1; ///< No need to add dummy to compensate the timing, device specific #ifdef SOC_SPI_SUPPORT_AS_CS - uint32_t as_cs : 1; ///< Whether to toggle the CS while the clock toggles, device specific + uint32_t as_cs : 1; ///< Whether to toggle the CS while the clock toggles, device specific #endif - uint32_t positive_cs : 1; ///< Whether the postive CS feature is abled, device specific + uint32_t positive_cs : 1; ///< Whether the postive CS feature is abled, device specific };//boolean configurations - - /* - * Transaction specific (data), all these parameters will be updated to the - * peripheral every transaction. - */ - uint16_t cmd; ///< Command value to be sent - int cmd_bits; ///< Length (in bits) of the command phase - int addr_bits; ///< Length (in bits) of the address phase - int dummy_bits; ///< Base length (in bits) of the dummy phase. Note when the compensation is enabled, some extra dummy bits may be appended. - int tx_bitlen; ///< TX length, in bits - int rx_bitlen; ///< RX length, in bits - uint64_t addr; ///< Address value to be sent - 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 - -} spi_hal_context_t; +} spi_hal_dev_config_t; /** * Init the peripheral and the context. * - * @param hal Context of the HAL layer. + * @param hal Context of the HAL layer. * @param host_id Index of the SPI peripheral. 0 for SPI1, 1 for HSPI (SPI2) and 2 for VSPI (SPI3). */ -void spi_hal_init(spi_hal_context_t *hal, int host_id); +void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id, const spi_hal_dma_config_t *hal_dma_config); /** * Deinit the peripheral (and the context if needed). @@ -126,23 +158,28 @@ void spi_hal_deinit(spi_hal_context_t *hal); /** * Setup device-related configurations according to the settings in the context. * - * @param hal Context of the HAL layer. + * @param hal Context of the HAL layer. + * @param hal_dev Device configuration */ -void spi_hal_setup_device(const spi_hal_context_t *hal); +void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev); /** * Setup transaction related configurations according to the settings in the context. * - * @param hal Context of the HAL layer. + * @param hal Context of the HAL layer. + * @param hal_dev Device configuration + * @param hal_trans Transaction configuration */ -void spi_hal_setup_trans(const spi_hal_context_t *hal); +void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev, const spi_hal_trans_config_t *hal_trans); /** * Prepare the data for the current transaction. * - * @param hal Context of the HAL layer. + * @param hal Context of the HAL layer. + * @param hal_dev Device configuration + * @param hal_trans Transaction configuration */ -void spi_hal_prepare_data(const spi_hal_context_t *hal); +void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev, const spi_hal_trans_config_t *hal_trans); /** * Trigger start a user-defined transaction. @@ -161,7 +198,7 @@ bool spi_hal_usr_is_done(const spi_hal_context_t *hal); /** * Post transaction operations, mainly fetch data from the buffer. * - * @param hal Context of the HAL layer. + * @param hal Context of the HAL layer. */ void spi_hal_fetch_result(const spi_hal_context_t *hal); @@ -173,50 +210,44 @@ void spi_hal_fetch_result(const spi_hal_context_t *hal); * * It is highly suggested to do this at initialization, since it takes long time. * - * @param hal Context of the HAL layer. - * @param speed_hz Desired frequency. - * @param duty_cycle Desired duty cycle of SPI clock - * @param use_gpio true if the GPIO matrix is used, otherwise false - * @param input_delay_ns Maximum delay between SPI launch clock and the data to - * be valid. This is used to compensate/calculate the maximum frequency - * allowed. Left 0 if not known. - * @param out_freq Output of the actual frequency, left NULL if not required. - * @param timing_conf Output of the timing configuration. + * @param timing_param Input parameters to calculate timing configuration + * @param out_freq Output of the actual frequency, left NULL if not required. + * @param timing_conf Output of the timing configuration. * * @return ESP_OK if desired is available, otherwise fail. */ -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); +esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, int *out_freq, spi_hal_timing_conf_t *timing_conf); /** * Get the frequency actual used. * - * @param hal Context of the HAL layer. - * @param fapb APB clock frequency. - * @param hz Desired frequencyc. - * @param duty_cycle Desired duty cycle. + * @param hal Context of the HAL layer. + * @param fapb APB clock frequency. + * @param hz Desired frequencyc. + * @param duty_cycle Desired duty cycle. */ int spi_hal_master_cal_clock(int fapb, int hz, int duty_cycle); /** * Get the timing configuration for given parameters. * - * @param eff_clk Actual SPI clock frequency - * @param gpio_is_used true if the GPIO matrix is used, otherwise false. + * @param eff_clk Actual SPI clock frequency + * @param gpio_is_used true if the GPIO matrix is used, otherwise false. * @param input_delay_ns Maximum delay between SPI launch clock and the data to - * be valid. This is used to compensate/calculate the maximum frequency - * allowed. Left 0 if not known. - * @param dummy_n Dummy cycles required to correctly read the data. - * @param miso_delay_n suggested delay on the MISO line, in APB clocks. + * be valid. This is used to compensate/calculate the maximum frequency + * allowed. Left 0 if not known. + * @param dummy_n Dummy cycles required to correctly read the data. + * @param miso_delay_n suggested delay on the MISO line, in APB clocks. */ void spi_hal_cal_timing(int eff_clk, bool gpio_is_used, int input_delay_ns, int *dummy_n, int *miso_delay_n); /** * Get the maximum frequency allowed to read if no compensation is used. * - * @param gpio_is_used true if the GPIO matrix is used, otherwise false. + * @param gpio_is_used true if the GPIO matrix is used, otherwise false. * @param input_delay_ns Maximum delay between SPI launch clock and the data to - * be valid. This is used to compensate/calculate the maximum frequency - * allowed. Left 0 if not known. + * be valid. This is used to compensate/calculate the maximum frequency + * allowed. Left 0 if not known. */ int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns); diff --git a/components/hal/include/hal/spi_slave_hd_hal.h b/components/hal/include/hal/spi_slave_hd_hal.h index 6e13e2c2902d..1621e46892ca 100644 --- a/components/hal/include/hal/spi_slave_hd_hal.h +++ b/components/hal/include/hal/spi_slave_hd_hal.h @@ -22,7 +22,7 @@ * The HAL layer for SPI Slave HD mode, currently only segment mode is supported * * Usage: - * - Firstly, initialize the slave with `slave_hd_hal_init` + * - Firstly, initialize the slave with `spi_slave_hd_hal_init` * * - Event handling: * - (Optional) Call ``spi_slave_hd_hal_enable_event_intr`` to enable the used interrupts @@ -59,51 +59,54 @@ /// Configuration of the HAL typedef struct { - int host_id; ///< Host ID of the spi peripheral - int spics_io_num; ///< CS GPIO pin for this device - uint8_t mode; ///< SPI mode (0-3) - int command_bits; ///< command field bits, multiples of 8 and at least 8. - int address_bits; ///< address field bits, multiples of 8 and at least 8. - int dummy_bits; ///< dummy field bits, multiples of 8 and at least 8. + uint32_t host_id; ///< Host ID of the spi peripheral + dma_dev_t *dma_in; ///< Input DMA(DMA -> RAM) peripheral register address + dma_dev_t *dma_out; ///< Output DMA(RAM -> DMA) peripheral register address + uint32_t spics_io_num; ///< CS GPIO pin for this device + uint8_t mode; ///< SPI mode (0-3) + uint32_t command_bits; ///< command field bits, multiples of 8 and at least 8. + uint32_t address_bits; ///< address field bits, multiples of 8 and at least 8. + uint32_t dummy_bits; ///< dummy field bits, multiples of 8 and at least 8. struct { - uint32_t tx_lsbfirst : 1;///< Whether TX data should be sent with LSB first. - uint32_t rx_lsbfirst : 1;///< Whether RX data should be read with LSB first. + uint32_t tx_lsbfirst : 1; ///< Whether TX data should be sent with LSB first. + uint32_t rx_lsbfirst : 1; ///< Whether RX data should be read with LSB first. }; - int dma_chan; ///< The dma channel used. + uint32_t dma_chan; ///< The dma channel used. } spi_slave_hd_hal_config_t; -/// Context of the HAL, initialized by :cpp:func:`slave_hd_hal_init`. +/// Context of the HAL, initialized by :cpp:func:`spi_slave_hd_hal_init`. typedef struct { - spi_dev_t* dev; ///< Beginning address of the peripheral registers. - lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA. - * The amount should be larger than dmadesc_n. The driver should ensure that - * the data to be sent is shorter than the descriptors can hold. - */ - lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA. - * The amount should be larger than dmadesc_n. The driver should ensure that - * the data to be sent is shorter than the descriptors can hold. - */ + spi_dev_t *dev; ///< Beginning address of the peripheral registers. + dma_dev_t *dma_in; ///< Address of the DMA peripheral registers which stores the data received from a peripheral into RAM. + dma_dev_t *dma_out; ///< Address of the DMA peripheral registers which transmits the data from RAM to a peripheral. + lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA. + * The amount should be larger than dmadesc_n. The driver should ensure that + * the data to be sent is shorter than the descriptors can hold. + */ + lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA. + * The amount should be larger than dmadesc_n. The driver should ensure that + * the data to be sent is shorter than the descriptors can hold. + */ /* Internal status used by the HAL implementation, initialized as 0. */ - uint32_t intr_not_triggered; + uint32_t intr_not_triggered; } spi_slave_hd_hal_context_t; - /** * @brief Initialize the hardware and part of the context * - * @param hal Context of the HAL layer - * @param config Configuration of the HAL + * @param hal Context of the HAL layer + * @param hal_config Configuration of the HAL */ -void slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_hal_config_t *config); +void spi_slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_hal_config_t *hal_config); /** * @brief Check and clear signal of one event * * @param hal Context of the HAL layer * @param ev Event to check - * @return true if event triggered, otherwise false + * @return True if event triggered, otherwise false */ bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t* hal, spi_event_t ev); @@ -116,7 +119,7 @@ bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t* hal, spi_eve * * @param hal Context of the HAL layer * @param ev Event to check and disable - * @return true if event triggered, otherwise false + * @return True if event triggered, otherwise false */ bool spi_slave_hd_hal_check_disable_event(spi_slave_hd_hal_context_t* hal, spi_event_t ev); @@ -156,7 +159,7 @@ void spi_slave_hd_hal_rxdma(spi_slave_hd_hal_context_t *hal, uint8_t *out_buf, s * @brief Get the length of total received data * * @param hal Context of the HAL layer - * @return The received length + * @return The received length */ int spi_slave_hd_hal_rxdma_get_len(spi_slave_hd_hal_context_t *hal); @@ -167,8 +170,8 @@ int spi_slave_hd_hal_rxdma_get_len(spi_slave_hd_hal_context_t *hal); * @brief Start the TX DMA operation with the specified buffer * * @param hal Context of the HAL layer - * @param data Buffer of data to send - * @param len Size of the buffer, also the maximum length to send + * @param data Buffer of data to send + * @param len Size of the buffer, also the maximum length to send */ void spi_slave_hd_hal_txdma(spi_slave_hd_hal_context_t *hal, uint8_t *data, size_t len); @@ -179,9 +182,9 @@ void spi_slave_hd_hal_txdma(spi_slave_hd_hal_context_t *hal, uint8_t *data, size * @brief Read from the shared register buffer * * @param hal Context of the HAL layer - * @param addr Address of the shared regsiter to read - * @param out_data Buffer to store the read data - * @param len Length to read from the shared buffer + * @param addr Address of the shared regsiter to read + * @param out_data Buffer to store the read data + * @param len Length to read from the shared buffer */ void spi_slave_hd_hal_read_buffer(spi_slave_hd_hal_context_t *hal, int addr, uint8_t *out_data, size_t len); @@ -199,7 +202,7 @@ void spi_slave_hd_hal_write_buffer(spi_slave_hd_hal_context_t *hal, int addr, ui * @brief Get the length of previous transaction. * * @param hal Context of the HAL layer - * @return The length of previous transaction + * @return The length of previous transaction */ int spi_slave_hd_hal_get_rxlen(spi_slave_hd_hal_context_t *hal); @@ -207,6 +210,6 @@ int spi_slave_hd_hal_get_rxlen(spi_slave_hd_hal_context_t *hal); * @brief Get the address of last transaction * * @param hal Context of the HAL layer - * @return The address of last transaction + * @return The address of last transaction */ int spi_slave_hd_hal_get_last_addr(spi_slave_hd_hal_context_t *hal); diff --git a/components/hal/spi_hal.c b/components/hal/spi_hal.c index d6df0d323b30..4a46c0a14ca0 100644 --- a/components/hal/spi_hal.c +++ b/components/hal/spi_hal.c @@ -24,11 +24,12 @@ static const char SPI_HAL_TAG[] = "spi_hal"; return (ret_val); \ } -void spi_hal_init(spi_hal_context_t *hal, int host_id) +void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id) { memset(hal, 0, sizeof(spi_hal_context_t)); - spi_dev_t *hw = spi_periph_signal[host_id].hw; + spi_dev_t *hw = SPI_LL_GET_HW(host_id); hal->hw = hw; + spi_ll_master_init(hw); //Force a transaction done interrupt. This interrupt won't fire yet because @@ -38,6 +39,9 @@ void spi_hal_init(spi_hal_context_t *hal, int host_id) spi_ll_enable_int(hw); spi_ll_set_int_stat(hw); spi_ll_set_mosi_delay(hw, 0, 0); + + //Save the dma configuration in ``spi_hal_context_t`` + memcpy(&hal->dma_config, dma_config, sizeof(spi_hal_dma_config_t)); } void spi_hal_deinit(spi_hal_context_t *hal) @@ -49,20 +53,20 @@ void spi_hal_deinit(spi_hal_context_t *hal) } } -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) +esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, int *out_freq, spi_hal_timing_conf_t *timing_conf) { spi_hal_timing_conf_t temp_conf; - int eff_clk_n = spi_ll_master_cal_clock(APB_CLK_FREQ, speed_hz, duty_cycle, &temp_conf.clock_reg); + int eff_clk_n = spi_ll_master_cal_clock(APB_CLK_FREQ, timing_param->clock_speed_hz, timing_param->duty_cycle, &temp_conf.clock_reg); //When the speed is too fast, we may need to use dummy cycles to compensate the reading. //But these don't work for full-duplex connections. - spi_hal_cal_timing(eff_clk_n, use_gpio, input_delay_ns, &temp_conf.timing_dummy, &temp_conf.timing_miso_delay); + spi_hal_cal_timing(eff_clk_n, timing_param->use_gpio, timing_param->input_delay_ns, &temp_conf.timing_dummy, &temp_conf.timing_miso_delay); #ifdef CONFIG_IDF_TARGET_ESP32 - const int freq_limit = spi_hal_get_freq_limit(use_gpio, input_delay_ns); + const int freq_limit = spi_hal_get_freq_limit(timing_param->use_gpio, timing_param->input_delay_ns); - SPI_HAL_CHECK(hal->half_duplex || temp_conf.timing_dummy == 0 || hal->no_compensate, + SPI_HAL_CHECK(timing_param->half_duplex || temp_conf.timing_dummy == 0 || timing_param->no_compensate, "When work in full-duplex mode at frequency > %.1fMHz, device cannot read correct data.\n\ Try to use IOMUX pins to increase the frequency limit, or use the half duplex mode.\n\ Please note the SPI master can only work at divisors of 80MHz, and the driver always tries to find the closest frequency to your configuration.\n\ @@ -127,4 +131,4 @@ int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns) } return APB_CLK_FREQ / (delay_apb_n + 1); -} \ No newline at end of file +} diff --git a/components/hal/spi_hal_iram.c b/components/hal/spi_hal_iram.c index 57f5607853ad..dc46b45c6f9e 100644 --- a/components/hal/spi_hal_iram.c +++ b/components/hal/spi_hal_iram.c @@ -17,29 +17,29 @@ #include "hal/spi_hal.h" -void spi_hal_setup_device(const spi_hal_context_t *hal) +void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev) { //Configure clock settings spi_dev_t *hw = hal->hw; #ifdef SOC_SPI_SUPPORT_AS_CS - spi_ll_master_set_cksel(hw, hal->cs_pin_id, hal->as_cs); + spi_ll_master_set_cksel(hw, dev->cs_pin_id, dev->as_cs); #endif - spi_ll_master_set_pos_cs(hw, hal->cs_pin_id, hal->positive_cs); - spi_ll_master_set_clock_by_reg(hw, &hal->timing_conf->clock_reg); + spi_ll_master_set_pos_cs(hw, dev->cs_pin_id, dev->positive_cs); + spi_ll_master_set_clock_by_reg(hw, &dev->timing_conf.clock_reg); //Configure bit order - spi_ll_set_rx_lsbfirst(hw, hal->rx_lsbfirst); - spi_ll_set_tx_lsbfirst(hw, hal->tx_lsbfirst); - spi_ll_master_set_mode(hw, hal->mode); + spi_ll_set_rx_lsbfirst(hw, dev->rx_lsbfirst); + spi_ll_set_tx_lsbfirst(hw, dev->tx_lsbfirst); + spi_ll_master_set_mode(hw, dev->mode); //Configure misc stuff - spi_ll_set_half_duplex(hw, hal->half_duplex); - spi_ll_set_sio_mode(hw, hal->sio); + spi_ll_set_half_duplex(hw, dev->half_duplex); + spi_ll_set_sio_mode(hw, dev->sio); //Configure CS pin and timing - spi_ll_master_set_cs_setup(hw, hal->cs_setup); - spi_ll_master_set_cs_hold(hw, hal->cs_hold); - spi_ll_master_select_cs(hw, hal->cs_pin_id); + spi_ll_master_set_cs_setup(hw, dev->cs_setup); + spi_ll_master_set_cs_hold(hw, dev->cs_hold); + spi_ll_master_select_cs(hw, dev->cs_pin_id); } -void spi_hal_setup_trans(const spi_hal_context_t *hal) +void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans) { spi_dev_t *hw = hal->hw; @@ -48,23 +48,23 @@ void spi_hal_setup_trans(const spi_hal_context_t *hal) //We should be done with the transmission. assert(spi_ll_get_running_cmd(hw) == 0); - spi_ll_master_set_io_mode(hw, hal->io_mode); + spi_ll_master_set_io_mode(hw, trans->io_mode); int extra_dummy = 0; //when no_dummy is not set and in half-duplex mode, sets the dummy bit if RX phase exist - if (hal->rcv_buffer && !hal->no_compensate && hal->half_duplex) { - extra_dummy = hal->timing_conf->timing_dummy; + if (trans->rcv_buffer && !dev->no_compensate && dev->half_duplex) { + extra_dummy = dev->timing_conf.timing_dummy; } //SPI iface needs to be configured for a delay in some cases. //configure dummy bits - spi_ll_set_dummy(hw, extra_dummy + hal->dummy_bits); + spi_ll_set_dummy(hw, extra_dummy + trans->dummy_bits); uint32_t miso_delay_num = 0; uint32_t miso_delay_mode = 0; - if (hal->timing_conf->timing_miso_delay < 0) { + if (dev->timing_conf.timing_miso_delay < 0) { //if the data comes too late, delay half a SPI clock to improve reading - switch (hal->mode) { + switch (dev->mode) { case 0: miso_delay_mode = 2; break; @@ -81,24 +81,24 @@ void spi_hal_setup_trans(const spi_hal_context_t *hal) miso_delay_num = 0; } else { //if the data is so fast that dummy_bit is used, delay some apb clocks to meet the timing - miso_delay_num = extra_dummy ? hal->timing_conf->timing_miso_delay : 0; + miso_delay_num = extra_dummy ? dev->timing_conf.timing_miso_delay : 0; miso_delay_mode = 0; } spi_ll_set_miso_delay(hw, miso_delay_mode, miso_delay_num); - spi_ll_set_mosi_bitlen(hw, hal->tx_bitlen); + spi_ll_set_mosi_bitlen(hw, trans->tx_bitlen); - if (hal->half_duplex) { - spi_ll_set_miso_bitlen(hw, hal->rx_bitlen); + if (dev->half_duplex) { + spi_ll_set_miso_bitlen(hw, trans->rx_bitlen); } else { //rxlength is not used in full-duplex mode - spi_ll_set_miso_bitlen(hw, hal->tx_bitlen); + spi_ll_set_miso_bitlen(hw, trans->tx_bitlen); } //Configure bit sizes, load addr and command - int cmdlen = hal->cmd_bits; - int addrlen = hal->addr_bits; - if (!hal->half_duplex && hal->cs_setup != 0) { + int cmdlen = trans->cmd_bits; + int addrlen = trans->addr_bits; + if (!dev->half_duplex && dev->cs_setup != 0) { /* The command and address phase is not compatible with cs_ena_pretrans * in full duplex mode. */ @@ -109,45 +109,56 @@ void spi_hal_setup_trans(const spi_hal_context_t *hal) spi_ll_set_addr_bitlen(hw, addrlen); spi_ll_set_command_bitlen(hw, cmdlen); - spi_ll_set_command(hw, hal->cmd, cmdlen, hal->tx_lsbfirst); - spi_ll_set_address(hw, hal->addr, addrlen, hal->tx_lsbfirst); + spi_ll_set_command(hw, trans->cmd, cmdlen, dev->tx_lsbfirst); + spi_ll_set_address(hw, trans->addr, addrlen, dev->tx_lsbfirst); + + //Save the transaction attributes for internal usage. + memcpy(&hal->trans_config, trans, sizeof(spi_hal_trans_config_t)); } -void spi_hal_prepare_data(const spi_hal_context_t *hal) +void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans) { spi_dev_t *hw = hal->hw; spi_ll_reset_dma(hw); //Fill DMA descriptors - if (hal->rcv_buffer) { + if (trans->rcv_buffer) { if (!hal->dma_enabled) { //No need to setup anything; we'll copy the result out of the work registers directly later. } else { - lldesc_setup_link(hal->dmadesc_rx, hal->rcv_buffer, ((hal->rx_bitlen + 7) / 8), true); - spi_ll_rxdma_start(hw, hal->dmadesc_rx); + lldesc_setup_link(hal->dma_config.dmadesc_rx, trans->rcv_buffer, ((trans->rx_bitlen + 7) / 8), true); + + spi_dma_ll_rx_reset(hal->dma_in); + + spi_ll_dma_rx_enable(hal->hw, 1); + spi_dma_ll_rx_start(hal->dma_in, hal->dma_config.dmadesc_rx); } } else { //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon if (hal->dma_enabled) { - spi_ll_rxdma_start(hw, 0); + spi_dma_ll_rx_start(hal->dma_in, 0); } } - if (hal->send_buffer) { + if (trans->send_buffer) { if (!hal->dma_enabled) { //Need to copy data to registers manually - spi_ll_write_buffer(hw, hal->send_buffer, hal->tx_bitlen); + spi_ll_write_buffer(hw, trans->send_buffer, trans->tx_bitlen); } else { - lldesc_setup_link(hal->dmadesc_tx, hal->send_buffer, (hal->tx_bitlen + 7) / 8, false); - spi_ll_txdma_start(hw, hal->dmadesc_tx); + lldesc_setup_link(hal->dma_config.dmadesc_tx, trans->send_buffer, (trans->tx_bitlen + 7) / 8, false); + + spi_dma_ll_tx_reset(hal->dma_out); + + spi_ll_dma_tx_enable(hal->hw, 1); + spi_dma_ll_tx_start(hal->dma_out, hal->dma_config.dmadesc_tx); } } //in ESP32 these registers should be configured after the DMA is set - if ((!hal->half_duplex && hal->rcv_buffer) || hal->send_buffer) { + if ((!dev->half_duplex && trans->rcv_buffer) || trans->send_buffer) { spi_ll_enable_mosi(hw, 1); } else { spi_ll_enable_mosi(hw, 0); } - spi_ll_enable_miso(hw, (hal->rcv_buffer) ? 1 : 0); + spi_ll_enable_miso(hw, (trans->rcv_buffer) ? 1 : 0); } void spi_hal_user_start(const spi_hal_context_t *hal) @@ -162,8 +173,10 @@ bool spi_hal_usr_is_done(const spi_hal_context_t *hal) void spi_hal_fetch_result(const spi_hal_context_t *hal) { - if (hal->rcv_buffer && !hal->dma_enabled) { + const spi_hal_trans_config_t *trans = &hal->trans_config; + + if (trans->rcv_buffer && !hal->dma_enabled) { //Need to copy from SPI regs to result buffer. - spi_ll_read_buffer(hal->hw, hal->rcv_buffer, hal->rx_bitlen); + spi_ll_read_buffer(hal->hw, trans->rcv_buffer, trans->rx_bitlen); } } diff --git a/components/hal/spi_slave_hd_hal.c b/components/hal/spi_slave_hd_hal.c index d762fbd07f07..099b6c6c50ff 100644 --- a/components/hal/spi_slave_hd_hal.c +++ b/components/hal/spi_slave_hd_hal.c @@ -19,27 +19,25 @@ #include "esp_attr.h" #include "esp_err.h" #include "sdkconfig.h" - #include "soc/spi_periph.h" #include "soc/lldesc.h" - #include "hal/spi_slave_hd_hal.h" -void slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_hal_config_t *config) +void spi_slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_hal_config_t *hal_config) { memset(hal, 0, sizeof(spi_slave_hd_hal_context_t)); - spi_dev_t* hw = SPI_LL_GET_HW(config->host_id); + spi_dev_t* hw = SPI_LL_GET_HW(hal_config->host_id); hal->dev = hw; //Configure slave spi_ll_slave_hd_init(hw); - spi_ll_set_addr_bitlen(hw, config->address_bits); - spi_ll_set_command_bitlen(hw, config->command_bits); - spi_ll_set_dummy(hw, config->dummy_bits); - spi_ll_set_rx_lsbfirst(hw, config->rx_lsbfirst); - spi_ll_set_tx_lsbfirst(hw, config->tx_lsbfirst); - spi_ll_slave_set_mode(hw, config->mode, (config->dma_chan != 0)); + spi_ll_set_addr_bitlen(hw, hal_config->address_bits); + spi_ll_set_command_bitlen(hw, hal_config->command_bits); + spi_ll_set_dummy(hw, hal_config->dummy_bits); + spi_ll_set_rx_lsbfirst(hw, hal_config->rx_lsbfirst); + spi_ll_set_tx_lsbfirst(hw, hal_config->tx_lsbfirst); + spi_ll_slave_set_mode(hw, hal_config->mode, (hal_config->dma_chan != 0)); spi_ll_disable_intr(hw, UINT32_MAX); spi_ll_clear_intr(hw, UINT32_MAX); @@ -66,7 +64,7 @@ void slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_hal_c SPI_LL_TRANS_LEN_COND_RDBUF | SPI_LL_TRANS_LEN_COND_RDDMA); - spi_ll_slave_set_seg_mode(hw, true); + spi_ll_slave_set_seg_mode(hal->dev, true); } void spi_slave_hd_hal_rxdma(spi_slave_hd_hal_context_t *hal, uint8_t *out_buf, size_t len) @@ -179,4 +177,4 @@ int spi_slave_hd_hal_rxdma_get_len(spi_slave_hd_hal_context_t *hal) { lldesc_t* desc = &hal->dmadesc_rx[0]; return lldesc_get_received_len(desc, NULL); -} \ No newline at end of file +}