essl: add essl spi support to communicate with spi slave hd mode

This commit is contained in:
Armando 2021-07-19 11:01:13 +08:00 committed by Armando (Dou Yiwen)
parent 69deaf528f
commit aca2bd5fcf
5 changed files with 482 additions and 79 deletions

View File

@ -919,16 +919,14 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_start(spi_device_handle_t handl
{
esp_err_t ret;
SPI_CHECK(ticks_to_wait == portMAX_DELAY, "currently timeout is not available for polling transactions", ESP_ERR_INVALID_ARG);
spi_host_t *host = handle->host;
ret = check_trans_valid(handle, trans_desc);
if (ret!=ESP_OK) return ret;
SPI_CHECK(!spi_bus_device_is_polling(handle), "Cannot send polling transaction while the previous polling transaction is not terminated.", ESP_ERR_INVALID_STATE );
/* If device_acquiring_lock is set to handle, it means that the user has already
* acquired the bus thanks to the function `spi_device_acquire_bus()`.
* In that case, we don't need to take the lock again. */
spi_host_t *host = handle->host;
if (host->device_acquiring_lock != handle) {
/* The user cannot ask for the CS to keep active has the bus is not locked/acquired. */
if ((trans_desc->flags & SPI_TRANS_CS_KEEP_ACTIVE) != 0) {

View File

@ -62,10 +62,7 @@ esp_err_t essl_wait_for_ready(essl_handle_t handle, uint32_t wait_ms)
esp_err_t essl_send_packet(essl_handle_t handle, const void *start, size_t length, uint32_t wait_ms)
{
if (handle == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (start == NULL || length == 0) {
if (handle == NULL || start == NULL || length == 0) {
return ESP_ERR_INVALID_ARG;
}
if (handle->send_packet == NULL) {
@ -87,9 +84,9 @@ esp_err_t essl_send_packet(essl_handle_t handle, const void *start, size_t lengt
} else if (err != ESP_ERR_NOT_FOUND) {
return err;
} // else ESP_ERR_NOT_FOUND
//the slave has no enough memory, retry
//the slave is not ready, retry
} while (remain_wait_ms > 0);
return ESP_OK;
return err;
}
esp_err_t essl_get_packet(essl_handle_t handle, void *out_data, size_t size, size_t *out_length, uint32_t wait_ms)

View File

@ -17,8 +17,46 @@
#include "esp_log.h"
#include "driver/spi_master.h"
#include "driver/periph_ctrl.h"
#include "essl_spi/esp32s2_defs.h"
#include "essl_internal.h"
#include "essl_spi.h"
#include "essl_spi/esp32s2_defs.h"
#define ESSL_SPI_CHECK(cond, warn, ret) do{if(!(cond)){ESP_LOGE(TAG, warn); return ret;}} while(0)
/**
* Initialise device function list of SPI by this macro.
*/
#define ESSL_SPI_DEFAULT_DEV_FUNC() (essl_dev_t) {\
.get_tx_buffer_num = essl_spi_get_tx_buffer_num,\
.update_tx_buffer_num = essl_spi_update_tx_buffer_num,\
.get_rx_data_size = essl_spi_get_rx_data_size,\
.update_rx_data_size = essl_spi_update_rx_data_size,\
.send_packet = essl_spi_send_packet,\
.get_packet = essl_spi_get_packet,\
.write_reg = essl_spi_write_reg,\
.read_reg = essl_spi_read_reg,\
}
static const char TAG[] = "essl_spi";
typedef struct {
spi_device_handle_t spi; // Pointer to SPI device handle.
/* Master TX, Slave RX */
struct {
size_t sent_buf_num; // Number of TX buffers that has been sent out by the master.
size_t slave_rx_buf_num; // Number of RX buffers laoded by the slave.
uint16_t tx_buffer_size; /* Buffer size for Master TX / Slave RX direction.
* Data with length within this size will still be regarded as one buffer.
* E.g. 10 bytes data costs 2 buffers if the size is 8 bytes per buffer. */
uint8_t tx_sync_reg; // The pre-negotiated register ID for Master-TX-SLAVE-RX synchronization. 1 word (4 Bytes) will be reserved for the synchronization.
} master_out;
/* Master RX, Slave TX */
struct {
size_t received_bytes; // Number of the RX bytes that has been received by the Master.
size_t slave_tx_bytes; // Number of the TX bytes that has been loaded by the Slave
uint8_t rx_sync_reg; // The pre-negotiated register ID for Master-RX-SLAVE-TX synchronization. 1 word (4 Bytes) will be reserved for the synchronization.
} master_in;
} essl_spi_context_t;
static uint16_t get_hd_command(uint16_t cmd_i, uint32_t flags)
@ -153,12 +191,12 @@ esp_err_t essl_spi_rddma(spi_device_handle_t spi, uint8_t *out_data, int len, in
seg_len = (seg_len > 0)? seg_len : len;
uint8_t* read_ptr = out_data;
esp_err_t err = ESP_OK;
esp_err_t ret = ESP_OK;
while (len > 0) {
int send_len = MIN(seg_len, len);
err = essl_spi_rddma_seg(spi, read_ptr, send_len, flags);
if (err != ESP_OK) return err;
ret = essl_spi_rddma_seg(spi, read_ptr, send_len, flags);
if (ret != ESP_OK) return ret;
len -= send_len;
read_ptr += send_len;
@ -217,3 +255,235 @@ esp_err_t essl_spi_int(spi_device_handle_t spi, int int_n, uint32_t flags)
};
return spi_device_transmit(spi, &end_t);
}
//------------------------------------ APPEND MODE ----------------------------------//
static uint32_t essl_spi_get_rx_data_size(void *arg);
static esp_err_t essl_spi_update_rx_data_size(void *arg, uint32_t wait_ms);
static uint32_t essl_spi_get_tx_buffer_num(void *arg);
static esp_err_t essl_spi_update_tx_buffer_num(void *arg, uint32_t wait_ms);
esp_err_t essl_spi_init_dev(essl_handle_t *out_handle, const essl_spi_config_t *init_config)
{
ESP_RETURN_ON_FALSE(init_config->spi, ESP_ERR_INVALID_STATE, TAG, "Check SPI initialization first");
ESP_RETURN_ON_FALSE(init_config->tx_sync_reg <= (SOC_SPI_MAXIMUM_BUFFER_SIZE - 1) * 4, ESP_ERR_INVALID_ARG, TAG, "GPSPI supports %d-byte-width internal registers", SOC_SPI_MAXIMUM_BUFFER_SIZE);
ESP_RETURN_ON_FALSE(init_config->rx_sync_reg <= (SOC_SPI_MAXIMUM_BUFFER_SIZE - 1) * 4, ESP_ERR_INVALID_ARG, TAG, "GPSPI supports %d-byte-width internal registers", SOC_SPI_MAXIMUM_BUFFER_SIZE);
ESP_RETURN_ON_FALSE(init_config->tx_sync_reg != init_config->rx_sync_reg, ESP_ERR_INVALID_ARG, TAG, "Should use different word of registers for synchronization");
essl_spi_context_t *context = calloc(1, sizeof(essl_spi_context_t));
essl_dev_t *dev = calloc(1, sizeof(essl_dev_t));
if (!context || !dev) {
free(context);
free(dev);
return ESP_ERR_NO_MEM;
}
*context = (essl_spi_context_t) {
.spi = *init_config->spi,
.master_out.tx_buffer_size = init_config->tx_buf_size,
.master_out.tx_sync_reg = init_config->tx_sync_reg,
.master_in.rx_sync_reg = init_config->rx_sync_reg
};
*dev = ESSL_SPI_DEFAULT_DEV_FUNC();
dev->args = context;
*out_handle = dev;
return ESP_OK;
}
esp_err_t essl_spi_deinit_dev(essl_handle_t handle)
{
ESSL_SPI_CHECK(handle, "ESSL SPI is not in use", ESP_ERR_INVALID_STATE);
free(handle->args);
free(handle);
return ESP_OK;
}
void essl_spi_reset_cnt(void *arg)
{
essl_spi_context_t *ctx = arg;
if (ctx) {
ctx->master_out.sent_buf_num = 0;
ctx->master_in.received_bytes = 0;
}
}
//------------------------------------ RX ----------------------------------//
esp_err_t essl_spi_read_reg(void *arg, uint8_t addr, uint8_t *out_value, uint32_t wait_ms)
{
essl_spi_context_t *ctx = arg;
ESP_RETURN_ON_FALSE(arg, ESP_ERR_INVALID_STATE, TAG, "Check ESSL SPI initialization first");
uint8_t reserved_1_head = ctx->master_out.tx_sync_reg < ctx->master_in.rx_sync_reg ? ctx->master_out.tx_sync_reg : ctx->master_in.rx_sync_reg;
uint8_t reserved_1_tail = reserved_1_head + 3;
uint8_t reserved_2_head = ctx->master_out.tx_sync_reg < ctx->master_in.rx_sync_reg ? ctx->master_in.rx_sync_reg : ctx->master_out.tx_sync_reg;
uint8_t reserved_2_tail = reserved_2_head + 3;
ESP_RETURN_ON_FALSE(addr < reserved_1_head || (addr > reserved_1_tail && addr < reserved_2_head) || addr > reserved_2_tail, ESP_ERR_INVALID_ARG, TAG, "Invalid address");
return essl_spi_rdbuf(ctx->spi, out_value, addr, sizeof(uint8_t), 0);
}
static uint32_t essl_spi_get_rx_data_size(void *arg)
{
essl_spi_context_t *ctx = arg;
ESP_LOGV(TAG, "slave tx buffer: %d bytes, master has read: %d bytes", ctx->master_in.slave_tx_bytes, ctx->master_in.received_bytes);
return ctx->master_in.slave_tx_bytes - ctx->master_in.received_bytes;
}
static esp_err_t essl_spi_update_rx_data_size(void *arg, uint32_t wait_ms)
{
essl_spi_context_t *ctx = arg;
uint32_t updated_size;
uint32_t previous_size;
esp_err_t ret;
ret = essl_spi_rdbuf_polling(ctx->spi, (uint8_t *)&previous_size, ctx->master_in.rx_sync_reg, sizeof(uint32_t), 0);
if (ret != ESP_OK) {
return ret;
}
/**
* Read until the last 2 reading result are same. Reason:
* SPI transaction is carried on per 1 Byte. So when Master is reading the shared register, if the
* register value is changed by Slave at this time, Master may get wrong data.
*/
while (1) {
ret = essl_spi_rdbuf_polling(ctx->spi, (uint8_t *)&updated_size, ctx->master_in.rx_sync_reg, sizeof(uint32_t), 0);
if (ret != ESP_OK) {
return ret;
}
if (updated_size == previous_size) {
ctx->master_in.slave_tx_bytes = updated_size;
ESP_LOGV(TAG, "updated: slave prepared tx buffer is: %d bytes", updated_size);
return ret;
}
previous_size = updated_size;
}
}
esp_err_t essl_spi_get_packet(void *arg, void *out_data, size_t size, uint32_t wait_ms)
{
ESSL_SPI_CHECK(arg, "Check ESSL SPI initialization first", ESP_ERR_INVALID_STATE);
if (!esp_ptr_dma_capable(out_data) || ((intptr_t)out_data % 4) != 0) {
return ESP_ERR_INVALID_ARG;
}
essl_spi_context_t *ctx = arg;
esp_err_t ret;
if (essl_spi_get_rx_data_size(arg) < size) {
/**
* For realistic situation, usually there will be a large overhead (Slave will load large amount of data),
* so here we only update the Slave's TX size when the last-updated size is smaller than what Master requires.
*/
ret = essl_spi_update_rx_data_size(arg, wait_ms);
if (ret != ESP_OK) {
return ret;
}
//Slave still did not load enough size of buffer
if (essl_spi_get_rx_data_size(arg) < size) {
ESP_LOGV(TAG, "slave buffer: %d is not enough, %d is required", ctx->master_in.slave_tx_bytes, ctx->master_in.received_bytes + size);
return ESP_ERR_NOT_FOUND;
}
}
ESP_LOGV(TAG, "get_packet: size to read is: %d", size);
ret = essl_spi_rddma_seg(ctx->spi, out_data, size, 0);
if (ret != ESP_OK) {
return ret;
}
ctx->master_in.received_bytes += size;
return ESP_OK;
}
//------------------------------------ TX ----------------------------------//
esp_err_t essl_spi_write_reg(void *arg, uint8_t addr, uint8_t value, uint8_t *out_value, uint32_t wait_ms)
{
essl_spi_context_t *ctx = arg;
ESP_RETURN_ON_FALSE(arg, ESP_ERR_INVALID_STATE, TAG, "Check ESSL SPI initialization first");
uint8_t reserved_1_head = ctx->master_out.tx_sync_reg < ctx->master_in.rx_sync_reg ? ctx->master_out.tx_sync_reg : ctx->master_in.rx_sync_reg;
uint8_t reserved_1_tail = reserved_1_head + 3;
uint8_t reserved_2_head = ctx->master_out.tx_sync_reg < ctx->master_in.rx_sync_reg ? ctx->master_in.rx_sync_reg : ctx->master_out.tx_sync_reg;
uint8_t reserved_2_tail = reserved_2_head + 3;
ESP_RETURN_ON_FALSE(addr < reserved_1_head || (addr > reserved_1_tail && addr < reserved_2_head) || addr > reserved_2_tail, ESP_ERR_INVALID_ARG, TAG, "Invalid address");
ESP_RETURN_ON_FALSE(out_value == NULL, ESP_ERR_NOT_SUPPORTED, TAG, "This feature is not supported");
return essl_spi_wrbuf(ctx->spi, &value, addr, sizeof(uint8_t), 0);
}
static uint32_t essl_spi_get_tx_buffer_num(void *arg)
{
essl_spi_context_t *ctx = arg;
ESP_LOGV(TAG, "slave rx buffer: %d, master has sent: %d", ctx->master_out.slave_rx_buf_num, ctx->master_out.sent_buf_num);
return ctx->master_out.slave_rx_buf_num - ctx->master_out.sent_buf_num;
}
static esp_err_t essl_spi_update_tx_buffer_num(void *arg, uint32_t wait_ms)
{
essl_spi_context_t *ctx = arg;
uint32_t updated_num;
uint32_t previous_size;
esp_err_t ret;
ret = essl_spi_rdbuf_polling(ctx->spi, (uint8_t *)&previous_size, ctx->master_out.tx_sync_reg, sizeof(uint32_t), 0);
if (ret != ESP_OK) {
return ret;
}
/**
* Read until the last 2 reading result are same. Reason:
* SPI transaction is carried on per 1 Byte. So when Master is reading the shared register, if the
* register value is changed by Slave at this time, Master may get wrong data.
*/
while (1) {
ret = essl_spi_rdbuf_polling(ctx->spi, (uint8_t *)&updated_num, ctx->master_out.tx_sync_reg, sizeof(uint32_t), 0);
if (ret != ESP_OK) {
return ret;
}
if (updated_num == previous_size) {
ctx->master_out.slave_rx_buf_num = updated_num;
ESP_LOGV(TAG, "updated: slave prepared rx buffer: %d", updated_num);
return ret;
}
previous_size = updated_num;
}
}
esp_err_t essl_spi_send_packet(void *arg, const void *data, size_t size, uint32_t wait_ms)
{
ESSL_SPI_CHECK(arg, "Check ESSL SPI initialization first", ESP_ERR_INVALID_STATE);
if (!esp_ptr_dma_capable(data)) {
return ESP_ERR_INVALID_ARG;
}
essl_spi_context_t *ctx = arg;
esp_err_t ret;
uint32_t buf_num_to_use = (size + ctx->master_out.tx_buffer_size - 1) / ctx->master_out.tx_buffer_size;
if (essl_spi_get_tx_buffer_num(arg) < buf_num_to_use) {
/**
* For realistic situation, usually there will be a large overhead (Slave will load enough number of RX buffers),
* so here we only update the Slave's RX buffer number when the last-updated number is smaller than what Master requires.
*/
ret = essl_spi_update_tx_buffer_num(arg, wait_ms);
if (ret != ESP_OK) {
return ret;
}
//Slave still did not load a sufficient amount of buffers
if (essl_spi_get_tx_buffer_num(arg) < buf_num_to_use) {
ESP_LOGV(TAG, "slave buffer: %d is not enough, %d is required", ctx->master_out.slave_rx_buf_num, ctx->master_out.sent_buf_num + buf_num_to_use);
return ESP_ERR_NOT_FOUND;
}
}
ESP_LOGV(TAG, "send_packet: size to write is: %d", size);
ret = essl_spi_wrdma_seg(ctx->spi, data, size, 0);
if (ret != ESP_OK) {
return ret;
}
ctx->master_out.sent_buf_num += buf_num_to_use;
return essl_spi_wrdma_done(ctx->spi, 0);
}

View File

@ -29,42 +29,48 @@ typedef struct essl_dev_t* essl_handle_t;
*
* @param handle Handle of an ESSL device.
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
* @return ESP_OK if success, or other value returned from lower layer `init`.
* @return
* - ESP_OK: If success
* - ESP_ERR_NOT_SUPPORTED: Current device does not support this function.
* - Other value returned from lower layer `init`.
*/
esp_err_t essl_init(essl_handle_t handle, uint32_t wait_ms);
/** Wait for interrupt of an ESP slave device.
/** Wait for interrupt of an ESSL slave device.
*
* @param handle Handle of an ESSL device.
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
*
* @return
* - ESP_OK if success
* - ESP_OK: If success
* - ESP_ERR_NOT_SUPPORTED: Current device does not support this function.
* - One of the error codes from SDMMC host controller
*/
esp_err_t essl_wait_for_ready(essl_handle_t handle, uint32_t wait_ms);
/** Get buffer num for the host to send data to the slave. The buffers are size of ``buffer_size``.
*
* @param handle Handle of an ESSL device.
* @param out_tx_num Output of buffer num that host can send data to an ESP slave.
* @param handle Handle of a ESSL device.
* @param out_tx_num Output of buffer num that host can send data to ESSL slave.
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
* - ESP_OK: Success
* - ESP_ERR_NOT_SUPPORTED: This API is not supported in this mode
* - One of the error codes from SDMMC/SPI host controller
*/
esp_err_t essl_get_tx_buffer_num(essl_handle_t handle, uint32_t *out_tx_num, uint32_t wait_ms);
/** Get amount of data the ESP slave preparing to send to host.
/** Get the size, in bytes, of the data that the ESSL slave is ready to send
*
* @param handle Handle of an ESSL device.
* @param out_rx_size Output of data size to read from slave.
* @param out_rx_size Output of data size to read from slave, in bytes
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
* - ESP_OK: Success
* - ESP_ERR_NOT_SUPPORTED: This API is not supported in this mode
* - One of the error codes from SDMMC/SPI host controller
*/
esp_err_t essl_get_rx_data_size(essl_handle_t handle, uint32_t *out_rx_size, uint32_t wait_ms);
@ -72,10 +78,15 @@ esp_err_t essl_get_rx_data_size(essl_handle_t handle, uint32_t *out_rx_size, uin
/** Reset the counters of this component. Usually you don't need to do this unless you know the slave is reset.
*
* @param handle Handle of an ESSL device.
*
* @return
* - ESP_OK: Success
* - ESP_ERR_NOT_SUPPORTED: This API is not supported in this mode
* - ESP_ERR_INVALID_ARG: Invalid argument, handle is not init.
*/
esp_err_t essl_reset_cnt(essl_handle_t handle);
/** Send a packet to the ESP slave. The slave receive the packet into buffers whose size is ``buffer_size`` (configured during initialization).
/** Send a packet to the ESSL Slave. The Slave receives the packet into buffers whose size is ``buffer_size`` (configured during initialization).
*
* @param handle Handle of an ESSL device.
* @param start Start address of the packet to send
@ -84,12 +95,15 @@ esp_err_t essl_reset_cnt(essl_handle_t handle);
*
* @return
* - ESP_OK Success
* - ESP_ERR_TIMEOUT No buffer to use, or error ftrom SDMMC host controller
* - One of the error codes from SDMMC host controller
* - ESP_ERR_INVALID_ARG: Invalid argument, handle is not init or other argument is not valid.
* - ESP_ERR_TIMEOUT: No buffer to use, or error ftrom SDMMC host controller.
* - ESP_ERR_NOT_FOUND: Slave is not ready for receiving.
* - ESP_ERR_NOT_SUPPORTED: This API is not supported in this mode
* - One of the error codes from SDMMC/SPI host controller.
*/
esp_err_t essl_send_packet(essl_handle_t handle, const void *start, size_t length, uint32_t wait_ms);
/** Get a packet from an ESP slave.
/** Get a packet from ESSL slave.
*
* @param handle Handle of an ESSL device.
* @param[out] out_data Data output address
@ -98,16 +112,19 @@ esp_err_t essl_send_packet(essl_handle_t handle, const void *start, size_t lengt
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
*
* @return
* - ESP_OK Success, all the data are read from the slave.
* - ESP_ERR_NOT_FINISHED Read success, while there're data remaining.
* - One of the error codes from SDMMC host controller
* - ESP_OK Success: All the data has been read from the slave.
* - ESP_ERR_INVALID_ARG: Invalid argument, The handle is not initialized or the other arguments are invalid.
* - ESP_ERR_NOT_FINISHED: Read was successful, but there is still data remaining.
* - ESP_ERR_NOT_FOUND: Slave is not ready to send data.
* - ESP_ERR_NOT_SUPPORTED: This API is not supported in this mode
* - One of the error codes from SDMMC/SPI host controller.
*/
esp_err_t essl_get_packet(essl_handle_t handle, void *out_data, size_t size, size_t *out_length, uint32_t wait_ms);
/** Write general purpose R/W registers (8-bit) of an ESP slave.
/** Write general purpose R/W registers (8-bit) of ESSL slave.
*
* @param handle Handle of an ESSL device.
* @param addr Address of register to write. Valid address: 0-59.
* @param addr Address of register to write. For SDIO, valid address: 0-59. For SPI, see ``essl_spi.h``
* @param value Value to write to the register.
* @param value_o Output of the returned written value.
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
@ -116,22 +133,20 @@ esp_err_t essl_get_packet(essl_handle_t handle, void *out_data, size_t size, siz
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Address not valid.
* - One of the error codes from SDMMC host controller
* - One of the error codes from SDMMC/SPI host controller
*/
esp_err_t essl_write_reg(essl_handle_t handle, uint8_t addr, uint8_t value, uint8_t *value_o, uint32_t wait_ms);
/** Read general purpose R/W registers (8-bit) of an ESP slave.
/** Read general purpose R/W registers (8-bit) of ESSL slave.
*
* @param handle Handle of an ESSL device.
* @param add Address of register to read. Valid address: 0-27, 32-63 (28-31 reserved, return interrupt bits on read).
* @param handle Handle of a ``essl`` device.
* @param add Address of register to read. For SDIO, Valid address: 0-27, 32-63 (28-31 reserved, return interrupt bits on read). For SPI, see ``essl_spi.h``
* @param value_o Output value read from the register.
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Address not valid.
* - One of the error codes from SDMMC host controller
* - One of the error codes from SDMMC/SPI host controller
*/
esp_err_t essl_read_reg(essl_handle_t handle, uint8_t add, uint8_t *value_o, uint32_t wait_ms);
@ -141,25 +156,26 @@ esp_err_t essl_read_reg(essl_handle_t handle, uint8_t add, uint8_t *value_o, uin
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
*
* @return
* - ESP_ERR_NOT_SUPPORTED Currently our driver doesnot support SDIO with SPI interface.
* - ESP_OK If interrupt triggered.
* - ESP_ERR_TIMEOUT No interrupts before timeout.
* - ESP_OK: If interrupt is triggered.
* - ESP_ERR_NOT_SUPPORTED: Current device does not support this function.
* - ESP_ERR_TIMEOUT: No interrupts before timeout.
*/
esp_err_t essl_wait_int(essl_handle_t handle, uint32_t wait_ms);
/** Clear interrupt bits of an ESP slave. All the bits set in the mask will be cleared, while other bits will stay the same.
/** Clear interrupt bits of ESSL slave. All the bits set in the mask will be cleared, while other bits will stay the same.
*
* @param handle Handle of an ESSL device.
* @param intr_mask Mask of interrupt bits to clear.
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
* - ESP_OK: Success
* - ESP_ERR_NOT_SUPPORTED: Current device does not support this function.
* - One of the error codes from SDMMC host controller
*/
esp_err_t essl_clear_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wait_ms);
/** Get interrupt bits of an ESP slave.
/** Get interrupt bits of ESSL slave.
*
* @param handle Handle of an ESSL device.
* @param intr_raw Output of the raw interrupt bits. Set to NULL if only masked bits are read.
@ -167,25 +183,27 @@ esp_err_t essl_clear_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wai
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
*
* @return
* - ESP_OK Success
* - ESP_INVALID_ARG if both ``intr_raw`` and ``intr_st`` are NULL.
* - One of the error codes from SDMMC host controller
* - ESP_OK: Success
* - ESP_INVALID_ARG: If both ``intr_raw`` and ``intr_st`` are NULL.
* - ESP_ERR_NOT_SUPPORTED: Current device does not support this function.
* - One of the error codes from SDMMC host controller
*/
esp_err_t essl_get_intr(essl_handle_t handle, uint32_t *intr_raw, uint32_t *intr_st, uint32_t wait_ms);
/** Set interrupt enable bits of an ESP slave. The slave only sends interrupt on the line when there is a bit both the raw status and the enable are set.
/** Set interrupt enable bits of ESSL slave. The slave only sends interrupt on the line when there is a bit both the raw status and the enable are set.
*
* @param handle Handle of an ESSL device.
* @param ena_mask Mask of the interrupt bits to enable.
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
* - ESP_OK: Success
* - ESP_ERR_NOT_SUPPORTED: Current device does not support this function.
* - One of the error codes from SDMMC host controller
*/
esp_err_t essl_set_intr_ena(essl_handle_t handle, uint32_t ena_mask, uint32_t wait_ms);
/** Get interrupt enable bits of an ESP slave.
/** Get interrupt enable bits of ESSL slave.
*
* @param handle Handle of an ESSL device.
* @param ena_mask_o Output of interrupt bit enable mask.
@ -204,7 +222,8 @@ esp_err_t essl_get_intr_ena(essl_handle_t handle, uint32_t *ena_mask_o, uint32_t
* @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
* - ESP_OK: Success
* - ESP_ERR_NOT_SUPPORTED: Current device does not support this function.
* - One of the error codes from SDMMC host controller
*/
esp_err_t essl_send_slave_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wait_ms);

View File

@ -23,22 +23,141 @@ extern "C"
{
#endif
/// Configuration of ESSL SPI device
typedef struct {
spi_device_handle_t *spi; ///< Pointer to SPI device handle.
uint32_t tx_buf_size; ///< The pre-negotiated Master TX buffer size used by both the host and the slave.
uint8_t tx_sync_reg; ///< The pre-negotiated register ID for Master-TX-SLAVE-RX synchronization. 1 word (4 Bytes) will be reserved for the synchronization.
uint8_t rx_sync_reg; ///< The pre-negotiated register ID for Master-RX-Slave-TX synchronization. 1 word (4 Bytes) will be reserved for the synchronization.
} essl_spi_config_t;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// APIs for DMA Append Mode
// This mode has a better performance for continuous Half Duplex SPI transactions.
//
// * You can use the ``essl_spi_init_dev`` and ``essl_spi_deinit_dev`` together with APIs in ``essl.h`` to communicate
// with ESP SPI Slaves in Half Duplex DMA Append Mode. See example for SPI SLAVE HALFDUPLEX APPEND MODE.
// * You can also use the following APIs to create your own logic.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* @brief Initialize the ESSL SPI device function list and get its handle
*
* @param[out] out_handle Output of the handle
* @param init_config Configuration for the ESSL SPI device
* @return
* - ESP_OK: On success
* - ESP_ERR_NO_MEM: Memory exhausted
* - ESP_ERR_INVALID_STATE: SPI driver is not initialized
* - ESP_ERR_INVALID_ARG: Wrong register ID
*/
esp_err_t essl_spi_init_dev(essl_handle_t *out_handle, const essl_spi_config_t *init_config);
/**
* @brief Deinitialize the ESSL SPI device and free the memory used by the device
*
* @param handle Handle of the ESSL SPI device
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_STATE: ESSL SPI is not in use
*/
esp_err_t essl_spi_deinit_dev(essl_handle_t handle);
/**
* @brief Read from the shared registers
*
* @note The registers for Master/Slave synchronization are reserved. Do not use them. (see `rx_sync_reg` in `essl_spi_config_t`)
*
* @param arg Context of the component. (Member ``arg`` from ``essl_handle_t``)
* @param addr Address of the shared registers. (Valid: 0 ~ SOC_SPI_MAXIMUM_BUFFER_SIZE, registers for M/S sync are reserved, see note1).
* @param[out] out_value Read buffer for the shared registers.
* @param wait_ms Time to wait before timeout (reserved for future use, user should set this to 0).
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: ESSL SPI has not been initialized.
* - ESP_ERR_INVALID_ARG: The address argument is not valid. See note 1.
* - or other return value from :cpp:func:`spi_device_transmit`.
*/
esp_err_t essl_spi_read_reg(void *arg, uint8_t addr, uint8_t *out_value, uint32_t wait_ms);
/**
* @brief Get a packet from Slave
*
* @param arg Context of the component. (Member ``arg`` from ``essl_handle_t``)
* @param[out] out_data Output data address
* @param size The size of the output data.
* @param wait_ms Time to wait before timeout (reserved for future use, user should set this to 0).
* @return
* - ESP_OK: On Success
* - ESP_ERR_INVALID_STATE: ESSL SPI has not been initialized.
* - ESP_ERR_INVALID_ARG: The output data address is neither DMA capable nor 4 byte-aligned
* - ESP_ERR_INVALID_SIZE: Master requires ``size`` bytes of data but Slave did not load enough bytes.
*/
esp_err_t essl_spi_get_packet(void *arg, void *out_data, size_t size, uint32_t wait_ms);
/**
* @brief Write to the shared registers
*
* @note The registers for Master/Slave synchronization are reserved. Do not use them. (see `tx_sync_reg` in `essl_spi_config_t`)
* @note Feature of checking the actual written value (``out_value``) is not supported.
*
* @param arg Context of the component. (Member ``arg`` from ``essl_handle_t``)
* @param addr Address of the shared registers. (Valid: 0 ~ SOC_SPI_MAXIMUM_BUFFER_SIZE, registers for M/S sync are reserved, see note1)
* @param value Buffer for data to send, should be align to 4.
* @param[out] out_value Not supported, should be set to NULL.
* @param wait_ms Time to wait before timeout (reserved for future use, user should set this to 0).
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: ESSL SPI has not been initialized.
* - ESP_ERR_INVALID_ARG: The address argument is not valid. See note 1.
* - ESP_ERR_NOT_SUPPORTED: Should set ``out_value`` to NULL. See note 2.
* - or other return value from :cpp:func:`spi_device_transmit`.
*
*/
esp_err_t essl_spi_write_reg(void *arg, uint8_t addr, uint8_t value, uint8_t *out_value, uint32_t wait_ms);
/**
* @brief Send a packet to Slave
*
* @param arg Context of the component. (Member ``arg`` from ``essl_handle_t``)
* @param data Address of the data to send
* @param size Size of the data to send.
* @param wait_ms Time to wait before timeout (reserved for future use, user should set this to 0).
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_STATE: ESSL SPI has not been initialized.
* - ESP_ERR_INVALID_ARG: The data address is not DMA capable
* - ESP_ERR_INVALID_SIZE: Master will send ``size`` bytes of data but Slave did not load enough RX buffer
*/
esp_err_t essl_spi_send_packet(void *arg, const void *data, size_t size, uint32_t wait_ms);
/**
* @brief Reset the counter in Master context
*
* @note Shall only be called if the slave has reset its counter. Else, Slave and Master would be desynchronized
*
* @param arg Context of the component. (Member ``arg`` from ``essl_handle_t``)
*/
void essl_spi_reset_cnt(void *arg);
////////////////////////////////////////////////////////////////////////////////
// Basic commands to communicate with the SPI Slave HD on ESP32-S2
////////////////////////////////////////////////////////////////////////////////
/**
* @brief Read the shared buffer from the slave in ISR way
*
* @note The slave's HW doesn't guarantee the data in one SPI transaction is consistent. It sends data in unit of byte.
* In other words, if the slave SW attempts to update the shared register when a rdbuf SPI transaction is in-flight,
* the data got by the master will be the combination of bytes of different writes of slave SW.
*
* @note ``out_data`` should be prepared in words and in the DRAM. The buffer may be written in words
* by the DMA. When a byte is written, the remaining bytes in the same word will also be
* overwritten, even the ``len`` is shorter than a word.
*
* @param spi SPI device handle representing the slave
* @param out_data Buffer for read data, strongly suggested to be in the DRAM and align to 4
* @param addr Address of the slave shared buffer
* @param len Length to read
* @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send.
* @param spi SPI device handle representing the slave
* @param[out] out_data Buffer for read data, strongly suggested to be in the DRAM and aligned to 4
* @param addr Address of the slave shared buffer
* @param len Length to read
* @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send.
* @return
* - ESP_OK: on success
* - or other return value from :cpp:func:`spi_device_transmit`.
@ -52,11 +171,11 @@ esp_err_t essl_spi_rdbuf(spi_device_handle_t spi, uint8_t *out_data, int addr, i
* by the DMA. When a byte is written, the remaining bytes in the same word will also be
* overwritten, even the ``len`` is shorter than a word.
*
* @param spi SPI device handle representing the slave
* @param out_data Buffer for read data, strongly suggested to be in the DRAM and align to 4
* @param addr Address of the slave shared buffer
* @param len Length to read
* @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send.
* @param spi SPI device handle representing the slave
* @param[out] out_data Buffer for read data, strongly suggested to be in the DRAM and aligned to 4
* @param addr Address of the slave shared buffer
* @param len Length to read
* @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send.
* @return
* - ESP_OK: on success
* - or other return value from :cpp:func:`spi_device_transmit`.
@ -71,7 +190,7 @@ esp_err_t essl_spi_rdbuf_polling(spi_device_handle_t spi, uint8_t *out_data, int
* overwritten, even the ``len`` is shorter than a word.
*
* @param spi SPI device handle representing the slave
* @param data Buffer for data to send, strongly suggested to be in the DRAM and align to 4
* @param data Buffer for data to send, strongly suggested to be in the DRAM
* @param addr Address of the slave shared buffer,
* @param len Length to write
* @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send.
@ -89,7 +208,7 @@ esp_err_t essl_spi_wrbuf(spi_device_handle_t spi, const uint8_t *data, int addr,
* overwritten, even the ``len`` is shorter than a word.
*
* @param spi SPI device handle representing the slave
* @param data Buffer for data to send, strongly suggested to be in the DRAM and align to 4
* @param data Buffer for data to send, strongly suggested to be in the DRAM
* @param addr Address of the slave shared buffer,
* @param len Length to write
* @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send.
@ -105,10 +224,10 @@ esp_err_t essl_spi_wrbuf_polling(spi_device_handle_t spi, const uint8_t *data, i
* @note This function combines several :cpp:func:`essl_spi_rddma_seg` and one
* :cpp:func:`essl_spi_rddma_done` at the end. Used when the slave is working in segment mode.
*
* @param spi SPI device handle representing the slave
* @param out_data Buffer to hold the received data, strongly suggested to be in the DRAM and align to 4
* @param len Total length of data to receive.
* @param seg_len Length of each segment, which is not larger than the maximum transaction length
* @param spi SPI device handle representing the slave
* @param[out] out_data Buffer to hold the received data, strongly suggested to be in the DRAM and aligned to 4
* @param len Total length of data to receive.
* @param seg_len Length of each segment, which is not larger than the maximum transaction length
* allowed for the spi device. Suggested to be multiples of 4. When set < 0, means send
* all data in one segment (the ``rddma_done`` will still be sent.)
* @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send.
@ -123,10 +242,10 @@ esp_err_t essl_spi_rddma(spi_device_handle_t spi, uint8_t *out_data, int len, in
*
* @note To read long buffer, call :cpp:func:`essl_spi_rddma` instead.
*
* @param spi SPI device handle representing the slave
* @param out_data Buffer to hold the received data, strongly suggested to be in the DRAM and align to 4
* @param seg_len Length of this segment
* @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send.
* @param spi SPI device handle representing the slave
* @param[out] out_data Buffer to hold the received data. strongly suggested to be in the DRAM and aligned to 4
* @param seg_len Length of this segment
* @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send.
* @return
* - ESP_OK: success
* - or other return value from :cpp:func:`spi_device_transmit`.
@ -155,7 +274,7 @@ esp_err_t essl_spi_rddma_done(spi_device_handle_t spi, uint32_t flags);
* :cpp:func:`essl_spi_wrdma_done` at the end. Used when the slave is working in segment mode.
*
* @param spi SPI device handle representing the slave
* @param data Buffer for data to send, strongly suggested to be in the DRAM and align to 4
* @param data Buffer for data to send, strongly suggested to be in the DRAM
* @param len Total length of data to send.
* @param seg_len Length of each segment, which is not larger than the maximum transaction length
* allowed for the spi device. Suggested to be multiples of 4. When set < 0, means send
@ -173,7 +292,7 @@ esp_err_t essl_spi_wrdma(spi_device_handle_t spi, const uint8_t *data, int len,
* @note To send long buffer, call :cpp:func:`essl_spi_wrdma` instead.
*
* @param spi SPI device handle representing the slave
* @param data Buffer for data to send, strongly suggested to be in the DRAM and align to 4
* @param data Buffer for data to send, strongly suggested to be in the DRAM
* @param seg_len Length of this segment
* @param flags `SPI_TRANS_*` flags to control the transaction mode of the transaction to send.
* @return