essl: new component to communicate with esp serial slave devices

This commit is contained in:
Michael (XIAO Xufeng) 2019-09-28 18:14:24 +08:00
parent 7c8139734d
commit 087c8f6065
17 changed files with 1409 additions and 18 deletions

View File

@ -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

View File

@ -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"
)

View File

@ -3,4 +3,5 @@
#
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := . include/esp_serial_slave_link

View File

@ -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);
}

View File

@ -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 <esp_types.h>
#include <esp_err.h>
/** 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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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 */

View File

@ -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 )

View File

@ -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
##

View File

@ -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,

View File

@ -127,9 +127,11 @@ SDIO initialization process (Sector 3.1.2 of `SDIO Simplified
Specification <https://www.sdcard.org/downloads/pls/>`_), 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 </api-reference/protocols/esp_serial_slave_link>`
for ESP32 master to communicate with ESP32 SDIO slave, see example :example:`peripherals/sdio`
when programming your host.
.. toctree::
:hidden:

View File

@ -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 </api-reference/storage/sdmmc>`)
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

View File

@ -16,6 +16,7 @@ Application Protocols
mDNS <mdns>
Modbus <modbus>
Websocket Client <esp_websocket_client>
ESP Serial Slave Link <esp_serial_slave_link>
Code examples for this API section are provided in the :example:`protocols` directory of ESP-IDF examples.

View File

@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/protocols/esp_serial_slave_link.rst

View File

@ -3,19 +3,20 @@
:link_to_translation:`en:[English]`
.. toctree::
:maxdepth: 1
:maxdepth: 1
mDNS <mdns>
ESP-TLS <esp_tls>
HTTP Client <esp_http_client>
Websocket Client <esp_websocket_client>
HTTP 服务器 <esp_http_server>
HTTPS Server <esp_https_server>
ICMP Echo <icmp_echo>
ASIO <asio>
ESP-MQTT <mqtt>
Modbus slave <modbus>
Local Control <esp_local_ctrl>
mDNS <mdns>
ESP-TLS <esp_tls>
HTTP Client <esp_http_client>
Websocket Client <esp_websocket_client>
HTTP 服务器 <esp_http_server>
HTTPS Server <esp_https_server>
ICMP Echo <icmp_echo>
ASIO <asio>
ESP-MQTT <mqtt>
Modbus slave <modbus>
Local Control <esp_local_ctrl>
ESP Serial Slave Link <esp_serial_slave_link>
此 API 部分的示例代码在 ESP-IDF 示例工程的 :example:`protocols` 目录下提供。