mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'feat/spi_slave_edma_psram_support' into 'master'
feat(driver_spi): spi slave edma psram support Closes IDFCI-7326 and IDF-15125 See merge request espressif/esp-idf!44317
This commit is contained in:
@@ -266,10 +266,9 @@ void spitest_gpio_output_sel(uint32_t gpio_num, int func, uint32_t signal_idx);
|
|||||||
//use this function to fix the input source when assign multiple functions to a same pin
|
//use this function to fix the input source when assign multiple functions to a same pin
|
||||||
void spitest_gpio_input_sel(uint32_t gpio_num, int func, uint32_t signal_idx);
|
void spitest_gpio_input_sel(uint32_t gpio_num, int func, uint32_t signal_idx);
|
||||||
|
|
||||||
//Note this cs_num is the ID of the connected devices' ID, e.g. if 2 devices are connected to the bus,
|
// Connect master and slave to the same pin
|
||||||
//then the cs_num of the 1st and 2nd devices are 0 and 1 respectively.
|
// master_id and slave_id are the IDs of the master and slave devices, set 0 for each to use soft master/slave.
|
||||||
//Enable `soft_master` to connect to soft spi master instead of hardware master.
|
void same_pin_func_sel(spi_host_device_t master_id, spi_host_device_t slave_id, spi_bus_config_t bus, uint8_t cs_pin);
|
||||||
void same_pin_func_sel(spi_bus_config_t bus, uint8_t cs_pin, uint8_t cs_dev_id, bool soft_master);
|
|
||||||
|
|
||||||
// Soft simulated spi master host for slave testing
|
// Soft simulated spi master host for slave testing
|
||||||
// `speed_hz` max 500kHz
|
// `speed_hz` max 500kHz
|
||||||
|
|||||||
@@ -224,20 +224,20 @@ void spitest_gpio_input_sel(uint32_t gpio_num, int func, uint32_t signal_idx)
|
|||||||
esp_rom_gpio_connect_in_signal(gpio_num, signal_idx, 0);
|
esp_rom_gpio_connect_in_signal(gpio_num, signal_idx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void same_pin_func_sel(spi_bus_config_t bus, uint8_t cs_pin, uint8_t cs_dev_id, bool soft_master)
|
void same_pin_func_sel(spi_host_device_t master_id, spi_host_device_t slave_id, spi_bus_config_t bus, uint8_t cs_pin)
|
||||||
{
|
{
|
||||||
spitest_gpio_output_sel(bus.mosi_io_num, FUNC_GPIO, soft_master ? SIG_GPIO_OUT_IDX : spi_periph_signal[TEST_SPI_HOST].spid_out);
|
spitest_gpio_output_sel(bus.mosi_io_num, FUNC_GPIO, (!master_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[master_id].spid_out);
|
||||||
spitest_gpio_input_sel(bus.mosi_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spid_in);
|
spitest_gpio_input_sel(bus.mosi_io_num, FUNC_GPIO, (!slave_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[slave_id].spid_in);
|
||||||
|
|
||||||
spitest_gpio_output_sel(bus.miso_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiq_out);
|
spitest_gpio_input_sel(bus.miso_io_num, FUNC_GPIO, (!master_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[master_id].spiq_in);
|
||||||
spitest_gpio_input_sel(bus.miso_io_num, FUNC_GPIO, soft_master ? SIG_GPIO_OUT_IDX : spi_periph_signal[TEST_SPI_HOST].spiq_in);
|
spitest_gpio_output_sel(bus.miso_io_num, FUNC_GPIO, (!slave_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[slave_id].spiq_out);
|
||||||
|
|
||||||
gpio_set_level(cs_pin, 1); //ensure CS is inactive when select to soft_master and before transaction start
|
gpio_set_level(cs_pin, 1); //ensure CS is inactive when select 0 for soft_master and before transaction start
|
||||||
spitest_gpio_output_sel(cs_pin, FUNC_GPIO, soft_master ? SIG_GPIO_OUT_IDX : spi_periph_signal[TEST_SPI_HOST].spics_out[cs_dev_id]);
|
spitest_gpio_output_sel(cs_pin, FUNC_GPIO, (!master_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[master_id].spics_out[0]);
|
||||||
spitest_gpio_input_sel(cs_pin, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spics_in);
|
spitest_gpio_input_sel(cs_pin, FUNC_GPIO, (!slave_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[slave_id].spics_in);
|
||||||
|
|
||||||
spitest_gpio_output_sel(bus.sclk_io_num, FUNC_GPIO, soft_master ? SIG_GPIO_OUT_IDX : spi_periph_signal[TEST_SPI_HOST].spiclk_out);
|
spitest_gpio_output_sel(bus.sclk_io_num, FUNC_GPIO, (!master_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[master_id].spiclk_out);
|
||||||
spitest_gpio_input_sel(bus.sclk_io_num, FUNC_GPIO, spi_periph_signal[TEST_SLAVE_HOST].spiclk_in);
|
spitest_gpio_input_sel(bus.sclk_io_num, FUNC_GPIO, (!slave_id) ? SIG_GPIO_OUT_IDX : spi_periph_signal[slave_id].spiclk_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GPIO_MAX_FREQ 500*1000 //max of soft spi clock at delay(0)
|
#define GPIO_MAX_FREQ 500*1000 //max of soft spi clock at delay(0)
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ typedef struct {
|
|||||||
gdma_channel_handle_t gdma_chan;
|
gdma_channel_handle_t gdma_chan;
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||||
//On ESP32S2, there is no gdma, so use SPI DMA to transmit data
|
//On ESP32S2, there is no gdma, so use SPI DMA to transmit data
|
||||||
spi_dma_ctx_t *spi_dma_ctx;
|
|
||||||
spi_dev_t *adc_spi_dev;
|
spi_dev_t *adc_spi_dev;
|
||||||
|
uint32_t dma_chan_id;
|
||||||
#elif CONFIG_IDF_TARGET_ESP32
|
#elif CONFIG_IDF_TARGET_ESP32
|
||||||
//On ESP32, there is no gdma, so use I2S DMA to transmit data
|
//On ESP32, there is no gdma, so use I2S DMA to transmit data
|
||||||
i2s_dev_t *adc_i2s_dev;
|
i2s_dev_t *adc_i2s_dev;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ static IRAM_ATTR void adc_dma_intr_handler(void *arg)
|
|||||||
bool conversion_finish = spi_ll_get_intr(ctx->adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
bool conversion_finish = spi_ll_get_intr(ctx->adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
||||||
if (conversion_finish) {
|
if (conversion_finish) {
|
||||||
spi_ll_clear_intr(ctx->adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
spi_ll_clear_intr(ctx->adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
||||||
intptr_t desc_addr = spi_dma_ll_get_in_suc_eof_desc_addr(ctx->adc_dma.adc_spi_dev, ctx->adc_dma.spi_dma_ctx->rx_dma_chan.chan_id);
|
intptr_t desc_addr = spi_dma_ll_get_in_suc_eof_desc_addr(ctx->adc_dma.adc_spi_dev, ctx->adc_dma.dma_chan_id);
|
||||||
ctx->rx_eof_desc_addr = desc_addr;
|
ctx->rx_eof_desc_addr = desc_addr;
|
||||||
need_yield = ctx->adc_intr_func(ctx);
|
need_yield = ctx->adc_intr_func(ctx);
|
||||||
}
|
}
|
||||||
@@ -58,10 +58,11 @@ esp_err_t adc_dma_init(adc_dma_t *adc_dma)
|
|||||||
if (spi_success != true) {
|
if (spi_success != true) {
|
||||||
return ESP_FAIL;
|
return ESP_FAIL;
|
||||||
}
|
}
|
||||||
ret = spicommon_dma_chan_alloc(SPI3_HOST, SPI_DMA_CH_AUTO, &(adc_dma->spi_dma_ctx));
|
ret = spicommon_dma_chan_alloc(ADC_DMA_SPI_HOST, SPI_DMA_CH_AUTO);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
adc_dma->dma_chan_id = spi_bus_get_dma_ctx(ADC_DMA_SPI_HOST)->rx_dma_chan.chan_id;
|
||||||
adc_dma->adc_spi_dev = SPI_LL_GET_HW(ADC_DMA_SPI_HOST);
|
adc_dma->adc_spi_dev = SPI_LL_GET_HW(ADC_DMA_SPI_HOST);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@@ -70,7 +71,7 @@ esp_err_t adc_dma_init(adc_dma_t *adc_dma)
|
|||||||
esp_err_t adc_dma_deinit(adc_dma_t adc_dma)
|
esp_err_t adc_dma_deinit(adc_dma_t adc_dma)
|
||||||
{
|
{
|
||||||
esp_intr_free(adc_dma.dma_intr_hdl);
|
esp_intr_free(adc_dma.dma_intr_hdl);
|
||||||
spicommon_dma_chan_free(adc_dma.spi_dma_ctx);
|
spicommon_dma_chan_free(ADC_DMA_SPI_HOST);
|
||||||
spicommon_periph_free(ADC_DMA_SPI_HOST);
|
spicommon_periph_free(ADC_DMA_SPI_HOST);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
@@ -79,7 +80,7 @@ esp_err_t adc_dma_start(adc_dma_t adc_dma, dma_descriptor_t *addr)
|
|||||||
{
|
{
|
||||||
spi_ll_clear_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
spi_ll_clear_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
||||||
spi_ll_enable_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
spi_ll_enable_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
||||||
spi_dma_ll_rx_start(adc_dma.adc_spi_dev, adc_dma.spi_dma_ctx->rx_dma_chan.chan_id, (lldesc_t *)addr);
|
spi_dma_ll_rx_start(adc_dma.adc_spi_dev, adc_dma.dma_chan_id, (lldesc_t *)addr);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,12 +88,12 @@ esp_err_t adc_dma_stop(adc_dma_t adc_dma)
|
|||||||
{
|
{
|
||||||
spi_ll_disable_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
spi_ll_disable_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
||||||
spi_ll_clear_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
spi_ll_clear_intr(adc_dma.adc_spi_dev, ADC_DMA_INTR_MASK);
|
||||||
spi_dma_ll_rx_stop(adc_dma.adc_spi_dev, adc_dma.spi_dma_ctx->rx_dma_chan.chan_id);
|
spi_dma_ll_rx_stop(adc_dma.adc_spi_dev, adc_dma.dma_chan_id);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t adc_dma_reset(adc_dma_t adc_dma)
|
esp_err_t adc_dma_reset(adc_dma_t adc_dma)
|
||||||
{
|
{
|
||||||
spi_dma_ll_rx_reset(adc_dma.adc_spi_dev, adc_dma.spi_dma_ctx->rx_dma_chan.chan_id);
|
spi_dma_ll_rx_reset(adc_dma.adc_spi_dev, adc_dma.dma_chan_id);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
void *periph_dev; /* DMA peripheral device address */
|
void *periph_dev; /* DMA peripheral device address */
|
||||||
uint32_t dma_chan;
|
uint32_t dma_chan;
|
||||||
spi_dma_ctx_t *spi_dma_ctx; /* spi_dma context */
|
|
||||||
intr_handle_t intr_handle; /* Interrupt handle */
|
intr_handle_t intr_handle; /* Interrupt handle */
|
||||||
bool use_apll; /* Whether use APLL as digital controller clock source */
|
bool use_apll; /* Whether use APLL as digital controller clock source */
|
||||||
} dac_dma_periph_spi_t;
|
} dac_dma_periph_spi_t;
|
||||||
@@ -145,10 +144,9 @@ esp_err_t dac_dma_periph_init(uint32_t freq_hz, bool is_alternate, bool is_apll)
|
|||||||
/* When transmit alternately, twice frequency is needed to guarantee the convert frequency in one channel */
|
/* When transmit alternately, twice frequency is needed to guarantee the convert frequency in one channel */
|
||||||
uint32_t trans_freq_hz = freq_hz * (is_alternate ? 2 : 1);
|
uint32_t trans_freq_hz = freq_hz * (is_alternate ? 2 : 1);
|
||||||
ESP_GOTO_ON_ERROR(s_dac_dma_periph_set_clock(trans_freq_hz, is_apll), err, TAG, "Failed to set clock of DMA peripheral");
|
ESP_GOTO_ON_ERROR(s_dac_dma_periph_set_clock(trans_freq_hz, is_apll), err, TAG, "Failed to set clock of DMA peripheral");
|
||||||
ESP_GOTO_ON_ERROR(spicommon_dma_chan_alloc(DAC_DMA_PERIPH_SPI_HOST, SPI_DMA_CH_AUTO, &s_ddp->spi_dma_ctx),
|
ESP_GOTO_ON_ERROR(spicommon_dma_chan_alloc(DAC_DMA_PERIPH_SPI_HOST, SPI_DMA_CH_AUTO),
|
||||||
err, TAG, "Failed to allocate dma peripheral channel");
|
err, TAG, "Failed to allocate dma peripheral channel");
|
||||||
|
s_ddp->dma_chan = spi_bus_get_dma_ctx(DAC_DMA_PERIPH_SPI_HOST)->rx_dma_chan.chan_id;
|
||||||
s_ddp->dma_chan = s_ddp->spi_dma_ctx->rx_dma_chan.chan_id;
|
|
||||||
spi_ll_enable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
|
spi_ll_enable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
|
||||||
dac_ll_digi_set_convert_mode(is_alternate);
|
dac_ll_digi_set_convert_mode(is_alternate);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -162,7 +160,7 @@ esp_err_t dac_dma_periph_deinit(void)
|
|||||||
ESP_RETURN_ON_FALSE(s_ddp != NULL, ESP_ERR_INVALID_STATE, TAG, "DAC DMA peripheral is not initialized");
|
ESP_RETURN_ON_FALSE(s_ddp != NULL, ESP_ERR_INVALID_STATE, TAG, "DAC DMA peripheral is not initialized");
|
||||||
ESP_RETURN_ON_FALSE(s_ddp->intr_handle == NULL, ESP_ERR_INVALID_STATE, TAG, "The interrupt is not deregistered yet");
|
ESP_RETURN_ON_FALSE(s_ddp->intr_handle == NULL, ESP_ERR_INVALID_STATE, TAG, "The interrupt is not deregistered yet");
|
||||||
if (s_ddp->dma_chan) {
|
if (s_ddp->dma_chan) {
|
||||||
ESP_RETURN_ON_ERROR(spicommon_dma_chan_free(s_ddp->spi_dma_ctx), TAG, "Failed to free dma peripheral channel");
|
ESP_RETURN_ON_ERROR(spicommon_dma_chan_free(DAC_DMA_PERIPH_SPI_HOST), TAG, "Failed to free dma peripheral channel");
|
||||||
}
|
}
|
||||||
ESP_RETURN_ON_FALSE(spicommon_periph_free(DAC_DMA_PERIPH_SPI_HOST), ESP_FAIL, TAG, "Failed to release DAC DMA peripheral");
|
ESP_RETURN_ON_FALSE(spicommon_periph_free(DAC_DMA_PERIPH_SPI_HOST), ESP_FAIL, TAG, "Failed to release DAC DMA peripheral");
|
||||||
spi_ll_disable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
|
spi_ll_disable_intr(s_ddp->periph_dev, SPI_LL_INTR_OUT_EOF | SPI_LL_INTR_OUT_TOTAL_EOF);
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ esp_err_t spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transact
|
|||||||
* @return
|
* @return
|
||||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||||
* - ESP_ERR_NOT_SUPPORTED if flag `SPI_SLAVE_NO_RETURN_RESULT` is set
|
* - ESP_ERR_NOT_SUPPORTED if flag `SPI_SLAVE_NO_RETURN_RESULT` is set
|
||||||
|
* - ESP_ERR_INVALID_STATE if dma over/underflow error occurs during psram transfer
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
*/
|
*/
|
||||||
esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transaction_t **trans_desc, uint32_t ticks_to_wait);
|
esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transaction_t **trans_desc, uint32_t ticks_to_wait);
|
||||||
@@ -204,6 +205,7 @@ esp_err_t spi_slave_get_trans_result(spi_host_device_t host, spi_slave_transacti
|
|||||||
* out.
|
* out.
|
||||||
* @return
|
* @return
|
||||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||||
|
* - ESP_ERR_INVALID_STATE if dma over/underflow error occurs during psram transfer
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
*/
|
*/
|
||||||
esp_err_t spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait);
|
esp_err_t spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait);
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ esp_err_t spi_slave_hd_queue_trans(spi_host_device_t host_id, spi_slave_chan_t c
|
|||||||
* - ESP_OK: on success
|
* - ESP_OK: on success
|
||||||
* - ESP_ERR_INVALID_ARG: Function is not valid
|
* - ESP_ERR_INVALID_ARG: Function is not valid
|
||||||
* - ESP_ERR_TIMEOUT: There's no transaction done before timeout
|
* - ESP_ERR_TIMEOUT: There's no transaction done before timeout
|
||||||
* - ESP_ERR_INVALID_STATE: Function called in invalid state. This API should be called under segment mode.
|
* - ESP_ERR_INVALID_STATE: Function called in invalid state. This API should be called under segment mode. Or DMA hardware over/underflow occurred.
|
||||||
*/
|
*/
|
||||||
esp_err_t spi_slave_hd_get_trans_res(spi_host_device_t host_id, spi_slave_chan_t chan, spi_slave_hd_data_t **out_trans, uint32_t timeout);
|
esp_err_t spi_slave_hd_get_trans_res(spi_host_device_t host_id, spi_slave_chan_t chan, spi_slave_hd_data_t **out_trans, uint32_t timeout);
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ esp_err_t spi_slave_hd_append_trans(spi_host_device_t host_id, spi_slave_chan_t
|
|||||||
* - ESP_OK: on success
|
* - ESP_OK: on success
|
||||||
* - ESP_ERR_INVALID_ARG: Function is not valid
|
* - ESP_ERR_INVALID_ARG: Function is not valid
|
||||||
* - ESP_ERR_TIMEOUT: There's no transaction done before timeout
|
* - ESP_ERR_TIMEOUT: There's no transaction done before timeout
|
||||||
* - ESP_ERR_INVALID_STATE: Function called in invalid state. This API should be called under append mode.
|
* - ESP_ERR_INVALID_STATE: Function called in invalid state. This API should be called under append mode. Or DMA hardware over/underflow occurred.
|
||||||
*/
|
*/
|
||||||
esp_err_t spi_slave_hd_get_append_trans_res(spi_host_device_t host_id, spi_slave_chan_t chan, spi_slave_hd_data_t **out_trans, uint32_t timeout);
|
esp_err_t spi_slave_hd_get_append_trans_res(spi_host_device_t host_id, spi_slave_chan_t chan, spi_slave_hd_data_t **out_trans, uint32_t timeout);
|
||||||
|
|
||||||
|
|||||||
@@ -104,19 +104,18 @@ esp_err_t spicommon_bus_free(spi_host_device_t host_id);
|
|||||||
*
|
*
|
||||||
* @param host_id SPI host ID
|
* @param host_id SPI host ID
|
||||||
* @param dma_chan DMA channel to be used
|
* @param dma_chan DMA channel to be used
|
||||||
* @param out_dma_ctx Actual DMA channel context (if you choose to assign a specific DMA channel, this will be the channel you assigned before)
|
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: On success
|
* - ESP_OK: On success
|
||||||
* - ESP_ERR_NO_MEM: No enough memory
|
* - ESP_ERR_NO_MEM: No enough memory
|
||||||
* - ESP_ERR_NOT_FOUND: There is no available DMA channel
|
* - ESP_ERR_NOT_FOUND: There is no available DMA channel
|
||||||
*/
|
*/
|
||||||
esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma_chan, spi_dma_ctx_t **out_dma_ctx);
|
esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma_chan);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Alloc DMA descriptors for SPI
|
* @brief Alloc DMA descriptors for SPI
|
||||||
*
|
*
|
||||||
* @param dma_ctx DMA context returned by `spicommon_dma_chan_alloc`
|
* @param[in] host_id SPI host ID
|
||||||
* @param[in] cfg_max_sz Expected maximum transfer size, in bytes.
|
* @param[in] cfg_max_sz Expected maximum transfer size, in bytes.
|
||||||
* @param[out] actual_max_sz Actual max transfer size one transaction can be, in bytes.
|
* @param[out] actual_max_sz Actual max transfer size one transaction can be, in bytes.
|
||||||
*
|
*
|
||||||
@@ -124,7 +123,7 @@ esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma
|
|||||||
* - ESP_OK: On success
|
* - ESP_OK: On success
|
||||||
* - ESP_ERR_NO_MEM: No enough memory
|
* - ESP_ERR_NO_MEM: No enough memory
|
||||||
*/
|
*/
|
||||||
esp_err_t spicommon_dma_desc_alloc(spi_dma_ctx_t *dma_ctx, int cfg_max_sz, int *actual_max_sz);
|
esp_err_t spicommon_dma_desc_alloc(spi_host_device_t host_id, int cfg_max_sz, int *actual_max_sz);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setupt/Configure dma descriptor link list
|
* Setupt/Configure dma descriptor link list
|
||||||
@@ -136,15 +135,40 @@ esp_err_t spicommon_dma_desc_alloc(spi_dma_ctx_t *dma_ctx, int cfg_max_sz, int *
|
|||||||
*/
|
*/
|
||||||
void spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, int len, bool is_rx);
|
void spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, int len, bool is_rx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setup private buffer for DMA transfer
|
||||||
|
*
|
||||||
|
* @param host_id SPI host ID to access the DMA context
|
||||||
|
* @param buffer buffer to be setup
|
||||||
|
* @param len length of buffer, in byte
|
||||||
|
* @param is_tx if buffer is for tx/transmit direction
|
||||||
|
* @param psram_prefer if psram is preferred
|
||||||
|
* @param auto_malloc if auto malloc is enabled
|
||||||
|
* @param ret_buffer return buffer, which is the buffer that is actually used for DMA transfer
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: On success
|
||||||
|
* - ESP_ERR_NO_MEM: No enough memory
|
||||||
|
*/
|
||||||
|
esp_err_t spicommon_dma_setup_priv_buffer(spi_host_device_t host_id, uint32_t *buffer, uint32_t len, bool is_tx, bool psram_prefer, bool auto_malloc, uint32_t **ret_buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Memory barrier for DMA RX buffer
|
||||||
|
*
|
||||||
|
* @param host_id SPI host ID
|
||||||
|
* @param rx_buffer RX buffer
|
||||||
|
*/
|
||||||
|
void spicommon_dma_rx_mb(spi_host_device_t host_id, void *rx_buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Free DMA for SPI
|
* @brief Free DMA for SPI
|
||||||
*
|
*
|
||||||
* @param dma_ctx spi_dma_ctx_t struct pointer
|
* @param host_id SPI host ID
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: On success
|
* - ESP_OK: On success
|
||||||
*/
|
*/
|
||||||
esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx);
|
esp_err_t spicommon_dma_chan_free(spi_host_device_t host_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Connect a SPI peripheral to GPIO pins
|
* @brief Connect a SPI peripheral to GPIO pins
|
||||||
@@ -180,7 +204,7 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx);
|
|||||||
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
* - ESP_ERR_INVALID_ARG if parameter is invalid
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
*/
|
*/
|
||||||
esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t *flags_o, uint64_t *io_reserved);
|
esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t *flags_o);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Free the IO used by a SPI peripheral
|
* @brief Free the IO used by a SPI peripheral
|
||||||
@@ -292,7 +316,7 @@ void spicommon_dmaworkaround_transfer_active(int dmachan);
|
|||||||
* @param host_id The specified host to get attribute
|
* @param host_id The specified host to get attribute
|
||||||
* @return (Const) Pointer to the attributes
|
* @return (Const) Pointer to the attributes
|
||||||
*/
|
*/
|
||||||
const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id);
|
spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the dma context of a specified SPI bus.
|
* @brief Get the dma context of a specified SPI bus.
|
||||||
@@ -300,7 +324,7 @@ const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id);
|
|||||||
* @param host_id The specified host to get attribute
|
* @param host_id The specified host to get attribute
|
||||||
* @return (Const) Pointer to the dma context
|
* @return (Const) Pointer to the dma context
|
||||||
*/
|
*/
|
||||||
const spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id);
|
spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Register a function to a initialized bus to make it called when deinitializing the bus.
|
* @brief Register a function to a initialized bus to make it called when deinitializing the bus.
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_cache.h"
|
#include "esp_cache.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_memory_utils.h"
|
||||||
#include "driver/spi_master.h"
|
#include "driver/spi_master.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "esp_private/gpio.h"
|
#include "esp_private/gpio.h"
|
||||||
@@ -28,6 +29,9 @@
|
|||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
#include "soc/dport_reg.h"
|
#include "soc/dport_reg.h"
|
||||||
#endif
|
#endif
|
||||||
|
#if CONFIG_SPIRAM
|
||||||
|
#include "esp_private/esp_psram_mspi.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if CONFIG_SPI_MASTER_ISR_IN_IRAM || CONFIG_SPI_SLAVE_ISR_IN_IRAM
|
#if CONFIG_SPI_MASTER_ISR_IN_IRAM || CONFIG_SPI_SLAVE_ISR_IN_IRAM
|
||||||
#define SPI_COMMON_ISR_ATTR IRAM_ATTR
|
#define SPI_COMMON_ISR_ATTR IRAM_ATTR
|
||||||
@@ -62,11 +66,11 @@ typedef struct {
|
|||||||
spi_destroy_func_t destroy_func;
|
spi_destroy_func_t destroy_func;
|
||||||
void* destroy_arg;
|
void* destroy_arg;
|
||||||
spi_bus_attr_t bus_attr;
|
spi_bus_attr_t bus_attr;
|
||||||
spi_dma_ctx_t *dma_ctx;
|
|
||||||
} spicommon_bus_context_t;
|
} spicommon_bus_context_t;
|
||||||
|
|
||||||
static spicommon_bus_context_t s_mainbus = SPI_MAIN_BUS_DEFAULT();
|
static spicommon_bus_context_t s_mainbus = SPI_MAIN_BUS_DEFAULT();
|
||||||
static spicommon_bus_context_t* bus_ctx[SOC_SPI_PERIPH_NUM] = {&s_mainbus};
|
static spicommon_bus_context_t* bus_ctx[SOC_SPI_PERIPH_NUM] = {&s_mainbus};
|
||||||
|
static spi_dma_ctx_t *spi_dma_ctx[SOC_SPI_PERIPH_NUM];
|
||||||
|
|
||||||
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
#if CONFIG_SPI_FLASH_SHARE_SPI1_BUS
|
||||||
/* The lock for the share SPI1 bus is registered here in a constructor due to need to access the context
|
/* The lock for the share SPI1 bus is registered here in a constructor due to need to access the context
|
||||||
@@ -90,6 +94,10 @@ esp_err_t spicommon_bus_alloc(spi_host_device_t host_id, const char *name)
|
|||||||
PERIPH_RCC_ATOMIC() {
|
PERIPH_RCC_ATOMIC() {
|
||||||
spi_ll_enable_clock(host_id, true);
|
spi_ll_enable_clock(host_id, true);
|
||||||
}
|
}
|
||||||
|
// Get cache alignment constraints
|
||||||
|
esp_cache_get_alignment(MALLOC_CAP_DMA, &ctx->bus_attr.cache_align_int);
|
||||||
|
esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &ctx->bus_attr.cache_align_ext);
|
||||||
|
|
||||||
ctx->host_id = host_id;
|
ctx->host_id = host_id;
|
||||||
bus_ctx[host_id] = ctx;
|
bus_ctx[host_id] = ctx;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@@ -107,20 +115,6 @@ esp_err_t spicommon_bus_free(spi_host_device_t host_id)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SOC_GDMA_SUPPORTED
|
|
||||||
//NOTE!! If both A and B are not defined, '#if (A==B)' is true, because GCC use 0 stand for undefined symbol
|
|
||||||
#if defined(SOC_GDMA_BUS_AXI) && (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AXI)
|
|
||||||
#define SPI_GDMA_NEW_CHANNEL gdma_new_axi_channel
|
|
||||||
#elif defined(SOC_GDMA_BUS_AHB) && (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AHB)
|
|
||||||
#define SPI_GDMA_NEW_CHANNEL gdma_new_ahb_channel
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
//Each bit stands for 1 dma channel, BIT(0) should be used for SPI1
|
|
||||||
static uint8_t spi_dma_chan_enabled = 0;
|
|
||||||
static portMUX_TYPE spi_dma_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
|
||||||
#endif //!SOC_GDMA_SUPPORTED
|
|
||||||
|
|
||||||
static inline bool is_valid_host(spi_host_device_t host)
|
static inline bool is_valid_host(spi_host_device_t host)
|
||||||
{
|
{
|
||||||
#if (SOC_SPI_PERIPH_NUM == 2)
|
#if (SOC_SPI_PERIPH_NUM == 2)
|
||||||
@@ -142,44 +136,52 @@ int spicommon_irqdma_source_for_host(spi_host_device_t host)
|
|||||||
|
|
||||||
//----------------------------------------------------------alloc dma periph-------------------------------------------------------//
|
//----------------------------------------------------------alloc dma periph-------------------------------------------------------//
|
||||||
#if !SOC_GDMA_SUPPORTED
|
#if !SOC_GDMA_SUPPORTED
|
||||||
|
//Each bit stands for 1 dma channel, BIT(0) should be used for SPI1
|
||||||
|
static uint8_t spi_dma_chan_enabled = 0;
|
||||||
|
static portMUX_TYPE spi_dma_spinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
|
||||||
#if SPI_LL_DMA_SHARED
|
static inline void _spicommon_dma_rcc_clock_ctrl(spi_dma_chan_t dma_chan, bool enable)
|
||||||
static inline shared_periph_module_t get_dma_periph(int dma_chan)
|
|
||||||
{
|
{
|
||||||
assert(dma_chan >= 1 && dma_chan <= SPI_LL_DMA_CHANNEL_NUM);
|
#if SPI_LL_DMA_SHARED
|
||||||
|
shared_periph_module_t dma_periph;
|
||||||
if (dma_chan == 1) {
|
if (dma_chan == 1) {
|
||||||
return PERIPH_SPI2_DMA_MODULE;
|
dma_periph = PERIPH_SPI2_DMA_MODULE;
|
||||||
} else if (dma_chan == 2) {
|
} else if (dma_chan == 2) {
|
||||||
return PERIPH_SPI3_DMA_MODULE;
|
dma_periph = PERIPH_SPI3_DMA_MODULE;
|
||||||
} else {
|
} else {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (enable) {
|
||||||
|
PERIPH_RCC_ACQUIRE_ATOMIC(dma_periph, ref_count) {
|
||||||
|
if (ref_count == 0) {
|
||||||
|
spi_dma_ll_enable_bus_clock(dma_chan, true);
|
||||||
|
spi_dma_ll_reset_register(dma_chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PERIPH_RCC_RELEASE_ATOMIC(dma_periph, ref_count) {
|
||||||
|
if (ref_count == 0) {
|
||||||
|
spi_dma_ll_enable_bus_clock(dma_chan, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
PERIPH_RCC_ATOMIC() {
|
||||||
|
spi_dma_ll_enable_bus_clock(dma_chan, enable);
|
||||||
|
spi_dma_ll_reset_register(dma_chan);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static bool claim_dma_chan(int dma_chan, uint32_t *out_actual_dma_chan)
|
static bool claim_dma_chan(int dma_chan, uint32_t *out_actual_dma_chan)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
portENTER_CRITICAL(&spi_dma_spinlock);
|
portENTER_CRITICAL(&spi_dma_spinlock);
|
||||||
bool is_used = (BIT(dma_chan) & spi_dma_chan_enabled);
|
if (!(BIT(dma_chan) & spi_dma_chan_enabled)) {
|
||||||
if (!is_used) {
|
|
||||||
spi_dma_chan_enabled |= BIT(dma_chan);
|
spi_dma_chan_enabled |= BIT(dma_chan);
|
||||||
#if SPI_LL_DMA_SHARED
|
_spicommon_dma_rcc_clock_ctrl(dma_chan, true);
|
||||||
PERIPH_RCC_ACQUIRE_ATOMIC(get_dma_periph(dma_chan), ref_count) {
|
|
||||||
//esp32s2: dma_chan index is same as spi host_id, no matter dma_chan_auto or not
|
|
||||||
if (ref_count == 0) {
|
|
||||||
spi_dma_ll_enable_bus_clock(dma_chan, true);
|
|
||||||
spi_dma_ll_reset_register(dma_chan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
PERIPH_RCC_ATOMIC() {
|
|
||||||
//esp32: have only one spi_dma
|
|
||||||
spi_dma_ll_enable_bus_clock(dma_chan, true);
|
|
||||||
spi_dma_ll_reset_register(dma_chan);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
*out_actual_dma_chan = dma_chan;
|
*out_actual_dma_chan = dma_chan;
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
@@ -249,6 +251,12 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else //SOC_GDMA_SUPPORTED
|
#else //SOC_GDMA_SUPPORTED
|
||||||
|
//NOTE!! If both A and B are not defined, '#if (A==B)' is true, because GCC use 0 stand for undefined symbol
|
||||||
|
#if defined(SOC_GDMA_BUS_AXI) && (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AXI)
|
||||||
|
#define SPI_GDMA_NEW_CHANNEL gdma_new_axi_channel
|
||||||
|
#elif defined(SOC_GDMA_BUS_AHB) && (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AHB)
|
||||||
|
#define SPI_GDMA_NEW_CHANNEL gdma_new_ahb_channel
|
||||||
|
#endif
|
||||||
|
|
||||||
static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_chan, spi_dma_ctx_t *dma_ctx)
|
static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_chan, spi_dma_ctx_t *dma_ctx)
|
||||||
{
|
{
|
||||||
@@ -291,7 +299,7 @@ static esp_err_t alloc_dma_chan(spi_host_device_t host_id, spi_dma_chan_t dma_ch
|
|||||||
}
|
}
|
||||||
#endif //#if !SOC_GDMA_SUPPORTED
|
#endif //#if !SOC_GDMA_SUPPORTED
|
||||||
|
|
||||||
esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma_chan, spi_dma_ctx_t **out_dma_ctx)
|
esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma_chan)
|
||||||
{
|
{
|
||||||
assert(is_valid_host(host_id));
|
assert(is_valid_host(host_id));
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
@@ -311,7 +319,7 @@ esp_err_t spicommon_dma_chan_alloc(spi_host_device_t host_id, spi_dma_chan_t dma
|
|||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
*out_dma_ctx = dma_ctx;
|
spi_dma_ctx[host_id] = dma_ctx;
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
@@ -319,13 +327,17 @@ cleanup:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t spicommon_dma_desc_alloc(spi_dma_ctx_t *dma_ctx, int cfg_max_sz, int *actual_max_sz)
|
esp_err_t spicommon_dma_desc_alloc(spi_host_device_t host_id, int cfg_max_sz, int *actual_max_sz)
|
||||||
{
|
{
|
||||||
int dma_desc_ct = (cfg_max_sz + DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED - 1) / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED;
|
int dma_desc_ct = (cfg_max_sz + DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED - 1) / DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED;
|
||||||
if (dma_desc_ct == 0) {
|
if (dma_desc_ct == 0) {
|
||||||
dma_desc_ct = 1; //default to 4k when max is not given
|
dma_desc_ct = 1; //default to 4k when max is not given
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spi_dma_ctx_t *dma_ctx = spi_bus_get_dma_ctx(host_id);
|
||||||
|
if (!dma_ctx) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
dma_ctx->dmadesc_tx = heap_caps_aligned_calloc(DMA_DESC_MEM_ALIGN_SIZE, 1, sizeof(spi_dma_desc_t) * dma_desc_ct, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
dma_ctx->dmadesc_tx = heap_caps_aligned_calloc(DMA_DESC_MEM_ALIGN_SIZE, 1, sizeof(spi_dma_desc_t) * dma_desc_ct, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
||||||
dma_ctx->dmadesc_rx = heap_caps_aligned_calloc(DMA_DESC_MEM_ALIGN_SIZE, 1, sizeof(spi_dma_desc_t) * dma_desc_ct, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
dma_ctx->dmadesc_rx = heap_caps_aligned_calloc(DMA_DESC_MEM_ALIGN_SIZE, 1, sizeof(spi_dma_desc_t) * dma_desc_ct, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
||||||
if (dma_ctx->dmadesc_tx == NULL || dma_ctx->dmadesc_rx == NULL) {
|
if (dma_ctx->dmadesc_tx == NULL || dma_ctx->dmadesc_rx == NULL) {
|
||||||
@@ -372,10 +384,71 @@ void SPI_COMMON_ISR_ATTR spicommon_dma_desc_setup_link(spi_dma_desc_t *dmadesc,
|
|||||||
dmadesc[n - 1].next = NULL;
|
dmadesc[n - 1].next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------free dma periph-------------------------------------------------------//
|
esp_err_t SPI_COMMON_ISR_ATTR spicommon_dma_setup_priv_buffer(spi_host_device_t host_id, uint32_t *buffer, uint32_t len, bool is_tx, bool psram_prefer, bool auto_malloc, uint32_t **ret_buffer)
|
||||||
esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx)
|
|
||||||
{
|
{
|
||||||
assert(dma_ctx);
|
spi_bus_attr_t *bus_attr = spi_bus_get_attr(host_id);
|
||||||
|
spi_dma_ctx_t *dma_ctx = spi_bus_get_dma_ctx(host_id);
|
||||||
|
assert(bus_attr && dma_ctx);
|
||||||
|
if ((buffer == NULL) || (len == 0)) {
|
||||||
|
*ret_buffer = buffer;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_ptr_ext = esp_ptr_external_ram(buffer);
|
||||||
|
bool use_psram = is_ptr_ext && psram_prefer;
|
||||||
|
#if SOC_IS(ESP32S2)
|
||||||
|
ESP_RETURN_ON_FALSE_ISR((host_id != SPI3_HOST) || !use_psram, ESP_ERR_NOT_SUPPORTED, SPI_TAG, "SPI3 does not support external memory");
|
||||||
|
#endif
|
||||||
|
bool need_malloc = is_ptr_ext ? (!use_psram || !esp_ptr_dma_ext_capable(buffer)) : !esp_ptr_dma_capable(buffer);
|
||||||
|
// If psram is wanted, re-malloc also from psram.
|
||||||
|
uint32_t mem_cap = MALLOC_CAP_DMA | (use_psram ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL);
|
||||||
|
uint16_t alignment = 0;
|
||||||
|
if (is_tx) {
|
||||||
|
alignment = use_psram ? dma_ctx->dma_align_tx_ext : dma_ctx->dma_align_tx_int;
|
||||||
|
} else {
|
||||||
|
// RX cache sync still need consider the cache alignment requirement
|
||||||
|
if (use_psram) {
|
||||||
|
alignment = MAX(dma_ctx->dma_align_rx_ext, bus_attr->cache_align_ext);
|
||||||
|
} else {
|
||||||
|
alignment = MAX(dma_ctx->dma_align_rx_int, bus_attr->cache_align_int);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
need_malloc |= (((uint32_t)buffer | len) & (alignment - 1));
|
||||||
|
uint32_t align_len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment
|
||||||
|
ESP_EARLY_LOGV(SPI_TAG, "SPI%d %s %p, len %d, is_ptr_ext %d, use_psram: %d, alignment: %d, need_malloc: %d from %s", host_id + 1, is_tx ? "TX" : "RX", buffer, len, is_ptr_ext, use_psram, alignment, need_malloc, (mem_cap & MALLOC_CAP_SPIRAM) ? "psram" : "internal");
|
||||||
|
if (need_malloc) {
|
||||||
|
ESP_RETURN_ON_FALSE_ISR(auto_malloc, ESP_ERR_INVALID_STATE, SPI_TAG, "%s addr&len not align to %d, or not dma_capable, suggest use 'heap_caps_malloc' or enable auto_align", is_tx ? "TX" : "RX", alignment);
|
||||||
|
uint32_t *temp = heap_caps_aligned_alloc(alignment, align_len, mem_cap);
|
||||||
|
ESP_RETURN_ON_FALSE_ISR(temp != NULL, ESP_ERR_NO_MEM, SPI_TAG, "Failed to allocate priv %s buffer", is_tx ? "TX" : "RX");
|
||||||
|
|
||||||
|
if (is_tx) {
|
||||||
|
memcpy(temp, buffer, len);
|
||||||
|
}
|
||||||
|
buffer = temp;
|
||||||
|
}
|
||||||
|
*ret_buffer = buffer;
|
||||||
|
uint32_t sync_flags = is_tx ? (ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED) : ESP_CACHE_MSYNC_FLAG_DIR_M2C;
|
||||||
|
esp_err_t ret = esp_cache_msync((void *)buffer, need_malloc ? align_len : len, sync_flags);
|
||||||
|
// ESP_ERR_NOT_SUPPORTED stands for not cache sync required, it's allowed here
|
||||||
|
ESP_RETURN_ON_FALSE_ISR((ret == ESP_OK) || (ret == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_ARG, SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPI_COMMON_ISR_ATTR spicommon_dma_rx_mb(spi_host_device_t host_id, void *rx_buffer)
|
||||||
|
{
|
||||||
|
(void)host_id;
|
||||||
|
#if SOC_PSRAM_DMA_CAPABLE && CONFIG_SPIRAM
|
||||||
|
if (esp_ptr_external_ram(rx_buffer)) {
|
||||||
|
// dma rx data to psram need memory barrier fix
|
||||||
|
esp_psram_mspi_mb();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------free dma periph-------------------------------------------------------//
|
||||||
|
esp_err_t spicommon_dma_chan_free(spi_host_device_t host_id)
|
||||||
|
{
|
||||||
|
spi_dma_ctx_t *dma_ctx = spi_bus_get_dma_ctx(host_id);
|
||||||
|
|
||||||
#if !SOC_GDMA_SUPPORTED
|
#if !SOC_GDMA_SUPPORTED
|
||||||
//On ESP32S2, each SPI controller has its own DMA channel
|
//On ESP32S2, each SPI controller has its own DMA channel
|
||||||
@@ -384,17 +457,7 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx)
|
|||||||
|
|
||||||
portENTER_CRITICAL(&spi_dma_spinlock);
|
portENTER_CRITICAL(&spi_dma_spinlock);
|
||||||
spi_dma_chan_enabled &= ~BIT(dma_chan);
|
spi_dma_chan_enabled &= ~BIT(dma_chan);
|
||||||
#if SPI_LL_DMA_SHARED
|
_spicommon_dma_rcc_clock_ctrl(dma_chan, false);
|
||||||
PERIPH_RCC_RELEASE_ATOMIC(get_dma_periph(dma_chan), ref_count) {
|
|
||||||
if (ref_count == 0) {
|
|
||||||
spi_dma_ll_enable_bus_clock(dma_ctx->tx_dma_chan.host_id, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
PERIPH_RCC_ATOMIC() {
|
|
||||||
spi_dma_ll_enable_bus_clock(dma_ctx->tx_dma_chan.host_id, false);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
portEXIT_CRITICAL(&spi_dma_spinlock);
|
portEXIT_CRITICAL(&spi_dma_spinlock);
|
||||||
|
|
||||||
#else //SOC_GDMA_SUPPORTED
|
#else //SOC_GDMA_SUPPORTED
|
||||||
@@ -408,7 +471,14 @@ esp_err_t spicommon_dma_chan_free(spi_dma_ctx_t *dma_ctx)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (dma_ctx->dmadesc_tx) {
|
||||||
|
free(dma_ctx->dmadesc_tx);
|
||||||
|
}
|
||||||
|
if (dma_ctx->dmadesc_rx) {
|
||||||
|
free(dma_ctx->dmadesc_rx);
|
||||||
|
}
|
||||||
free(dma_ctx);
|
free(dma_ctx);
|
||||||
|
spi_dma_ctx[host_id] = NULL;
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,7 +616,7 @@ Do the common stuff to hook up a SPI host to a bus defined by a bunch of GPIO pi
|
|||||||
bus config struct and it'll set up the GPIO matrix and enable the device. If a pin is set to non-negative value,
|
bus config struct and it'll set up the GPIO matrix and enable the device. If a pin is set to non-negative value,
|
||||||
it should be able to be initialized.
|
it should be able to be initialized.
|
||||||
*/
|
*/
|
||||||
esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t* flags_o, uint64_t *io_reserved)
|
esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t* flags_o)
|
||||||
{
|
{
|
||||||
#if SOC_SPI_SUPPORT_OCT
|
#if SOC_SPI_SUPPORT_OCT
|
||||||
// In the driver of previous version, spi data4 ~ spi data7 are not in spi_bus_config_t struct. So the new-added pins come as 0
|
// In the driver of previous version, spi data4 ~ spi data7 are not in spi_bus_config_t struct. So the new-added pins come as 0
|
||||||
@@ -837,18 +907,14 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t *
|
|||||||
bus_attr->dma_enabled = (dma_chan != SPI_DMA_DISABLED);
|
bus_attr->dma_enabled = (dma_chan != SPI_DMA_DISABLED);
|
||||||
bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE;
|
bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE;
|
||||||
if (bus_attr->dma_enabled) {
|
if (bus_attr->dma_enabled) {
|
||||||
err = spicommon_dma_chan_alloc(host_id, dma_chan, &ctx->dma_ctx);
|
err = spicommon_dma_chan_alloc(host_id, dma_chan);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
err = spicommon_dma_desc_alloc(ctx->dma_ctx, bus_config->max_transfer_sz, &bus_attr->max_transfer_sz);
|
err = spicommon_dma_desc_alloc(host_id, bus_config->max_transfer_sz, &bus_attr->max_transfer_sz);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get cache alignment constraints
|
|
||||||
esp_cache_get_alignment(MALLOC_CAP_DMA, &bus_attr->cache_align_int);
|
|
||||||
esp_cache_get_alignment(MALLOC_CAP_SPIRAM, &bus_attr->cache_align_ext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spi_bus_lock_config_t lock_config = {
|
spi_bus_lock_config_t lock_config = {
|
||||||
@@ -902,7 +968,7 @@ esp_err_t spi_bus_initialize(spi_host_device_t host_id, const spi_bus_config_t *
|
|||||||
}
|
}
|
||||||
#endif //CONFIG_PM_ENABLE
|
#endif //CONFIG_PM_ENABLE
|
||||||
|
|
||||||
err = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_MASTER | bus_config->flags, NULL, NULL);
|
err = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_MASTER | bus_config->flags, NULL);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -917,12 +983,10 @@ cleanup:
|
|||||||
if (bus_attr->lock) {
|
if (bus_attr->lock) {
|
||||||
spi_bus_deinit_lock(bus_attr->lock);
|
spi_bus_deinit_lock(bus_attr->lock);
|
||||||
}
|
}
|
||||||
if (ctx->dma_ctx) {
|
}
|
||||||
free(ctx->dma_ctx->dmadesc_tx);
|
if (bus_attr->dma_enabled) {
|
||||||
free(ctx->dma_ctx->dmadesc_rx);
|
// free dma channel and descriptors
|
||||||
spicommon_dma_chan_free(ctx->dma_ctx);
|
spicommon_dma_chan_free(host_id);
|
||||||
ctx->dma_ctx = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
spicommon_bus_free(host_id);
|
spicommon_bus_free(host_id);
|
||||||
return err;
|
return err;
|
||||||
@@ -931,19 +995,20 @@ cleanup:
|
|||||||
void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t extra_heap_caps)
|
void *spi_bus_dma_memory_alloc(spi_host_device_t host_id, size_t size, uint32_t extra_heap_caps)
|
||||||
{
|
{
|
||||||
SPI_CHECK(bus_ctx[host_id], "SPI %d not initialized", NULL, host_id + 1);
|
SPI_CHECK(bus_ctx[host_id], "SPI %d not initialized", NULL, host_id + 1);
|
||||||
|
spi_dma_ctx_t *dma_ctx = spi_bus_get_dma_ctx(host_id);
|
||||||
|
|
||||||
size_t alignment = 16;
|
size_t alignment = 1; // return 1 anyway if dma not used but user use it.
|
||||||
// detailed alignment requirement is not available for slave bus, so use 16 bytes as default
|
if (dma_ctx) {
|
||||||
if (bus_ctx[host_id]->bus_attr.flags & SPICOMMON_BUSFLAG_MASTER) {
|
|
||||||
// As don't know the buffer will used for TX or RX, so use the max alignment requirement
|
// As don't know the buffer will used for TX or RX, so use the max alignment requirement
|
||||||
alignment = (extra_heap_caps & MALLOC_CAP_SPIRAM) ? \
|
alignment = (extra_heap_caps & MALLOC_CAP_SPIRAM) ? \
|
||||||
MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_ext, bus_ctx[host_id]->dma_ctx->dma_align_rx_ext) : \
|
MAX(dma_ctx->dma_align_tx_ext, dma_ctx->dma_align_rx_ext) : \
|
||||||
MAX(bus_ctx[host_id]->dma_ctx->dma_align_tx_int, bus_ctx[host_id]->dma_ctx->dma_align_rx_int);
|
MAX(dma_ctx->dma_align_tx_int, dma_ctx->dma_align_rx_int);
|
||||||
}
|
}
|
||||||
|
|
||||||
return heap_caps_aligned_calloc(alignment, 1, size, extra_heap_caps | MALLOC_CAP_DMA);
|
return heap_caps_aligned_calloc(alignment, 1, size, extra_heap_caps | MALLOC_CAP_DMA);
|
||||||
}
|
}
|
||||||
|
|
||||||
const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id)
|
SPI_COMMON_ISR_ATTR spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id)
|
||||||
{
|
{
|
||||||
if (bus_ctx[host_id] == NULL) {
|
if (bus_ctx[host_id] == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -952,13 +1017,9 @@ const spi_bus_attr_t* spi_bus_get_attr(spi_host_device_t host_id)
|
|||||||
return &bus_ctx[host_id]->bus_attr;
|
return &bus_ctx[host_id]->bus_attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id)
|
SPI_COMMON_ISR_ATTR spi_dma_ctx_t* spi_bus_get_dma_ctx(spi_host_device_t host_id)
|
||||||
{
|
{
|
||||||
if (bus_ctx[host_id] == NULL) {
|
return spi_dma_ctx[host_id];
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bus_ctx[host_id]->dma_ctx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t spi_bus_free(spi_host_device_t host_id)
|
esp_err_t spi_bus_free(spi_host_device_t host_id)
|
||||||
@@ -998,11 +1059,8 @@ esp_err_t spi_bus_free(spi_host_device_t host_id)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
spi_bus_deinit_lock(bus_attr->lock);
|
spi_bus_deinit_lock(bus_attr->lock);
|
||||||
if (ctx->dma_ctx) {
|
if (bus_attr->dma_enabled) {
|
||||||
free(ctx->dma_ctx->dmadesc_tx);
|
spicommon_dma_chan_free(host_id);
|
||||||
free(ctx->dma_ctx->dmadesc_rx);
|
|
||||||
spicommon_dma_chan_free(ctx->dma_ctx);
|
|
||||||
ctx->dma_ctx = NULL;
|
|
||||||
}
|
}
|
||||||
spicommon_bus_free(host_id);
|
spicommon_bus_free(host_id);
|
||||||
return err;
|
return err;
|
||||||
|
|||||||
@@ -124,10 +124,10 @@ We have two bits to control the interrupt:
|
|||||||
#include "esp_ipc.h"
|
#include "esp_ipc.h"
|
||||||
#include "esp_cache.h"
|
#include "esp_cache.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_memory_utils.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
#include "soc/soc_memory_layout.h"
|
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "hal/spi_hal.h"
|
#include "hal/spi_hal.h"
|
||||||
#include "hal/spi_ll.h"
|
#include "hal/spi_ll.h"
|
||||||
@@ -992,6 +992,7 @@ static void SPI_MASTER_ISR_ATTR spi_intr(void *arg)
|
|||||||
//This workaround is only for esp32, where tx_dma_chan and rx_dma_chan are always same
|
//This workaround is only for esp32, where tx_dma_chan and rx_dma_chan are always same
|
||||||
spicommon_dmaworkaround_idle(dma_ctx->tx_dma_chan.chan_id);
|
spicommon_dmaworkaround_idle(dma_ctx->tx_dma_chan.chan_id);
|
||||||
#endif //#if CONFIG_IDF_TARGET_ESP32
|
#endif //#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
spicommon_dma_rx_mb(host->id, host->cur_trans_buf.buffer_to_rcv);
|
||||||
spi_trans_dma_error_check(host);
|
spi_trans_dma_error_check(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1170,47 +1171,6 @@ static SPI_MASTER_ISR_ATTR void uninstall_priv_desc(spi_trans_priv_t* trans_buf)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static SPI_MASTER_ISR_ATTR esp_err_t setup_dma_priv_buffer(spi_host_t *host, uint32_t *buffer, uint32_t len, bool is_tx, uint32_t flags, uint32_t **ret_buffer)
|
|
||||||
{
|
|
||||||
#if CONFIG_IDF_TARGET_ESP32S2
|
|
||||||
ESP_RETURN_ON_FALSE_ISR((host->id != SPI3_HOST) || !(flags & SPI_TRANS_DMA_USE_PSRAM), ESP_ERR_NOT_SUPPORTED, SPI_TAG, "SPI3 does not support external memory");
|
|
||||||
#endif
|
|
||||||
bool is_ptr_ext = esp_ptr_external_ram(buffer);
|
|
||||||
bool use_psram = is_ptr_ext && (flags & SPI_TRANS_DMA_USE_PSRAM);
|
|
||||||
bool need_malloc = is_ptr_ext ? (!use_psram || !esp_ptr_dma_ext_capable(buffer)) : !esp_ptr_dma_capable(buffer);
|
|
||||||
uint16_t alignment = 0;
|
|
||||||
// If psram is wanted, re-malloc also from psram.
|
|
||||||
uint32_t mem_cap = MALLOC_CAP_DMA | (use_psram ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL);
|
|
||||||
if (is_tx) {
|
|
||||||
alignment = use_psram ? host->dma_ctx->dma_align_tx_ext : host->dma_ctx->dma_align_tx_int;
|
|
||||||
} else {
|
|
||||||
// RX cache sync still need consider the cache alignment requirement
|
|
||||||
if (use_psram) {
|
|
||||||
alignment = MAX(host->dma_ctx->dma_align_rx_ext, host->bus_attr->cache_align_ext);
|
|
||||||
} else {
|
|
||||||
alignment = MAX(host->dma_ctx->dma_align_rx_int, host->bus_attr->cache_align_int);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
need_malloc |= (((uint32_t)buffer | len) & (alignment - 1));
|
|
||||||
ESP_EARLY_LOGV(SPI_TAG, "%s %p, len %d, is_ptr_ext %d, use_psram: %d, alignment: %d, need_malloc: %d from %s", is_tx ? "TX" : "RX", buffer, len, is_ptr_ext, use_psram, alignment, need_malloc, (mem_cap & MALLOC_CAP_SPIRAM) ? "psram" : "internal");
|
|
||||||
if (need_malloc) {
|
|
||||||
ESP_RETURN_ON_FALSE_ISR(!(flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL), ESP_ERR_INVALID_ARG, SPI_TAG, "Set flag SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL but %s addr&len not align to %d, or not dma_capable", is_tx ? "TX" : "RX", alignment);
|
|
||||||
len = (len + alignment - 1) & (~(alignment - 1)); // up align alignment
|
|
||||||
uint32_t *temp = heap_caps_aligned_alloc(alignment, len, mem_cap);
|
|
||||||
ESP_RETURN_ON_FALSE_ISR(temp != NULL, ESP_ERR_NO_MEM, SPI_TAG, "Failed to allocate priv %s buffer", is_tx ? "TX" : "RX");
|
|
||||||
|
|
||||||
if (is_tx) {
|
|
||||||
memcpy(temp, buffer, len);
|
|
||||||
}
|
|
||||||
buffer = temp;
|
|
||||||
}
|
|
||||||
esp_err_t ret = esp_cache_msync((void *)buffer, len, is_tx ? (ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED) : ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
|
||||||
// ESP_ERR_NOT_SUPPORTED stands for not cache sync required, it's allowed here
|
|
||||||
ESP_RETURN_ON_FALSE_ISR((ret == ESP_OK) || (ret == ESP_ERR_NOT_SUPPORTED), ESP_ERR_INVALID_ARG, SPI_TAG, "sync failed for %s buffer", is_tx ? "TX" : "RX");
|
|
||||||
*ret_buffer = buffer;
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_host_t *host, spi_trans_priv_t* priv_desc)
|
static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_host_t *host, spi_trans_priv_t* priv_desc)
|
||||||
{
|
{
|
||||||
spi_transaction_t *trans_desc = priv_desc->trans;
|
spi_transaction_t *trans_desc = priv_desc->trans;
|
||||||
@@ -1220,25 +1180,23 @@ static SPI_MASTER_ISR_ATTR esp_err_t setup_priv_desc(spi_host_t *host, spi_trans
|
|||||||
uint32_t* rcv_ptr = (trans_desc->flags & SPI_TRANS_USE_RXDATA) ? (uint32_t *)trans_desc->rx_data : (uint32_t *)trans_desc->rx_buffer;
|
uint32_t* rcv_ptr = (trans_desc->flags & SPI_TRANS_USE_RXDATA) ? (uint32_t *)trans_desc->rx_data : (uint32_t *)trans_desc->rx_buffer;
|
||||||
// tx memory assign
|
// tx memory assign
|
||||||
uint32_t *send_ptr = (trans_desc->flags & SPI_TRANS_USE_TXDATA) ? (uint32_t *)trans_desc->tx_data : (uint32_t *)trans_desc->tx_buffer;
|
uint32_t *send_ptr = (trans_desc->flags & SPI_TRANS_USE_TXDATA) ? (uint32_t *)trans_desc->tx_data : (uint32_t *)trans_desc->tx_buffer;
|
||||||
|
if (!bus_attr->dma_enabled) {
|
||||||
esp_err_t ret = ESP_OK;
|
priv_desc->buffer_to_send = send_ptr;
|
||||||
if (send_ptr && bus_attr->dma_enabled) {
|
priv_desc->buffer_to_rcv = rcv_ptr;
|
||||||
ret = setup_dma_priv_buffer(host, send_ptr, (trans_desc->length + 7) / 8, true, trans_desc->flags, &send_ptr);
|
return ESP_OK;
|
||||||
if (ret != ESP_OK) {
|
|
||||||
goto clean_up;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rcv_ptr && bus_attr->dma_enabled) {
|
bool use_psram = trans_desc->flags & SPI_TRANS_DMA_USE_PSRAM;
|
||||||
ret = setup_dma_priv_buffer(host, rcv_ptr, (trans_desc->rxlength + 7) / 8, false, trans_desc->flags, &rcv_ptr);
|
bool auto_malloc = !(trans_desc->flags & SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL);
|
||||||
if (ret != ESP_OK) {
|
esp_err_t ret = spicommon_dma_setup_priv_buffer(host->id, send_ptr, (trans_desc->length + 7) / 8, true, use_psram, auto_malloc, (void *)&priv_desc->buffer_to_send);
|
||||||
goto clean_up;
|
if (ret != ESP_OK) {
|
||||||
}
|
goto clean_up;
|
||||||
}
|
}
|
||||||
|
ret = spicommon_dma_setup_priv_buffer(host->id, rcv_ptr, (trans_desc->rxlength + 7) / 8, false, use_psram, auto_malloc, &priv_desc->buffer_to_rcv);
|
||||||
priv_desc->buffer_to_send = send_ptr;
|
if (ret != ESP_OK) {
|
||||||
priv_desc->buffer_to_rcv = rcv_ptr;
|
goto clean_up;
|
||||||
return ESP_OK;
|
}
|
||||||
|
return ret;
|
||||||
|
|
||||||
clean_up:
|
clean_up:
|
||||||
uninstall_priv_desc(priv_desc);
|
uninstall_priv_desc(priv_desc);
|
||||||
@@ -1467,6 +1425,7 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle,
|
|||||||
return ESP_ERR_TIMEOUT;
|
return ESP_ERR_TIMEOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
spicommon_dma_rx_mb(host->id, host->cur_trans_buf.buffer_to_rcv);
|
||||||
spi_trans_dma_error_check(host);
|
spi_trans_dma_error_check(host);
|
||||||
uint32_t trans_flags = host->cur_trans_buf.trans->flags; // save the flags before bus_lock release
|
uint32_t trans_flags = host->cur_trans_buf.trans->flags; // save the flags before bus_lock release
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -54,12 +54,13 @@ static const char *SPI_TAG = "spi_slave";
|
|||||||
/// struct to hold private transaction data (like tx and rx buffer for DMA).
|
/// struct to hold private transaction data (like tx and rx buffer for DMA).
|
||||||
typedef struct {
|
typedef struct {
|
||||||
spi_slave_transaction_t *trans; //original trans
|
spi_slave_transaction_t *trans; //original trans
|
||||||
void *tx_buffer; //actually tx buffer (re-malloced if needed)
|
uint32_t *tx_buffer; //actually tx buffer (re-malloced if needed)
|
||||||
void *rx_buffer; //actually rx buffer (re-malloced if needed)
|
uint32_t *rx_buffer; //actually rx buffer (re-malloced if needed)
|
||||||
|
bool dma_hw_error; //true if DMA hardware over/underflow occurred
|
||||||
} spi_slave_trans_priv_t;
|
} spi_slave_trans_priv_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int id;
|
spi_host_device_t id;
|
||||||
_Atomic spi_bus_fsm_t fsm;
|
_Atomic spi_bus_fsm_t fsm;
|
||||||
spi_bus_attr_t* bus_attr;
|
spi_bus_attr_t* bus_attr;
|
||||||
spi_dma_ctx_t *dma_ctx;
|
spi_dma_ctx_t *dma_ctx;
|
||||||
@@ -69,13 +70,10 @@ typedef struct {
|
|||||||
spi_slave_trans_priv_t cur_trans;
|
spi_slave_trans_priv_t cur_trans;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint32_t intr_flags;
|
uint32_t intr_flags;
|
||||||
int max_transfer_sz;
|
|
||||||
QueueHandle_t trans_queue;
|
QueueHandle_t trans_queue;
|
||||||
QueueHandle_t ret_queue;
|
QueueHandle_t ret_queue;
|
||||||
bool dma_enabled;
|
|
||||||
bool cs_iomux;
|
bool cs_iomux;
|
||||||
uint8_t cs_in_signal;
|
uint8_t cs_in_signal;
|
||||||
uint16_t internal_mem_align_size;
|
|
||||||
#ifdef CONFIG_PM_ENABLE
|
#ifdef CONFIG_PM_ENABLE
|
||||||
esp_pm_lock_handle_t pm_lock;
|
esp_pm_lock_handle_t pm_lock;
|
||||||
#endif
|
#endif
|
||||||
@@ -185,13 +183,15 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
|
|||||||
atomic_store(&spihost[host]->fsm, SPI_BUS_FSM_ENABLED);
|
atomic_store(&spihost[host]->fsm, SPI_BUS_FSM_ENABLED);
|
||||||
spi_slave_hal_context_t *hal = &spihost[host]->hal;
|
spi_slave_hal_context_t *hal = &spihost[host]->hal;
|
||||||
|
|
||||||
spihost[host]->dma_enabled = (dma_chan != SPI_DMA_DISABLED);
|
spihost[host]->bus_attr->dma_enabled = (dma_chan != SPI_DMA_DISABLED);
|
||||||
if (spihost[host]->dma_enabled) {
|
spihost[host]->bus_attr->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE;
|
||||||
ret = spicommon_dma_chan_alloc(host, dma_chan, &spihost[host]->dma_ctx);
|
if (spihost[host]->bus_attr->dma_enabled) {
|
||||||
|
ret = spicommon_dma_chan_alloc(host, dma_chan);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
ret = spicommon_dma_desc_alloc(spihost[host]->dma_ctx, bus_config->max_transfer_sz, &spihost[host]->max_transfer_sz);
|
spihost[host]->dma_ctx = spi_bus_get_dma_ctx(host);
|
||||||
|
ret = spicommon_dma_desc_alloc(host, bus_config->max_transfer_sz, &spihost[host]->bus_attr->max_transfer_sz);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -199,20 +199,9 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
|
|||||||
hal->dmadesc_tx = spihost[host]->dma_ctx->dmadesc_tx;
|
hal->dmadesc_tx = spihost[host]->dma_ctx->dmadesc_tx;
|
||||||
hal->dmadesc_rx = spihost[host]->dma_ctx->dmadesc_rx;
|
hal->dmadesc_rx = spihost[host]->dma_ctx->dmadesc_rx;
|
||||||
hal->dmadesc_n = spihost[host]->dma_ctx->dma_desc_num;
|
hal->dmadesc_n = spihost[host]->dma_ctx->dma_desc_num;
|
||||||
|
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
|
||||||
size_t alignment;
|
|
||||||
esp_cache_get_alignment(MALLOC_CAP_DMA, &alignment);
|
|
||||||
spihost[host]->internal_mem_align_size = alignment;
|
|
||||||
#else
|
|
||||||
spihost[host]->internal_mem_align_size = 4;
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
//We're limited to non-DMA transfers: the SPI work registers can hold 64 bytes at most.
|
|
||||||
spihost[host]->max_transfer_sz = SOC_SPI_MAXIMUM_BUFFER_SIZE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = spicommon_bus_initialize_io(host, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL, NULL);
|
err = spicommon_bus_initialize_io(host, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ret = err;
|
ret = err;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@@ -226,7 +215,7 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
|
|||||||
spihost[host]->flags = spihost[host]->bus_attr->flags; // This flag MUST be set after spicommon_bus_initialize_io is called
|
spihost[host]->flags = spihost[host]->bus_attr->flags; // This flag MUST be set after spicommon_bus_initialize_io is called
|
||||||
|
|
||||||
// The slave DMA suffers from unexpected transactions. Forbid reading if DMA is enabled by disabling the CS line.
|
// The slave DMA suffers from unexpected transactions. Forbid reading if DMA is enabled by disabling the CS line.
|
||||||
if (spihost[host]->dma_enabled) {
|
if (spihost[host]->bus_attr->dma_enabled) {
|
||||||
freeze_cs(spihost[host]);
|
freeze_cs(spihost[host]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +302,7 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
|
|||||||
hal->rx_lsbfirst = (slave_config->flags & SPI_SLAVE_RXBIT_LSBFIRST) ? 1 : 0;
|
hal->rx_lsbfirst = (slave_config->flags & SPI_SLAVE_RXBIT_LSBFIRST) ? 1 : 0;
|
||||||
hal->tx_lsbfirst = (slave_config->flags & SPI_SLAVE_TXBIT_LSBFIRST) ? 1 : 0;
|
hal->tx_lsbfirst = (slave_config->flags & SPI_SLAVE_TXBIT_LSBFIRST) ? 1 : 0;
|
||||||
hal->mode = slave_config->mode;
|
hal->mode = slave_config->mode;
|
||||||
hal->use_dma = spihost[host]->dma_enabled;
|
hal->use_dma = spihost[host]->bus_attr->dma_enabled;
|
||||||
spi_slave_hal_setup_device(hal);
|
spi_slave_hal_setup_device(hal);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
|
|
||||||
@@ -332,10 +321,8 @@ esp_err_t spi_slave_free(spi_host_device_t host)
|
|||||||
if (spihost[host]->ret_queue) {
|
if (spihost[host]->ret_queue) {
|
||||||
vQueueDelete(spihost[host]->ret_queue);
|
vQueueDelete(spihost[host]->ret_queue);
|
||||||
}
|
}
|
||||||
if (spihost[host]->dma_enabled) {
|
if (spihost[host]->bus_attr->dma_enabled) {
|
||||||
free(spihost[host]->dma_ctx->dmadesc_tx);
|
spicommon_dma_chan_free(host);
|
||||||
free(spihost[host]->dma_ctx->dmadesc_rx);
|
|
||||||
spicommon_dma_chan_free(spihost[host]->dma_ctx);
|
|
||||||
}
|
}
|
||||||
spicommon_bus_free_io_cfg(host);
|
spicommon_bus_free_io_cfg(host);
|
||||||
if (spihost[host]->cfg.spics_io_num >= 0) {
|
if (spihost[host]->cfg.spics_io_num >= 0) {
|
||||||
@@ -409,12 +396,11 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_uninstall_priv_trans(spi_host_device_t
|
|||||||
{
|
{
|
||||||
__attribute__((unused)) spi_slave_transaction_t *trans = (spi_slave_transaction_t *)priv_trans->trans;
|
__attribute__((unused)) spi_slave_transaction_t *trans = (spi_slave_transaction_t *)priv_trans->trans;
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
if (spihost[host]->dma_enabled && (trans->trans_len % 32)) {
|
if (spihost[host]->bus_attr->dma_enabled && (trans->trans_len % 32)) {
|
||||||
ESP_EARLY_LOGW(SPI_TAG, "Use DMA but real trans_len is not 4 bytes aligned, slave may loss data");
|
ESP_EARLY_LOGW(SPI_TAG, "Use DMA but real trans_len is not 4 bytes aligned, slave may loss data");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
if (spihost[host]->bus_attr->dma_enabled) {
|
||||||
if (spihost[host]->dma_enabled) {
|
|
||||||
if (trans->tx_buffer && (trans->tx_buffer != priv_trans->tx_buffer)) {
|
if (trans->tx_buffer && (trans->tx_buffer != priv_trans->tx_buffer)) {
|
||||||
free(priv_trans->tx_buffer);
|
free(priv_trans->tx_buffer);
|
||||||
}
|
}
|
||||||
@@ -423,54 +409,28 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_uninstall_priv_trans(spi_host_device_t
|
|||||||
free(priv_trans->rx_buffer);
|
free(priv_trans->rx_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_setup_priv_trans(spi_host_device_t host, spi_slave_trans_priv_t *priv_trans)
|
static esp_err_t SPI_SLAVE_ATTR spi_slave_setup_priv_trans(spi_host_device_t host, spi_slave_trans_priv_t *priv_trans)
|
||||||
{
|
{
|
||||||
spi_slave_transaction_t *trans = (spi_slave_transaction_t *)priv_trans->trans;
|
spi_slave_transaction_t *trans = (spi_slave_transaction_t *)priv_trans->trans;
|
||||||
|
if (!spihost[host]->bus_attr->dma_enabled) {
|
||||||
priv_trans->tx_buffer = (void *)trans->tx_buffer;
|
priv_trans->tx_buffer = (uint32_t *)trans->tx_buffer;
|
||||||
priv_trans->rx_buffer = trans->rx_buffer;
|
priv_trans->rx_buffer = (uint32_t *)trans->rx_buffer;
|
||||||
|
return ESP_OK;
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
|
||||||
uint16_t alignment = spihost[host]->internal_mem_align_size;
|
|
||||||
uint32_t buffer_byte_len = (trans->length + 7) / 8;
|
|
||||||
|
|
||||||
if (spihost[host]->dma_enabled && trans->tx_buffer) {
|
|
||||||
if ((!esp_ptr_dma_capable(trans->tx_buffer) || ((((uint32_t)trans->tx_buffer) | buffer_byte_len) & (alignment - 1)))) {
|
|
||||||
ESP_RETURN_ON_FALSE_ISR(trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO, ESP_ERR_INVALID_ARG, SPI_TAG, "TX buffer addr&len not align to %d byte, or not dma_capable", alignment);
|
|
||||||
//if txbuf in the desc not DMA-capable, or not align to "alignment", malloc a new one
|
|
||||||
ESP_EARLY_LOGD(SPI_TAG, "Allocate TX buffer for DMA");
|
|
||||||
buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1)); // up align to "alignment"
|
|
||||||
uint32_t *temp = heap_caps_aligned_alloc(alignment, buffer_byte_len, MALLOC_CAP_DMA);
|
|
||||||
if (temp == NULL) {
|
|
||||||
return ESP_ERR_NO_MEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(temp, trans->tx_buffer, (trans->length + 7) / 8);
|
|
||||||
priv_trans->tx_buffer = temp;
|
|
||||||
}
|
|
||||||
esp_err_t ret = esp_cache_msync((void *)priv_trans->tx_buffer, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
|
||||||
ESP_RETURN_ON_FALSE_ISR(ESP_OK == ret, ESP_ERR_INVALID_STATE, SPI_TAG, "mem sync c2m(writeback) fail");
|
|
||||||
}
|
}
|
||||||
if (spihost[host]->dma_enabled && trans->rx_buffer) {
|
|
||||||
if ((!esp_ptr_dma_capable(trans->rx_buffer) || ((((uint32_t)trans->rx_buffer) | (trans->length + 7) / 8) & (alignment - 1)))) {
|
bool auto_malloc = (trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO);
|
||||||
ESP_RETURN_ON_FALSE_ISR(trans->flags & SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO, ESP_ERR_INVALID_ARG, SPI_TAG, "RX buffer addr&len not align to %d byte, or not dma_capable", alignment);
|
esp_err_t ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->tx_buffer, (trans->length + 7) / 8, true, true, auto_malloc, &priv_trans->tx_buffer);
|
||||||
//if rxbuf in the desc not DMA-capable, or not align to "alignment", malloc a new one
|
if (ret != ESP_OK) {
|
||||||
ESP_EARLY_LOGD(SPI_TAG, "Allocate RX buffer for DMA");
|
spi_slave_uninstall_priv_trans(host, priv_trans);
|
||||||
buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1)); // up align to "alignment"
|
return ret;
|
||||||
priv_trans->rx_buffer = heap_caps_aligned_alloc(alignment, buffer_byte_len, MALLOC_CAP_DMA);
|
|
||||||
if (priv_trans->rx_buffer == NULL) {
|
|
||||||
free(priv_trans->tx_buffer);
|
|
||||||
return ESP_ERR_NO_MEM;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
esp_err_t ret = esp_cache_msync((void *)priv_trans->rx_buffer, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
|
||||||
ESP_RETURN_ON_FALSE_ISR(ESP_OK == ret, ESP_ERR_INVALID_STATE, SPI_TAG, "mem sync m2c(invalid) fail");
|
|
||||||
}
|
}
|
||||||
#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
ret = spicommon_dma_setup_priv_buffer(spihost[host]->id, (uint32_t *)trans->rx_buffer, (trans->length + 7) / 8, false, true, auto_malloc, &priv_trans->rx_buffer);
|
||||||
return ESP_OK;
|
if (ret != ESP_OK) {
|
||||||
|
spi_slave_uninstall_priv_trans(host, priv_trans);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait)
|
esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait)
|
||||||
@@ -478,20 +438,7 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_queue_trans(spi_host_device_t host, const spi
|
|||||||
BaseType_t r;
|
BaseType_t r;
|
||||||
SPI_CHECK(is_valid_host(host), "invalid host", ESP_ERR_INVALID_ARG);
|
SPI_CHECK(is_valid_host(host), "invalid host", ESP_ERR_INVALID_ARG);
|
||||||
SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG);
|
SPI_CHECK(spihost[host], "host not slave", ESP_ERR_INVALID_ARG);
|
||||||
SPI_CHECK(spihost[host]->dma_enabled == 0 || trans_desc->tx_buffer == NULL || esp_ptr_dma_capable(trans_desc->tx_buffer),
|
SPI_CHECK(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG);
|
||||||
"txdata not in DMA-capable memory", ESP_ERR_INVALID_ARG);
|
|
||||||
|
|
||||||
// We don't check length WORD alignment for rx when using DMA, seems break DMA requirement,
|
|
||||||
// however peripheral can also stop DMA from over writing memory even if it not aligned (except esp32).
|
|
||||||
// ATTENTION!: On esp32, peripheral can NOT stop DMA, if length not WORD aligned,
|
|
||||||
// remain bytes in last word domain will overwritten by DMA HW, which may cause unexpected issues!
|
|
||||||
// But driver already used for long time, to avoid breaking changes, we still don't add alignment limit.
|
|
||||||
SPI_CHECK(spihost[host]->dma_enabled == 0 || trans_desc->rx_buffer == NULL ||
|
|
||||||
(esp_ptr_dma_capable(trans_desc->rx_buffer) && esp_ptr_word_aligned(trans_desc->rx_buffer) &&
|
|
||||||
(trans_desc->length % 8 == 0)),
|
|
||||||
"rxdata not in DMA-capable memory or not BYTE aligned", ESP_ERR_INVALID_ARG);
|
|
||||||
|
|
||||||
SPI_CHECK(trans_desc->length <= spihost[host]->max_transfer_sz * 8, "data transfer > host maximum", ESP_ERR_INVALID_ARG);
|
|
||||||
|
|
||||||
spi_slave_trans_priv_t priv_trans = {.trans = (spi_slave_transaction_t *)trans_desc};
|
spi_slave_trans_priv_t priv_trans = {.trans = (spi_slave_transaction_t *)trans_desc};
|
||||||
SPI_CHECK(ESP_OK == spi_slave_setup_priv_trans(host, &priv_trans), "slave setup priv_trans failed", ESP_ERR_NO_MEM);
|
SPI_CHECK(ESP_OK == spi_slave_setup_priv_trans(host, &priv_trans), "slave setup priv_trans failed", ESP_ERR_NO_MEM);
|
||||||
@@ -543,30 +490,18 @@ esp_err_t SPI_SLAVE_ISR_ATTR spi_slave_queue_trans_isr(spi_host_device_t host, c
|
|||||||
BaseType_t do_yield = pdFALSE;
|
BaseType_t do_yield = pdFALSE;
|
||||||
ESP_RETURN_ON_FALSE_ISR(is_valid_host(host), ESP_ERR_INVALID_ARG, SPI_TAG, "invalid host");
|
ESP_RETURN_ON_FALSE_ISR(is_valid_host(host), ESP_ERR_INVALID_ARG, SPI_TAG, "invalid host");
|
||||||
ESP_RETURN_ON_FALSE_ISR(spihost[host], ESP_ERR_INVALID_ARG, SPI_TAG, "host not slave");
|
ESP_RETURN_ON_FALSE_ISR(spihost[host], ESP_ERR_INVALID_ARG, SPI_TAG, "host not slave");
|
||||||
ESP_RETURN_ON_FALSE_ISR(trans_desc->length <= spihost[host]->max_transfer_sz * 8, ESP_ERR_INVALID_ARG, SPI_TAG, "data transfer > host maximum");
|
ESP_RETURN_ON_FALSE_ISR(trans_desc->length <= spihost[host]->bus_attr->max_transfer_sz * 8, ESP_ERR_INVALID_ARG, SPI_TAG, "data transfer > host maximum");
|
||||||
if (spihost[host]->dma_enabled) {
|
|
||||||
uint16_t alignment = spihost[host]->internal_mem_align_size;
|
|
||||||
(void) alignment;
|
|
||||||
|
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
|
||||||
// For those targets length and addr alignment is still required from Cache side
|
|
||||||
uint32_t buffer_byte_len = (trans_desc->length + 7) / 8;
|
|
||||||
bool tx_aligned = (trans_desc->tx_buffer == NULL) || (esp_ptr_dma_capable(trans_desc->tx_buffer) && ((((uint32_t)trans_desc->tx_buffer | buffer_byte_len) & (alignment - 1)) == 0));
|
|
||||||
bool rx_aligned = (trans_desc->rx_buffer == NULL) || (esp_ptr_dma_capable(trans_desc->rx_buffer) && ((((uint32_t)trans_desc->rx_buffer | buffer_byte_len) & (alignment - 1)) == 0));
|
|
||||||
#else
|
|
||||||
bool tx_aligned = (trans_desc->tx_buffer == NULL) || esp_ptr_dma_capable(trans_desc->tx_buffer);
|
|
||||||
bool rx_aligned = (trans_desc->rx_buffer == NULL) || (esp_ptr_dma_capable(trans_desc->rx_buffer) && esp_ptr_word_aligned(trans_desc->rx_buffer) && (trans_desc->length % 8 == 0));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ESP_RETURN_ON_FALSE_ISR(tx_aligned, ESP_ERR_INVALID_ARG, SPI_TAG, "txdata addr & len not align to %d bytes or not dma_capable", alignment);
|
|
||||||
ESP_RETURN_ON_FALSE_ISR(rx_aligned, ESP_ERR_INVALID_ARG, SPI_TAG, "rxdata addr & len not align to %d bytes or not dma_capable", alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
spi_slave_trans_priv_t priv_trans = {
|
spi_slave_trans_priv_t priv_trans = {
|
||||||
.trans = (spi_slave_transaction_t *)trans_desc,
|
.trans = (spi_slave_transaction_t *)trans_desc,
|
||||||
.tx_buffer = (void *)trans_desc->tx_buffer,
|
.tx_buffer = (void *)trans_desc->tx_buffer,
|
||||||
.rx_buffer = trans_desc->rx_buffer,
|
.rx_buffer = trans_desc->rx_buffer,
|
||||||
};
|
};
|
||||||
|
if (spihost[host]->bus_attr->dma_enabled) {
|
||||||
|
// isr api is not allowed to auto_malloc, so don't need to 'uninstall' anything here, return directly
|
||||||
|
ESP_RETURN_ON_ERROR_ISR(spicommon_dma_setup_priv_buffer(host, (uint32_t *)trans_desc->tx_buffer, (trans_desc->length + 7) / 8, true, true, false, &priv_trans.tx_buffer), SPI_TAG, "");
|
||||||
|
ESP_RETURN_ON_ERROR_ISR(spicommon_dma_setup_priv_buffer(host, (uint32_t *)trans_desc->rx_buffer, (trans_desc->length + 7) / 8, false, true, false, &priv_trans.rx_buffer), SPI_TAG, "");
|
||||||
|
}
|
||||||
r = xQueueSendFromISR(spihost[host]->trans_queue, (void *)&priv_trans, &do_yield);
|
r = xQueueSendFromISR(spihost[host]->trans_queue, (void *)&priv_trans, &do_yield);
|
||||||
if (!r) {
|
if (!r) {
|
||||||
return ESP_ERR_NO_MEM;
|
return ESP_ERR_NO_MEM;
|
||||||
@@ -617,7 +552,7 @@ esp_err_t SPI_SLAVE_ATTR spi_slave_get_trans_result(spi_host_device_t host, spi_
|
|||||||
|
|
||||||
spi_slave_uninstall_priv_trans(host, &priv_trans);
|
spi_slave_uninstall_priv_trans(host, &priv_trans);
|
||||||
*trans_desc = priv_trans.trans;
|
*trans_desc = priv_trans.trans;
|
||||||
return ESP_OK;
|
return priv_trans.dma_hw_error ? ESP_ERR_INVALID_STATE : ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t SPI_SLAVE_ATTR spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait)
|
esp_err_t SPI_SLAVE_ATTR spi_slave_transmit(spi_host_device_t host, spi_slave_transaction_t *trans_desc, uint32_t ticks_to_wait)
|
||||||
@@ -665,7 +600,7 @@ static void SPI_SLAVE_ISR_ATTR s_spi_slave_prepare_data(spi_slave_t *host)
|
|||||||
{
|
{
|
||||||
spi_slave_hal_context_t *hal = &host->hal;
|
spi_slave_hal_context_t *hal = &host->hal;
|
||||||
|
|
||||||
if (host->dma_enabled) {
|
if (host->bus_attr->dma_enabled) {
|
||||||
s_spi_slave_dma_prepare_data(host->dma_ctx, &host->hal);
|
s_spi_slave_dma_prepare_data(host->dma_ctx, &host->hal);
|
||||||
} else {
|
} else {
|
||||||
//No DMA. Copy data to transmit buffers.
|
//No DMA. Copy data to transmit buffers.
|
||||||
@@ -688,6 +623,20 @@ static void SPI_SLAVE_ISR_ATTR spi_slave_restart_after_dmareset(void *arg)
|
|||||||
}
|
}
|
||||||
#endif //#if CONFIG_IDF_TARGET_ESP32
|
#endif //#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
|
||||||
|
static void SPI_SLAVE_ISR_ATTR spi_slave_trans_dma_error_check(spi_slave_t *host)
|
||||||
|
{
|
||||||
|
#if SOC_PSRAM_DMA_CAPABLE && CONFIG_SPIRAM //error checks only for psram dma
|
||||||
|
if (esp_ptr_external_ram(host->cur_trans.rx_buffer) && spi_slave_hal_get_intr_status(&host->hal, SPI_LL_INTR_IN_FULL)) {
|
||||||
|
host->cur_trans.dma_hw_error = true;
|
||||||
|
ESP_DRAM_LOGE(SPI_TAG, "DMA RX overflow detected");
|
||||||
|
}
|
||||||
|
if (esp_ptr_external_ram(host->cur_trans.tx_buffer) && spi_slave_hal_get_intr_status(&host->hal, SPI_LL_INTR_OUT_EMPTY)) {
|
||||||
|
host->cur_trans.dma_hw_error = true;
|
||||||
|
ESP_DRAM_LOGE(SPI_TAG, "DMA TX underflow detected");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
//This is run in interrupt context and apart from initialization and destruction, this is the only code
|
//This is run in interrupt context and apart from initialization and destruction, this is the only code
|
||||||
//touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are
|
//touching the host (=spihost[x]) variable. The rest of the data arrives in queues. That is why there are
|
||||||
//no muxes in this code.
|
//no muxes in this code.
|
||||||
@@ -700,11 +649,13 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
|
|||||||
|
|
||||||
assert(spi_slave_hal_usr_is_done(hal));
|
assert(spi_slave_hal_usr_is_done(hal));
|
||||||
|
|
||||||
bool use_dma = host->dma_enabled;
|
bool use_dma = host->bus_attr->dma_enabled;
|
||||||
if (host->cur_trans.trans) {
|
if (host->cur_trans.trans) {
|
||||||
// When DMA is enabled, the slave rx dma suffers from unexpected transactions. Forbid reading until transaction ready.
|
// When DMA is enabled, the slave rx dma suffers from unexpected transactions. Forbid reading until transaction ready.
|
||||||
if (use_dma) {
|
if (use_dma) {
|
||||||
freeze_cs(host);
|
freeze_cs(host);
|
||||||
|
spicommon_dma_rx_mb(host->id, host->cur_trans.rx_buffer);
|
||||||
|
spi_slave_trans_dma_error_check(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
spi_slave_hal_store_result(hal);
|
spi_slave_hal_store_result(hal);
|
||||||
@@ -718,17 +669,6 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
|
|||||||
}
|
}
|
||||||
#endif //#if CONFIG_IDF_TARGET_ESP32
|
#endif //#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE //invalidate here to let user access rx data in post_cb if possible
|
|
||||||
if (use_dma && host->cur_trans.rx_buffer) {
|
|
||||||
uint16_t alignment = host->internal_mem_align_size;
|
|
||||||
uint32_t buffer_byte_len = (host->cur_trans.trans->length + 7) / 8;
|
|
||||||
buffer_byte_len = (buffer_byte_len + alignment - 1) & (~(alignment - 1));
|
|
||||||
// invalidate priv_trans.buffer_to_rcv anyway, only user provide aligned buffer can rcv correct data in post_cb
|
|
||||||
esp_err_t ret = esp_cache_msync((void *)host->cur_trans.rx_buffer, buffer_byte_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
|
||||||
assert(ret == ESP_OK);
|
|
||||||
(void)ret;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (host->cfg.post_trans_cb) {
|
if (host->cfg.post_trans_cb) {
|
||||||
host->cfg.post_trans_cb(host->cur_trans.trans);
|
host->cfg.post_trans_cb(host->cur_trans.trans);
|
||||||
}
|
}
|
||||||
@@ -789,7 +729,9 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
|
|||||||
if (use_dma) {
|
if (use_dma) {
|
||||||
restore_cs(host);
|
restore_cs(host);
|
||||||
}
|
}
|
||||||
|
#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
|
||||||
|
spi_slave_hal_clear_intr_status(hal, SPI_LL_INTR_IN_FULL | SPI_LL_INTR_OUT_EMPTY);
|
||||||
|
#endif
|
||||||
//Kick off transfer
|
//Kick off transfer
|
||||||
spi_slave_hal_user_start(hal);
|
spi_slave_hal_user_start(hal);
|
||||||
if (host->cfg.post_setup_cb) {
|
if (host->cfg.post_setup_cb) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2010-2025 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2010-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "esp_compiler.h"
|
#include "esp_compiler.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
|
#include "esp_cache.h"
|
||||||
#include "esp_memory_utils.h"
|
#include "esp_memory_utils.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
@@ -19,9 +20,6 @@
|
|||||||
#include "esp_private/esp_cache_private.h"
|
#include "esp_private/esp_cache_private.h"
|
||||||
#include "driver/spi_slave_hd.h"
|
#include "driver/spi_slave_hd.h"
|
||||||
#include "hal/spi_slave_hd_hal.h"
|
#include "hal/spi_slave_hd_hal.h"
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
|
||||||
#include "esp_cache.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SPI_SLAVE_ISR_IN_IRAM
|
#ifdef CONFIG_SPI_SLAVE_ISR_IN_IRAM
|
||||||
#define SPI_SLAVE_ISR_ATTR IRAM_ATTR
|
#define SPI_SLAVE_ISR_ATTR IRAM_ATTR
|
||||||
@@ -40,6 +38,7 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
spi_slave_hd_data_t *trans; //original trans
|
spi_slave_hd_data_t *trans; //original trans
|
||||||
void *aligned_buffer; //actually trans buffer (re-malloced if needed)
|
void *aligned_buffer; //actually trans buffer (re-malloced if needed)
|
||||||
|
bool dma_hw_error; //true if DMA hardware over/underflow occurred
|
||||||
} spi_slave_hd_trans_priv_t;
|
} spi_slave_hd_trans_priv_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -48,9 +47,6 @@ typedef struct {
|
|||||||
spi_bus_attr_t* bus_attr;
|
spi_bus_attr_t* bus_attr;
|
||||||
_Atomic spi_bus_fsm_t fsm;
|
_Atomic spi_bus_fsm_t fsm;
|
||||||
spi_dma_ctx_t *dma_ctx;
|
spi_dma_ctx_t *dma_ctx;
|
||||||
uint16_t internal_mem_align_size;
|
|
||||||
int max_transfer_sz;
|
|
||||||
uint32_t flags;
|
|
||||||
portMUX_TYPE int_spinlock;
|
portMUX_TYPE int_spinlock;
|
||||||
intr_handle_t intr;
|
intr_handle_t intr;
|
||||||
intr_handle_t intr_dma;
|
intr_handle_t intr_dma;
|
||||||
@@ -131,10 +127,11 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b
|
|||||||
host->bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host_id);
|
host->bus_attr = (spi_bus_attr_t *)spi_bus_get_attr(host_id);
|
||||||
host->cs_io_num = config->spics_io_num;
|
host->cs_io_num = config->spics_io_num;
|
||||||
|
|
||||||
ret = spicommon_dma_chan_alloc(host_id, config->dma_chan, &host->dma_ctx);
|
ret = spicommon_dma_chan_alloc(host_id, config->dma_chan);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
host->dma_ctx = spi_bus_get_dma_ctx(host_id);
|
||||||
#if SOC_GDMA_SUPPORTED
|
#if SOC_GDMA_SUPPORTED
|
||||||
gdma_strategy_config_t dma_strategy = {
|
gdma_strategy_config_t dma_strategy = {
|
||||||
.auto_update_desc = true,
|
.auto_update_desc = true,
|
||||||
@@ -145,7 +142,7 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b
|
|||||||
spi_dma_ll_enable_out_auto_wrback(SPI_LL_GET_HW(host->dma_ctx->tx_dma_chan.host_id), host->dma_ctx->tx_dma_chan.chan_id, 1);
|
spi_dma_ll_enable_out_auto_wrback(SPI_LL_GET_HW(host->dma_ctx->tx_dma_chan.host_id), host->dma_ctx->tx_dma_chan.chan_id, 1);
|
||||||
spi_dma_ll_set_out_eof_generation(SPI_LL_GET_HW(host->dma_ctx->tx_dma_chan.host_id), host->dma_ctx->tx_dma_chan.chan_id, 1);
|
spi_dma_ll_set_out_eof_generation(SPI_LL_GET_HW(host->dma_ctx->tx_dma_chan.host_id), host->dma_ctx->tx_dma_chan.chan_id, 1);
|
||||||
#endif
|
#endif
|
||||||
ret = spicommon_dma_desc_alloc(host->dma_ctx, bus_config->max_transfer_sz, &host->max_transfer_sz);
|
ret = spicommon_dma_desc_alloc(host_id, bus_config->max_transfer_sz, &host->bus_attr->max_transfer_sz);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -163,20 +160,11 @@ esp_err_t spi_slave_hd_init(spi_host_device_t host_id, const spi_bus_config_t *b
|
|||||||
host->hal.dmadesc_rx[i].desc = &host->dma_ctx->dmadesc_rx[i];
|
host->hal.dmadesc_rx[i].desc = &host->dma_ctx->dmadesc_rx[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL);
|
||||||
size_t alignment;
|
|
||||||
esp_cache_get_alignment(MALLOC_CAP_DMA, &alignment);
|
|
||||||
host->internal_mem_align_size = alignment;
|
|
||||||
#else
|
|
||||||
host->internal_mem_align_size = 4;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ret = spicommon_bus_initialize_io(host_id, bus_config, SPICOMMON_BUSFLAG_SLAVE | bus_config->flags, NULL, NULL);
|
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
spicommon_cs_initialize(host_id, config->spics_io_num, 0, !(bus_config->flags & SPICOMMON_BUSFLAG_NATIVE_PINS), NULL);
|
spicommon_cs_initialize(host_id, config->spics_io_num, 0, !(bus_config->flags & SPICOMMON_BUSFLAG_NATIVE_PINS), NULL);
|
||||||
host->flags = host->bus_attr->flags; // This flag MUST be set after spicommon_bus_initialize_io is called
|
|
||||||
|
|
||||||
spi_slave_hd_hal_config_t hal_config = {
|
spi_slave_hd_hal_config_t hal_config = {
|
||||||
.host_id = host_id,
|
.host_id = host_id,
|
||||||
@@ -351,12 +339,10 @@ esp_err_t spi_slave_hd_deinit(spi_host_device_t host_id)
|
|||||||
|
|
||||||
spicommon_bus_free_io_cfg(host_id);
|
spicommon_bus_free_io_cfg(host_id);
|
||||||
spicommon_cs_free_io(host->cs_io_num, &host->bus_attr->gpio_reserve);
|
spicommon_cs_free_io(host->cs_io_num, &host->bus_attr->gpio_reserve);
|
||||||
spicommon_bus_free(host_id);
|
|
||||||
free(host->dma_ctx->dmadesc_tx);
|
|
||||||
free(host->dma_ctx->dmadesc_rx);
|
|
||||||
free(host->hal.dmadesc_tx);
|
free(host->hal.dmadesc_tx);
|
||||||
free(host->hal.dmadesc_rx);
|
free(host->hal.dmadesc_rx);
|
||||||
spicommon_dma_chan_free(host->dma_ctx);
|
spicommon_dma_chan_free(host_id);
|
||||||
|
spicommon_bus_free(host_id);
|
||||||
|
|
||||||
free(host);
|
free(host);
|
||||||
spihost[host_id] = NULL;
|
spihost[host_id] = NULL;
|
||||||
@@ -426,6 +412,25 @@ static inline SPI_SLAVE_ISR_ATTR BaseType_t intr_check_clear_callback(spi_slave_
|
|||||||
}
|
}
|
||||||
return cb_awoken;
|
return cb_awoken;
|
||||||
}
|
}
|
||||||
|
static void SPI_SLAVE_ISR_ATTR spi_slave_hd_tx_dma_error_check(spi_slave_hd_slot_t *host, spi_slave_hd_trans_priv_t priv_trans)
|
||||||
|
{
|
||||||
|
#if SOC_PSRAM_DMA_CAPABLE && CONFIG_SPIRAM //error checks only for psram dma
|
||||||
|
if (esp_ptr_external_ram(priv_trans.aligned_buffer) && spi_slave_hd_hal_check_clear_intr(&host->hal, SPI_LL_INTR_OUT_EMPTY)) {
|
||||||
|
priv_trans.dma_hw_error = true;
|
||||||
|
ESP_DRAM_LOGE(TAG, "DMA TX underflow detected");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
static void SPI_SLAVE_ISR_ATTR spi_slave_hd_rx_dma_error_check(spi_slave_hd_slot_t *host, spi_slave_hd_trans_priv_t priv_trans)
|
||||||
|
{
|
||||||
|
#if SOC_PSRAM_DMA_CAPABLE && CONFIG_SPIRAM //error checks only for psram dma
|
||||||
|
if (esp_ptr_external_ram(priv_trans.aligned_buffer) && spi_slave_hd_hal_check_clear_intr(&host->hal, SPI_LL_INTR_IN_FULL)) {
|
||||||
|
priv_trans.dma_hw_error = true;
|
||||||
|
ESP_DRAM_LOGE(TAG, "DMA RX overflow detected");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_segment_isr(void *arg)
|
static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_segment_isr(void *arg)
|
||||||
{
|
{
|
||||||
spi_slave_hd_slot_t *host = (spi_slave_hd_slot_t *)arg;
|
spi_slave_hd_slot_t *host = (spi_slave_hd_slot_t *)arg;
|
||||||
@@ -450,6 +455,7 @@ static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_segment_isr(void *arg)
|
|||||||
portEXIT_CRITICAL_ISR(&host->int_spinlock);
|
portEXIT_CRITICAL_ISR(&host->int_spinlock);
|
||||||
|
|
||||||
if (tx_done) {
|
if (tx_done) {
|
||||||
|
spi_slave_hd_tx_dma_error_check(host, host->tx_curr_trans);
|
||||||
bool ret_queue = true;
|
bool ret_queue = true;
|
||||||
if (callback->cb_sent) {
|
if (callback->cb_sent) {
|
||||||
spi_slave_hd_event_t ev = {
|
spi_slave_hd_event_t ev = {
|
||||||
@@ -468,15 +474,10 @@ static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_segment_isr(void *arg)
|
|||||||
host->tx_curr_trans.trans = NULL;
|
host->tx_curr_trans.trans = NULL;
|
||||||
}
|
}
|
||||||
if (rx_done) {
|
if (rx_done) {
|
||||||
|
spicommon_dma_rx_mb(host->host_id, host->rx_curr_trans.aligned_buffer);
|
||||||
|
spi_slave_hd_rx_dma_error_check(host, host->rx_curr_trans);
|
||||||
bool ret_queue = true;
|
bool ret_queue = true;
|
||||||
host->rx_curr_trans.trans->trans_len = spi_slave_hd_hal_rxdma_seg_get_len(hal);
|
host->rx_curr_trans.trans->trans_len = spi_slave_hd_hal_rxdma_seg_get_len(hal);
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE //invalidate here to let user access rx data in post_cb if possible
|
|
||||||
uint16_t alignment = host->internal_mem_align_size;
|
|
||||||
uint32_t buff_len = (host->rx_curr_trans.trans->len + alignment - 1) & (~(alignment - 1));
|
|
||||||
esp_err_t ret = esp_cache_msync((void *)host->rx_curr_trans.aligned_buffer, buff_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
|
||||||
assert(ret == ESP_OK);
|
|
||||||
(void)ret;
|
|
||||||
#endif
|
|
||||||
if (callback->cb_recv) {
|
if (callback->cb_recv) {
|
||||||
spi_slave_hd_event_t ev = {
|
spi_slave_hd_event_t ev = {
|
||||||
.event = SPI_EV_RECV,
|
.event = SPI_EV_RECV,
|
||||||
@@ -557,7 +558,7 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_tx_isr(void *arg)
|
|||||||
BaseType_t awoken = pdFALSE;
|
BaseType_t awoken = pdFALSE;
|
||||||
BaseType_t ret __attribute__((unused));
|
BaseType_t ret __attribute__((unused));
|
||||||
|
|
||||||
spi_slave_hd_trans_priv_t ret_priv_trans;
|
spi_slave_hd_trans_priv_t ret_priv_trans = {};
|
||||||
while (1) {
|
while (1) {
|
||||||
bool trans_finish = false;
|
bool trans_finish = false;
|
||||||
trans_finish = spi_slave_hd_hal_get_tx_finished_trans(hal, (void **)&ret_priv_trans.trans, &ret_priv_trans.aligned_buffer);
|
trans_finish = spi_slave_hd_hal_get_tx_finished_trans(hal, (void **)&ret_priv_trans.trans, &ret_priv_trans.aligned_buffer);
|
||||||
@@ -569,6 +570,7 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_tx_isr(void *arg)
|
|||||||
portEXIT_CRITICAL_ISR(&host->int_spinlock);
|
portEXIT_CRITICAL_ISR(&host->int_spinlock);
|
||||||
|
|
||||||
bool ret_queue = true;
|
bool ret_queue = true;
|
||||||
|
spi_slave_hd_tx_dma_error_check(host, ret_priv_trans);
|
||||||
if (callback->cb_sent) {
|
if (callback->cb_sent) {
|
||||||
spi_slave_hd_event_t ev = {
|
spi_slave_hd_event_t ev = {
|
||||||
.event = SPI_EV_SEND,
|
.event = SPI_EV_SEND,
|
||||||
@@ -600,7 +602,7 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_rx_isr(void *arg)
|
|||||||
BaseType_t awoken = pdFALSE;
|
BaseType_t awoken = pdFALSE;
|
||||||
BaseType_t ret __attribute__((unused));
|
BaseType_t ret __attribute__((unused));
|
||||||
|
|
||||||
spi_slave_hd_trans_priv_t ret_priv_trans;
|
spi_slave_hd_trans_priv_t ret_priv_trans = {};
|
||||||
size_t trans_len;
|
size_t trans_len;
|
||||||
while (1) {
|
while (1) {
|
||||||
bool trans_finish = false;
|
bool trans_finish = false;
|
||||||
@@ -613,14 +615,9 @@ static SPI_SLAVE_ISR_ATTR void spi_slave_hd_append_rx_isr(void *arg)
|
|||||||
portEXIT_CRITICAL_ISR(&host->int_spinlock);
|
portEXIT_CRITICAL_ISR(&host->int_spinlock);
|
||||||
ret_priv_trans.trans->trans_len = trans_len;
|
ret_priv_trans.trans->trans_len = trans_len;
|
||||||
|
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE //invalidate here to let user access rx data in post_cb if possible
|
|
||||||
uint16_t alignment = host->internal_mem_align_size;
|
|
||||||
uint32_t buff_len = (ret_priv_trans.trans->len + alignment - 1) & (~(alignment - 1));
|
|
||||||
esp_err_t ret = esp_cache_msync((void *)ret_priv_trans.aligned_buffer, buff_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
|
||||||
assert(ret == ESP_OK);
|
|
||||||
(void)ret;
|
|
||||||
#endif
|
|
||||||
bool ret_queue = true;
|
bool ret_queue = true;
|
||||||
|
spicommon_dma_rx_mb(host->host_id, ret_priv_trans.aligned_buffer);
|
||||||
|
spi_slave_hd_rx_dma_error_check(host, ret_priv_trans);
|
||||||
if (callback->cb_recv) {
|
if (callback->cb_recv) {
|
||||||
spi_slave_hd_event_t ev = {
|
spi_slave_hd_event_t ev = {
|
||||||
.event = SPI_EV_RECV,
|
.event = SPI_EV_RECV,
|
||||||
@@ -689,7 +686,6 @@ static SPI_SLAVE_ISR_ATTR void s_spi_slave_hd_append_legacy_isr(void *arg)
|
|||||||
|
|
||||||
static void s_spi_slave_hd_destroy_priv_trans(spi_host_device_t host, spi_slave_hd_trans_priv_t *priv_trans, spi_slave_chan_t chan)
|
static void s_spi_slave_hd_destroy_priv_trans(spi_host_device_t host, spi_slave_hd_trans_priv_t *priv_trans, spi_slave_chan_t chan)
|
||||||
{
|
{
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
|
||||||
spi_slave_hd_data_t *orig_trans = priv_trans->trans;
|
spi_slave_hd_data_t *orig_trans = priv_trans->trans;
|
||||||
if (priv_trans->aligned_buffer != orig_trans->data) {
|
if (priv_trans->aligned_buffer != orig_trans->data) {
|
||||||
if (chan == SPI_SLAVE_CHAN_RX) {
|
if (chan == SPI_SLAVE_CHAN_RX) {
|
||||||
@@ -697,39 +693,19 @@ static void s_spi_slave_hd_destroy_priv_trans(spi_host_device_t host, spi_slave_
|
|||||||
}
|
}
|
||||||
free(priv_trans->aligned_buffer);
|
free(priv_trans->aligned_buffer);
|
||||||
}
|
}
|
||||||
#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t s_spi_slave_hd_setup_priv_trans(spi_host_device_t host, spi_slave_hd_trans_priv_t *priv_trans, spi_slave_chan_t chan)
|
static esp_err_t s_spi_slave_hd_setup_priv_trans(spi_host_device_t host, spi_slave_hd_trans_priv_t *priv_trans, spi_slave_chan_t chan)
|
||||||
{
|
{
|
||||||
spi_slave_hd_data_t *orig_trans = priv_trans->trans;
|
spi_slave_hd_data_t *orig_trans = priv_trans->trans;
|
||||||
|
|
||||||
priv_trans->aligned_buffer = orig_trans->data;
|
bool auto_malloc = (orig_trans->flags & SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO);
|
||||||
|
bool is_tx = (chan == SPI_SLAVE_CHAN_TX);
|
||||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
esp_err_t ret = spicommon_dma_setup_priv_buffer(host, (uint32_t *)orig_trans->data, orig_trans->len, is_tx, true, auto_malloc, (uint32_t **)&priv_trans->aligned_buffer);
|
||||||
uint16_t alignment = spihost[host]->internal_mem_align_size;
|
if (ret != ESP_OK) {
|
||||||
uint32_t byte_len = orig_trans->len;
|
s_spi_slave_hd_destroy_priv_trans(host, priv_trans, chan);
|
||||||
|
return ret;
|
||||||
if (((uint32_t)orig_trans->data | byte_len) & (alignment - 1)) {
|
|
||||||
ESP_RETURN_ON_FALSE(orig_trans->flags & SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO, ESP_ERR_INVALID_ARG, TAG, "data buffer addr&len not align to %d byte, or not dma_capable", alignment);
|
|
||||||
byte_len = (byte_len + alignment - 1) & (~(alignment - 1)); // up align to alignment
|
|
||||||
ESP_LOGD(TAG, "Re-allocate %s buffer of len %" PRIu32 " for DMA", (chan == SPI_SLAVE_CHAN_TX) ? "TX" : "RX", byte_len);
|
|
||||||
priv_trans->aligned_buffer = heap_caps_aligned_alloc(64, byte_len, MALLOC_CAP_DMA);
|
|
||||||
if (priv_trans->aligned_buffer == NULL) {
|
|
||||||
return ESP_ERR_NO_MEM;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (chan == SPI_SLAVE_CHAN_TX) {
|
|
||||||
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-overlapping-buffers") // TODO IDF-11086
|
|
||||||
memcpy(priv_trans->aligned_buffer, orig_trans->data, orig_trans->len);
|
|
||||||
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-overlapping-buffers")
|
|
||||||
esp_err_t ret = esp_cache_msync((void *)priv_trans->aligned_buffer, byte_len, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
|
|
||||||
ESP_RETURN_ON_FALSE(ESP_OK == ret, ESP_ERR_INVALID_STATE, TAG, "mem sync c2m(writeback) fail");
|
|
||||||
} else {
|
|
||||||
esp_err_t ret = esp_cache_msync((void *)priv_trans->aligned_buffer, byte_len, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
|
|
||||||
ESP_RETURN_ON_FALSE(ESP_OK == ret, ESP_ERR_INVALID_STATE, TAG, "mem sync m2c(invalid) fail");
|
|
||||||
}
|
|
||||||
#endif //SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -750,7 +726,7 @@ static esp_err_t get_ret_queue_result(spi_host_device_t host_id, spi_slave_chan_
|
|||||||
|
|
||||||
s_spi_slave_hd_destroy_priv_trans(host_id, &hd_priv_trans, chan);
|
s_spi_slave_hd_destroy_priv_trans(host_id, &hd_priv_trans, chan);
|
||||||
*out_trans = hd_priv_trans.trans;
|
*out_trans = hd_priv_trans.trans;
|
||||||
return ESP_OK;
|
return hd_priv_trans.dma_hw_error ? ESP_ERR_INVALID_STATE : ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t s_spi_slave_hd_append_txdma(spi_slave_hd_slot_t *host, uint8_t *data, size_t len, void *arg)
|
esp_err_t s_spi_slave_hd_append_txdma(spi_slave_hd_slot_t *host, uint8_t *data, size_t len, void *arg)
|
||||||
@@ -841,12 +817,11 @@ esp_err_t spi_slave_hd_queue_trans(spi_host_device_t host_id, spi_slave_chan_t c
|
|||||||
spi_slave_hd_slot_t *host = spihost[host_id];
|
spi_slave_hd_slot_t *host = spihost[host_id];
|
||||||
|
|
||||||
SPIHD_CHECK(host->append_mode == 0, "This API should be used for SPI Slave HD Segment Mode", ESP_ERR_INVALID_STATE);
|
SPIHD_CHECK(host->append_mode == 0, "This API should be used for SPI Slave HD Segment Mode", ESP_ERR_INVALID_STATE);
|
||||||
SPIHD_CHECK(esp_ptr_dma_capable(trans->data), "The buffer should be DMA capable.", ESP_ERR_INVALID_ARG);
|
SPIHD_CHECK(trans->len <= host->bus_attr->max_transfer_sz && trans->len > 0, "Invalid buffer size", ESP_ERR_INVALID_ARG);
|
||||||
SPIHD_CHECK(trans->len <= host->max_transfer_sz && trans->len > 0, "Invalid buffer size", ESP_ERR_INVALID_ARG);
|
|
||||||
SPIHD_CHECK(chan == SPI_SLAVE_CHAN_TX || chan == SPI_SLAVE_CHAN_RX, "Invalid channel", ESP_ERR_INVALID_ARG);
|
SPIHD_CHECK(chan == SPI_SLAVE_CHAN_TX || chan == SPI_SLAVE_CHAN_RX, "Invalid channel", ESP_ERR_INVALID_ARG);
|
||||||
|
|
||||||
spi_slave_hd_trans_priv_t hd_priv_trans = {.trans = trans};
|
spi_slave_hd_trans_priv_t hd_priv_trans = {.trans = trans};
|
||||||
SPIHD_CHECK(ESP_OK == s_spi_slave_hd_setup_priv_trans(host_id, &hd_priv_trans, chan), "No mem to allocate new cache buffer", ESP_ERR_NO_MEM);
|
ESP_RETURN_ON_ERROR(s_spi_slave_hd_setup_priv_trans(host_id, &hd_priv_trans, chan), TAG, "Setup dma buffer failed");
|
||||||
|
|
||||||
if (chan == SPI_SLAVE_CHAN_TX) {
|
if (chan == SPI_SLAVE_CHAN_TX) {
|
||||||
BaseType_t ret = xQueueSend(host->tx_trans_queue, &hd_priv_trans, timeout);
|
BaseType_t ret = xQueueSend(host->tx_trans_queue, &hd_priv_trans, timeout);
|
||||||
@@ -894,12 +869,11 @@ esp_err_t spi_slave_hd_append_trans(spi_host_device_t host_id, spi_slave_chan_t
|
|||||||
|
|
||||||
SPIHD_CHECK(trans->len <= SPI_MAX_DMA_LEN, "Currently we only support transaction with data length within 4092 bytes", ESP_ERR_INVALID_ARG);
|
SPIHD_CHECK(trans->len <= SPI_MAX_DMA_LEN, "Currently we only support transaction with data length within 4092 bytes", ESP_ERR_INVALID_ARG);
|
||||||
SPIHD_CHECK(host->append_mode == 1, "This API should be used for SPI Slave HD Append Mode", ESP_ERR_INVALID_STATE);
|
SPIHD_CHECK(host->append_mode == 1, "This API should be used for SPI Slave HD Append Mode", ESP_ERR_INVALID_STATE);
|
||||||
SPIHD_CHECK(esp_ptr_dma_capable(trans->data), "The buffer should be DMA capable.", ESP_ERR_INVALID_ARG);
|
SPIHD_CHECK(trans->len <= host->bus_attr->max_transfer_sz && trans->len > 0, "Invalid buffer size", ESP_ERR_INVALID_ARG);
|
||||||
SPIHD_CHECK(trans->len <= host->max_transfer_sz && trans->len > 0, "Invalid buffer size", ESP_ERR_INVALID_ARG);
|
|
||||||
SPIHD_CHECK(chan == SPI_SLAVE_CHAN_TX || chan == SPI_SLAVE_CHAN_RX, "Invalid channel", ESP_ERR_INVALID_ARG);
|
SPIHD_CHECK(chan == SPI_SLAVE_CHAN_TX || chan == SPI_SLAVE_CHAN_RX, "Invalid channel", ESP_ERR_INVALID_ARG);
|
||||||
|
|
||||||
spi_slave_hd_trans_priv_t hd_priv_trans = {.trans = trans};
|
spi_slave_hd_trans_priv_t hd_priv_trans = {.trans = trans};
|
||||||
SPIHD_CHECK(ESP_OK == s_spi_slave_hd_setup_priv_trans(host_id, &hd_priv_trans, chan), "No mem to allocate new cache buffer", ESP_ERR_NO_MEM);
|
ESP_RETURN_ON_ERROR(s_spi_slave_hd_setup_priv_trans(host_id, &hd_priv_trans, chan), TAG, "Setup dma buffer failed");
|
||||||
|
|
||||||
if (chan == SPI_SLAVE_CHAN_TX) {
|
if (chan == SPI_SLAVE_CHAN_TX) {
|
||||||
BaseType_t ret = xSemaphoreTake(host->tx_cnting_sem, timeout);
|
BaseType_t ret = xSemaphoreTake(host->tx_cnting_sem, timeout);
|
||||||
|
|||||||
@@ -502,9 +502,9 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "test 4 iomux output pins...");
|
ESP_LOGI(TAG, "test 4 iomux output pins...");
|
||||||
@@ -513,9 +513,9 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "test 6 output pins...");
|
ESP_LOGI(TAG, "test 6 output pins...");
|
||||||
@@ -525,9 +525,9 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "test 4 output pins...");
|
ESP_LOGI(TAG, "test 4 output pins...");
|
||||||
@@ -537,9 +537,9 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
|
|
||||||
#if TEST_SOC_HAS_INPUT_ONLY_PINS //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
|
#if TEST_SOC_HAS_INPUT_ONLY_PINS //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
|
||||||
@@ -549,7 +549,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "test slave 5 output pins and MISO on input-only pin...");
|
ESP_LOGI(TAG, "test slave 5 output pins and MISO on input-only pin...");
|
||||||
@@ -558,7 +558,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "test master 3 output pins and MOSI on input-only pin...");
|
ESP_LOGI(TAG, "test master 3 output pins and MOSI on input-only pin...");
|
||||||
@@ -568,7 +568,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "test slave 3 output pins and MISO on input-only pin...");
|
ESP_LOGI(TAG, "test slave 3 output pins and MISO on input-only pin...");
|
||||||
@@ -577,7 +577,7 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
.mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ESP_OK(spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
TEST_ASSERT_EQUAL_HEX32(flags_expected, flags_o);
|
||||||
//There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
|
//There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
|
||||||
#endif //#if TEST_SOC_HAS_INPUT_ONLY_PINS
|
#endif //#if TEST_SOC_HAS_INPUT_ONLY_PINS
|
||||||
@@ -589,8 +589,8 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
|
|
||||||
ESP_LOGI(TAG, "check native flag for 4 output pins...");
|
ESP_LOGI(TAG, "check native flag for 4 output pins...");
|
||||||
flags_expected = SPICOMMON_BUSFLAG_IOMUX_PINS;
|
flags_expected = SPICOMMON_BUSFLAG_IOMUX_PINS;
|
||||||
@@ -599,8 +599,8 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
|
|
||||||
#if TEST_SOC_HAS_INPUT_ONLY_PINS //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
|
#if TEST_SOC_HAS_INPUT_ONLY_PINS //There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
|
||||||
ESP_LOGI(TAG, "check dual flag for master 5 output pins and MISO/MOSI on input-only pin...");
|
ESP_LOGI(TAG, "check dual flag for master 5 output pins and MISO/MOSI on input-only pin...");
|
||||||
@@ -609,14 +609,14 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
cfg = (spi_bus_config_t) {
|
cfg = (spi_bus_config_t) {
|
||||||
.mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
|
|
||||||
ESP_LOGI(TAG, "check dual flag for master 3 output pins and MISO/MOSI on input-only pin...");
|
ESP_LOGI(TAG, "check dual flag for master 3 output pins and MISO/MOSI on input-only pin...");
|
||||||
flags_expected = SPICOMMON_BUSFLAG_DUAL | SPICOMMON_BUSFLAG_GPIO_PINS;
|
flags_expected = SPICOMMON_BUSFLAG_DUAL | SPICOMMON_BUSFLAG_GPIO_PINS;
|
||||||
@@ -624,14 +624,14 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = INPUT_ONLY_PIN, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
cfg = (spi_bus_config_t) {
|
cfg = (spi_bus_config_t) {
|
||||||
.mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
.mosi_io_num = INPUT_ONLY_PIN, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = -1,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
//There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
|
//There is no input-only pin on esp32c3 and esp32s3, so this test could be ignored.
|
||||||
#endif //#if TEST_SOC_HAS_INPUT_ONLY_PINS
|
#endif //#if TEST_SOC_HAS_INPUT_ONLY_PINS
|
||||||
|
|
||||||
@@ -641,8 +641,8 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = -1, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = -1, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
|
|
||||||
ESP_LOGI(TAG, "check mosi flag...");
|
ESP_LOGI(TAG, "check mosi flag...");
|
||||||
flags_expected = SPICOMMON_BUSFLAG_MOSI;
|
flags_expected = SPICOMMON_BUSFLAG_MOSI;
|
||||||
@@ -650,8 +650,8 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = -1, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = -1, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
|
|
||||||
ESP_LOGI(TAG, "check miso flag...");
|
ESP_LOGI(TAG, "check miso flag...");
|
||||||
flags_expected = SPICOMMON_BUSFLAG_MISO;
|
flags_expected = SPICOMMON_BUSFLAG_MISO;
|
||||||
@@ -659,8 +659,8 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = -1, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = -1, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
|
|
||||||
ESP_LOGI(TAG, "check quad flag...");
|
ESP_LOGI(TAG, "check quad flag...");
|
||||||
flags_expected = SPICOMMON_BUSFLAG_QUAD;
|
flags_expected = SPICOMMON_BUSFLAG_QUAD;
|
||||||
@@ -668,14 +668,14 @@ TEST_CASE("spi bus setting with different pin configs", "[spi]")
|
|||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = -1, .quadwp_io_num = spi_periph_signal[TEST_SPI_HOST].spiwp_iomux_pin,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
cfg = (spi_bus_config_t) {
|
cfg = (spi_bus_config_t) {
|
||||||
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = -1,
|
.mosi_io_num = spi_periph_signal[TEST_SPI_HOST].spid_iomux_pin, .miso_io_num = spi_periph_signal[TEST_SPI_HOST].spiq_iomux_pin, .sclk_io_num = spi_periph_signal[TEST_SPI_HOST].spiclk_iomux_pin, .quadhd_io_num = spi_periph_signal[TEST_SPI_HOST].spihd_iomux_pin, .quadwp_io_num = -1,
|
||||||
.max_transfer_sz = 8, .flags = flags_expected
|
.max_transfer_sz = 8, .flags = flags_expected
|
||||||
};
|
};
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_MASTER, &flags_o));
|
||||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o, NULL));
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, spicommon_bus_initialize_io(TEST_SPI_HOST, &cfg, flags_expected | SPICOMMON_BUSFLAG_SLAVE, &flags_o));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("SPI Master no response when switch from host1 (SPI2) to host2 (SPI3)", "[spi]")
|
TEST_CASE("SPI Master no response when switch from host1 (SPI2) to host2 (SPI3)", "[spi]")
|
||||||
@@ -912,7 +912,7 @@ void test_cmd_addr(spi_slave_task_context_t *slave_context, bool lsb_first)
|
|||||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi));
|
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi));
|
||||||
|
|
||||||
//connecting pins to two peripherals breaks the output, fix it.
|
//connecting pins to two peripherals breaks the output, fix it.
|
||||||
same_pin_func_sel(buscfg, devcfg.spics_io_num, 0, false);
|
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, buscfg, devcfg.spics_io_num);
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
//prepare slave tx data
|
//prepare slave tx data
|
||||||
@@ -1097,7 +1097,7 @@ TEST_CASE("SPI master variable dummy test", "[spi]")
|
|||||||
spi_slave_interface_config_t slave_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
spi_slave_interface_config_t slave_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slave_cfg, SPI_DMA_DISABLED));
|
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slave_cfg, SPI_DMA_DISABLED));
|
||||||
|
|
||||||
same_pin_func_sel(bus_cfg, dev_cfg.spics_io_num, 0, false);
|
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, dev_cfg.spics_io_num);
|
||||||
|
|
||||||
uint8_t data_to_send[] = {0x12, 0x34, 0x56, 0x78};
|
uint8_t data_to_send[] = {0x12, 0x34, 0x56, 0x78};
|
||||||
|
|
||||||
@@ -1140,7 +1140,7 @@ TEST_CASE("SPI master hd dma TX without RX test", "[spi]")
|
|||||||
spi_slave_interface_config_t slave_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
spi_slave_interface_config_t slave_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slave_cfg, SPI_DMA_CH_AUTO));
|
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slave_cfg, SPI_DMA_CH_AUTO));
|
||||||
|
|
||||||
same_pin_func_sel(bus_cfg, dev_cfg.spics_io_num, 0, false);
|
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, dev_cfg.spics_io_num);
|
||||||
|
|
||||||
uint32_t buf_size = 32;
|
uint32_t buf_size = 32;
|
||||||
uint8_t *mst_send_buf = spi_bus_dma_memory_alloc(TEST_SPI_HOST, buf_size, 0);
|
uint8_t *mst_send_buf = spi_bus_dma_memory_alloc(TEST_SPI_HOST, buf_size, 0);
|
||||||
@@ -2076,11 +2076,11 @@ void test_spi_psram_trans(spi_device_handle_t dev_handle, void *tx, void *rx)
|
|||||||
|
|
||||||
// To use psram, hardware will pass data through MSPI and GDMA to GPSPI, which need some time
|
// To use psram, hardware will pass data through MSPI and GDMA to GPSPI, which need some time
|
||||||
// GPSPI bandwidth(speed * line_num) should always no more than PSRAM bandwidth
|
// GPSPI bandwidth(speed * line_num) should always no more than PSRAM bandwidth
|
||||||
trans_cfg.override_freq_hz = (CONFIG_SPIRAM_SPEED / 4) * 1000 * 1000;
|
trans_cfg.override_freq_hz = MIN(80000000, (CONFIG_SPIRAM_SPEED / 2) * 1000 * 1000);
|
||||||
printf("%d TX %p RX %p len %d @%ld kHz\n", cnt, trans_cfg.tx_buffer, trans_cfg.rx_buffer, trans_len, trans_cfg.override_freq_hz / 1000);
|
printf("%d TX %p RX %p len %d @%ld kHz\n", cnt, trans_cfg.tx_buffer, trans_cfg.rx_buffer, trans_len, trans_cfg.override_freq_hz / 1000);
|
||||||
TEST_ESP_OK(spi_device_transmit(dev_handle, &trans_cfg));
|
TEST_ESP_OK(spi_device_transmit(dev_handle, &trans_cfg));
|
||||||
TEST_ASSERT(!(trans_cfg.flags & (SPI_TRANS_DMA_RX_FAIL | SPI_TRANS_DMA_TX_FAIL)));
|
TEST_ASSERT(!(trans_cfg.flags & (SPI_TRANS_DMA_RX_FAIL | SPI_TRANS_DMA_TX_FAIL)));
|
||||||
spitest_cmp_or_dump(trans_cfg.tx_buffer, trans_cfg.rx_buffer, trans_len);
|
TEST_ASSERT_EQUAL_HEX8_ARRAY(trans_cfg.tx_buffer, trans_cfg.rx_buffer, trans_len);
|
||||||
trans_cfg.tx_buffer += trans_len;
|
trans_cfg.tx_buffer += trans_len;
|
||||||
trans_cfg.rx_buffer += trans_len;
|
trans_cfg.rx_buffer += trans_len;
|
||||||
trans_len ++;
|
trans_len ++;
|
||||||
@@ -2125,9 +2125,11 @@ TEST_CASE("SPI_Master: PSRAM buffer transaction via EDMA", "[spi]")
|
|||||||
TEST_ASSERT(i ? (before - after) < 2 * TEST_EDMA_TRANS_LEN : (before - after) > 2 * TEST_EDMA_TRANS_LEN);
|
TEST_ASSERT(i ? (before - after) < 2 * TEST_EDMA_TRANS_LEN : (before - after) > 2 * TEST_EDMA_TRANS_LEN);
|
||||||
spi_device_polling_end(dev_handle, portMAX_DELAY);
|
spi_device_polling_end(dev_handle, portMAX_DELAY);
|
||||||
printf("TX fail: %d, RX fail: %d\n", !!(trans_cfg.flags & SPI_TRANS_DMA_TX_FAIL), !!(trans_cfg.flags & SPI_TRANS_DMA_RX_FAIL));
|
printf("TX fail: %d, RX fail: %d\n", !!(trans_cfg.flags & SPI_TRANS_DMA_TX_FAIL), !!(trans_cfg.flags & SPI_TRANS_DMA_RX_FAIL));
|
||||||
|
#if !SOC_IS(ESP32P4) // P4 can't reach error condition since it has powerful 16bits ddr psram
|
||||||
TEST_ASSERT((!!i) == !!(trans_cfg.flags & (SPI_TRANS_DMA_TX_FAIL | SPI_TRANS_DMA_RX_FAIL)));
|
TEST_ASSERT((!!i) == !!(trans_cfg.flags & (SPI_TRANS_DMA_TX_FAIL | SPI_TRANS_DMA_RX_FAIL)));
|
||||||
|
#endif
|
||||||
if (!i) { // data should be correct if using auto malloc
|
if (!i) { // data should be correct if using auto malloc
|
||||||
spitest_cmp_or_dump(trans_cfg.tx_buffer, trans_cfg.rx_buffer, TEST_EDMA_TRANS_LEN);
|
TEST_ASSERT_EQUAL_HEX8_ARRAY(trans_cfg.tx_buffer, trans_cfg.rx_buffer, TEST_EDMA_TRANS_LEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ TEST_CASE("SPI Single Board Test SIO", "[spi]")
|
|||||||
spi_slave_interface_config_t slv_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
spi_slave_interface_config_t slv_cfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slv_cfg, SPI_DMA_DISABLED));
|
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &bus_cfg, &slv_cfg, SPI_DMA_DISABLED));
|
||||||
|
|
||||||
same_pin_func_sel(bus_cfg, dev_cfg.spics_io_num, 0, false);
|
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, dev_cfg.spics_io_num);
|
||||||
inner_connect(bus_cfg);
|
inner_connect(bus_cfg);
|
||||||
|
|
||||||
WORD_ALIGNED_ATTR uint8_t master_rx_buffer[320];
|
WORD_ALIGNED_ATTR uint8_t master_rx_buffer[320];
|
||||||
|
|||||||
@@ -1567,15 +1567,18 @@ static void test_slave_hd_dma(void)
|
|||||||
test_fill_random_to_buffers_dualboard(985 + mode + speed_level + i, slave_expect, slave_send, TEST_STEP_LEN);
|
test_fill_random_to_buffers_dualboard(985 + mode + speed_level + i, slave_expect, slave_send, TEST_STEP_LEN);
|
||||||
uint32_t test_trans_len = TEST_STEP_LEN;
|
uint32_t test_trans_len = TEST_STEP_LEN;
|
||||||
|
|
||||||
spi_slave_hd_data_t *ret_trans, slave_trans = {
|
spi_slave_hd_data_t *ret_trans, slave_tx = {
|
||||||
.data = slave_send,
|
.data = slave_send,
|
||||||
.len = test_trans_len,
|
.len = test_trans_len,
|
||||||
.flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO,
|
.flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO,
|
||||||
|
}, slave_rx = {
|
||||||
|
.data = slave_receive,
|
||||||
|
.len = test_trans_len,
|
||||||
|
.flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO,
|
||||||
};
|
};
|
||||||
unity_send_signal("Slave ready");
|
unity_send_signal("Slave ready");
|
||||||
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &slave_trans, portMAX_DELAY));
|
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &slave_tx, portMAX_DELAY));
|
||||||
slave_trans.data = slave_receive;
|
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_rx, portMAX_DELAY));
|
||||||
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_trans, portMAX_DELAY));
|
|
||||||
TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY));
|
TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY));
|
||||||
TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
|
TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
|
||||||
|
|
||||||
@@ -1677,16 +1680,19 @@ static void test_slave_hd_no_dma(void)
|
|||||||
test_fill_random_to_buffers_dualboard(911 + mode + speed_level + i, slave_expect, slave_send, SOC_SPI_MAXIMUM_BUFFER_SIZE);
|
test_fill_random_to_buffers_dualboard(911 + mode + speed_level + i, slave_expect, slave_send, SOC_SPI_MAXIMUM_BUFFER_SIZE);
|
||||||
uint32_t test_trans_len = SOC_SPI_MAXIMUM_BUFFER_SIZE;
|
uint32_t test_trans_len = SOC_SPI_MAXIMUM_BUFFER_SIZE;
|
||||||
|
|
||||||
spi_slave_hd_data_t *ret_trans, slave_trans = {
|
spi_slave_hd_data_t *ret_trans, slave_tx = {
|
||||||
.data = slave_send,
|
.data = slave_send,
|
||||||
.len = test_trans_len,
|
.len = test_trans_len,
|
||||||
.flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO,
|
.flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO,
|
||||||
|
}, slave_rx = {
|
||||||
|
.data = slave_receive,
|
||||||
|
.len = test_trans_len,
|
||||||
|
.flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO,
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &slave_trans, portMAX_DELAY));
|
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &slave_tx, portMAX_DELAY));
|
||||||
unity_send_signal("Slave ready");
|
unity_send_signal("Slave ready");
|
||||||
TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY));
|
TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY));
|
||||||
slave_trans.data = slave_receive;
|
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_rx, portMAX_DELAY));
|
||||||
TEST_ESP_OK(spi_slave_hd_queue_trans(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &slave_trans, portMAX_DELAY));
|
|
||||||
unity_send_signal("Slave ready");
|
unity_send_signal("Slave ready");
|
||||||
TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
|
TEST_ESP_OK(spi_slave_hd_get_trans_res(TEST_SPI_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/param.h>
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
#include "test_utils.h"
|
#include "test_utils.h"
|
||||||
@@ -32,13 +33,9 @@ static WORD_ALIGNED_ATTR uint8_t slave_rxbuf[320];
|
|||||||
static const uint8_t master_send[] = { 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43 };
|
static const uint8_t master_send[] = { 0x93, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xaa, 0xcc, 0xff, 0xee, 0x55, 0x77, 0x88, 0x43 };
|
||||||
static const uint8_t slave_send[] = { 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0 };
|
static const uint8_t slave_send[] = { 0xaa, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x13, 0x57, 0x9b, 0xdf, 0x24, 0x68, 0xac, 0xe0 };
|
||||||
|
|
||||||
#if (TEST_SPI_PERIPH_NUM >= 2)
|
static spi_host_device_t master_slave_ids[2];
|
||||||
//These will only be enabled on chips with 2 or more SPI peripherals
|
|
||||||
#ifndef CONFIG_SPIRAM
|
|
||||||
//This test should be removed once the timing test is merged.
|
|
||||||
|
|
||||||
static spi_device_handle_t spi;
|
static spi_device_handle_t spi;
|
||||||
static void custom_setup(void)
|
void custom_setup(spi_host_device_t master_id, spi_host_device_t slave_id)
|
||||||
{
|
{
|
||||||
//Initialize buffers
|
//Initialize buffers
|
||||||
memset(master_txbuf, 0, sizeof(master_txbuf));
|
memset(master_txbuf, 0, sizeof(master_txbuf));
|
||||||
@@ -46,46 +43,60 @@ static void custom_setup(void)
|
|||||||
memset(slave_txbuf, 0, sizeof(slave_txbuf));
|
memset(slave_txbuf, 0, sizeof(slave_txbuf));
|
||||||
memset(slave_rxbuf, 0, sizeof(slave_rxbuf));
|
memset(slave_rxbuf, 0, sizeof(slave_rxbuf));
|
||||||
|
|
||||||
//Initialize SPI Master
|
|
||||||
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||||
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
||||||
|
buscfg.max_transfer_sz = 40960;
|
||||||
spi_device_interface_config_t devcfg = {
|
spi_device_interface_config_t devcfg = {
|
||||||
.clock_speed_hz = 4 * 1000 * 1000, //currently only up to 4MHz for internal connect
|
.clock_speed_hz = 4 * 1000 * 1000, //currently only up to 4MHz for internal connect
|
||||||
.mode = 0, //SPI mode 0
|
|
||||||
.spics_io_num = PIN_NUM_CS, //CS pin
|
.spics_io_num = PIN_NUM_CS, //CS pin
|
||||||
.queue_size = 7, //We want to be able to queue 7 transactions at a time
|
.queue_size = 7, //We want to be able to queue 7 transactions at a time
|
||||||
.pre_cb = NULL,
|
|
||||||
.cs_ena_posttrans = 5,
|
.cs_ena_posttrans = 5,
|
||||||
.cs_ena_pretrans = 1,
|
.cs_ena_pretrans = 1,
|
||||||
};
|
};
|
||||||
//Initialize the SPI bus
|
|
||||||
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
|
||||||
//Attach the device to the SPI bus
|
|
||||||
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &spi));
|
|
||||||
|
|
||||||
//Configuration for the SPI slave interface
|
//Initialize SPI Master
|
||||||
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
if (master_id) {
|
||||||
//Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected.
|
//Initialize the SPI bus
|
||||||
gpio_set_pull_mode(PIN_NUM_MOSI, GPIO_PULLUP_ONLY);
|
TEST_ESP_OK(spi_bus_initialize(master_id, &buscfg, SPI_DMA_CH_AUTO));
|
||||||
gpio_set_pull_mode(PIN_NUM_CLK, GPIO_PULLUP_ONLY);
|
//Attach the device to the SPI bus
|
||||||
gpio_set_pull_mode(PIN_NUM_CS, GPIO_PULLUP_ONLY);
|
TEST_ESP_OK(spi_bus_add_device(master_id, &devcfg, &spi));
|
||||||
//Initialize SPI slave interface
|
}
|
||||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO));
|
|
||||||
|
if (slave_id) {
|
||||||
|
//Configuration for the SPI slave interface
|
||||||
|
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||||
|
//Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected.
|
||||||
|
slave_pull_up(&buscfg, devcfg.spics_io_num);
|
||||||
|
//Initialize SPI slave interface
|
||||||
|
TEST_ESP_OK(spi_slave_initialize(slave_id, &buscfg, &slvcfg, SPI_DMA_CH_AUTO));
|
||||||
|
}
|
||||||
|
|
||||||
//Do internal connections
|
//Do internal connections
|
||||||
same_pin_func_sel(buscfg, devcfg.spics_io_num, 0, false);
|
same_pin_func_sel(master_id, slave_id, buscfg, devcfg.spics_io_num);
|
||||||
|
master_slave_ids[0] = master_id;
|
||||||
|
master_slave_ids[1] = slave_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void custom_teardown(void)
|
void custom_teardown(void)
|
||||||
{
|
{
|
||||||
TEST_ESP_OK(spi_slave_free(TEST_SLAVE_HOST));
|
if (master_slave_ids[1]) {
|
||||||
TEST_ESP_OK(spi_bus_remove_device(spi));
|
TEST_ESP_OK(spi_slave_free(master_slave_ids[1]));
|
||||||
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
|
master_slave_ids[1] = 0;
|
||||||
|
}
|
||||||
|
if (spi != NULL) {
|
||||||
|
TEST_ESP_OK(spi_bus_remove_device(spi));
|
||||||
|
spi = NULL;
|
||||||
|
}
|
||||||
|
if (master_slave_ids[0]) {
|
||||||
|
TEST_ESP_OK(spi_bus_free(master_slave_ids[0]));
|
||||||
|
master_slave_ids[0] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (TEST_SPI_PERIPH_NUM >= 2)
|
||||||
TEST_CASE("test fullduplex slave with only RX direction", "[spi]")
|
TEST_CASE("test fullduplex slave with only RX direction", "[spi]")
|
||||||
{
|
{
|
||||||
custom_setup();
|
custom_setup(TEST_SPI_HOST, TEST_SLAVE_HOST);
|
||||||
|
|
||||||
memcpy(master_txbuf, master_send, sizeof(master_send));
|
memcpy(master_txbuf, master_send, sizeof(master_send));
|
||||||
|
|
||||||
@@ -132,7 +143,7 @@ TEST_CASE("test fullduplex slave with only RX direction", "[spi]")
|
|||||||
|
|
||||||
TEST_CASE("test fullduplex slave with only TX direction", "[spi]")
|
TEST_CASE("test fullduplex slave with only TX direction", "[spi]")
|
||||||
{
|
{
|
||||||
custom_setup();
|
custom_setup(TEST_SPI_HOST, TEST_SLAVE_HOST);
|
||||||
|
|
||||||
memcpy(slave_txbuf, slave_send, sizeof(slave_send));
|
memcpy(slave_txbuf, slave_send, sizeof(slave_send));
|
||||||
|
|
||||||
@@ -191,7 +202,7 @@ TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi]
|
|||||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED));
|
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED));
|
||||||
|
|
||||||
//initialize master and slave on the same pins break some of the output configs, fix them
|
//initialize master and slave on the same pins break some of the output configs, fix them
|
||||||
same_pin_func_sel(buscfg, devcfg.spics_io_num, 0, false);
|
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, buscfg, devcfg.spics_io_num);
|
||||||
|
|
||||||
uint8_t master_tx[TEST_SLV_RX_BUF_LEN], slave_rx[TEST_SLV_RX_BUF_LEN];
|
uint8_t master_tx[TEST_SLV_RX_BUF_LEN], slave_rx[TEST_SLV_RX_BUF_LEN];
|
||||||
for (uint8_t i = 0; i < TEST_SLV_RX_BUF_LEN; i++) {
|
for (uint8_t i = 0; i < TEST_SLV_RX_BUF_LEN; i++) {
|
||||||
@@ -249,16 +260,80 @@ TEST_CASE("Test slave rx no_dma overwrite when length below/over config", "[spi]
|
|||||||
TEST_ESP_OK(spi_bus_remove_device(spidev0));
|
TEST_ESP_OK(spi_bus_remove_device(spidev0));
|
||||||
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
|
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
|
||||||
}
|
}
|
||||||
#endif // !CONFIG_SPIRAM
|
|
||||||
#endif // #if (TEST_SPI_PERIPH_NUM >= 2)
|
#endif // #if (TEST_SPI_PERIPH_NUM >= 2)
|
||||||
|
|
||||||
|
#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
|
||||||
|
#define PSRAM_TRANS_LEN 16000
|
||||||
|
TEST_CASE("test slave using external ram", "[spi]")
|
||||||
|
{
|
||||||
|
custom_setup((TEST_SPI_PERIPH_NUM >= 2) ? TEST_SLAVE_HOST : 0, TEST_SPI_HOST);
|
||||||
|
|
||||||
|
uint8_t *slave_ext_tx = heap_caps_aligned_calloc(32, 1, PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM);
|
||||||
|
uint8_t *slave_ext_rx = heap_caps_aligned_calloc(32, 1, PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM);
|
||||||
|
uint8_t *master_tx = heap_caps_malloc(PSRAM_TRANS_LEN, MALLOC_CAP_DMA);
|
||||||
|
uint8_t *master_rx = heap_caps_malloc(PSRAM_TRANS_LEN, MALLOC_CAP_DMA);
|
||||||
|
|
||||||
|
spi_slave_transaction_t slave_tans = {}, *out_trans;
|
||||||
|
slave_tans.length = 8 * PSRAM_TRANS_LEN;
|
||||||
|
slave_tans.tx_buffer = slave_ext_tx;
|
||||||
|
slave_tans.rx_buffer = slave_ext_rx;
|
||||||
|
slave_tans.flags = SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO;
|
||||||
|
|
||||||
|
spi_transaction_t master_tans = {};
|
||||||
|
master_tans.override_freq_hz = MIN(60000000, (CONFIG_SPIRAM_SPEED / 2) * 1000 * 1000);
|
||||||
|
master_tans.tx_buffer = master_tx;
|
||||||
|
master_tans.rx_buffer = master_rx;
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i ++) {
|
||||||
|
test_fill_random_to_buffers_dualboard(7 + i, master_tx, slave_ext_tx, PSRAM_TRANS_LEN);
|
||||||
|
slave_tans.length -= i * 8;
|
||||||
|
master_tans.length = slave_tans.length;
|
||||||
|
master_tans.rxlength = slave_tans.length;
|
||||||
|
ESP_LOGI(SLAVE_TAG, "Test freq: %ld, tx: %p, rx: %p, len: %d", master_tans.override_freq_hz, slave_tans.tx_buffer, slave_tans.rx_buffer, slave_tans.length / 8);
|
||||||
|
|
||||||
|
uint32_t before = esp_get_free_heap_size();
|
||||||
|
TEST_ESP_OK(spi_slave_queue_trans(TEST_SPI_HOST, &slave_tans, portMAX_DELAY));
|
||||||
|
uint32_t after = esp_get_free_heap_size();
|
||||||
|
#if (TEST_SPI_PERIPH_NUM >= 2)
|
||||||
|
spi_device_transmit(spi, &master_tans);
|
||||||
|
#else
|
||||||
|
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||||
|
spi_master_trans_impl_gpio(buscfg, PIN_NUM_CS, 0, (uint8_t *)master_tans.tx_buffer, master_tans.rx_buffer, master_tans.length / 8, false);
|
||||||
|
#endif
|
||||||
|
ESP_LOGI(SLAVE_TAG, "slave malloc: %ld", after - before);
|
||||||
|
TEST_ASSERT(i ? (before - after) > PSRAM_TRANS_LEN : (before - after) < PSRAM_TRANS_LEN);
|
||||||
|
TEST_ESP_OK(spi_slave_get_trans_result(TEST_SPI_HOST, &out_trans, portMAX_DELAY));
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(master_tans.length, slave_tans.trans_len);
|
||||||
|
TEST_ASSERT_EQUAL_HEX8_ARRAY(slave_tans.tx_buffer, master_tans.rx_buffer, master_tans.length / 8);
|
||||||
|
TEST_ASSERT_EQUAL_HEX8_ARRAY(master_tans.tx_buffer, slave_tans.rx_buffer, master_tans.length / 8);
|
||||||
|
ESP_LOGI(SLAVE_TAG, "ok\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (TEST_SPI_PERIPH_NUM >= 2) && !SOC_IS(ESP32P4) // P4 can't reach error condition since it has powerful 16bits ddr psram
|
||||||
|
master_tans.override_freq_hz = 61000000; // real freq will be 40 if just config 60M
|
||||||
|
ESP_LOGI(SLAVE_TAG, "Testing over freq: %ld", master_tans.override_freq_hz);
|
||||||
|
TEST_ESP_OK(spi_slave_queue_trans(TEST_SPI_HOST, &slave_tans, portMAX_DELAY));
|
||||||
|
spi_device_transmit(spi, (spi_transaction_t *)&master_tans);
|
||||||
|
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, spi_slave_get_trans_result(TEST_SPI_HOST, &out_trans, portMAX_DELAY));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
free(slave_ext_tx);
|
||||||
|
free(slave_ext_rx);
|
||||||
|
free(master_tx);
|
||||||
|
free(master_rx);
|
||||||
|
custom_teardown();
|
||||||
|
ESP_LOGI(SLAVE_TAG, "test passed.");
|
||||||
|
}
|
||||||
|
#endif // CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
|
||||||
|
|
||||||
TEST_CASE("test slave send unaligned", "[spi]")
|
TEST_CASE("test slave send unaligned", "[spi]")
|
||||||
{
|
{
|
||||||
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||||
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
||||||
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO));
|
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO));
|
||||||
same_pin_func_sel(buscfg, slvcfg.spics_io_num, 0, true);
|
same_pin_func_sel(0, TEST_SLAVE_HOST, buscfg, slvcfg.spics_io_num);
|
||||||
|
|
||||||
memcpy(master_txbuf, master_send, sizeof(master_send));
|
memcpy(master_txbuf, master_send, sizeof(master_send));
|
||||||
memcpy(slave_txbuf, slave_send, sizeof(slave_send));
|
memcpy(slave_txbuf, slave_send, sizeof(slave_send));
|
||||||
@@ -678,7 +753,7 @@ TEST_CASE("test_spi_slave_sleep_retention", "[spi]")
|
|||||||
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
buscfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
||||||
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
spi_slave_interface_config_t slvcfg = SPI_SLAVE_TEST_DEFAULT_CONFIG();
|
||||||
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED));
|
TEST_ESP_OK(spi_slave_initialize(TEST_SLAVE_HOST, &buscfg, &slvcfg, SPI_DMA_DISABLED));
|
||||||
same_pin_func_sel(buscfg, slvcfg.spics_io_num, 0, true);
|
same_pin_func_sel(0, TEST_SLAVE_HOST, buscfg, slvcfg.spics_io_num);
|
||||||
|
|
||||||
for (uint8_t cnt = 0; cnt < 3; cnt ++) {
|
for (uint8_t cnt = 0; cnt < 3; cnt ++) {
|
||||||
printf("Going into sleep with power %s ...\n", (buscfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? "down" : "hold");
|
printf("Going into sleep with power %s ...\n", (buscfg.flags & SPICOMMON_BUSFLAG_SLP_ALLOW_PD) ? "down" : "hold");
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_SPIRAM=y
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_SPIRAM=y
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_SPIRAM=y
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_SPIRAM=y
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_SPIRAM=y
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -171,7 +171,7 @@ static void test_hd_start(spi_device_handle_t *spi, int freq, const spitest_para
|
|||||||
//when test with single board via same set of mosi, miso, clk and cs pins.
|
//when test with single board via same set of mosi, miso, clk and cs pins.
|
||||||
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||||
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
||||||
same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, false);
|
same_pin_func_sel(TEST_SPI_HOST, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
|
||||||
|
|
||||||
wait_wrbuf_sig(ctx, 0);
|
wait_wrbuf_sig(ctx, 0);
|
||||||
wait_rdbuf_sig(ctx, 0);
|
wait_rdbuf_sig(ctx, 0);
|
||||||
@@ -529,7 +529,7 @@ TEST_CASE("test spi slave hd segment mode, master too long", "[spi][spi_slv_hd]"
|
|||||||
//Use GPIO matrix to connect signal of master and slave via same set of pins on one board.
|
//Use GPIO matrix to connect signal of master and slave via same set of pins on one board.
|
||||||
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||||
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
||||||
same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, true);
|
same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
|
||||||
|
|
||||||
const int send_buf_size = 1024;
|
const int send_buf_size = 1024;
|
||||||
WORD_ALIGNED_ATTR uint8_t* slave_send_buf = malloc(send_buf_size * 2);
|
WORD_ALIGNED_ATTR uint8_t* slave_send_buf = malloc(send_buf_size * 2);
|
||||||
@@ -955,7 +955,7 @@ TEST_CASE("test_spi_slave_hd_sleep_retention", "[spi]")
|
|||||||
bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
||||||
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
||||||
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
|
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
|
||||||
same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, true);
|
same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
|
||||||
vTaskDelay(1);
|
vTaskDelay(1);
|
||||||
|
|
||||||
for (uint8_t cnt = 0; cnt < 3; cnt ++) {
|
for (uint8_t cnt = 0; cnt < 3; cnt ++) {
|
||||||
@@ -1033,7 +1033,7 @@ TEST_CASE("test_spi_slave_hd_append_sleep_retention", "[spi]")
|
|||||||
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
||||||
slave_hd_cfg.flags |= SPI_SLAVE_HD_APPEND_MODE;
|
slave_hd_cfg.flags |= SPI_SLAVE_HD_APPEND_MODE;
|
||||||
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
|
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
|
||||||
same_pin_func_sel(bus_cfg, slave_hd_cfg.spics_io_num, 0, true);
|
same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
|
||||||
vTaskDelay(1);
|
vTaskDelay(1);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
for (uint8_t i = 0; i < 2; i++) {
|
||||||
@@ -1085,3 +1085,66 @@ TEST_CASE("test_spi_slave_hd_append_sleep_retention", "[spi]")
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif //SOC_LIGHT_SLEEP_SUPPORTED
|
#endif //SOC_LIGHT_SLEEP_SUPPORTED
|
||||||
|
|
||||||
|
#if CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
|
||||||
|
// function pointers for segment and append mode
|
||||||
|
static esp_err_t (*hd_trans[2])(spi_host_device_t host_id, spi_slave_chan_t chan, spi_slave_hd_data_t *trans, uint32_t timeout) = {
|
||||||
|
spi_slave_hd_queue_trans, spi_slave_hd_append_trans
|
||||||
|
};
|
||||||
|
static esp_err_t (*hd_get_trans_res[2])(spi_host_device_t host_id, spi_slave_chan_t chan, spi_slave_hd_data_t **out_trans, uint32_t timeout) = {
|
||||||
|
spi_slave_hd_get_trans_res, spi_slave_hd_get_append_trans_res
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TEST_PSRAM_TRANS_LEN 1000
|
||||||
|
TEST_CASE("test slave hd edma segment and append mode", "[spi]")
|
||||||
|
{
|
||||||
|
uint8_t *mst_tx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_DEFAULT);
|
||||||
|
uint8_t *mst_rx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_DEFAULT);
|
||||||
|
uint8_t *slv_tx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM);
|
||||||
|
uint8_t *slv_rx = heap_caps_malloc(TEST_PSRAM_TRANS_LEN, MALLOC_CAP_SPIRAM);
|
||||||
|
spi_slave_hd_data_t *ret_trans, tx_data = {
|
||||||
|
.data = slv_tx,
|
||||||
|
.len = TEST_PSRAM_TRANS_LEN,
|
||||||
|
}, rx_data = {
|
||||||
|
.data = slv_rx,
|
||||||
|
.len = TEST_PSRAM_TRANS_LEN,
|
||||||
|
.flags = SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
printf("\ntest slave hd edma %s mode\n", i ? "append" : "segment");
|
||||||
|
test_fill_random_to_buffers_dualboard(i + 1, mst_tx, slv_tx, TEST_PSRAM_TRANS_LEN);
|
||||||
|
|
||||||
|
spi_bus_config_t bus_cfg = SPI_BUS_TEST_DEFAULT_CONFIG();
|
||||||
|
bus_cfg.max_transfer_sz = 4092 * 4; // append mode require at least 2 for tx and 2 for rx dma descs
|
||||||
|
bus_cfg.flags |= SPICOMMON_BUSFLAG_GPIO_PINS;
|
||||||
|
spi_slave_hd_slot_config_t slave_hd_cfg = SPI_SLOT_TEST_DEFAULT_CONFIG();
|
||||||
|
slave_hd_cfg.flags |= i ? SPI_SLAVE_HD_APPEND_MODE : 0;
|
||||||
|
TEST_ESP_OK(spi_slave_hd_init(TEST_SLAVE_HOST, &bus_cfg, &slave_hd_cfg));
|
||||||
|
same_pin_func_sel(0, TEST_SLAVE_HOST, bus_cfg, slave_hd_cfg.spics_io_num);
|
||||||
|
vTaskDelay(1);
|
||||||
|
|
||||||
|
TEST_ESP_OK(hd_trans[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_TX, &tx_data, portMAX_DELAY));
|
||||||
|
TEST_ESP_OK(hd_trans[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &rx_data, portMAX_DELAY));
|
||||||
|
|
||||||
|
// tx append transaction
|
||||||
|
printf("tx %d bytes\n", TEST_PSRAM_TRANS_LEN);
|
||||||
|
essl_sspi_hd_dma_trans_seg(bus_cfg, slave_hd_cfg.spics_io_num, 0, false, mst_tx, TEST_PSRAM_TRANS_LEN, -1);
|
||||||
|
TEST_ESP_OK(hd_get_trans_res[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_RX, &ret_trans, portMAX_DELAY));
|
||||||
|
|
||||||
|
// rx append transaction
|
||||||
|
printf("rx %d bytes\n", TEST_PSRAM_TRANS_LEN);
|
||||||
|
essl_sspi_hd_dma_trans_seg(bus_cfg, slave_hd_cfg.spics_io_num, 0, true, mst_rx, TEST_PSRAM_TRANS_LEN, -1);
|
||||||
|
TEST_ESP_OK(hd_get_trans_res[i](TEST_SLAVE_HOST, SPI_SLAVE_CHAN_TX, &ret_trans, portMAX_DELAY));
|
||||||
|
|
||||||
|
spitest_cmp_or_dump(slv_rx, mst_tx, TEST_PSRAM_TRANS_LEN);
|
||||||
|
spitest_cmp_or_dump(mst_rx, slv_tx, TEST_PSRAM_TRANS_LEN);
|
||||||
|
spi_slave_hd_deinit(TEST_SLAVE_HOST);
|
||||||
|
printf("test done\n");
|
||||||
|
}
|
||||||
|
free(mst_tx);
|
||||||
|
free(mst_rx);
|
||||||
|
free(slv_tx);
|
||||||
|
free(slv_rx);
|
||||||
|
}
|
||||||
|
#endif //CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE
|
||||||
|
|||||||
@@ -172,6 +172,23 @@ void spi_slave_hal_user_start(const spi_slave_hal_context_t *hal);
|
|||||||
*/
|
*/
|
||||||
bool spi_slave_hal_usr_is_done(spi_slave_hal_context_t* hal);
|
bool spi_slave_hal_usr_is_done(spi_slave_hal_context_t* hal);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get SPI interrupt bits status by mask
|
||||||
|
*
|
||||||
|
* @param hal Context of the HAL layer.
|
||||||
|
* @param mask Mask of the interrupt bits to check.
|
||||||
|
* @return True if the masked interrupts are set, false otherwise.
|
||||||
|
*/
|
||||||
|
bool spi_slave_hal_get_intr_status(spi_slave_hal_context_t *hal, uint32_t mask);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear SPI interrupt bits by mask
|
||||||
|
*
|
||||||
|
* @param hal Context of the HAL layer.
|
||||||
|
* @param mask Mask of the interrupt bits to clear.
|
||||||
|
*/
|
||||||
|
void spi_slave_hal_clear_intr_status(spi_slave_hal_context_t *hal, uint32_t mask);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Post transaction operations, fetch data from the buffer and recorded the length.
|
* Post transaction operations, fetch data from the buffer and recorded the length.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -138,6 +138,15 @@ void spi_slave_hd_hal_init(spi_slave_hd_hal_context_t *hal, const spi_slave_hd_h
|
|||||||
*/
|
*/
|
||||||
bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t* hal, spi_event_t ev);
|
bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t* hal, spi_event_t ev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check and clear the interrupt by mask
|
||||||
|
*
|
||||||
|
* @param hal Context of the HAL layer
|
||||||
|
* @param mask Mask of the interrupt bits to check
|
||||||
|
* @return True if the masked interrupts are set, false otherwise
|
||||||
|
*/
|
||||||
|
bool spi_slave_hd_hal_check_clear_intr(spi_slave_hd_hal_context_t *hal, uint32_t mask);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check and clear the interrupt of one event.
|
* @brief Check and clear the interrupt of one event.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -99,3 +99,15 @@ bool spi_slave_hal_dma_need_reset(const spi_slave_hal_context_t *hal)
|
|||||||
#endif // SPI_LL_SLAVE_NEEDS_RESET_WORKAROUND
|
#endif // SPI_LL_SLAVE_NEEDS_RESET_WORKAROUND
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SOC_SPI_SUPPORT_SLAVE_HD_VER2
|
||||||
|
bool spi_slave_hal_get_intr_status(spi_slave_hal_context_t *hal, uint32_t mask)
|
||||||
|
{
|
||||||
|
return spi_ll_get_intr(hal->hw, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_slave_hal_clear_intr_status(spi_slave_hal_context_t *hal, uint32_t mask)
|
||||||
|
{
|
||||||
|
spi_ll_clear_intr(hal->hw, mask);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -154,16 +154,20 @@ static spi_ll_intr_t get_event_intr(spi_slave_hd_hal_context_t *hal, spi_event_t
|
|||||||
return intr;
|
return intr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t *hal, spi_event_t ev)
|
bool spi_slave_hd_hal_check_clear_intr(spi_slave_hd_hal_context_t *hal, uint32_t mask)
|
||||||
{
|
{
|
||||||
spi_ll_intr_t intr = get_event_intr(hal, ev);
|
if (spi_ll_get_intr(hal->dev, mask)) {
|
||||||
if (spi_ll_get_intr(hal->dev, intr)) {
|
spi_ll_clear_intr(hal->dev, mask);
|
||||||
spi_ll_clear_intr(hal->dev, intr);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool spi_slave_hd_hal_check_clear_event(spi_slave_hd_hal_context_t *hal, spi_event_t ev)
|
||||||
|
{
|
||||||
|
return spi_slave_hd_hal_check_clear_intr(hal, get_event_intr(hal, ev));
|
||||||
|
}
|
||||||
|
|
||||||
bool spi_slave_hd_hal_check_disable_event(spi_slave_hd_hal_context_t *hal, spi_event_t ev)
|
bool spi_slave_hd_hal_check_disable_event(spi_slave_hd_hal_context_t *hal, spi_event_t ev)
|
||||||
{
|
{
|
||||||
//The trans_done interrupt is used for the workaround when some interrupt is not writable
|
//The trans_done interrupt is used for the workaround when some interrupt is not writable
|
||||||
|
|||||||
@@ -79,6 +79,14 @@ As not every transaction requires both writing and reading data, you can choose
|
|||||||
|
|
||||||
A Host should not start a transaction before its Device is ready for receiving data. It is recommended to use another GPIO pin for a handshake signal to sync the Devices. For more details, see :ref:`transaction_interval`.
|
A Host should not start a transaction before its Device is ready for receiving data. It is recommended to use another GPIO pin for a handshake signal to sync the Devices. For more details, see :ref:`transaction_interval`.
|
||||||
|
|
||||||
|
.. only:: SOC_PSRAM_DMA_CAPABLE
|
||||||
|
|
||||||
|
Using PSRAM for DMA transfer
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
SPI Slave driver supports using PSRAM for DMA transfer. Directly passing a PSRAM address as :cpp:member:`spi_slave_transaction_t::tx_buffer` or :cpp:member:`spi_slave_transaction_t::rx_buffer` is supported. For the rx_buffer, it has alignment requirements, using :cpp:func:`heap_caps_malloc` to allocate memory can automatically handle the alignment requirements. For the buffers that you can not control, you can also use the :c:macro:`SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO` flag to enable driver to automatically align the buffer from PSRAM.
|
||||||
|
|
||||||
|
Note that this feature shares the MSPI bus bandwidth (bus frequency * bus width), so the transmission bandwidth of the host to this device should be less than the PSRAM bandwidth, otherwise **data may be lost**, and the ``spi_slave_transmit`` function will return the :c:macro:`ESP_ERR_INVALID_STATE` error.
|
||||||
|
|
||||||
Driver Usage
|
Driver Usage
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -63,6 +63,12 @@ Send/Receive Data by DMA Channels
|
|||||||
|
|
||||||
To send data to the master through the sending DMA channel, the application should properly wrap the data in an :cpp:type:`spi_slave_hd_data_t` descriptor structure before calling :cpp:func:`spi_slave_hd_queue_trans` with the data descriptor and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. The pointers to descriptors are stored in the queue, and the data is sent to the master in the same order they are enqueued using :cpp:func:`spi_slave_hd_queue_trans`, upon receiving the master's ``Rd_DMA`` command.
|
To send data to the master through the sending DMA channel, the application should properly wrap the data in an :cpp:type:`spi_slave_hd_data_t` descriptor structure before calling :cpp:func:`spi_slave_hd_queue_trans` with the data descriptor and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. The pointers to descriptors are stored in the queue, and the data is sent to the master in the same order they are enqueued using :cpp:func:`spi_slave_hd_queue_trans`, upon receiving the master's ``Rd_DMA`` command.
|
||||||
|
|
||||||
|
.. only:: SOC_PSRAM_DMA_CAPABLE
|
||||||
|
|
||||||
|
The driver supports using PSRAM for DMA transfer. Directly passing a PSRAM address as :cpp:member:`spi_slave_hd_data_t::data` is supported. For the DMA receive channel, its memory address and transfer length have alignment requirements, using :cpp:func:`heap_caps_malloc` to allocate memory can automatically handle the alignment requirements. For the buffers that you can not control, you can also use the :c:macro:`SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO` flag to enable driver to automatically align the buffer from PSRAM.
|
||||||
|
|
||||||
|
Note that this feature shares the MSPI bus bandwidth (bus frequency * bus width), so the transmission bandwidth of the host to this device should be less than the PSRAM bandwidth, otherwise **data may be lost**, and then getting the transmission result will return the :c:macro:`ESP_ERR_INVALID_STATE` error.
|
||||||
|
|
||||||
The application should check the result of data sending by calling :cpp:func:`spi_slave_hd_get_trans_res` with the channel set as :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. This function blocks until the transaction with the command ``Rd_DMA`` from the master successfully completes (or timeout). The ``out_trans`` argument of the function outputs the pointer of the data descriptor which is just finished, providing information about the sending.
|
The application should check the result of data sending by calling :cpp:func:`spi_slave_hd_get_trans_res` with the channel set as :cpp:enumerator:`SPI_SLAVE_CHAN_TX`. This function blocks until the transaction with the command ``Rd_DMA`` from the master successfully completes (or timeout). The ``out_trans`` argument of the function outputs the pointer of the data descriptor which is just finished, providing information about the sending.
|
||||||
|
|
||||||
Receiving data from the master through the receiving DMA channel is quite similar. The application calls :cpp:func:`spi_slave_hd_queue_trans` with proper data descriptor and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_RX`. And the application calls the :cpp:func:`spi_slave_hd_get_trans_res` later to get the descriptor to the receiving buffer before it handles the data in the receiving buffer.
|
Receiving data from the master through the receiving DMA channel is quite similar. The application calls :cpp:func:`spi_slave_hd_queue_trans` with proper data descriptor and the channel argument of :cpp:enumerator:`SPI_SLAVE_CHAN_RX`. And the application calls the :cpp:func:`spi_slave_hd_get_trans_res` later to get the descriptor to the receiving buffer before it handles the data in the receiving buffer.
|
||||||
|
|||||||
@@ -79,6 +79,14 @@ SPI 传输事务
|
|||||||
|
|
||||||
主机应在从机设备准备好接收数据之后再进行传输事务。建议使用另外一个 GPIO 管脚作为握手信号来同步设备。更多细节,请参阅 :ref:`transaction_interval`。
|
主机应在从机设备准备好接收数据之后再进行传输事务。建议使用另外一个 GPIO 管脚作为握手信号来同步设备。更多细节,请参阅 :ref:`transaction_interval`。
|
||||||
|
|
||||||
|
.. only:: SOC_PSRAM_DMA_CAPABLE
|
||||||
|
|
||||||
|
使用 PSRAM 的传输
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
SPI Slave 驱动程序支持使用 PSRAM 进行传输。直接传入 PSRAM 地址作为 :cpp:member:`spi_slave_transaction_t::tx_buffer` 或 :cpp:member:`spi_slave_transaction_t::rx_buffer` 即可。对于 rx_buffer ,其地址和传输长度有对齐要求,使用 :cpp:func:`heap_caps_malloc` 分配内存可以自动处理对齐要求。对于不能控制的内存,也可以使用 :c:macro:`SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO` 标志位,驱动会自动从 PSRAM 重新分配满足要求的内存。
|
||||||
|
|
||||||
|
请注意该功能共享 MSPI 总线带宽(总线频率 * 总线位宽),因此主机对该设备的传输带宽应小于 PSRAM 带宽,否则 **可能会丢失传输数据**,此时 ``spi_slave_transmit`` 函数将会返回 :c:macro:`ESP_ERR_INVALID_STATE` 错误。
|
||||||
|
|
||||||
使用驱动程序
|
使用驱动程序
|
||||||
------------
|
------------
|
||||||
|
|||||||
@@ -63,6 +63,12 @@ SPI 从机半双工模式
|
|||||||
|
|
||||||
要通过 DMA 通道向主设备发送数据,应用程序需要先将数据正确地封装在 :cpp:type:`spi_slave_hd_data_t` 描述符结构体中,然后再将数据描述符和通道参数 :cpp:enumerator:`SPI_SLAVE_CHAN_TX` 传递给 :cpp:func:`spi_slave_hd_queue_trans`。数据描述符的指针存储在队列中,一旦接收到主设备的 Rd_DMA 命令,就会按照调用 :cpp:func:`spi_slave_hd_queue_trans` 时数据进入队列的顺序,依次将数据发送给主设备。
|
要通过 DMA 通道向主设备发送数据,应用程序需要先将数据正确地封装在 :cpp:type:`spi_slave_hd_data_t` 描述符结构体中,然后再将数据描述符和通道参数 :cpp:enumerator:`SPI_SLAVE_CHAN_TX` 传递给 :cpp:func:`spi_slave_hd_queue_trans`。数据描述符的指针存储在队列中,一旦接收到主设备的 Rd_DMA 命令,就会按照调用 :cpp:func:`spi_slave_hd_queue_trans` 时数据进入队列的顺序,依次将数据发送给主设备。
|
||||||
|
|
||||||
|
.. only:: SOC_PSRAM_DMA_CAPABLE
|
||||||
|
|
||||||
|
驱动程序支持使用 PSRAM 进行传输。直接传入 PSRAM 地址作为 :cpp:member:`spi_slave_hd_data_t::data` 即可。对于 DMA 接收通道,其内存地址和传输长度有对齐要求,使用 :cpp:func:`heap_caps_malloc` 分配内存可以自动处理对齐要求。对于不能控制的内存,也可以使用 :c:macro:`SPI_SLAVE_HD_TRANS_DMA_BUFFER_ALIGN_AUTO` 标志位,驱动会自动从 PSRAM 重新分配满足要求的内存。
|
||||||
|
|
||||||
|
请注意该功能共享 MSPI 总线带宽(总线频率 * 总线位宽),因此主机对该设备的传输带宽应小于 PSRAM 带宽,否则 **可能会丢失传输数据**,此时获取传输结果会返回 :c:macro:`ESP_ERR_INVALID_STATE` 错误。
|
||||||
|
|
||||||
应用程序需要检查数据发送的结果。为此,应用程序可以调用 :cpp:func:`spi_slave_hd_get_trans_res`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_TX`。该函数将阻塞程序,直到主设备发起的 Rd_DMA 命令事务成功完成或超时。函数中的参数 ``out_trans`` 将输出刚刚完成的数据描述符的指针,从而提供有关已完成的发送操作的信息。
|
应用程序需要检查数据发送的结果。为此,应用程序可以调用 :cpp:func:`spi_slave_hd_get_trans_res`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_TX`。该函数将阻塞程序,直到主设备发起的 Rd_DMA 命令事务成功完成或超时。函数中的参数 ``out_trans`` 将输出刚刚完成的数据描述符的指针,从而提供有关已完成的发送操作的信息。
|
||||||
|
|
||||||
通过 DMA 通道从主设备接收数据的操作与发送数据类似。应用程序需要使用正确的数据描述符调用 :cpp:func:`spi_slave_hd_queue_trans`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_RX`。随后,应用程序调用 :cpp:func:`spi_slave_hd_get_trans_res` 获取接收 buffer 的描述符,然后处理接收 buffer 中的数据。
|
通过 DMA 通道从主设备接收数据的操作与发送数据类似。应用程序需要使用正确的数据描述符调用 :cpp:func:`spi_slave_hd_queue_trans`,并将通道参数设置为 :cpp:enumerator:`SPI_SLAVE_CHAN_RX`。随后,应用程序调用 :cpp:func:`spi_slave_hd_get_trans_res` 获取接收 buffer 的描述符,然后处理接收 buffer 中的数据。
|
||||||
|
|||||||
Reference in New Issue
Block a user