diff --git a/components/esp_common/src/esp_err_to_name.c b/components/esp_common/src/esp_err_to_name.c index 1a06c99059..24971ca490 100644 --- a/components/esp_common/src/esp_err_to_name.c +++ b/components/esp_common/src/esp_err_to_name.c @@ -35,6 +35,9 @@ #if __has_include("esp_ping.h") #include "esp_ping.h" #endif +#if __has_include("esp_serial_slave_link/essl.h") +#include "esp_serial_slave_link/essl.h" +#endif #if __has_include("esp_spi_flash.h") #include "esp_spi_flash.h" #endif @@ -105,6 +108,10 @@ static const esp_err_msg_t esp_err_msg_table[] = { # endif # ifdef ESP_ERR_INVALID_MAC ERR_TBL_IT(ESP_ERR_INVALID_MAC), /* 267 0x10b MAC address was invalid */ +# endif + // components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h +# ifdef ESP_ERR_NOT_FINISHED + ERR_TBL_IT(ESP_ERR_NOT_FINISHED), /* 513 0x201 */ # endif // components/nvs_flash/include/nvs.h # ifdef ESP_ERR_NVS_BASE diff --git a/components/esp_serial_slave_link/CMakeLists.txt b/components/esp_serial_slave_link/CMakeLists.txt new file mode 100644 index 0000000000..32ccb89077 --- /dev/null +++ b/components/esp_serial_slave_link/CMakeLists.txt @@ -0,0 +1,9 @@ +idf_component_register(SRCS "essl.c" + "essl_sdio.c" + INCLUDE_DIRS "include" + REQUIRES "sdmmc" + "driver" + PRIV_INCLUDE_DIRS "." + "include/esp_serial_slave_link" +) + diff --git a/examples/peripherals/sdio/host/components/esp_slave/component.mk b/components/esp_serial_slave_link/component.mk similarity index 50% rename from examples/peripherals/sdio/host/components/esp_slave/component.mk rename to components/esp_serial_slave_link/component.mk index 5b1ef9e204..a006c287d3 100644 --- a/examples/peripherals/sdio/host/components/esp_slave/component.mk +++ b/components/esp_serial_slave_link/component.mk @@ -3,4 +3,5 @@ # COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_PRIV_INCLUDEDIRS := . include/esp_serial_slave_link diff --git a/components/esp_serial_slave_link/essl.c b/components/esp_serial_slave_link/essl.c new file mode 100644 index 0000000000..020b65270c --- /dev/null +++ b/components/esp_serial_slave_link/essl.c @@ -0,0 +1,244 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "essl.h" +#include "essl_internal.h" +#include "esp_log.h" +#include "freertos/task.h" + +#define TIME_EXPIRED_SINCE_CORE(start, end, timeout, max) (bool)((end)>=(start)? \ + ((end)-(start)>(timeout)) :\ + ((max)-(timeout)>(start)-(end))) + +#define TIME_EXPIRED_SINCE(start, end, timeout) TIME_EXPIRED_SINCE_CORE(start, end, timeout, UINT32_MAX) + +#define MINUS_UNTIL_ZERO(a, b) ( ((a) > (b)) ? ((a)-(b)): 0) + +#define TIME_REMAIN_CORE(start, end, timeout, max) ((end)>=(start)?\ + MINUS_UNTIL_ZERO(timeout, (end)-(start)):\ + MINUS_UNTIL_ZERO((start)-(end), (max)-(timeout))) + +#define TIME_REMAIN(start, end, timeout) TIME_REMAIN_CORE(start, end, timeout, UINT32_MAX) + + +#define ESSL_MIN(a, b) ((a) < (b) ? (a) : (b)) + +__attribute__((unused)) static const char TAG[] = "esp_serial_slave_link"; + +#define _CHECK_EXECUTE_CMD(DEV, CMD, STR, ...) do{ \ + if ((DEV) == NULL) { \ + return ESP_ERR_INVALID_ARG; \ + } \ + if ((DEV)->CMD) { \ + return (DEV)->CMD((DEV)->args,##__VA_ARGS__); \ + } else { \ + ESP_LOGE(TAG, STR); \ + return ESP_ERR_NOT_SUPPORTED; \ + } } while(0) + +#define CHECK_EXECUTE_CMD(DEV, CMD, ...) _CHECK_EXECUTE_CMD(DEV, CMD, #CMD" not supported for the current device.",##__VA_ARGS__) + + +esp_err_t essl_init(essl_handle_t handle, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, init, wait_ms); +} + +esp_err_t essl_wait_for_ready(essl_handle_t handle, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, wait_for_ready, 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) { + return ESP_ERR_INVALID_ARG; + } + if (handle->send_packet == NULL) { + return ESP_ERR_NOT_SUPPORTED; + } + esp_err_t err; + const uint32_t timeout_ticks = pdMS_TO_TICKS(wait_ms); + + uint32_t pre = xTaskGetTickCount(); + uint32_t now; + uint32_t remain_wait_ms = 0; + + do { + now = xTaskGetTickCount(); + remain_wait_ms = pdTICKS_TO_MS(TIME_REMAIN(pre, now, timeout_ticks)); + err = handle->send_packet(handle->args, start, length, remain_wait_ms); + if (err == ESP_OK) { + break; + } else if (err != ESP_ERR_NOT_FOUND) { + return err; + } // else ESP_ERR_NOT_FOUND + //the slave has no enough memory, retry + } while (remain_wait_ms > 0); + return ESP_OK; +} + +esp_err_t essl_get_packet(essl_handle_t handle, void *out_data, size_t size, size_t *out_length, uint32_t wait_ms) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (out_data == NULL || size == 0 || out_length == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (handle->get_packet == NULL || handle->update_rx_data_size == NULL || handle->get_rx_data_size == NULL) { + return ESP_ERR_NOT_SUPPORTED; + } + esp_err_t err; + const uint32_t timeout_ticks = pdMS_TO_TICKS(wait_ms); + + uint32_t pre = xTaskGetTickCount(); + uint32_t now = 3; + uint32_t wait_remain_ms = 0; + int data_available = handle->get_rx_data_size(handle->args); + + // if there is already enough data to read, skip the length update. + if (data_available < size) { + //loop until timeout, or there is at least one byte + do { + now = xTaskGetTickCount(); + wait_remain_ms = pdTICKS_TO_MS(TIME_REMAIN(pre, now, timeout_ticks)); + err = handle->update_rx_data_size(handle->args, wait_remain_ms); + if (err != ESP_OK) { + return err; + } + data_available = handle->get_rx_data_size(handle->args); + if (data_available > 0) { + break; + } + } while (wait_remain_ms > 0); + } + + if (data_available == 0) { + //the slave has no data to send + return ESP_ERR_NOT_FOUND; + } + + int len = ESSL_MIN(data_available, size); + now = xTaskGetTickCount(); + wait_remain_ms = pdTICKS_TO_MS(TIME_REMAIN(pre, now, timeout_ticks)); + err = handle->get_packet(handle->args, out_data, len, wait_remain_ms); + if (err != ESP_OK) { + return err; + } + + *out_length = len; + if (len < data_available) { + return ESP_ERR_NOT_FINISHED; + } + return ESP_OK; +} + +esp_err_t essl_get_tx_buffer_num(essl_handle_t handle, uint32_t *out_tx_num, uint32_t wait_ms) +{ + if (handle == NULL || out_tx_num == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (handle->update_tx_buffer_num == NULL|| handle->get_tx_buffer_num == NULL) { + return ESP_ERR_NOT_SUPPORTED; + } + + esp_err_t err = handle->update_tx_buffer_num(handle->args, wait_ms); + if (err != ESP_OK) { + return err; + } + + *out_tx_num = handle->get_tx_buffer_num(handle->args); + return ESP_OK; +} + +esp_err_t essl_get_rx_data_size(essl_handle_t handle, uint32_t *out_rx_size, uint32_t wait_ms) +{ + if (handle == NULL || out_rx_size == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (handle->update_rx_data_size == NULL|| handle->get_rx_data_size == NULL) { + return ESP_ERR_NOT_SUPPORTED; + } + + esp_err_t err = handle->update_rx_data_size(handle->args, wait_ms); + if (err != ESP_OK) { + return err; + } + + *out_rx_size = handle->get_rx_data_size(handle->args); + return ESP_OK; +} + +esp_err_t essl_write_reg(essl_handle_t handle, uint8_t addr, uint8_t value, uint8_t *value_o, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, write_reg, addr, value, value_o, wait_ms); +} + +esp_err_t essl_read_reg(essl_handle_t handle, uint8_t add, uint8_t *value_o, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, read_reg, add, value_o, wait_ms); +} + +esp_err_t essl_wait_int(essl_handle_t handle, TickType_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, wait_int, wait_ms); +} + +esp_err_t essl_reset_cnt(essl_handle_t handle) +{ + if (handle == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (handle->reset_cnt == NULL) { + return ESP_ERR_NOT_SUPPORTED; + } + handle->reset_cnt(handle->args); + return ESP_OK; +} + +esp_err_t essl_clear_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, clear_intr, intr_mask, wait_ms); +} + +esp_err_t essl_get_intr(essl_handle_t handle, uint32_t *intr_raw, uint32_t *intr_st, uint32_t wait_ms) +{ + if (intr_raw == NULL && intr_st == NULL) { + return ESP_ERR_INVALID_ARG; + } + CHECK_EXECUTE_CMD(handle, get_intr, intr_raw, intr_st, wait_ms); +} + +esp_err_t essl_set_intr_ena(essl_handle_t handle, uint32_t ena_mask, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, set_intr_ena, ena_mask, wait_ms); +} + +esp_err_t essl_get_intr_ena(essl_handle_t handle, uint32_t *ena_mask_o, uint32_t wait_ms) +{ + if (ena_mask_o == NULL) { + return ESP_ERR_INVALID_ARG; + } + CHECK_EXECUTE_CMD(handle, get_intr_ena, ena_mask_o, wait_ms); +} + +esp_err_t essl_send_slave_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wait_ms) +{ + CHECK_EXECUTE_CMD(handle, send_slave_intr, intr_mask, wait_ms); +} + diff --git a/components/esp_serial_slave_link/essl_internal.h b/components/esp_serial_slave_link/essl_internal.h new file mode 100644 index 0000000000..0070e53212 --- /dev/null +++ b/components/esp_serial_slave_link/essl_internal.h @@ -0,0 +1,45 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#include + +/** Context used by the ``esp_serial_slave_link`` component. + */ +struct essl_dev_t { + void* args; + + esp_err_t (*init)(void* ctx, uint32_t wait_ms); + + esp_err_t (*wait_for_ready)(void *ctx, uint32_t wait_ms); + esp_err_t (*update_tx_buffer_num)(void *ctx, uint32_t wait_ms); + esp_err_t (*update_rx_data_size)(void *ctx, uint32_t wait_ms); + esp_err_t (*send_packet)(void *ctx, const void* start, size_t length, uint32_t wait_ms); + esp_err_t (*get_packet)(void *ctx, void* out_data, size_t size, uint32_t wait_ms); + esp_err_t (*write_reg)(void *ctx, uint8_t addr, uint8_t value, uint8_t* value_o, uint32_t wait_ms); + esp_err_t (*read_reg)(void *ctx, uint8_t add, uint8_t *value_o, uint32_t wait_ms); + esp_err_t (*wait_int)(void *ctx, uint32_t wait_ms); + esp_err_t (*clear_intr)(void* ctx, uint32_t intr_mask, uint32_t wait_ms); + esp_err_t (*get_intr)(void* ctx, uint32_t* intr_raw, uint32_t *intr_st, uint32_t wait_ms); + esp_err_t (*set_intr_ena)(void* ctx, uint32_t ena_mask, uint32_t wait_ms); + esp_err_t (*get_intr_ena)(void* ctx, uint32_t* ena_mask_o, uint32_t wait_ms); + esp_err_t (*send_slave_intr)(void* ctx, uint32_t intr_mask, uint32_t wait_ms); + + uint32_t (*get_tx_buffer_num)(void *ctx); + uint32_t (*get_rx_data_size)(void *ctx); + void (*reset_cnt)(void *ctx); +}; + +typedef struct essl_dev_t essl_dev_t; diff --git a/components/esp_serial_slave_link/essl_sdio.c b/components/esp_serial_slave_link/essl_sdio.c new file mode 100644 index 0000000000..b1e69d352b --- /dev/null +++ b/components/esp_serial_slave_link/essl_sdio.c @@ -0,0 +1,455 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "essl_sdio.h" +#include "esp_log.h" +#include "soc/host_reg.h" +#include "freertos/task.h" +#include "essl_internal.h" + +static const char TAG[] = "essl_sdio"; + +#define ESSL_CMD53_END_ADDR 0x1f800 + +#define TX_BUFFER_MAX 0x1000 +#define TX_BUFFER_MASK 0xFFF +#define RX_BYTE_MAX 0x100000 +#define RX_BYTE_MASK 0xFFFFF + +#define FUNC1_EN_MASK (BIT(1)) + +/** + * Initialize ``void`` over SDIO by this macro. + */ +#define ESSL_SDIO_DEFAULT_CONTEXT() (essl_dev_t){\ + .init = essl_sdio_init, \ + .wait_for_ready = essl_sdio_wait_for_ready, \ + .get_tx_buffer_num = essl_sdio_get_tx_buffer_num,\ + .update_tx_buffer_num = essl_sdio_update_tx_buffer_num,\ + .get_rx_data_size = essl_sdio_get_rx_data_size,\ + .update_rx_data_size = essl_sdio_update_rx_data_size,\ + .send_packet = essl_sdio_send_packet,\ + .get_packet = essl_sdio_get_packet,\ + .write_reg = essl_sdio_write_reg,\ + .read_reg = essl_sdio_read_reg,\ + .wait_int = essl_sdio_wait_int,\ + .send_slave_intr = essl_sdio_send_slave_intr, \ + .get_intr = essl_sdio_get_intr, \ + .clear_intr = essl_sdio_clear_intr, \ + .set_intr_ena = essl_sdio_set_intr_ena, \ + .reset_cnt = essl_sdio_reset_cnt, \ + } + +typedef struct{ + //common part + uint16_t buffer_size; + ///< All data that do not fully fill a buffer is still counted as one buffer. E.g. 10 bytes data costs 2 buffers if the size is 8 bytes per buffer. + ///< Buffer size of the slave pre-defined between host and slave before communication. + size_t tx_sent_buffers; ///< Counter holding the amount of buffers already sent to ESP32 slave. Should be set to 0 when initialization. + size_t tx_sent_buffers_latest; ///< The latest reading (from the slave) of counter holding the amount of buffers loaded. Should be set to 0 when initialization. + size_t rx_got_bytes; ///< Counter holding the amount of bytes already received from ESP32 slave. Should be set to 0 when initialization. + size_t rx_got_bytes_latest; ///< The latest reading (from the slave) of counter holding the amount of bytes to send. Should be set to 0 when initialization. + + sdmmc_card_t* card; ///< Initialized sdmmc_cmd card + uint16_t block_size; + ///< If this is too large, it takes time to send stuff bits; while if too small, intervals between blocks cost much. + ///< Should be set according to length of data, and larger than ``TRANS_LEN_MAX/511``. + ///< Block size of the SDIO function 1. After the initialization this will hold the value the slave really do. Valid value is 1-2048. +} essl_sdio_context_t; + + +esp_err_t essl_sdio_update_tx_buffer_num(void *arg, uint32_t wait_ms); +esp_err_t essl_sdio_update_rx_data_size(void *arg, uint32_t wait_ms); + +static inline esp_err_t essl_sdio_write_byte(sdmmc_card_t *card, uint32_t addr, uint8_t val, uint8_t *val_o) +{ + return sdmmc_io_write_byte(card, 1, addr&0x3FF, val, val_o); +} + +static inline esp_err_t essl_sdio_write_bytes(sdmmc_card_t *card, uint32_t addr, uint8_t *val, int len) +{ + return sdmmc_io_write_bytes(card, 1, addr&0x3FF, val, len); +} + +static inline esp_err_t essl_sdio_read_byte(sdmmc_card_t *card, uint32_t addr, uint8_t *val_o) +{ + return sdmmc_io_read_byte(card, 1, addr&0x3FF, val_o); +} + +static inline esp_err_t essl_sdio_read_bytes(sdmmc_card_t *card, uint32_t addr, uint8_t *val_o, int len) +{ + return sdmmc_io_read_bytes(card, 1, addr&0x3FF, val_o, len); +} + +esp_err_t essl_sdio_init_dev(essl_handle_t *out_handle, const essl_sdio_config_t *config) +{ + esp_err_t ret = ESP_OK; + essl_sdio_context_t* arg = NULL; + essl_dev_t* dev = NULL; + arg = (essl_sdio_context_t*)heap_caps_malloc(sizeof(essl_sdio_context_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + dev = (essl_dev_t*)heap_caps_malloc(sizeof(essl_dev_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + + if (arg == NULL || dev == NULL) { + ret = ESP_ERR_NO_MEM; + goto cleanup; + } + + *dev = ESSL_SDIO_DEFAULT_CONTEXT(); + dev->args = arg; + + *arg = (essl_sdio_context_t) { + .card = config->card, + .block_size = 0x200, + .buffer_size = config->recv_buffer_size, + .tx_sent_buffers = 0, + .rx_got_bytes = 0, + }; + + *out_handle = dev; + return ESP_OK; + +cleanup: + free(arg); + free(dev); + return ret; +} + +esp_err_t essl_sdio_deinit_dev(essl_handle_t handle) +{ + if (handle) free (handle->args); + free(handle); + return ESP_OK; +} + +esp_err_t essl_sdio_init(void *arg, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + esp_err_t err; + uint8_t ioe; + sdmmc_card_t* card = ctx->card; + + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_ENABLE, &ioe); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IOE: 0x%02x", ioe); + + uint8_t ior = 0; + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IOR: 0x%02x", ior); + + // enable function 1 + ioe |= FUNC1_EN_MASK; + err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_FN_ENABLE, ioe, &ioe); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IOE: 0x%02x", ioe); + + // wait for the card to become ready + while ((ior & FUNC1_EN_MASK) == 0) { + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IOR: 0x%02x", ior); + } + + // get interrupt status + uint8_t ie; + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_INT_ENABLE, &ie); + if (err != ESP_OK) return err; + ESP_LOGD(TAG,"IE: 0x%02x", ie); + + // enable interrupts for function 1&2 and master enable + ie |= BIT(0) | FUNC1_EN_MASK; + err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_INT_ENABLE, ie, &ie); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IE: 0x%02x", ie); + + // get bus width register + uint8_t bus_width; + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BUS_WIDTH, &bus_width); + if (err != ESP_OK) return err; + ESP_LOGD(TAG,"BUS_WIDTH: 0x%02x", bus_width); + + // enable continuous SPI interrupts + bus_width |= CCCR_BUS_WIDTH_ECSI; + err = sdmmc_io_write_byte(card, 0, SD_IO_CCCR_BUS_WIDTH, bus_width, &bus_width); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "BUS_WIDTH: 0x%02x", bus_width); + + uint16_t bs = 512; + const uint8_t* bs_u8 = (const uint8_t*) &bs; + uint16_t bs_read = 0; + uint8_t* bs_read_u8 = (uint8_t*) &bs_read; + // Set block sizes for functions 0 to 512 bytes + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); + ESP_LOGD(TAG, "Function 0 BS: %04x", (int) bs_read); + + ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, SD_IO_CCCR_BLKSIZEL, bs_u8[0], NULL)); + ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, SD_IO_CCCR_BLKSIZEH, bs_u8[1], NULL)); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); + ESP_LOGD(TAG, "Function 0 BS: %04x", (int) bs_read); + + // Set block sizes for functions 1 to given value (default value = 512). + if (ctx->block_size > 0 || ctx->block_size <= 2048) { + bs = ctx->block_size; + } else { + bs = 512; + } + size_t offset = SD_IO_FBR_START * 1; + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); + ESP_LOGD(TAG, "Function 1 BS: %04x", (int) bs_read); + + ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, bs_u8[0], NULL)); + ESP_ERROR_CHECK(sdmmc_io_write_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, bs_u8[1], NULL)); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEL, &bs_read_u8[0])); + ESP_ERROR_CHECK(sdmmc_io_read_byte(card, 0, offset + SD_IO_CCCR_BLKSIZEH, &bs_read_u8[1])); + ESP_LOGD(TAG, "Function 1 BS: %04x", (int) bs_read); + + if (bs_read != ctx->block_size) { + ESP_LOGW(TAG, "Function1 block size %d different than set value %d", bs_read, ctx->block_size); + ctx->block_size = bs_read; + } + return ESP_OK; +} + +esp_err_t essl_sdio_wait_for_ready(void *arg, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "wait_for_ioready"); + esp_err_t err; + sdmmc_card_t *card = ((essl_sdio_context_t*)arg)->card; + // wait for the card to become ready + uint8_t ior = 0; + while ((ior & FUNC1_EN_MASK) == 0) { + err = sdmmc_io_read_byte(card, 0, SD_IO_CCCR_FN_READY, &ior); + if (err != ESP_OK) return err; + ESP_LOGD(TAG, "IOR: 0x%02x", ior); + } + return ESP_OK; +} + +esp_err_t essl_sdio_send_packet(void *arg, const void *start, size_t length, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + uint16_t buffer_size = ctx->buffer_size; + int buffer_used = (length + buffer_size - 1)/buffer_size; + esp_err_t err; + + if (essl_sdio_get_tx_buffer_num(arg) < buffer_used) { + //slave has no enough buffer, try update for once + esp_err_t err = essl_sdio_update_tx_buffer_num(arg, wait_ms); + if (err != ESP_OK) { + return err; + } + if (essl_sdio_get_tx_buffer_num(arg) < buffer_used) { + ESP_LOGV(TAG, "buffer is not enough: %d, %d required.", ctx->tx_sent_buffers_latest, ctx->tx_sent_buffers + buffer_used); + return ESP_ERR_NOT_FOUND; + } + } + + ESP_LOGV(TAG, "send_packet: len: %d", length); + + uint8_t *start_ptr = (uint8_t*)start; + uint32_t len_remain = length; + do { + const int block_size = 512; + /* Though the driver supports to split packet of unaligned size into + * length of 4x and 1~3, we still send aligned size of data to get + * higher effeciency. The length is determined by the SDIO address, and + * the remainning will be discard by the slave hardware. + */ + int block_n = len_remain/block_size; + int len_to_send; + if (block_n) { + len_to_send = block_n * block_size; + err = sdmmc_io_write_blocks(ctx->card, 1, ESSL_CMD53_END_ADDR - len_remain, start_ptr, len_to_send); + } else { + len_to_send = len_remain; + err = sdmmc_io_write_bytes(ctx->card, 1, ESSL_CMD53_END_ADDR - len_remain, start_ptr, (len_to_send + 3) & (~3)); + } + if (err != ESP_OK) return err; + start_ptr += len_to_send; + len_remain -= len_to_send; + } while (len_remain); + + ctx->tx_sent_buffers += buffer_used; + return ESP_OK; +} + +esp_err_t essl_sdio_get_packet(void *arg, void *out_data, size_t size, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + esp_err_t err; + + ESP_LOGV(TAG, "get_packet: read size=%d", size); + if (essl_sdio_get_rx_data_size(arg) < size) { + err = essl_sdio_update_rx_data_size(arg, wait_ms); + if (err != ESP_OK) { + return err; + } + if (essl_sdio_get_rx_data_size(arg) < size) { + return ESP_ERR_NOT_FOUND; + } + } + + uint8_t *start = out_data; + uint32_t len_remain = size; + do { + const int block_size = 512; //currently our driver don't support block size other than 512 + int len_to_send; + + int block_n = len_remain/block_size; + if (block_n != 0) { + len_to_send = block_n * block_size; + err = sdmmc_io_read_blocks(ctx->card, 1, ESSL_CMD53_END_ADDR - len_remain, start, len_to_send); + } else { + len_to_send = len_remain; + /* though the driver supports to split packet of unaligned size into length + * of 4x and 1~3, we still get aligned size of data to get higher + * effeciency. The length is determined by the SDIO address, and the + * remainning will be ignored by the slave hardware. + */ + err = sdmmc_io_read_bytes(ctx->card, 1, ESSL_CMD53_END_ADDR - len_remain, start, (len_to_send + 3) & (~3)); + } + if (err != ESP_OK) return err; + start += len_to_send; + len_remain -= len_to_send; + ctx->rx_got_bytes += len_to_send; + } while(len_remain!=0); + + return err; +} + +uint32_t essl_sdio_get_tx_buffer_num(void *arg) +{ + essl_sdio_context_t* ctx = arg; + ESP_LOGV(TAG, "tx latest: %d, sent: %d", ctx->tx_sent_buffers_latest, ctx->tx_sent_buffers); + return (ctx->tx_sent_buffers_latest + TX_BUFFER_MAX - ctx->tx_sent_buffers)%TX_BUFFER_MAX; +} + +esp_err_t essl_sdio_update_tx_buffer_num(void *arg, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + uint32_t len; + esp_err_t err; + + err = essl_sdio_read_bytes(ctx->card, HOST_SLC0HOST_TOKEN_RDATA_REG, (uint8_t *) &len, 4); + if (err != ESP_OK) return err; + len = (len>>16)&TX_BUFFER_MASK; + ctx->tx_sent_buffers_latest = len; + ESP_LOGV(TAG, "update_tx_buffer_num: %d", len); + return ESP_OK; +} + +uint32_t essl_sdio_get_rx_data_size(void *arg) +{ + essl_sdio_context_t* ctx = arg; + ESP_LOGV(TAG, "rx latest: %d, read: %d", ctx->rx_got_bytes_latest, ctx->rx_got_bytes); + return (ctx->rx_got_bytes_latest + RX_BYTE_MAX - ctx->rx_got_bytes)%RX_BYTE_MAX; +} + +esp_err_t essl_sdio_update_rx_data_size(void *arg, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + uint32_t len; + esp_err_t err; + + ESP_LOGV(TAG, "get_rx_data_size: got_bytes: %d", ctx->rx_got_bytes); + err = essl_sdio_read_bytes(ctx->card, HOST_SLCHOST_PKT_LEN_REG, (uint8_t *) &len, 4); + if (err != ESP_OK) return err; + len &= RX_BYTE_MASK; + ctx->rx_got_bytes_latest = len; + return ESP_OK; +} + + +esp_err_t essl_sdio_write_reg(void *arg, uint8_t addr, uint8_t value, uint8_t *value_o, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "write_reg: %08X", value); + // addrress over range + if (addr >= 60) return ESP_ERR_INVALID_ARG; + //W7 is reserved for interrupts + if (addr >= 28) addr += 4; + return essl_sdio_write_byte(((essl_sdio_context_t*)arg)->card, HOST_SLCHOST_CONF_W_REG(addr), value, value_o); +} + +esp_err_t essl_sdio_read_reg(void *arg, uint8_t add, uint8_t *value_o, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "read_reg"); + // address over range + if (add >= 60) return ESP_ERR_INVALID_ARG; + //W7 is reserved for interrupts + if (add >= 28) add += 4; + esp_err_t ret = essl_sdio_read_byte(((essl_sdio_context_t*)arg)->card, HOST_SLCHOST_CONF_W_REG(add), value_o); + ESP_LOGV(TAG, "reg: %08X", *value_o); + return ret; +} + +esp_err_t essl_sdio_clear_intr(void *arg, uint32_t intr_mask, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "clear_intr: %08X", intr_mask); + return essl_sdio_write_bytes(((essl_sdio_context_t *) arg)->card, HOST_SLC0HOST_INT_CLR_REG, (uint8_t *) &intr_mask, 4); +} + +esp_err_t essl_sdio_get_intr(void *arg, uint32_t *intr_raw, uint32_t *intr_st, uint32_t wait_ms) +{ + essl_sdio_context_t* ctx = arg; + esp_err_t r; + ESP_LOGV(TAG, "get_intr"); + if (intr_raw == NULL && intr_st == NULL) return ESP_ERR_INVALID_ARG; + + if (intr_raw != NULL) { + r= essl_sdio_read_bytes(ctx->card, HOST_SLC0HOST_INT_RAW_REG, (uint8_t *) intr_raw, 4); + if (r != ESP_OK) return r; + } + if (intr_st != NULL) { + r = essl_sdio_read_bytes(ctx->card, HOST_SLC0HOST_INT_ST_REG, (uint8_t *) intr_st, 4); + if (r != ESP_OK) return r; + } + return ESP_OK; +} + +esp_err_t essl_sdio_set_intr_ena(void *arg, uint32_t ena_mask, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "set_intr_ena: %08X", ena_mask); + return essl_sdio_write_bytes(((essl_sdio_context_t*)arg)->card, HOST_SLC0HOST_FUNC1_INT_ENA_REG, + (uint8_t *) &ena_mask, 4); +} + +esp_err_t essl_sdio_get_intr_ena(void *arg, uint32_t *ena_mask_o, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "get_intr_ena"); + esp_err_t ret = essl_sdio_read_bytes(((essl_sdio_context_t*)arg)->card, HOST_SLC0HOST_FUNC1_INT_ENA_REG, + (uint8_t *) ena_mask_o, 4); + ESP_LOGV(TAG, "ena: %08X", *ena_mask_o); + return ret; +} + +esp_err_t essl_sdio_send_slave_intr(void *arg, uint32_t intr_mask, uint32_t wait_ms) +{ + ESP_LOGV(TAG, "send_slave_intr: %02x", intr_mask); + return essl_sdio_write_byte(((essl_sdio_context_t*)arg)->card, HOST_SLCHOST_CONF_W7_REG + 0, intr_mask, NULL); +} + +esp_err_t essl_sdio_wait_int(void *arg, uint32_t wait_ms) +{ + return sdmmc_io_wait_int(((essl_sdio_context_t*)arg)->card, wait_ms); +} + +void essl_sdio_reset_cnt(void *arg) +{ + essl_sdio_context_t* ctx = arg; + ctx->rx_got_bytes = 0; + ctx->tx_sent_buffers = 0; +} \ No newline at end of file diff --git a/components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h b/components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h new file mode 100644 index 0000000000..100e86a163 --- /dev/null +++ b/components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h @@ -0,0 +1,214 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "sdmmc_cmd.h" +#include "driver/spi_master.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + + +#define ESP_ERR_NOT_FINISHED 0x201 ///< There is still remaining data. + +struct essl_dev_t; +/// Handle of an ESSL device +typedef struct essl_dev_t* essl_handle_t; + +/** + * @brief Initialize the slave. + * + * @param handle Handle of a ``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`. + */ +esp_err_t essl_init(essl_handle_t handle, uint32_t wait_ms); + +/** Wait for interrupt of a ESP32 slave device. + * + * @param handle Handle of a ``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 + * - 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 a ``essl`` device. + * @param out_tx_num Output of buffer num that host can send data to ESP32 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_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 ESP32 slave preparing to send to host. + * + * @param handle Handle of a ``essl`` device. + * @param out_rx_size Output of data size to read from 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_err_t essl_get_rx_data_size(essl_handle_t handle, uint32_t *out_rx_size, uint32_t wait_ms); + + +/** 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 a ``essl`` device. + */ +esp_err_t essl_reset_cnt(essl_handle_t handle); + +/** Send a packet to the ESP32 slave. The slave receive the packet into buffers whose size is ``buffer_size`` (configured during initialization). + * + * @param handle Handle of a ``essl`` device. + * @param start Start address of the packet to send + * @param length Length of data to send, if the packet is over-size, the it will be divided into blocks and hold into different buffers automatically. + * @param wait_ms Millisecond to wait before timeout, will not wait at all if set to 0-9. + * + * @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_t essl_send_packet(essl_handle_t handle, const void *start, size_t length, uint32_t wait_ms); + +/** Get a packet from ESP32 slave. + * + * @param handle Handle of a ``essl`` device. + * @param[out] out_data Data output address + * @param size The size of the output buffer, if the buffer is smaller than the size of data to receive from slave, the driver returns ``ESP_ERR_NOT_FINISHED`` + * @param[out] out_length Output of length the data actually received from slave. + * @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_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 ESP32 slave. + * + * @param handle Handle of a ``essl`` device. + * @param addr Address of register to write. Valid address: 0-59. + * @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. + * + * @note sdio 28-31 are reserved, the lower API helps to skip. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Address not valid. + * - One of the error codes from SDMMC 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 ESP32 slave. + * + * @param handle Handle of a ``essl`` device. + * @param add Address of register to read. Valid address: 0-27, 32-63 (28-31 reserved, return interrupt bits on read). + * @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 + */ +esp_err_t essl_read_reg(essl_handle_t handle, uint8_t add, uint8_t *value_o, uint32_t wait_ms); + +/** wait for an interrupt of the slave + * + * @param handle Handle of a ``essl`` device. + * @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_err_t essl_wait_int(essl_handle_t handle, uint32_t wait_ms); + +/** Clear interrupt bits of ESP32 slave. All the bits set in the mask will be cleared, while other bits will stay the same. + * + * @param handle Handle of a ``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_err_t essl_clear_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wait_ms); + +/** Get interrupt bits of ESP32 slave. + * + * @param handle Handle of a ``essl`` device. + * @param intr_raw Output of the raw interrupt bits. Set to NULL if only masked bits are read. + * @param intr_st Output of the masked interrupt bits. set to NULL if only raw bits are read. + * @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_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 ESP32 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 a ``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_err_t essl_set_intr_ena(essl_handle_t handle, uint32_t ena_mask, uint32_t wait_ms); + +/** Get interrupt enable bits of ESP32 slave. + * + * @param handle Handle of a ``essl`` device. + * @param ena_mask_o Output of interrupt bit enable mask. + * @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_err_t essl_get_intr_ena(essl_handle_t handle, uint32_t *ena_mask_o, uint32_t wait_ms); + +/** Send interrupts to slave. Each bit of the interrupt will be triggered. + * + * @param handle Handle of a ``essl`` device. + * @param intr_mask Mask of interrupt bits to send to 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_err_t essl_send_slave_intr(essl_handle_t handle, uint32_t intr_mask, uint32_t wait_ms); + + diff --git a/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h b/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h new file mode 100644 index 0000000000..93fa2823c2 --- /dev/null +++ b/components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h @@ -0,0 +1,248 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ESP SDIO slave link used by the ESP host to communicate with ESP32 SDIO slave. + +#pragma once + +#include "esp_err.h" +#include "esp_serial_slave_link/essl.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/sdmmc_defs.h" + +/// Configuration for the essl SDIO device +typedef struct { + sdmmc_card_t *card; ///< The initialized sdmmc card pointer of the slave. + int recv_buffer_size; ///< The pre-negotiated recv buffer size used by both the host and the slave. +} essl_sdio_config_t; + +/** + * @brief Initialize the ESSL SDIO device and get its handle. + * + * @param out_handle Output of the handle. + * @param config Configuration for the ESSL SDIO device. + * @return + * - ESP_OK: on success + * - ESP_ERR_NO_MEM: memory exhausted. + */ +esp_err_t essl_sdio_init_dev(essl_handle_t *out_handle, const essl_sdio_config_t *config); + +/** + * @brief Deinitialize and free the space used by the ESSL SDIO device. + * + * @param handle Handle of the ESSL SDIO device to deinit. + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_ARG: wrong handle passed + */ +esp_err_t essl_sdio_deinit_dev(essl_handle_t handle); + +//Please call `essl_` functions witout `sdio` instead of calling these functions directly. +/** @cond */ +/** + * SDIO Initialize process of a ESP32 slave device. + * + * @param arg Context of the ``essl`` component. Send to other functions later. + * @param wait_ms Time to wait before operation is done, in ms. + * + * @return + * - ESP_OK if success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_init(void *arg, uint32_t wait_ms); + +/** + * Wait for interrupt of a ESP32 slave device. + * + * @param arg Context of the ``essl`` component. + * @param wait_ms Time to wait before operation is done, in ms. + * + * @return + * - ESP_OK if success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_wait_for_ready(void *arg, uint32_t wait_ms); + +/** + * Get buffer num for the host to send data to the slave. The buffers are size of ``buffer_size``. + * + * @param arg Context of the component. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +uint32_t essl_sdio_get_tx_buffer_num(void *arg); + +/** Get amount of data the ESP32 slave preparing to send to host. + * + * @param arg Context of the component. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +uint32_t essl_sdio_get_rx_data_size(void *arg); + +/** + * Send a packet to the ESP32 slave. The slave receive the packet into buffers whose size is ``buffer_size`` in the arg. + * + * @param arg Context of the component. + * @param start Start address of the packet to send + * @param length Length of data to send, if the packet is over-size, the it will be divided into blocks and hold into different buffers automatically. + * @param wait_ms Time to wait before timeout, in ms. + * + * @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_t essl_sdio_send_packet(void *arg, const void *start, size_t length, uint32_t wait_ms); + +/** + * Get a packet from ESP32 slave. + * + * @param arg Context of the component. + * @param[out] out_data Data output address + * @param size The size of the output buffer, if the buffer is smaller than the size of data to receive from slave, the driver returns ``ESP_ERR_NOT_FINISHED`` + * @param wait_ms Time to wait before timeout, in ms. + * + * @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_err_t essl_sdio_get_packet(void *arg, void *out_data, size_t size, uint32_t wait_ms); + +/** + * Wait for the interrupt from the SDIO slave. + * + * @param arg Context of the component. + * @param wait_ms Time to wait before timeout, in ms. + * @return + * - ESP_ERR_NOT_SUPPORTED: if the interrupt line is not initialized properly. + * - ESP_OK: if interrupt happened + * - ESP_ERR_TIMEOUT: if timeout before interrupt happened. + * - or other values returned from the `io_int_wait` member of the `card->host` structure. + */ +esp_err_t essl_sdio_wait_int(void *arg, uint32_t wait_ms); + +/** + * Clear interrupt bits of ESP32 slave. All the bits set in the mask will be cleared, while other bits will stay the same. + * + * @param arg Context of the component. + * @param intr_mask Mask of interrupt bits to clear. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_clear_intr(void *arg, uint32_t intr_mask, uint32_t wait_ms); + +/** + * Get interrupt bits of ESP32 slave. + * + * @param arg Context of the component. + * @param intr_raw Output of the raw interrupt bits. Set to NULL if only masked bits are read. + * @param intr_st Output of the masked interrupt bits. set to NULL if only raw bits are read. + * @param wait_ms Time to wait before timeout, in ms. + * + * @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_err_t essl_sdio_get_intr(void *arg, uint32_t *intr_raw, uint32_t *intr_st, uint32_t wait_ms); + +/** + * Set interrupt enable bits of ESP32 slave. The slave only sends interrupt on the line when there is a bit both the raw status and the enable are set. + * + * @param arg Context of the component. + * @param ena_mask Mask of the interrupt bits to enable. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_set_intr_ena(void *arg, uint32_t ena_mask, uint32_t wait_ms); + +/** + * Get interrupt enable bits of ESP32 slave. + * + * @param arg Context of the component. + * @param ena_mask_o Output of interrupt bit enable mask. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_get_intr_ena(void *arg, uint32_t *ena_mask_o, uint32_t wait_ms); + +/** + * Write general purpose R/W registers (8-bit) of ESP32 slave. + * + * @param arg Context of the component. + * @param addr Address of register to write. Valid address: 0-27, 32-63 (28-31 reserved). + * @param value Value to write to the register. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Address not valid. + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_write_reg(void *arg, uint8_t addr, uint8_t value, uint8_t *value_o, uint32_t wait_ms); + +/** + * Read general purpose R/W registers (8-bit) of ESP32 slave. + * + * @param arg Context of the component. + * @param add Address of register to read. Valid address: 0-27, 32-63 (28-31 reserved, return interrupt bits on read). + * @param value Output value read from the register. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Address not valid. + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_read_reg(void *arg, uint8_t add, uint8_t *value_o, uint32_t wait_ms); + +/** + * Send interrupts to slave. Each bit of the interrupt will be triggered. + * + * @param arg Context of the component. + * @param intr_mask Mask of interrupt bits to send to slave. + * @param wait_ms Time to wait before timeout, in ms. + * + * @return + * - ESP_OK Success + * - One of the error codes from SDMMC host controller + */ +esp_err_t essl_sdio_send_slave_intr(void *arg, uint32_t intr_mask, uint32_t wait_ms); + +/** + * @brief Reset the counter on the host side. + * + * @note Only call when you know the slave has reset its counter, or there will be inconsistent between the master and the slave. + * + * @param arg Context of the component. + */ +void essl_sdio_reset_cnt(void *arg); + +/** @endcond */ diff --git a/components/freertos/include/freertos/projdefs.h b/components/freertos/include/freertos/projdefs.h index 1bd39c58d8..edb5007303 100644 --- a/components/freertos/include/freertos/projdefs.h +++ b/components/freertos/include/freertos/projdefs.h @@ -78,6 +78,7 @@ typedef void (*TaskFunction_t)( void * ); /* Converts a time in milliseconds to a time in ticks. */ #define pdMS_TO_TICKS( xTimeInMs ) ( ( ( TickType_t ) ( xTimeInMs ) * configTICK_RATE_HZ ) / ( TickType_t ) 1000 ) +#define pdTICKS_TO_MS( xTicks ) ( ( uint32_t ) ( xTicks ) * 1000 / configTICK_RATE_HZ ) #define pdFALSE ( ( BaseType_t ) 0 ) #define pdTRUE ( ( BaseType_t ) 1 ) diff --git a/docs/Doxyfile b/docs/Doxyfile index 72b101fc6b..497ce96afb 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -145,6 +145,9 @@ INPUT = \ ../../components/esp_https_server/include/esp_https_server.h \ ## ESP Local Ctrl ../../components/esp_local_ctrl/include/esp_local_ctrl.h \ + ## ESP Serial Slave Link + ../../components/esp_serial_slave_link/include/esp_serial_slave_link/essl.h \ + ../../components/esp_serial_slave_link/include/esp_serial_slave_link/essl_sdio.h \ ## ## Provisioning - API Reference ## diff --git a/docs/en/api-reference/peripherals/esp_slave_protocol.rst b/docs/en/api-reference/peripherals/esp_slave_protocol.rst index 37fd21912e..e99f7830ab 100644 --- a/docs/en/api-reference/peripherals/esp_slave_protocol.rst +++ b/docs/en/api-reference/peripherals/esp_slave_protocol.rst @@ -76,7 +76,10 @@ ESP SDIO slave protocol ----------------------- The protocol is based on Function 1 access by CMD52 and CMD53, offering 3 services: (1) sending and receiving FIFO, (2) 52 8-bit R/W -register shared by host and slave, (3) 8 general purpose interrupt sources from host to slave and 8 in the oppsite direction. +register shared by host and slave, (3) 8 general purpose interrupt sources from host to slave and 8 in the opposite direction. + +There is a component `esp_serial_slave_link` implementing the logic of this protocol for +ESP32 master to communicate with the ESP32 slave. See :doc:`/api-reference/protocols/esp_serial_slave_link`. The host should access the registers below as described to communicate with slave. @@ -154,7 +157,7 @@ To write the receiving FIFO in the slave, host should work in the following step 1. Read the TOKEN1 field (bits 27-16) of TOKEN_RDATA (0x044) register. The buffer number remaining is TOKEN1 minus the number of buffers used by host. -2. Make sure the buffer number is sufficient (*buffer_size* * *buffer_num* is greater than data to write, *buffer_size* +2. Make sure the buffer number is sufficient (*recv_buffer_size* * *buffer_num* is greater than data to write, *recv_buffer_size* is pre-defined between the host and the slave before the communication starts). Or go back to step 1 until the buffer is enough. 3. Write to the FIFO address with CMD53. Note that the *requested length* should not be larger than calculated in step 2, diff --git a/docs/en/api-reference/peripherals/sdio_slave.rst b/docs/en/api-reference/peripherals/sdio_slave.rst index d24b8a9be0..d7473be27e 100644 --- a/docs/en/api-reference/peripherals/sdio_slave.rst +++ b/docs/en/api-reference/peripherals/sdio_slave.rst @@ -127,9 +127,11 @@ SDIO initialization process (Sector 3.1.2 of `SDIO Simplified Specification `_), which is described briefly in :ref:`esp_slave_init`. -Furthermore, there's an ESP32-specific upper-level communication protocol upon -the CMD52/CMD53 to Func 1. Please refer to :ref:`esp_slave_protocol_layer`, -or example :example:`peripherals/sdio` when programming your host. +Furthermore, there's an ESP32-specific upper-level communication protocol upon the CMD52/CMD53 to +Func 1. Please refer to :ref:`esp_slave_protocol_layer`. There is also a component +:doc:`ESP Serial Slave Link ` +for ESP32 master to communicate with ESP32 SDIO slave, see example :example:`peripherals/sdio` +when programming your host. .. toctree:: :hidden: diff --git a/docs/en/api-reference/protocols/esp_serial_slave_link.rst b/docs/en/api-reference/protocols/esp_serial_slave_link.rst new file mode 100644 index 0000000000..90c39563b0 --- /dev/null +++ b/docs/en/api-reference/protocols/esp_serial_slave_link.rst @@ -0,0 +1,154 @@ +ESP Serial Slave Link +===================== + +Overview +-------- + +Espressif provides several chips that can work as slaves. These slave devices rely on some +common buses, and have their own communication protocols over those buses. The `esp_serial_slave_link` component is +designed for the master to communicate with ESP slave devices through those protocols over the +bus drivers. + +After an `esp_serial_slave_link` device is initialized properly, the application can use it to communicate with the ESP +slave devices conveniently. + +For more details about ESP32 SDIO slave protocol, see document :doc:`/api-reference/peripherals/esp_slave_protocol`. + +Terminology +----------- + +- ESSL: Abbreviation for ESP Serial Slave Link, the component described by this document. + +- Master: The device running the `esp_serial_slave_link` component. + +- ESSL device: a virtual device on the master associated with an ESP slave device. The device + context has the knowledge of the slave protocol above the bus, relying on some bus drivers to + communicate with the slave. + +- ESSL device handle: a handle to ESSL device context containing the configuration, status and + data required by the ESSL component. The context stores the driver configurations, + communication state, data shared by master and slave, etc. + + The context should be initialized before it is used, and get deinitialized if not used any more. The + master application operates on the ESSL device through this handle. + +- ESP slave: the slave device connected to the bus, which ESSL component is designed to + communicate with. + +- Bus: The bus over which the master and the slave communicate with each other. + +- Slave protocol: The special communication protocol specified by Espressif HW/SW over the bus. + +- TX buffer num: a counter, which is on the slave and can be read by the master, indicates the + accumulated buffer numbers that the slave has loaded to the hardware to receive data from the + master. + +- RX data size: a counter, which is on the slave and can be read by the master, indicates the + accumulated data size that the slave has loaded to the hardware to send to the master. + +Services provided by ESP slave +------------------------------ + +There are some common services provided by the Espressif slaves: + +1. Tohost Interrupts: The slave can inform the master about certain events by the interrupt line. + +2. Frhost Interrupts: The master can inform the slave about certain events. + +3. Tx FIFO (master to slave): the slave can send data in stream to the master. The SDIO slave can + also indicate it has new data to send to master by the interrupt line. + + The slave updates the TX buffer num to inform the master how much data it can receive, and the + master then read the TX buffer num, and take off the used buffer number to know how many buffers are remaining. + +4. Rx FIFO (slave to master): the slave can receive data from the master in units of receiving + buffers. + + The slave updates the RX data size to inform the master how much data it has prepared to + send, and then the master read the data size, and take off the data length it has already received to know how many + data is remaining. + +5. Shared registers: the master can read some part of the registers on the slave, and also write + these registers to let the slave read. + + +Initialization of ESP SDIO Slave Link +------------------------------------- + +The ESP SDIO slave link (ESSL SDIO) devices relies on the sdmmc component. The ESSL device should +be initialized as below: + +1. Initialize a sdmmc card (see :doc:` Document of SDMMC driver `) + structure. + +2. Call :cpp:func:`sdmmc_card_init` to initialize the card. + +3. Initialize the ESSL device with :cpp:type:`essl_sdio_config_t`. The `card` member should be + the :cpp:type:`sdmmc_card_t` got in step 2, and the `recv_buffer_size` member should be filled + correctly according to pre-negotiated value. + +4. Call :cpp:func:`essl_init` to do initialization of the SDIO part. + +5. Call :cpp:func:`essl_wait_for_ready` to wait for the slave to be ready. + +APIs +---- + +After the initialization process above is performed, you can call the APIs below to make use of +the services provided by the slave: + +Interrupts +^^^^^^^^^^ + +1. Call :cpp:func:`essl_get_intr_ena` to know which events will trigger the interrupts to the master. + +2. Call :cpp:func:`essl_set_intr_ena` to set the events that will trigger interrupts to the master. + +3. Call :cpp:func:`essl_wait_int` to wait until interrupt from the slave, or timeout. + +4. When interrupt is triggered, call :cpp:func:`essl_get_intr` to know which events are active, + and call :cpp:func:`essl_clear_intr` to clear them. + +5. Call :cpp:func:`essl_send_slave_intr` to trigger general purpose interrupt of the slave. + +TX FIFO +^^^^^^^ + +1. Call :cpp:func:`essl_get_tx_buffer_num` to know how many buffers the slave has prepared to + receive data from the master. This is optional. The master will poll `tx_buffer_num` when it try + to send packets to the slave, until the slave has enough buffer or timeout. + +2. Call :cpp:func:`essl_send_paket` to send data to the slave. + +RX FIFO +^^^^^^^ + +1. Call :cpp:func:`essl_get_rx_data_size` to know how many data the slave has prepared to send to + the master. This is optional. When the master tries to receive data from the slave, it will update + the `rx_data_size` for once, if the current `rx_data_size` is shorter than the buffer size the + master prepared to receive. And it may poll the `rx_data_size` if the `rx_dat_size` keeps 0, + until timeout. + +2. Call :cpp:func:`essl_get_packet` to receive data from the slave. + +Reset counters (Optional) +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Call :cpp:func:`essl_reset_cnt` to reset the internal counter if you find the slave has reset its +counter. + + +Application Example +------------------- + +The example below shows how ESP32 SDIO host and slave communicate with each other. The host use the ESSL SDIO. + +:example:`peripherals/sdio`. + +Please refer to the specific example README.md for details. + +API Reference +------------- + +.. include:: /_build/inc/essl.inc +.. include:: /_build/inc/essl_sdio.inc \ No newline at end of file diff --git a/docs/en/api-reference/protocols/index.rst b/docs/en/api-reference/protocols/index.rst index 7c5cdd92c3..ded1cbbae1 100644 --- a/docs/en/api-reference/protocols/index.rst +++ b/docs/en/api-reference/protocols/index.rst @@ -16,6 +16,7 @@ Application Protocols mDNS Modbus Websocket Client + ESP Serial Slave Link Code examples for this API section are provided in the :example:`protocols` directory of ESP-IDF examples. diff --git a/docs/en/api-reference/storage/sdmmc.rst b/docs/en/api-reference/storage/sdmmc.rst index d650ccdaf4..e0a8c38702 100644 --- a/docs/en/api-reference/storage/sdmmc.rst +++ b/docs/en/api-reference/storage/sdmmc.rst @@ -71,7 +71,7 @@ In particular, the driver does not set any bits in (1) I/O Enable and Int Enable For card configuration and data transfer, choose the pair of functions relevant to your case from the table below. ========================================================================= ================================= ================================= -Action Read Function Write Function +Action Read Function Write Function ========================================================================= ================================= ================================= Read and write a single byte using IO_RW_DIRECT (CMD52) :cpp:func:`sdmmc_io_read_byte` :cpp:func:`sdmmc_io_write_byte` Read and write multiple bytes using IO_RW_EXTENDED (CMD53) in byte mode :cpp:func:`sdmmc_io_read_bytes` :cpp:func:`sdmmc_io_write_bytes` @@ -82,6 +82,8 @@ SDIO interrupts can be enabled by the application using the function :cpp:func:` If you want the application to wait until the SDIO interrupt occurs, use :cpp:func:`sdmmc_io_wait_int`. +There is a component ESSL (ESP Serial Slave Link) to use if you are communicating with an ESP32 +SDIO slave. See :doc:`/api-reference/protocols/esp_serial_slave_link` and example :example:`peripherals/sdio/host`. Combo (memory + IO) cards ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/zh_CN/api-reference/protocols/esp_serial_slave_link.rst b/docs/zh_CN/api-reference/protocols/esp_serial_slave_link.rst new file mode 100644 index 0000000000..42578ae599 --- /dev/null +++ b/docs/zh_CN/api-reference/protocols/esp_serial_slave_link.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/protocols/esp_serial_slave_link.rst \ No newline at end of file diff --git a/docs/zh_CN/api-reference/protocols/index.rst b/docs/zh_CN/api-reference/protocols/index.rst index 8b1e4064fd..66a1b3303c 100644 --- a/docs/zh_CN/api-reference/protocols/index.rst +++ b/docs/zh_CN/api-reference/protocols/index.rst @@ -3,19 +3,20 @@ :link_to_translation:`en:[English]` .. toctree:: - :maxdepth: 1 + :maxdepth: 1 - mDNS - ESP-TLS - HTTP Client - Websocket Client - HTTP 服务器 - HTTPS Server - ICMP Echo - ASIO - ESP-MQTT - Modbus slave - Local Control + mDNS + ESP-TLS + HTTP Client + Websocket Client + HTTP 服务器 + HTTPS Server + ICMP Echo + ASIO + ESP-MQTT + Modbus slave + Local Control + ESP Serial Slave Link 此 API 部分的示例代码在 ESP-IDF 示例工程的 :example:`protocols` 目录下提供。