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