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
@ -144,7 +144,7 @@ typedef struct {
|
|||||||
int id;
|
int id;
|
||||||
spi_device_t* device[DEV_NUM_MAX];
|
spi_device_t* device[DEV_NUM_MAX];
|
||||||
intr_handle_t intr;
|
intr_handle_t intr;
|
||||||
spi_hal_context_t hal;
|
spi_hal_context_t hal;
|
||||||
spi_trans_priv_t cur_trans_buf;
|
spi_trans_priv_t cur_trans_buf;
|
||||||
int cur_cs; //current device doing transaction
|
int cur_cs; //current device doing transaction
|
||||||
const spi_bus_attr_t* bus_attr;
|
const spi_bus_attr_t* bus_attr;
|
||||||
@ -164,9 +164,8 @@ struct spi_device_t {
|
|||||||
QueueHandle_t trans_queue;
|
QueueHandle_t trans_queue;
|
||||||
QueueHandle_t ret_queue;
|
QueueHandle_t ret_queue;
|
||||||
spi_device_interface_config_t cfg;
|
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_host_t *host;
|
||||||
|
|
||||||
spi_bus_lock_dev_handle_t dev_lock;
|
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.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) {
|
if (host_id != SPI1_HOST) {
|
||||||
//SPI1 attributes are already initialized at start up.
|
//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)
|
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);
|
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)
|
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;
|
spi_device_t *dev = NULL;
|
||||||
esp_err_t err = ESP_OK;
|
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);
|
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);
|
SPI_CHECK(freecs != -1, "no free cs pins for the host", ESP_ERR_NOT_FOUND);
|
||||||
|
|
||||||
duty_cycle = (dev_config->duty_cycle_pos==0) ? 128 : dev_config->duty_cycle_pos;
|
//input parameters to calculate timing configuration
|
||||||
|
int half_duplex = dev_config->flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0;
|
||||||
|
int no_compensate = dev_config->flags & SPI_DEVICE_NO_DUMMY ? 1 : 0;
|
||||||
|
int duty_cycle = (dev_config->duty_cycle_pos==0) ? 128 : dev_config->duty_cycle_pos;
|
||||||
|
int use_gpio = !(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS);
|
||||||
|
spi_hal_timing_param_t timing_param = {
|
||||||
|
.half_duplex = half_duplex,
|
||||||
|
.no_compensate = no_compensate,
|
||||||
|
.clock_speed_hz = dev_config->clock_speed_hz,
|
||||||
|
.duty_cycle = duty_cycle,
|
||||||
|
.input_delay_ns = dev_config->input_delay_ns,
|
||||||
|
.use_gpio = use_gpio
|
||||||
|
};
|
||||||
|
|
||||||
|
//output values of timing configuration
|
||||||
|
spi_hal_timing_conf_t temp_timing_conf;
|
||||||
int freq;
|
int freq;
|
||||||
spi_hal_context_t *hal = &(host->hal);
|
esp_err_t ret = spi_hal_cal_clock_conf(&timing_param, &freq, &temp_timing_conf);
|
||||||
hal->half_duplex = dev_config->flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0;
|
|
||||||
#ifdef SOC_SPI_SUPPORT_AS_CS
|
|
||||||
hal->as_cs = dev_config->flags & SPI_DEVICE_CLK_AS_CS ? 1 : 0;
|
|
||||||
#endif
|
|
||||||
hal->positive_cs = dev_config->flags & SPI_DEVICE_POSITIVE_CS ? 1 : 0;
|
|
||||||
hal->no_compensate = dev_config->flags & SPI_DEVICE_NO_DUMMY ? 1 : 0;
|
|
||||||
|
|
||||||
spi_hal_timing_conf_t temp_timing_conf;
|
|
||||||
|
|
||||||
esp_err_t ret = spi_hal_cal_clock_conf(hal, dev_config->clock_speed_hz, duty_cycle,
|
|
||||||
!(bus_attr->flags & SPICOMMON_BUSFLAG_IOMUX_PINS),
|
|
||||||
dev_config->input_delay_ns, &freq,
|
|
||||||
&temp_timing_conf);
|
|
||||||
|
|
||||||
SPI_CHECK(ret==ESP_OK, "assigned clock speed not supported", ret);
|
SPI_CHECK(ret==ESP_OK, "assigned clock speed not supported", ret);
|
||||||
|
|
||||||
//Allocate memory for device
|
//Allocate memory for device
|
||||||
dev=malloc(sizeof(spi_device_t));
|
dev = malloc(sizeof(spi_device_t));
|
||||||
if (dev==NULL) goto nomem;
|
if (dev == NULL) goto nomem;
|
||||||
memset(dev, 0, sizeof(spi_device_t));
|
memset(dev, 0, sizeof(spi_device_t));
|
||||||
host->device[freecs] = dev;
|
|
||||||
dev->id = freecs;
|
dev->id = freecs;
|
||||||
dev->timing_conf = temp_timing_conf;
|
|
||||||
dev->dev_lock = dev_handle;
|
dev->dev_lock = dev_handle;
|
||||||
|
|
||||||
//Allocate queues, set defaults
|
//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;
|
goto nomem;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->host= host;
|
|
||||||
|
|
||||||
//We want to save a copy of the dev config in the dev struct.
|
//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));
|
memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t));
|
||||||
dev->cfg.duty_cycle_pos = duty_cycle;
|
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
|
//Set CS pin, CS options
|
||||||
if (dev_config->spics_io_num >= 0) {
|
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);
|
ESP_LOGD(SPI_TAG, "SPI%d: New device added to CS%d, effective clock: %dkHz", host_id+1, freecs, freq/1000);
|
||||||
|
|
||||||
return ESP_OK;
|
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.
|
//if the configuration is already applied, skip the following.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
spi_hal_context_t *hal = &dev->host->hal;
|
||||||
spi_host_t* host = dev->host;
|
spi_hal_dev_config_t *hal_dev = &(dev->hal_dev);
|
||||||
spi_hal_context_t *hal = &host->hal;
|
spi_hal_setup_device(hal, hal_dev);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static SPI_MASTER_ISR_ATTR spi_device_t *get_acquiring_dev(spi_host_t *host)
|
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.
|
// 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)
|
// 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_transaction_t *trans = NULL;
|
||||||
spi_host_t *host = dev->host;
|
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;
|
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.
|
//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;
|
//set the transaction specific configuration each time before a transaction setup
|
||||||
hal->rx_bitlen = trans->rxlength;
|
spi_hal_trans_config_t hal_trans = {};
|
||||||
hal->rcv_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_rcv;
|
hal_trans.tx_bitlen = trans->length;
|
||||||
hal->send_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_send;
|
hal_trans.rx_bitlen = trans->rxlength;
|
||||||
hal->half_duplex = dev->cfg.flags & SPI_DEVICE_HALFDUPLEX ? 1 : 0;
|
hal_trans.rcv_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_rcv;
|
||||||
hal->cmd = trans->cmd;
|
hal_trans.send_buffer = (uint8_t*)host->cur_trans_buf.buffer_to_send;
|
||||||
hal->addr = trans->addr;
|
hal_trans.cmd = trans->cmd;
|
||||||
|
hal_trans.addr = trans->addr;
|
||||||
//Set up QIO/DIO if needed
|
//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_DIOQIO_ADDR ? SPI_LL_IO_MODE_DIO : SPI_LL_IO_MODE_DUAL) :
|
||||||
(trans->flags & SPI_TRANS_MODE_QIO ?
|
(trans->flags & SPI_TRANS_MODE_QIO ?
|
||||||
(trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR ? SPI_LL_IO_MODE_QIO : SPI_LL_IO_MODE_QUAD) :
|
(trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR ? SPI_LL_IO_MODE_QIO : SPI_LL_IO_MODE_QUAD) :
|
||||||
SPI_LL_IO_MODE_NORMAL
|
SPI_LL_IO_MODE_NORMAL
|
||||||
));
|
));
|
||||||
|
|
||||||
hal->tx_bitlen = trans->length;
|
|
||||||
hal->rx_bitlen = trans->rxlength;
|
|
||||||
|
|
||||||
if (trans->flags & SPI_TRANS_VARIABLE_CMD) {
|
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 {
|
} else {
|
||||||
hal->cmd_bits = dev->cfg.command_bits;
|
hal_trans.cmd_bits = dev->cfg.command_bits;
|
||||||
}
|
}
|
||||||
if (trans->flags & SPI_TRANS_VARIABLE_ADDR) {
|
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 {
|
} else {
|
||||||
hal->addr_bits = dev->cfg.address_bits;
|
hal_trans.addr_bits = dev->cfg.address_bits;
|
||||||
}
|
}
|
||||||
if (trans->flags & SPI_TRANS_VARIABLE_DUMMY) {
|
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 {
|
} else {
|
||||||
hal->dummy_bits = dev->cfg.dummy_bits;
|
hal_trans.dummy_bits = dev->cfg.dummy_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
spi_hal_setup_trans(hal);
|
spi_hal_setup_trans(hal, hal_dev, &hal_trans);
|
||||||
spi_hal_prepare_data(hal);
|
spi_hal_prepare_data(hal, hal_dev, &hal_trans);
|
||||||
|
|
||||||
//Call pre-transmission callback, if any
|
//Call pre-transmission callback, if any
|
||||||
if (dev->cfg.pre_cb) dev->cfg.pre_cb(trans);
|
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)
|
static void SPI_MASTER_ISR_ATTR spi_post_trans(spi_host_t *host)
|
||||||
{
|
{
|
||||||
spi_transaction_t *cur_trans = host->cur_trans_buf.trans;
|
spi_transaction_t *cur_trans = host->cur_trans_buf.trans;
|
||||||
|
|
||||||
spi_hal_fetch_result(&host->hal);
|
spi_hal_fetch_result(&host->hal);
|
||||||
//Call post-transaction callback, if any
|
//Call post-transaction callback, if any
|
||||||
spi_device_t* dev = host->device[host->cur_cs];
|
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;
|
host->cur_cs = DEV_NUM_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// This is run in interrupt context.
|
// This is run in interrupt context.
|
||||||
static void SPI_MASTER_ISR_ATTR spi_intr(void *arg)
|
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
|
//mark channel as active, so that the DMA will not be reset by the slave
|
||||||
spicommon_dmaworkaround_transfer_active(bus_attr->dma_chan);
|
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),
|
// 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).
|
// 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);
|
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;
|
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 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);
|
bool is_half_duplex = ((handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) != 0);
|
||||||
|
|
||||||
//check transmission length
|
//check transmission length
|
||||||
@ -763,7 +775,6 @@ clean_up:
|
|||||||
return ESP_ERR_NO_MEM;
|
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 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);
|
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;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
esp_err_t SPI_MASTER_ISR_ATTR spi_device_acquire_bus(spi_device_t *device, TickType_t wait)
|
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;
|
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);
|
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 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;
|
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;
|
host->polling = true;
|
||||||
|
|
||||||
ESP_LOGV(SPI_TAG, "polling trans");
|
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;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_end(spi_device_handle_t handle, TickType_t ticks_to_wait)
|
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);
|
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;
|
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 SPI_MASTER_ISR_ATTR spi_device_polling_transmit(spi_device_handle_t handle, spi_transaction_t* trans_desc)
|
||||||
{
|
{
|
||||||
esp_err_t ret;
|
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 = {
|
spi_slave_hd_hal_config_t hal_config = {
|
||||||
.host_id = host_id,
|
.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),
|
.tx_lsbfirst = (config->flags & SPI_SLAVE_HD_RXBIT_LSBFIRST),
|
||||||
.rx_lsbfirst = (config->flags & SPI_SLAVE_HD_TXBIT_LSBFIRST),
|
.rx_lsbfirst = (config->flags & SPI_SLAVE_HD_TXBIT_LSBFIRST),
|
||||||
.dma_chan = config->dma_chan,
|
.dma_chan = config->dma_chan,
|
||||||
.mode = config->mode,
|
.mode = config->mode
|
||||||
};
|
};
|
||||||
|
spi_slave_hd_hal_init(&host->hal, &hal_config);
|
||||||
slave_hd_hal_init(&host->hal, &hal_config);
|
|
||||||
|
|
||||||
if (config->dma_chan != 0) {
|
if (config->dma_chan != 0) {
|
||||||
//See how many dma descriptors we need and allocate them
|
//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 hw Beginning address of the peripheral registers.
|
||||||
* @param val stored clock configuration calculated before (by ``spi_ll_cal_clock``).
|
* @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;
|
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 hw Beginning address of the peripheral registers.
|
||||||
* @param val stored clock configuration calculated before (by ``spi_ll_cal_clock``).
|
* @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;
|
hw->clock.val = *(uint32_t *)val;
|
||||||
}
|
}
|
||||||
|
@ -38,83 +38,115 @@
|
|||||||
#include <esp_err.h>
|
#include <esp_err.h>
|
||||||
#include "soc/lldesc.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
|
* 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.
|
* ``timing_conf`` member of the context of HAL before setup a device.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
spi_ll_clock_val_t clock_reg; ///< Register value used by the LL layer
|
spi_ll_clock_val_t clock_reg; ///< Register value used by the LL layer
|
||||||
int timing_dummy; ///< Extra dummy needed to compensate the timing
|
int timing_dummy; ///< Extra dummy needed to compensate the timing
|
||||||
int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing
|
int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing
|
||||||
} spi_hal_timing_conf_t;
|
} spi_hal_timing_conf_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DMA configuration structure
|
||||||
|
* Should be set by driver at initialization
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
spi_dma_dev_t *dma_in; ///< Input DMA(DMA -> RAM) peripheral register address
|
||||||
|
spi_dma_dev_t *dma_out; ///< Output DMA(RAM -> DMA) peripheral register address
|
||||||
|
lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA.
|
||||||
|
* The amount should be larger than dmadesc_n. The driver should ensure that
|
||||||
|
* the data to be sent is shorter than the descriptors can hold.
|
||||||
|
*/
|
||||||
|
lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA.
|
||||||
|
* The amount should be larger than dmadesc_n. The driver should ensure that
|
||||||
|
* the data to be sent is shorter than the descriptors can hold.
|
||||||
|
*/
|
||||||
|
int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use.
|
||||||
|
} spi_hal_dma_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transaction configuration structure, this should be assigned by driver each time.
|
||||||
|
* All these parameters will be updated to the peripheral every transaction.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint16_t cmd; ///< Command value to be sent
|
||||||
|
int cmd_bits; ///< Length (in bits) of the command phase
|
||||||
|
int addr_bits; ///< Length (in bits) of the address phase
|
||||||
|
int dummy_bits; ///< Base length (in bits) of the dummy phase. Note when the compensation is enabled, some extra dummy bits may be appended.
|
||||||
|
int tx_bitlen; ///< TX length, in bits
|
||||||
|
int rx_bitlen; ///< RX length, in bits
|
||||||
|
uint64_t addr; ///< Address value to be sent
|
||||||
|
uint8_t *send_buffer; ///< Data to be sent
|
||||||
|
uint8_t *rcv_buffer; ///< Buffer to hold the receive data.
|
||||||
|
spi_ll_io_mode_t io_mode; ///< IO mode of the master
|
||||||
|
} spi_hal_trans_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context that should be maintained by both the driver and the HAL.
|
* Context that should be maintained by both the driver and the HAL.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* configured by driver at initialization, don't touch */
|
/* Configured by driver at initialization, don't touch */
|
||||||
spi_dev_t *hw; ///< Beginning address of the peripheral registers.
|
spi_dev_t *hw; ///< Beginning address of the peripheral registers.
|
||||||
/* should be configured by driver at initialization */
|
spi_dma_dev_t *dma_in; ///< Address of the DMA peripheral registers which stores the data received from a peripheral into RAM (DMA -> RAM).
|
||||||
lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA.
|
spi_dma_dev_t *dma_out; ///< Address of the DMA peripheral registers which transmits the data from RAM to a peripheral (RAM -> DMA).
|
||||||
* The amount should be larger than dmadesc_n. The driver should ensure that
|
bool dma_enabled; ///< Whether the DMA is enabled, do not update after initialization
|
||||||
* the data to be sent is shorter than the descriptors can hold.
|
spi_hal_dma_config_t dma_config; ///< DMA configuration
|
||||||
*/
|
|
||||||
lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA.
|
/* Internal parameters, don't touch */
|
||||||
* The amount should be larger than dmadesc_n. The driver should ensure that
|
spi_hal_trans_config_t trans_config; ///< Transaction configuration
|
||||||
* the data to be sent is shorter than the descriptors can hold.
|
} spi_hal_context_t;
|
||||||
*/
|
|
||||||
int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use.
|
/**
|
||||||
/*
|
* Device configuration structure, this should be initialised by driver based on different devices respectively.
|
||||||
* Device specific, all these parameters will be updated to the peripheral
|
* All these parameters will be updated to the peripheral only when ``spi_hal_setup_device``.
|
||||||
* only when ``spi_hal_setup_device``. They may not get updated when
|
* They may not get updated when ``spi_hal_setup_trans``.
|
||||||
* ``spi_hal_setup_trans``.
|
*/
|
||||||
*/
|
typedef struct {
|
||||||
int mode; ///< SPI mode, device specific
|
int mode; ///< SPI mode, device specific
|
||||||
int cs_setup; ///< Setup time of CS active edge before the first SPI clock, 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_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
|
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
|
spi_hal_timing_conf_t timing_conf; /**< This structure holds the pre-calculated timing configuration for the device
|
||||||
* the pre-calculated timing configuration for the device at initialization,
|
* at initialization, device specific
|
||||||
* device specific
|
|
||||||
*/
|
*/
|
||||||
struct {
|
struct {
|
||||||
uint32_t sio : 1; ///< Whether to use SIO mode, device specific
|
uint32_t sio : 1; ///< Whether to use SIO mode, device specific
|
||||||
uint32_t half_duplex : 1; ///< Whether half duplex mode is used, device specific
|
uint32_t 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 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 rx_lsbfirst : 1; ///< Whether LSB is received first for RX data, device specific
|
||||||
uint32_t dma_enabled : 1; ///< Whether the DMA is enabled, do not update after initialization
|
uint32_t no_compensate : 1; ///< No need to add dummy to compensate the timing, device specific
|
||||||
uint32_t no_compensate : 1; ///< No need to add dummy to compensate the timing, device specific
|
|
||||||
#ifdef SOC_SPI_SUPPORT_AS_CS
|
#ifdef SOC_SPI_SUPPORT_AS_CS
|
||||||
uint32_t as_cs : 1; ///< Whether to toggle the CS while the clock toggles, device specific
|
uint32_t as_cs : 1; ///< Whether to toggle the CS while the clock toggles, device specific
|
||||||
#endif
|
#endif
|
||||||
uint32_t positive_cs : 1; ///< Whether the postive CS feature is abled, device specific
|
uint32_t positive_cs : 1; ///< Whether the postive CS feature is abled, device specific
|
||||||
};//boolean configurations
|
};//boolean configurations
|
||||||
|
} spi_hal_dev_config_t;
|
||||||
/*
|
|
||||||
* Transaction specific (data), all these parameters will be updated to the
|
|
||||||
* peripheral every transaction.
|
|
||||||
*/
|
|
||||||
uint16_t cmd; ///< Command value to be sent
|
|
||||||
int cmd_bits; ///< Length (in bits) of the command phase
|
|
||||||
int addr_bits; ///< Length (in bits) of the address phase
|
|
||||||
int dummy_bits; ///< Base length (in bits) of the dummy phase. Note when the compensation is enabled, some extra dummy bits may be appended.
|
|
||||||
int tx_bitlen; ///< TX length, in bits
|
|
||||||
int rx_bitlen; ///< RX length, in bits
|
|
||||||
uint64_t addr; ///< Address value to be sent
|
|
||||||
uint8_t *send_buffer; ///< Data to be sent
|
|
||||||
uint8_t *rcv_buffer; ///< Buffer to hold the receive data.
|
|
||||||
spi_ll_io_mode_t io_mode; ///< IO mode of the master
|
|
||||||
|
|
||||||
} spi_hal_context_t;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init the peripheral and the context.
|
* Init the peripheral and the context.
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer.
|
* @param hal Context of the HAL layer.
|
||||||
* @param host_id Index of the SPI peripheral. 0 for SPI1, 1 for HSPI (SPI2) and 2 for VSPI (SPI3).
|
* @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).
|
* Deinit the peripheral (and the context if needed).
|
||||||
@ -126,23 +158,28 @@ void spi_hal_deinit(spi_hal_context_t *hal);
|
|||||||
/**
|
/**
|
||||||
* Setup device-related configurations according to the settings in the context.
|
* Setup device-related configurations according to the settings in the context.
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer.
|
* @param hal Context of the HAL layer.
|
||||||
|
* @param hal_dev Device configuration
|
||||||
*/
|
*/
|
||||||
void spi_hal_setup_device(const spi_hal_context_t *hal);
|
void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup transaction related configurations according to the settings in the context.
|
* Setup transaction related configurations according to the settings in the context.
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer.
|
* @param hal Context of the HAL layer.
|
||||||
|
* @param hal_dev Device configuration
|
||||||
|
* @param hal_trans Transaction configuration
|
||||||
*/
|
*/
|
||||||
void spi_hal_setup_trans(const spi_hal_context_t *hal);
|
void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev, const spi_hal_trans_config_t *hal_trans);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare the data for the current transaction.
|
* Prepare the data for the current transaction.
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer.
|
* @param hal Context of the HAL layer.
|
||||||
|
* @param hal_dev Device configuration
|
||||||
|
* @param hal_trans Transaction configuration
|
||||||
*/
|
*/
|
||||||
void spi_hal_prepare_data(const spi_hal_context_t *hal);
|
void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *hal_dev, const spi_hal_trans_config_t *hal_trans);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger start a user-defined transaction.
|
* Trigger start a user-defined transaction.
|
||||||
@ -161,7 +198,7 @@ bool spi_hal_usr_is_done(const spi_hal_context_t *hal);
|
|||||||
/**
|
/**
|
||||||
* Post transaction operations, mainly fetch data from the buffer.
|
* Post transaction operations, mainly fetch data from the buffer.
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer.
|
* @param hal Context of the HAL layer.
|
||||||
*/
|
*/
|
||||||
void spi_hal_fetch_result(const spi_hal_context_t *hal);
|
void spi_hal_fetch_result(const spi_hal_context_t *hal);
|
||||||
|
|
||||||
@ -173,50 +210,44 @@ void spi_hal_fetch_result(const spi_hal_context_t *hal);
|
|||||||
*
|
*
|
||||||
* It is highly suggested to do this at initialization, since it takes long time.
|
* It is highly suggested to do this at initialization, since it takes long time.
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer.
|
* @param timing_param Input parameters to calculate timing configuration
|
||||||
* @param speed_hz Desired frequency.
|
* @param out_freq Output of the actual frequency, left NULL if not required.
|
||||||
* @param duty_cycle Desired duty cycle of SPI clock
|
* @param timing_conf Output of the timing configuration.
|
||||||
* @param use_gpio true if the GPIO matrix is used, otherwise false
|
|
||||||
* @param input_delay_ns Maximum delay between SPI launch clock and the data to
|
|
||||||
* be valid. This is used to compensate/calculate the maximum frequency
|
|
||||||
* allowed. Left 0 if not known.
|
|
||||||
* @param out_freq Output of the actual frequency, left NULL if not required.
|
|
||||||
* @param timing_conf Output of the timing configuration.
|
|
||||||
*
|
*
|
||||||
* @return ESP_OK if desired is available, otherwise fail.
|
* @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.
|
* Get the frequency actual used.
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer.
|
* @param hal Context of the HAL layer.
|
||||||
* @param fapb APB clock frequency.
|
* @param fapb APB clock frequency.
|
||||||
* @param hz Desired frequencyc.
|
* @param hz Desired frequencyc.
|
||||||
* @param duty_cycle Desired duty cycle.
|
* @param duty_cycle Desired duty cycle.
|
||||||
*/
|
*/
|
||||||
int spi_hal_master_cal_clock(int fapb, int hz, int duty_cycle);
|
int spi_hal_master_cal_clock(int fapb, int hz, int duty_cycle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the timing configuration for given parameters.
|
* Get the timing configuration for given parameters.
|
||||||
*
|
*
|
||||||
* @param eff_clk Actual SPI clock frequency
|
* @param eff_clk Actual SPI clock frequency
|
||||||
* @param gpio_is_used true if the GPIO matrix is used, otherwise false.
|
* @param gpio_is_used true if the GPIO matrix is used, otherwise false.
|
||||||
* @param input_delay_ns Maximum delay between SPI launch clock and the data to
|
* @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
|
* be valid. This is used to compensate/calculate the maximum frequency
|
||||||
* allowed. Left 0 if not known.
|
* allowed. Left 0 if not known.
|
||||||
* @param dummy_n Dummy cycles required to correctly read the data.
|
* @param dummy_n Dummy cycles required to correctly read the data.
|
||||||
* @param miso_delay_n suggested delay on the MISO line, in APB clocks.
|
* @param miso_delay_n suggested delay on the MISO line, in APB clocks.
|
||||||
*/
|
*/
|
||||||
void spi_hal_cal_timing(int eff_clk, bool gpio_is_used, int input_delay_ns, int *dummy_n, int *miso_delay_n);
|
void spi_hal_cal_timing(int eff_clk, bool gpio_is_used, int input_delay_ns, int *dummy_n, int *miso_delay_n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the maximum frequency allowed to read if no compensation is used.
|
* Get the maximum frequency allowed to read if no compensation is used.
|
||||||
*
|
*
|
||||||
* @param gpio_is_used true if the GPIO matrix is used, otherwise false.
|
* @param gpio_is_used true if the GPIO matrix is used, otherwise false.
|
||||||
* @param input_delay_ns Maximum delay between SPI launch clock and the data to
|
* @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
|
* be valid. This is used to compensate/calculate the maximum frequency
|
||||||
* allowed. Left 0 if not known.
|
* allowed. Left 0 if not known.
|
||||||
*/
|
*/
|
||||||
int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns);
|
int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns);
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
* The HAL layer for SPI Slave HD mode, currently only segment mode is supported
|
* The HAL layer for SPI Slave HD mode, currently only segment mode is supported
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* - Firstly, initialize the slave with `slave_hd_hal_init`
|
* - Firstly, initialize the slave with `spi_slave_hd_hal_init`
|
||||||
*
|
*
|
||||||
* - Event handling:
|
* - Event handling:
|
||||||
* - (Optional) Call ``spi_slave_hd_hal_enable_event_intr`` to enable the used interrupts
|
* - (Optional) Call ``spi_slave_hd_hal_enable_event_intr`` to enable the used interrupts
|
||||||
@ -59,51 +59,54 @@
|
|||||||
|
|
||||||
/// Configuration of the HAL
|
/// Configuration of the HAL
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int host_id; ///< Host ID of the spi peripheral
|
uint32_t host_id; ///< Host ID of the spi peripheral
|
||||||
int spics_io_num; ///< CS GPIO pin for this device
|
dma_dev_t *dma_in; ///< Input DMA(DMA -> RAM) peripheral register address
|
||||||
uint8_t mode; ///< SPI mode (0-3)
|
dma_dev_t *dma_out; ///< Output DMA(RAM -> DMA) peripheral register address
|
||||||
int command_bits; ///< command field bits, multiples of 8 and at least 8.
|
uint32_t spics_io_num; ///< CS GPIO pin for this device
|
||||||
int address_bits; ///< address field bits, multiples of 8 and at least 8.
|
uint8_t mode; ///< SPI mode (0-3)
|
||||||
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 {
|
struct {
|
||||||
uint32_t tx_lsbfirst : 1;///< Whether TX data should be sent 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.
|
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;
|
} 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 {
|
typedef struct {
|
||||||
spi_dev_t* dev; ///< Beginning address of the peripheral registers.
|
spi_dev_t *dev; ///< Beginning address of the peripheral registers.
|
||||||
lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA.
|
dma_dev_t *dma_in; ///< Address of the DMA peripheral registers which stores the data received from a peripheral into RAM.
|
||||||
* The amount should be larger than dmadesc_n. The driver should ensure that
|
dma_dev_t *dma_out; ///< Address of the DMA peripheral registers which transmits the data from RAM to a peripheral.
|
||||||
* the data to be sent is shorter than the descriptors can hold.
|
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
|
||||||
lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA.
|
* the data to be sent is shorter than the descriptors can hold.
|
||||||
* The amount should be larger than dmadesc_n. The driver should ensure that
|
*/
|
||||||
* the data to be sent is shorter than the descriptors can hold.
|
lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA.
|
||||||
*/
|
* The amount should be larger than dmadesc_n. The driver should ensure that
|
||||||
|
* the data to be sent is shorter than the descriptors can hold.
|
||||||
|
*/
|
||||||
|
|
||||||
/* Internal status used by the HAL implementation, initialized as 0. */
|
/* Internal status used by the HAL implementation, initialized as 0. */
|
||||||
uint32_t intr_not_triggered;
|
uint32_t intr_not_triggered;
|
||||||
} spi_slave_hd_hal_context_t;
|
} spi_slave_hd_hal_context_t;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize the hardware and part of the context
|
* @brief Initialize the hardware and part of the context
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer
|
* @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
|
* @brief Check and clear signal of one event
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer
|
* @param hal Context of the HAL layer
|
||||||
* @param ev Event to check
|
* @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);
|
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 hal Context of the HAL layer
|
||||||
* @param ev Event to check and disable
|
* @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);
|
bool spi_slave_hd_hal_check_disable_event(spi_slave_hd_hal_context_t* hal, spi_event_t ev);
|
||||||
|
|
||||||
@ -156,7 +159,7 @@ void spi_slave_hd_hal_rxdma(spi_slave_hd_hal_context_t *hal, uint8_t *out_buf, s
|
|||||||
* @brief Get the length of total received data
|
* @brief Get the length of total received data
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer
|
* @param hal Context of the HAL layer
|
||||||
* @return The received length
|
* @return The received length
|
||||||
*/
|
*/
|
||||||
int spi_slave_hd_hal_rxdma_get_len(spi_slave_hd_hal_context_t *hal);
|
int spi_slave_hd_hal_rxdma_get_len(spi_slave_hd_hal_context_t *hal);
|
||||||
|
|
||||||
@ -167,8 +170,8 @@ int spi_slave_hd_hal_rxdma_get_len(spi_slave_hd_hal_context_t *hal);
|
|||||||
* @brief Start the TX DMA operation with the specified buffer
|
* @brief Start the TX DMA operation with the specified buffer
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer
|
* @param hal Context of the HAL layer
|
||||||
* @param data Buffer of data to send
|
* @param data Buffer of data to send
|
||||||
* @param len Size of the buffer, also the maximum length to send
|
* @param len Size of the buffer, also the maximum length to send
|
||||||
*/
|
*/
|
||||||
void spi_slave_hd_hal_txdma(spi_slave_hd_hal_context_t *hal, uint8_t *data, size_t len);
|
void spi_slave_hd_hal_txdma(spi_slave_hd_hal_context_t *hal, uint8_t *data, size_t len);
|
||||||
|
|
||||||
@ -179,9 +182,9 @@ void spi_slave_hd_hal_txdma(spi_slave_hd_hal_context_t *hal, uint8_t *data, size
|
|||||||
* @brief Read from the shared register buffer
|
* @brief Read from the shared register buffer
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer
|
* @param hal Context of the HAL layer
|
||||||
* @param addr Address of the shared regsiter to read
|
* @param addr Address of the shared regsiter to read
|
||||||
* @param out_data Buffer to store the read data
|
* @param out_data Buffer to store the read data
|
||||||
* @param len Length to read from the shared buffer
|
* @param len Length to read from the shared buffer
|
||||||
*/
|
*/
|
||||||
void spi_slave_hd_hal_read_buffer(spi_slave_hd_hal_context_t *hal, int addr, uint8_t *out_data, size_t len);
|
void spi_slave_hd_hal_read_buffer(spi_slave_hd_hal_context_t *hal, int addr, uint8_t *out_data, size_t len);
|
||||||
|
|
||||||
@ -199,7 +202,7 @@ void spi_slave_hd_hal_write_buffer(spi_slave_hd_hal_context_t *hal, int addr, ui
|
|||||||
* @brief Get the length of previous transaction.
|
* @brief Get the length of previous transaction.
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer
|
* @param hal Context of the HAL layer
|
||||||
* @return The length of previous transaction
|
* @return The length of previous transaction
|
||||||
*/
|
*/
|
||||||
int spi_slave_hd_hal_get_rxlen(spi_slave_hd_hal_context_t *hal);
|
int spi_slave_hd_hal_get_rxlen(spi_slave_hd_hal_context_t *hal);
|
||||||
|
|
||||||
@ -207,6 +210,6 @@ int spi_slave_hd_hal_get_rxlen(spi_slave_hd_hal_context_t *hal);
|
|||||||
* @brief Get the address of last transaction
|
* @brief Get the address of last transaction
|
||||||
*
|
*
|
||||||
* @param hal Context of the HAL layer
|
* @param hal Context of the HAL layer
|
||||||
* @return The address of last transaction
|
* @return The address of last transaction
|
||||||
*/
|
*/
|
||||||
int spi_slave_hd_hal_get_last_addr(spi_slave_hd_hal_context_t *hal);
|
int spi_slave_hd_hal_get_last_addr(spi_slave_hd_hal_context_t *hal);
|
||||||
|
@ -24,11 +24,12 @@ static const char SPI_HAL_TAG[] = "spi_hal";
|
|||||||
return (ret_val); \
|
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));
|
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;
|
hal->hw = hw;
|
||||||
|
|
||||||
spi_ll_master_init(hw);
|
spi_ll_master_init(hw);
|
||||||
|
|
||||||
//Force a transaction done interrupt. This interrupt won't fire yet because
|
//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_enable_int(hw);
|
||||||
spi_ll_set_int_stat(hw);
|
spi_ll_set_int_stat(hw);
|
||||||
spi_ll_set_mosi_delay(hw, 0, 0);
|
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)
|
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;
|
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.
|
//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.
|
//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
|
#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\
|
"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\
|
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\
|
Please note the SPI master can only work at divisors of 80MHz, and the driver always tries to find the closest frequency to your configuration.\n\
|
||||||
@ -127,4 +131,4 @@ int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return APB_CLK_FREQ / (delay_apb_n + 1);
|
return APB_CLK_FREQ / (delay_apb_n + 1);
|
||||||
}
|
}
|
||||||
|
@ -17,29 +17,29 @@
|
|||||||
|
|
||||||
#include "hal/spi_hal.h"
|
#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
|
//Configure clock settings
|
||||||
spi_dev_t *hw = hal->hw;
|
spi_dev_t *hw = hal->hw;
|
||||||
#ifdef SOC_SPI_SUPPORT_AS_CS
|
#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
|
#endif
|
||||||
spi_ll_master_set_pos_cs(hw, hal->cs_pin_id, hal->positive_cs);
|
spi_ll_master_set_pos_cs(hw, dev->cs_pin_id, dev->positive_cs);
|
||||||
spi_ll_master_set_clock_by_reg(hw, &hal->timing_conf->clock_reg);
|
spi_ll_master_set_clock_by_reg(hw, &dev->timing_conf.clock_reg);
|
||||||
//Configure bit order
|
//Configure bit order
|
||||||
spi_ll_set_rx_lsbfirst(hw, hal->rx_lsbfirst);
|
spi_ll_set_rx_lsbfirst(hw, dev->rx_lsbfirst);
|
||||||
spi_ll_set_tx_lsbfirst(hw, hal->tx_lsbfirst);
|
spi_ll_set_tx_lsbfirst(hw, dev->tx_lsbfirst);
|
||||||
spi_ll_master_set_mode(hw, hal->mode);
|
spi_ll_master_set_mode(hw, dev->mode);
|
||||||
//Configure misc stuff
|
//Configure misc stuff
|
||||||
spi_ll_set_half_duplex(hw, hal->half_duplex);
|
spi_ll_set_half_duplex(hw, dev->half_duplex);
|
||||||
spi_ll_set_sio_mode(hw, hal->sio);
|
spi_ll_set_sio_mode(hw, dev->sio);
|
||||||
//Configure CS pin and timing
|
//Configure CS pin and timing
|
||||||
spi_ll_master_set_cs_setup(hw, hal->cs_setup);
|
spi_ll_master_set_cs_setup(hw, dev->cs_setup);
|
||||||
spi_ll_master_set_cs_hold(hw, hal->cs_hold);
|
spi_ll_master_set_cs_hold(hw, dev->cs_hold);
|
||||||
spi_ll_master_select_cs(hw, hal->cs_pin_id);
|
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;
|
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.
|
//We should be done with the transmission.
|
||||||
assert(spi_ll_get_running_cmd(hw) == 0);
|
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;
|
int extra_dummy = 0;
|
||||||
//when no_dummy is not set and in half-duplex mode, sets the dummy bit if RX phase exist
|
//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) {
|
if (trans->rcv_buffer && !dev->no_compensate && dev->half_duplex) {
|
||||||
extra_dummy = hal->timing_conf->timing_dummy;
|
extra_dummy = dev->timing_conf.timing_dummy;
|
||||||
}
|
}
|
||||||
|
|
||||||
//SPI iface needs to be configured for a delay in some cases.
|
//SPI iface needs to be configured for a delay in some cases.
|
||||||
//configure dummy bits
|
//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_num = 0;
|
||||||
uint32_t miso_delay_mode = 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
|
//if the data comes too late, delay half a SPI clock to improve reading
|
||||||
switch (hal->mode) {
|
switch (dev->mode) {
|
||||||
case 0:
|
case 0:
|
||||||
miso_delay_mode = 2;
|
miso_delay_mode = 2;
|
||||||
break;
|
break;
|
||||||
@ -81,24 +81,24 @@ void spi_hal_setup_trans(const spi_hal_context_t *hal)
|
|||||||
miso_delay_num = 0;
|
miso_delay_num = 0;
|
||||||
} else {
|
} else {
|
||||||
//if the data is so fast that dummy_bit is used, delay some apb clocks to meet the timing
|
//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;
|
miso_delay_mode = 0;
|
||||||
}
|
}
|
||||||
spi_ll_set_miso_delay(hw, miso_delay_mode, miso_delay_num);
|
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) {
|
if (dev->half_duplex) {
|
||||||
spi_ll_set_miso_bitlen(hw, hal->rx_bitlen);
|
spi_ll_set_miso_bitlen(hw, trans->rx_bitlen);
|
||||||
} else {
|
} else {
|
||||||
//rxlength is not used in full-duplex mode
|
//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
|
//Configure bit sizes, load addr and command
|
||||||
int cmdlen = hal->cmd_bits;
|
int cmdlen = trans->cmd_bits;
|
||||||
int addrlen = hal->addr_bits;
|
int addrlen = trans->addr_bits;
|
||||||
if (!hal->half_duplex && hal->cs_setup != 0) {
|
if (!dev->half_duplex && dev->cs_setup != 0) {
|
||||||
/* The command and address phase is not compatible with cs_ena_pretrans
|
/* The command and address phase is not compatible with cs_ena_pretrans
|
||||||
* in full duplex mode.
|
* 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_addr_bitlen(hw, addrlen);
|
||||||
spi_ll_set_command_bitlen(hw, cmdlen);
|
spi_ll_set_command_bitlen(hw, cmdlen);
|
||||||
|
|
||||||
spi_ll_set_command(hw, hal->cmd, cmdlen, hal->tx_lsbfirst);
|
spi_ll_set_command(hw, trans->cmd, cmdlen, dev->tx_lsbfirst);
|
||||||
spi_ll_set_address(hw, hal->addr, addrlen, hal->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_dev_t *hw = hal->hw;
|
||||||
spi_ll_reset_dma(hw);
|
spi_ll_reset_dma(hw);
|
||||||
//Fill DMA descriptors
|
//Fill DMA descriptors
|
||||||
if (hal->rcv_buffer) {
|
if (trans->rcv_buffer) {
|
||||||
if (!hal->dma_enabled) {
|
if (!hal->dma_enabled) {
|
||||||
//No need to setup anything; we'll copy the result out of the work registers directly later.
|
//No need to setup anything; we'll copy the result out of the work registers directly later.
|
||||||
} else {
|
} else {
|
||||||
lldesc_setup_link(hal->dmadesc_rx, hal->rcv_buffer, ((hal->rx_bitlen + 7) / 8), true);
|
lldesc_setup_link(hal->dma_config.dmadesc_rx, trans->rcv_buffer, ((trans->rx_bitlen + 7) / 8), true);
|
||||||
spi_ll_rxdma_start(hw, hal->dmadesc_rx);
|
|
||||||
|
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 {
|
} else {
|
||||||
//DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon
|
//DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon
|
||||||
if (hal->dma_enabled) {
|
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) {
|
if (!hal->dma_enabled) {
|
||||||
//Need to copy data to registers manually
|
//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 {
|
} else {
|
||||||
lldesc_setup_link(hal->dmadesc_tx, hal->send_buffer, (hal->tx_bitlen + 7) / 8, false);
|
lldesc_setup_link(hal->dma_config.dmadesc_tx, trans->send_buffer, (trans->tx_bitlen + 7) / 8, false);
|
||||||
spi_ll_txdma_start(hw, hal->dmadesc_tx);
|
|
||||||
|
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
|
//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);
|
spi_ll_enable_mosi(hw, 1);
|
||||||
} else {
|
} else {
|
||||||
spi_ll_enable_mosi(hw, 0);
|
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)
|
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)
|
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.
|
//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_attr.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
#include "soc/spi_periph.h"
|
#include "soc/spi_periph.h"
|
||||||
#include "soc/lldesc.h"
|
#include "soc/lldesc.h"
|
||||||
|
|
||||||
#include "hal/spi_slave_hd_hal.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));
|
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;
|
hal->dev = hw;
|
||||||
|
|
||||||
//Configure slave
|
//Configure slave
|
||||||
spi_ll_slave_hd_init(hw);
|
spi_ll_slave_hd_init(hw);
|
||||||
spi_ll_set_addr_bitlen(hw, config->address_bits);
|
spi_ll_set_addr_bitlen(hw, hal_config->address_bits);
|
||||||
spi_ll_set_command_bitlen(hw, config->command_bits);
|
spi_ll_set_command_bitlen(hw, hal_config->command_bits);
|
||||||
spi_ll_set_dummy(hw, config->dummy_bits);
|
spi_ll_set_dummy(hw, hal_config->dummy_bits);
|
||||||
spi_ll_set_rx_lsbfirst(hw, config->rx_lsbfirst);
|
spi_ll_set_rx_lsbfirst(hw, hal_config->rx_lsbfirst);
|
||||||
spi_ll_set_tx_lsbfirst(hw, config->tx_lsbfirst);
|
spi_ll_set_tx_lsbfirst(hw, hal_config->tx_lsbfirst);
|
||||||
spi_ll_slave_set_mode(hw, config->mode, (config->dma_chan != 0));
|
spi_ll_slave_set_mode(hw, hal_config->mode, (hal_config->dma_chan != 0));
|
||||||
|
|
||||||
spi_ll_disable_intr(hw, UINT32_MAX);
|
spi_ll_disable_intr(hw, UINT32_MAX);
|
||||||
spi_ll_clear_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_RDBUF |
|
||||||
SPI_LL_TRANS_LEN_COND_RDDMA);
|
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)
|
void spi_slave_hd_hal_rxdma(spi_slave_hd_hal_context_t *hal, uint8_t *out_buf, size_t len)
|
||||||
@ -179,4 +177,4 @@ int spi_slave_hd_hal_rxdma_get_len(spi_slave_hd_hal_context_t *hal)
|
|||||||
{
|
{
|
||||||
lldesc_t* desc = &hal->dmadesc_rx[0];
|
lldesc_t* desc = &hal->dmadesc_rx[0];
|
||||||
return lldesc_get_received_len(desc, NULL);
|
return lldesc_get_received_len(desc, NULL);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user