Merge branch 'feat/esp_ser_and_sdio_test' into 'master'

essl: new component to communicate with esp serial slave devices

Closes IDF-1002

See merge request espressif/esp-idf!6166
This commit is contained in:
Angus Gratton 2019-12-04 11:03:14 +08:00
commit 38be5c140a
29 changed files with 2340 additions and 706 deletions

View File

@ -162,7 +162,7 @@ typedef enum {
ringbuf_free_ptr = offset_of(sdio_ringbuf_t, free_ptr),
} sdio_ringbuf_pointer_t;
#define SDIO_RINGBUF_INITIALIZER() (sdio_ringbuf_t){.write_spinlock = portMUX_INITIALIZER_UNLOCKED,}
#define SDIO_RINGBUF_INITIALIZER {.write_spinlock = portMUX_INITIALIZER_UNLOCKED,}
typedef struct {
sdio_slave_config_t config;
@ -191,24 +191,26 @@ typedef struct {
portMUX_TYPE recv_spinlock;
} sdio_context_t;
static sdio_context_t context = {
.intr_handle = NULL,
/*------- events ---------------*/
.events = {},
.reg_spinlock = portMUX_INITIALIZER_UNLOCKED,
/*------- sending ---------------*/
.send_state = STATE_IDLE,
.sendbuf = SDIO_RINGBUF_INITIALIZER(),
.ret_queue = NULL,
.in_flight = NULL,
.in_flight_end = NULL,
.in_flight_next = NULL,
/*------- receiving ---------------*/
.recv_link_list = STAILQ_HEAD_INITIALIZER(context.recv_link_list),
.recv_reg_list = TAILQ_HEAD_INITIALIZER(context.recv_reg_list),
.recv_cur_ret = NULL,
.recv_spinlock = portMUX_INITIALIZER_UNLOCKED,
};
#define CONTEXT_INIT_VAL { \
.intr_handle = NULL, \
/*------- events ---------------*/ \
.events = {}, \
.reg_spinlock = portMUX_INITIALIZER_UNLOCKED, \
/*------- sending ---------------*/ \
.send_state = STATE_IDLE, \
.sendbuf = SDIO_RINGBUF_INITIALIZER, \
.ret_queue = NULL, \
.in_flight = NULL, \
.in_flight_end = NULL, \
.in_flight_next = NULL, \
/*------- receiving ---------------*/ \
.recv_link_list = STAILQ_HEAD_INITIALIZER(context.recv_link_list), \
.recv_reg_list = TAILQ_HEAD_INITIALIZER(context.recv_reg_list), \
.recv_cur_ret = NULL, \
.recv_spinlock = portMUX_INITIALIZER_UNLOCKED, \
}
static sdio_context_t context = CONTEXT_INIT_VAL;
static void sdio_intr(void*);
static void sdio_intr_host(void*);
@ -238,7 +240,7 @@ static void sdio_ringbuf_deinit(sdio_ringbuf_t* buf)
{
if (buf->remain_cnt != NULL) vSemaphoreDelete(buf->remain_cnt);
if (buf->data != NULL) free(buf->data);
*buf = SDIO_RINGBUF_INITIALIZER();
*buf = (sdio_ringbuf_t) SDIO_RINGBUF_INITIALIZER;
}
static esp_err_t sdio_ringbuf_init(sdio_ringbuf_t* buf, int item_size, int item_cnt)
@ -251,7 +253,7 @@ static esp_err_t sdio_ringbuf_init(sdio_ringbuf_t* buf, int item_size, int item_
//one item is not used.
buf->size = item_size * (item_cnt+1);
//apply for resources
buf->data = (uint8_t*)malloc(buf->size);
buf->data = (uint8_t*)heap_caps_malloc(buf->size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (buf->data == NULL) goto no_mem;
buf->remain_cnt = xSemaphoreCreateCounting(item_cnt, item_cnt);
if (buf->remain_cnt == NULL) goto no_mem;
@ -451,6 +453,7 @@ static esp_err_t init_context(sdio_slave_config_t *config)
{
SDIO_SLAVE_CHECK(*(uint32_t*)&context.config == 0, "sdio slave already initialized", ESP_ERR_INVALID_STATE);
context = (sdio_context_t)CONTEXT_INIT_VAL;
context.config = *config;
// in theory we can queue infinite buffers in the linked list, but for multi-core reason we have to use a queue to
@ -472,8 +475,6 @@ static esp_err_t init_context(sdio_slave_config_t *config)
context.ret_queue = xQueueCreate(config->send_queue_size, sizeof(void*));
if (context.ret_queue == NULL) goto no_mem;
context.recv_link_list = (buf_stailq_t)STAILQ_HEAD_INITIALIZER(context.recv_link_list);
context.recv_reg_list = (buf_tailq_t)TAILQ_HEAD_INITIALIZER(context.recv_reg_list);
return ESP_OK;
no_mem:
@ -485,9 +486,9 @@ static void configure_pin(int pin, uint32_t func, bool pullup)
{
const int sdmmc_func = func;
const int drive_strength = 3;
assert(pin!=-1);
assert(pin != -1);
uint32_t reg = GPIO_PIN_MUX_REG[pin];
assert(reg!=UINT32_MAX);
assert(reg != UINT32_MAX);
PIN_INPUT_ENABLE(reg);
PIN_FUNC_SELECT(reg, sdmmc_func);
@ -574,6 +575,29 @@ static inline esp_err_t sdio_slave_hw_init(sdio_slave_config_t *config)
return ESP_OK;
}
static void recover_pin(int pin, int sdio_func)
{
uint32_t reg = GPIO_PIN_MUX_REG[pin];
assert(reg != UINT32_MAX);
int func = REG_GET_FIELD(reg, MCU_SEL);
if (func == sdio_func) {
gpio_set_direction(pin, GPIO_MODE_INPUT);
PIN_FUNC_SELECT(reg, PIN_FUNC_GPIO);
}
}
static void sdio_slave_hw_deinit(void)
{
const sdio_slave_slot_info_t *slot = &sdio_slave_slot_info[1];
recover_pin(slot->clk_gpio, slot->func);
recover_pin(slot->cmd_gpio, slot->func);
recover_pin(slot->d0_gpio, slot->func);
recover_pin(slot->d1_gpio, slot->func);
recover_pin(slot->d2_gpio, slot->func);
recover_pin(slot->d3_gpio, slot->func);
}
esp_err_t sdio_slave_initialize(sdio_slave_config_t *config)
{
esp_err_t r;
@ -594,6 +618,20 @@ esp_err_t sdio_slave_initialize(sdio_slave_config_t *config)
void sdio_slave_deinit(void)
{
sdio_slave_hw_deinit();
//unregister all buffers in the queue, and not in the queue
buf_desc_t *temp_desc;
buf_desc_t *desc;
TAILQ_FOREACH_SAFE(desc, &context.recv_reg_list, te, temp_desc) {
TAILQ_REMOVE(&context.recv_reg_list, desc, te);
free(desc);
}
STAILQ_FOREACH_SAFE(desc, &context.recv_link_list, qe, temp_desc) {
STAILQ_REMOVE(&context.recv_link_list, desc, buf_desc_s, qe);
free(desc);
}
esp_err_t ret = esp_intr_free(context.intr_handle);
assert(ret==ESP_OK);
context.intr_handle = NULL;
@ -1165,9 +1203,9 @@ static void sdio_intr_recv(void* arg)
// This may cause the ``cur_ret`` pointer to be NULL, indicating the list is empty,
// in this case the ``tx_done`` should happen no longer until new desc is appended.
// The app is responsible to place the pointer to the right place again when appending new desc.
critical_enter_recv();
portENTER_CRITICAL_ISR(&context.recv_spinlock);
context.recv_cur_ret = STAILQ_NEXT(context.recv_cur_ret, qe);
critical_exit_recv();
portEXIT_CRITICAL_ISR(&context.recv_spinlock);
ESP_EARLY_LOGV(TAG, "intr_recv: Give");
xSemaphoreGiveFromISR(context.recv_event, &yield);
SLC.slc0_int_clr.tx_done = 1;
@ -1201,6 +1239,7 @@ esp_err_t sdio_slave_recv_load_buf(sdio_slave_buf_handle_t handle)
SLC.slc0_tx_link.addr = (uint32_t)desc;
SLC.slc0_tx_link.start = 1;
ESP_LOGV(TAG, "recv_load_buf: start new");
SLC.slc0_int_ena.tx_done = 1;
} else {
//restart former ll operation
SLC.slc0_tx_link.restart = 1;
@ -1216,7 +1255,7 @@ sdio_slave_buf_handle_t sdio_slave_recv_register_buf(uint8_t *start)
{
SDIO_SLAVE_CHECK(esp_ptr_dma_capable(start) && (uint32_t)start%4==0,
"buffer to register should be DMA capable and 32-bit aligned", NULL);
buf_desc_t *desc = (buf_desc_t*)malloc(sizeof(buf_desc_t));
buf_desc_t *desc = (buf_desc_t*)heap_caps_malloc(sizeof(buf_desc_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (desc == NULL) {
SDIO_SLAVE_LOGE("cannot allocate lldesc for new buffer");
return NULL;

View File

@ -241,6 +241,23 @@ esp_err_t sdspi_host_deinit(void)
s_slots[i].transactions = NULL;
spi_bus_free((spi_host_device_t) i);
s_slots[i].handle = NULL;
uint64_t pin_bit_mask = BIT64(s_slots[i].gpio_cs);
if (s_slots[i].gpio_cd != GPIO_UNUSED) {
pin_bit_mask |= BIT64(s_slots[i].gpio_cd);
}
if (s_slots[i].gpio_wp != GPIO_UNUSED) {
pin_bit_mask |= BIT64(s_slots[i].gpio_wp);
}
if (s_slots[i].gpio_int != GPIO_UNUSED) {
pin_bit_mask |= BIT64(s_slots[i].gpio_int);
}
gpio_config_t config = {
.pin_bit_mask = pin_bit_mask,
.mode = GPIO_MODE_INPUT,
};
gpio_config(&config);
}
if (s_slots[i].semphr_int) {
vSemaphoreDelete(s_slots[i].semphr_int);

View File

@ -5,5 +5,5 @@ endif()
idf_component_register(SRC_DIRS ${srcdirs}
INCLUDE_DIRS include param_test/include
REQUIRES unity test_utils driver nvs_flash
REQUIRES unity test_utils driver nvs_flash esp_serial_slave_link
)

View File

@ -0,0 +1,769 @@
// Copyright 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 "unity.h"
#include "esp_serial_slave_link/essl_sdio.h"
#include "driver/sdio_slave.h"
#include "driver/sdmmc_host.h"
#include "driver/sdspi_host.h"
#include "test_utils.h"
#include "param_test.h"
#include "esp_log.h"
#define TIMEOUT_MAX UINT32_MAX
#define INT_MASK_ALL 0xff
#define SDIO_SLAVE_QUEUE_SIZE 20
#define RX_BUFFER_SIZE 2048
#define RX_BUFFER_NUM 10
#define TX_BUFFER_SIZE 2048
#define REG_ADDR_MAX 60
//the test should run accross the boundary, i.e. over 0x100000 bytes.
//TEST_CNT > 512
#define TEST_CNT 10000
#define TEST_RESET_DATA_LEN 10
#ifndef MIN
#define MIN(a, b) ((a)<(b)? (a): (b))
#endif
typedef enum {
SDIO_1BIT = 0,
SDIO_4BIT = 1,
SDIO_SPI = 2,
} sdio_mode_t;
typedef void (*sdio_test_func)(essl_handle_t handle);
typedef struct {
const char test_name[16];
sdio_mode_t sdio_mode;
uint32_t freq;
bool check_data;
bool packet_mode;
} sdio_test_config_t;
sdio_test_config_t test_cfg_array[] = {
//the first item will be the default config used by all tests
{
.test_name = "HS4B",
.sdio_mode = SDIO_4BIT,
.freq = SDMMC_FREQ_HIGHSPEED,
.check_data = true,
},
{
.test_name = "HS1B",
.sdio_mode = SDIO_1BIT,
.freq = SDMMC_FREQ_HIGHSPEED,
.check_data = true,
},
{
.test_name = "SPI",
.sdio_mode = SDIO_SPI,
.freq = SDMMC_FREQ_HIGHSPEED,
.check_data = true,
},
//the performance test is only done when psram is not enabled
#if !CONFIG_SPIRAM && !CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE
{
.test_name = "HS4B (perf)",
.sdio_mode = SDIO_4BIT,
.freq = SDMMC_FREQ_HIGHSPEED,
},
{
.test_name = "HS1B (perf)",
.sdio_mode = SDIO_1BIT,
.freq = SDMMC_FREQ_HIGHSPEED,
},
{
.test_name = "SPI (perf)",
.sdio_mode = SDIO_SPI,
.freq = SDMMC_FREQ_HIGHSPEED,
},
#endif
};
sdio_test_config_t packet_config = {
.test_name = "HS4B packet",
.sdio_mode = SDIO_4BIT,
.freq = SDMMC_FREQ_HIGHSPEED,
.check_data = true,
.packet_mode = true,
};
const sdio_test_config_t* default_config = &test_cfg_array[0];
#define TEST_SIZE (sizeof(test_cfg_array)/sizeof(sdio_test_config_t))
static const char MASTER_TAG[] = "master";
static const char SLAVE_TAG[] = "slave";
/*******************************************************************************
* Master
******************************************************************************/
static sdmmc_card_t s_card;
typedef void (*test_func_t)(essl_handle_t handle, const sdio_test_config_t* config);
static void init_sdmmc_host(void);
static void init_essl(essl_handle_t *out_handle, const sdio_test_config_t *conf);
static void deinit_essl(essl_handle_t handle, const sdio_test_config_t *conf);
static void test_framework_master(test_func_t test_func, const sdio_test_config_t* config)
{
ESP_LOGI(MASTER_TAG, "### Testing %s... ####", config->test_name);
essl_handle_t handle;
esp_err_t err;
init_essl(&handle, config);
err = essl_init(handle, TIMEOUT_MAX);
TEST_ESP_OK(err);
(*test_func)(handle, config);
deinit_essl(handle, config);
}
static void init_sdmmc_host(void)
{
esp_err_t err;
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
err = sdmmc_host_init();
TEST_ESP_OK(err);
err = sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config);
TEST_ESP_OK(err);
}
static void init_essl(essl_handle_t *out_handle, const sdio_test_config_t *conf)
{
sdmmc_host_t config;
esp_err_t err;
/* Probe */
switch (conf->sdio_mode) {
case SDIO_4BIT:
ESP_LOGI(MASTER_TAG, "Probe using SD 4-bit...\n");
config = (sdmmc_host_t)SDMMC_HOST_DEFAULT();
config.flags = SDMMC_HOST_FLAG_4BIT;
config.max_freq_khz = conf->freq;
init_sdmmc_host();
break;
case SDIO_1BIT:
ESP_LOGI(MASTER_TAG, "Probe using SD 1-bit...\n");
config = (sdmmc_host_t)SDMMC_HOST_DEFAULT();
config.flags = SDMMC_HOST_FLAG_1BIT;
config.max_freq_khz = conf->freq;
init_sdmmc_host();
break;
case SDIO_SPI:
config = (sdmmc_host_t)SDSPI_HOST_DEFAULT();
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
slot_config.gpio_miso = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_D0;
slot_config.gpio_mosi = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_CMD;
slot_config.gpio_sck = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_CLK;
slot_config.gpio_cs = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_D3;
slot_config.gpio_int = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_D1;
err = gpio_install_isr_service(0);
TEST_ASSERT(err == ESP_OK || err == ESP_ERR_INVALID_STATE);
err = sdspi_host_init();
TEST_ESP_OK(err);
err = sdspi_host_init_slot(HSPI_HOST, &slot_config);
TEST_ESP_OK(err);
ESP_LOGI(MASTER_TAG, "Probe using SPI...\n");
break;
}
sdmmc_card_t *card = &s_card;
//wait for at least 5 seconds
int retry_times = 5;
do {
if (sdmmc_card_init(&config, card) == ESP_OK) {
break;
}
ESP_LOGW(MASTER_TAG, "slave init failed, retry...");
vTaskDelay(1000 / portTICK_PERIOD_MS);
} while (--retry_times);
TEST_ASSERT_MESSAGE(retry_times != 0, "Initializing slave failed.");
essl_sdio_config_t ser_config = {
.card = card,
.recv_buffer_size = RX_BUFFER_SIZE,
};
err = essl_sdio_init_dev(out_handle, &ser_config);
TEST_ESP_OK(err);
err = essl_init(*out_handle, TIMEOUT_MAX);
TEST_ESP_OK(err);
}
static void deinit_essl(essl_handle_t handle, const sdio_test_config_t *conf)
{
esp_err_t err;
essl_sdio_deinit_dev(handle);
if (conf->sdio_mode == SDIO_SPI) {
gpio_uninstall_isr_service();
err = sdspi_host_deinit();
TEST_ESP_OK(err);
} else {
err = sdmmc_host_deinit();
TEST_ESP_OK(err);
}
}
static void send_finish_test(essl_handle_t handle)
{
//the slave needs a signal to quite the test
essl_send_slave_intr(handle, BIT(7), TIMEOUT_MAX);
}
static void test_int(essl_handle_t handle, const sdio_test_config_t* config)
{
esp_err_t err;
err = essl_set_intr_ena(handle, INT_MASK_ALL, TIMEOUT_MAX);
TEST_ESP_OK(err);
err = essl_wait_int(handle, 0);
TEST_ASSERT_EQUAL_HEX(ESP_ERR_TIMEOUT, err);
//tests all 8 interrupts of the slave, in which int 7 is used to terminate the test on the slave.
for (int i = 0; i < 8; i ++) {
uint32_t int_st;
err = essl_send_slave_intr(handle, BIT(i), TIMEOUT_MAX);
TEST_ESP_OK(err);
//the slave should return interrupt with the same bit in 10 ms
err = essl_wait_int(handle, 10);
TEST_ESP_OK(err);
err = essl_get_intr(handle, NULL, &int_st, TIMEOUT_MAX);
TEST_ESP_OK(err);
//check and clear the returned interrupt
TEST_ASSERT_EQUAL_HEX(BIT(i), int_st);
err = essl_clear_intr(handle, int_st, TIMEOUT_MAX);
TEST_ESP_OK(err);
}
}
static void test_sdio_interrupt_master(void)
{
test_framework_master(test_int, default_config);
}
static void test_reg(essl_handle_t handle, const sdio_test_config_t* config)
{
esp_err_t err;
uint8_t data[REG_ADDR_MAX];
srand(850);
//initialize the buffer
for (int i = 0; i < REG_ADDR_MAX; i++) {
data[i] = rand();
err = essl_write_reg(handle, i, data[i], NULL, 10);
TEST_ESP_OK(err);
}
for (int i = 0; i < 512; i++) {
//randomly write one
int offset = rand() % REG_ADDR_MAX;
uint8_t data_write = rand();
data[offset] = data_write;
err = essl_write_reg(handle, offset, data_write, NULL, 10);
TEST_ESP_OK(err);
//randomly read another one and compare
offset = rand() % REG_ADDR_MAX;
uint8_t data_read;
err = essl_read_reg(handle, offset, &data_read, 10);
TEST_ESP_OK(err);
TEST_ASSERT_EQUAL_HEX8(data[offset], data_read);
}
send_finish_test(handle);
}
static void test_sdio_reg_master(void)
{
test_framework_master(test_reg, default_config);
}
static uint8_t tx_buffer[TX_BUFFER_SIZE*2];
static uint8_t rcv_buffer[RX_BUFFER_SIZE*RX_BUFFER_NUM];
static void init_tx_buffer(void)
{
srand(776);
for (int i = 0; i < sizeof(tx_buffer); i++) {
tx_buffer[i] = rand();
}
}
static void get_master_send_data(int offset, uint8_t** out_start, int* out_len)
{
int page_cnt = offset / TX_BUFFER_SIZE;
int offset_in_page = offset % TX_BUFFER_SIZE;
srand(page_cnt);
int page_offset = (rand() % (sizeof(tx_buffer) - (TX_BUFFER_SIZE) + 1)) & (~3);
*out_start = &tx_buffer[page_offset + offset_in_page];
*out_len = TX_BUFFER_SIZE - offset_in_page;
}
static void log_performance_tohost(uint32_t speed, const sdio_test_config_t* config)
{
if (!config->check_data) {
switch (config->sdio_mode) {
case SDIO_4BIT:
TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_TOHOST_4BIT, "%d", speed);
break;
case SDIO_1BIT:
TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_TOHOST_1BIT, "%d", speed);
break;
case SDIO_SPI:
TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_TOHOST_SPI, "%d", speed);
break;
}
}
ESP_LOGI(MASTER_TAG, "Throughput: %.2lf MB/s", speed/1000.);
}
static void test_tp_tohost_master(essl_handle_t handle, const sdio_test_config_t* config)
{
esp_err_t err;
int expected_length = TEST_CNT * TX_BUFFER_SIZE;
int recv_size = 4096;
init_tx_buffer();
//wait for the slave to get ready
vTaskDelay(20);
int remain_length = expected_length;
int offset = 0;
// though the flow is the same, the check of config->check_data influences the throughput much, put it outside
uint32_t pre = xTaskGetTickCount();
if (config->check_data) {
do {
size_t rcv_len;
err = essl_get_packet(handle, rcv_buffer, recv_size, &rcv_len, TIMEOUT_MAX);
TEST_ASSERT(err == ESP_OK || err == ESP_ERR_NOT_FINISHED);
TEST_ASSERT_LESS_OR_EQUAL(remain_length, rcv_len);
//compare until all received data are used
int compared_len = 0;
do {
//get the expected master sent data, there may be several segments, so get and compare
//several times
uint8_t* cmp_start;
int seg_len;
get_master_send_data(offset, &cmp_start, &seg_len);
int cmp_len = MIN(rcv_len-compared_len, seg_len);
TEST_ASSERT_EQUAL_HEX8_ARRAY(cmp_start, &rcv_buffer[compared_len], cmp_len);
compared_len += cmp_len;
offset += cmp_len;
} while (compared_len < rcv_len);
remain_length -= rcv_len;
} while (remain_length > 0);
} else {
do {
size_t rcv_len;
err = essl_get_packet(handle, rcv_buffer, recv_size, &rcv_len, TIMEOUT_MAX);
TEST_ASSERT(err == ESP_OK || err == ESP_ERR_NOT_FINISHED);
TEST_ASSERT_LESS_OR_EQUAL(remain_length, rcv_len);
offset += rcv_len;
remain_length -= rcv_len;
} while (remain_length > 0);
}
uint32_t end = xTaskGetTickCount();
uint32_t total_time_ms = (end-pre)*portTICK_PERIOD_MS;
uint32_t throughput_byte_per_ms = expected_length / total_time_ms;
ESP_LOGI(MASTER_TAG, "test done, total time: %d ms, bytes transferred: %d", total_time_ms, expected_length);
log_performance_tohost(throughput_byte_per_ms, config);
send_finish_test(handle);
}
static void log_performance_frhost(uint32_t speed, const sdio_test_config_t* config)
{
if (!config->check_data) {
switch (config->sdio_mode) {
case SDIO_4BIT:
TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_FRHOST_4BIT, "%d", speed);
break;
case SDIO_1BIT:
TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_FRHOST_1BIT, "%d", speed);
break;
case SDIO_SPI:
TEST_PERFORMANCE_GREATER_THAN(SDIO_THROUGHPUT_MBSEC_FRHOST_SPI, "%d", speed);
break;
}
}
ESP_LOGI(MASTER_TAG, "Throughput: %.2lf MB/s", speed/1000.);
}
static void test_tp_frhost_master(essl_handle_t handle, const sdio_test_config_t* config)
{
esp_err_t err;
int expected_length = TEST_CNT * TX_BUFFER_SIZE;
init_tx_buffer();
//wait for the slave to get ready
vTaskDelay(20);
int remain_length = expected_length;
int offset = 0;
uint32_t pre = xTaskGetTickCount();
do {
int send_len;
uint8_t* send_start;
get_master_send_data(offset, &send_start, &send_len);
TEST_ASSERT_EQUAL(TX_BUFFER_SIZE, send_len);
err = essl_send_packet(handle, send_start, send_len, TIMEOUT_MAX);
TEST_ASSERT(err == ESP_OK);
remain_length -= send_len;
offset += send_len;
} while (remain_length > 0);
uint32_t end = xTaskGetTickCount();
uint32_t total_time_ms = (end-pre)*portTICK_PERIOD_MS;
uint32_t throughput_byte_per_ms = expected_length / total_time_ms;
ESP_LOGI(MASTER_TAG, "test done, total time: %d ms, bytes transferred: %d", total_time_ms, expected_length);
log_performance_frhost(throughput_byte_per_ms, config);
send_finish_test(handle);
}
void test_reset_master(essl_handle_t handle, const sdio_test_config_t* config)
{
init_tx_buffer();
//wait for the slave to stop, reset and start again
vTaskDelay(10);
for (int i = 0; i < 10; i++) {
WORD_ALIGNED_ATTR uint8_t buffer[TEST_RESET_DATA_LEN];
size_t read_len;
esp_err_t err = essl_get_packet(handle, buffer, TEST_RESET_DATA_LEN, &read_len, portMAX_DELAY);
if (err == ESP_ERR_NOT_FINISHED) {
TEST_ASSERT_LESS_THAN(10, i);
err = ESP_OK;
}
TEST_ESP_OK(err);
TEST_ASSERT_EQUAL(TEST_RESET_DATA_LEN, read_len);
TEST_ASSERT_EQUAL_HEX8_ARRAY(tx_buffer + 4*i, buffer, read_len);
}
for (int i = 0; i < 10; i++) {
esp_err_t err = essl_send_packet(handle, tx_buffer + i * 8, TEST_RESET_DATA_LEN, portMAX_DELAY);
TEST_ESP_OK(err);
}
send_finish_test(handle);
}
void test_sdio_reset_master(void)
{
test_framework_master(test_reset_master, &packet_config);
}
/*******************************************************************************
* Slave
******************************************************************************/
typedef struct {
int queued_cnt;
bool s_finished;
} slave_context_t;
typedef void (*test_func_slave_t)(slave_context_t *context, const sdio_test_config_t* config);
static slave_context_t slave_context;
static void event_cb(uint8_t event)
{
ESP_EARLY_LOGI(SLAVE_TAG, "event: %d", event);
sdio_slave_send_host_int(event);
if (event == 7) slave_context.s_finished = true;
}
static void wait_for_finish(slave_context_t *ctx)
{
while (!ctx->s_finished) {
vTaskDelay(10);
}
//wait for host to read the respond from slave
vTaskDelay(10);
}
static void test_framework_slave(test_func_slave_t test_func, const sdio_test_config_t* config)
{
slave_context.s_finished = false;
esp_err_t err;
sdio_slave_config_t slave_config = {
.sending_mode = (config->packet_mode? SDIO_SLAVE_SEND_PACKET: SDIO_SLAVE_SEND_STREAM),
.send_queue_size = SDIO_SLAVE_QUEUE_SIZE,
.recv_buffer_size = RX_BUFFER_SIZE,
.event_cb = event_cb,
};
err = sdio_slave_initialize(&slave_config);
TEST_ESP_OK(err);
err = sdio_slave_start();
TEST_ESP_OK(err);
ESP_LOGI(SLAVE_TAG, "slave ready");
test_func(&slave_context, config);
sdio_slave_stop();
sdio_slave_deinit();
}
static void test_int_slave(slave_context_t* ctx, const sdio_test_config_t* config)
{
wait_for_finish(ctx);
}
static void test_sdio_interrupt_slave(void)
{
test_framework_slave(test_int_slave, default_config);
}
static void test_tp_tohost_slave(slave_context_t* ctx, const sdio_test_config_t* config)
{
#define QUEUE_FULL() (ctx->queued_cnt == SDIO_SLAVE_QUEUE_SIZE)
#define QUEUE_EMPTY() (ctx->queued_cnt == 0)
init_tx_buffer();
esp_err_t err;
int offset = 0;
for (int i = 0; i < TEST_CNT; i++) {
do {
void* arg;
//when the queue is full, do a blocking wait for 10ms, otherwise non-blocking
err = sdio_slave_send_get_finished(&arg, QUEUE_FULL()? 1: 0);
if (err == ESP_OK) {
ctx->queued_cnt --;
continue;
}
TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, err);
} while (QUEUE_FULL());
uint8_t* start;
int send_len;
get_master_send_data(offset, &start, &send_len);
TEST_ASSERT_EQUAL(TX_BUFFER_SIZE, send_len);
err = sdio_slave_send_queue(start, send_len, NULL, portMAX_DELAY);
TEST_ESP_OK(err);
ctx->queued_cnt++;
offset += TX_BUFFER_SIZE;
}
while (!QUEUE_EMPTY()) {
void* arg;
err = sdio_slave_send_get_finished(&arg, portMAX_DELAY);
TEST_ESP_OK(err);
ctx->queued_cnt--;
}
wait_for_finish(ctx);
}
static void slave_parepare_recv_buffer(void)
{
for (int i = 0; i < RX_BUFFER_NUM; i++) {
sdio_slave_buf_handle_t buf_handle = sdio_slave_recv_register_buf(&rcv_buffer[i*RX_BUFFER_SIZE]);
esp_err_t err = sdio_slave_recv_load_buf(buf_handle);
TEST_ESP_OK(err);
}
}
static void test_tp_frhost_slave(slave_context_t *ctx, const sdio_test_config_t* config)
{
esp_err_t err;
init_tx_buffer();
slave_parepare_recv_buffer();
int offset = 0;
for (int i = 0; i < TEST_CNT; i++) {
sdio_slave_buf_handle_t buf_handle;
uint8_t* buf;
size_t rcv_len;
err = sdio_slave_recv(&buf_handle, &buf, &rcv_len, portMAX_DELAY);
TEST_ESP_OK(err);
if (config->check_data) {
//compare until all received data are used
int compared_len = 0;
do {
//get the expected master sent data, there may be several segments, so get and compare
//several times
uint8_t* cmp_start;
int seg_len;
get_master_send_data(offset, &cmp_start, &seg_len);
int cmp_len = MIN(rcv_len-compared_len, seg_len);
TEST_ASSERT_EQUAL_HEX8_ARRAY(cmp_start, &buf[compared_len], cmp_len);
compared_len += cmp_len;
offset += cmp_len;
} while (compared_len < rcv_len);
} else {
offset += rcv_len;
}
err = sdio_slave_recv_load_buf(buf_handle);
TEST_ESP_OK(err);
}
wait_for_finish(ctx);
}
static void slave_tx_rx_short_data(void)
{
esp_err_t err;
for (int i = 0; i < 10; i++) {
err = sdio_slave_send_queue(tx_buffer + 4*i, TEST_RESET_DATA_LEN, (void*)i, portMAX_DELAY);
TEST_ESP_OK(err);
}
for (int i = 0; i < 10; i++) {
uint8_t* addr;
size_t size;
sdio_slave_buf_handle_t recv_handle;
err = sdio_slave_recv(&recv_handle, &addr, &size, portMAX_DELAY);
TEST_ESP_OK(err);
TEST_ASSERT_EQUAL(TEST_RESET_DATA_LEN, size);
TEST_ASSERT_EQUAL_HEX8_ARRAY(tx_buffer+i*8, addr, size);
}
for (int i = 0; i < 10; i++) {
void* arg;
err = sdio_slave_send_get_finished(&arg, portMAX_DELAY);
TEST_ESP_OK(err);
TEST_ASSERT_EQUAL(i, arg);
}
}
void test_reset_slave(slave_context_t *context, const sdio_test_config_t* config)
{
sdio_slave_stop();
esp_err_t err = sdio_slave_reset();
TEST_ESP_OK(err);
err = sdio_slave_start();
TEST_ESP_OK(err);
init_tx_buffer();
slave_parepare_recv_buffer();
slave_tx_rx_short_data();
wait_for_finish(context);
}
void test_sdio_reset_slave(void)
{
test_framework_slave(test_reset_slave, &packet_config);
}
TEST_CASE_MULTIPLE_DEVICES("sdio interrupt", "[sdio][test_env=UT_SDIO]", test_sdio_interrupt_master, test_sdio_interrupt_slave);
TEST_CASE_MULTIPLE_DEVICES("sdio register", "[sdio][test_env=UT_SDIO]", test_sdio_reg_master, test_sdio_interrupt_slave);
TEST_CASE_MULTIPLE_DEVICES("sdio reset", "[sdio][test_env=UT_SDIO]", test_sdio_reset_master, test_sdio_reset_slave);
static void test_sdio_frhost_master(const void* pset, void* context)
{
test_framework_master(test_tp_frhost_master, pset);
}
static void test_sdio_frhost_slave(const void* pset, void* context)
{
test_framework_slave(test_tp_frhost_slave, pset);
}
static void test_sdio_tohost_master(const void* pset, void* context)
{
test_framework_master(test_tp_tohost_master, pset);
}
static void test_sdio_tohost_slave(const void* pset, void* context)
{
test_framework_slave(test_tp_tohost_slave, pset);
}
static void null_pre(void** arg)
{
}
static void null_post(void* arg)
{
}
ptest_func_t frhost_master = {
.pre_test = null_pre,
.loop = test_sdio_frhost_master,
.post_test = null_post,
};
ptest_func_t frhost_slave = {
.pre_test = null_pre,
.loop = test_sdio_frhost_slave,
.post_test = null_post,
};
PARAM_GROUP_DECLARE_TYPE(IO_MODE, sdio_test_config_t, test_cfg_array);
TEST_MASTER_SLAVE(FRHOST, test_cfg_array, "[sdio][timeout=180][test_env=UT_SDIO]", &frhost_master, &frhost_slave);
ptest_func_t tohost_master = {
.pre_test = null_pre,
.loop = test_sdio_tohost_master,
.post_test = null_post,
};
ptest_func_t tohost_slave = {
.pre_test = null_pre,
.loop = test_sdio_tohost_slave,
.post_test = null_post,
};
TEST_MASTER_SLAVE(TOHOST, test_cfg_array, "[sdio][timeout=180][test_env=UT_SDIO]", &tohost_master, &tohost_slave);

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

@ -37,3 +37,11 @@
#define IDF_PERFORMANCE_MAX_RSA_2048KEY_PRIVATE_OP 180000
#define IDF_PERFORMANCE_MAX_RSA_4096KEY_PUBLIC_OP 65000
#define IDF_PERFORMANCE_MAX_RSA_4096KEY_PRIVATE_OP 850000
#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_TOHOST_4BIT 13000
#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_FRHOST_4BIT 13000
#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_TOHOST_1BIT 4000
#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_FRHOST_1BIT 4000
#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_TOHOST_SPI 1000
#define IDF_PERFORMANCE_MIN_SDIO_THROUGHPUT_MBSEC_FRHOST_SPI 1000

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

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

@ -16,6 +16,7 @@
ESP-MQTT <mqtt>
Modbus slave <modbus>
Local Control <esp_local_ctrl>
ESP Serial Slave Link <esp_serial_slave_link>
此 API 部分的示例代码在 ESP-IDF 示例工程的 :example:`protocols` 目录下提供。

View File

@ -101,13 +101,8 @@ and ``api_reference/peripherals/sd_pullup_requirements`` to see more
descriptions about pullups and MTDI requirements and solutions of official
modules and devkits.
## About esp_slave component in this example
## About `esp_serial_slave_link` component used in this example
The component in this example shows how to communicate with esp32 sdio slave
correctly. However, currently it is for example purpose only.
The example shows how to talk with the slave, but doesn't show how to handle
exceptions. Assertion fails if any of the preconditions (connections,
grounding, slave data preparation, etc.) is not met.
Please do check and handle the return value in your real product.
`esp_serial_slave_link` component in the IDF is used to communicate to a ESP slave device.
When the `esp_serial_slave_link` device is initialized with an `essl_sdio_config_t` structure,
the `esp_serial_slave_link` can be used to communicate with an ESP32 SDIO slave.

View File

@ -1,3 +0,0 @@
idf_component_register(SRCS "esp_slave.c"
INCLUDE_DIRS "include"
REQUIRES driver sdmmc)

View File

@ -1,357 +0,0 @@
// Copyright 2015-2018 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 "esp_slave.h"
#include "esp_log.h"
#include "freertos/task.h"
#include "soc/sdio_slave_periph.h"
static const char TAG[] = "esp_slave";
#define ESP_SLAVE_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))
esp_err_t esp_slave_init_io(esp_slave_context_t *context)
{
esp_err_t err;
uint8_t ioe;
sdmmc_card_t* card = context->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_LOGI(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_LOGI(TAG, "Function 0 BS: %04x", (int) bs_read);
// Set block sizes for functions 1 to given value (default value = 512).
if (context->block_size > 0 || context->block_size <= 2048) {
bs = context->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_LOGI(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_LOGI(TAG, "Function 1 BS: %04x", (int) bs_read);
if (bs_read != context->block_size) {
ESP_LOGW(TAG, "Function1 block size %d different than set value %d", bs_read, context->block_size);
context->block_size = bs_read;
}
return ESP_OK;
}
esp_err_t esp_slave_wait_for_ioready(esp_slave_context_t *context)
{
ESP_LOGV(TAG, "wait_for_ioready");
esp_err_t err;
sdmmc_card_t *card = context->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_LOGI(TAG, "IOR: 0x%02x", ior);
}
return ESP_OK;
}
static inline esp_err_t esp_slave_write_byte(esp_slave_context_t *context, uint32_t addr, uint8_t val, uint8_t *val_o)
{
return sdmmc_io_write_byte(context->card, 1, addr&0x3FF, val, val_o);
}
static inline esp_err_t esp_slave_write_bytes(esp_slave_context_t *context, uint32_t addr, uint8_t *val, int len)
{
return sdmmc_io_write_bytes(context->card, 1, addr&0x3FF, val, len);
}
static inline esp_err_t esp_slave_read_byte(esp_slave_context_t *context, uint32_t addr, uint8_t *val_o)
{
return sdmmc_io_read_byte(context->card, 1, addr&0x3FF, val_o);
}
static inline esp_err_t esp_slave_read_bytes(esp_slave_context_t *context, uint32_t addr, uint8_t *val_o, int len)
{
return sdmmc_io_read_bytes(context->card, 1, addr&0x3FF, val_o, len);
}
esp_err_t esp_slave_send_packet(esp_slave_context_t *context, const void* start, size_t length, uint32_t wait_ms)
{
sdmmc_card_t *card = context->card;
uint16_t buffer_size = context->buffer_size;
int buffer_used = (length + buffer_size - 1)/buffer_size;
esp_err_t err;
const uint32_t wait_ticks = wait_ms/portTICK_PERIOD_MS;
uint32_t pre = xTaskGetTickCount();
assert(length>0);
for(;;) {
uint32_t num = 0;
err = esp_slave_get_tx_buffer_num(context, &num);
if (err == ESP_OK && num * buffer_size >= length) break;
if (err != ESP_OK && err != ESP_ERR_TIMEOUT) return err;
//not error and buffer not enough, retry ``timeout_cnt`` times
uint32_t now = xTaskGetTickCount();
if (now-pre >= wait_ticks) {
ESP_LOGD(TAG, "buffer is not enough: %d, %d required.", num, buffer_used);
return ESP_ERR_TIMEOUT;
} else {
ESP_LOGV(TAG, "buffer is not enough: %d, %d required. Retry...", num, buffer_used);
}
vTaskDelay(1);
}
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(card, 1, ESP_SLAVE_CMD53_END_ADDR - len_remain, start_ptr, len_to_send);
} else {
len_to_send = len_remain;
err = sdmmc_io_write_bytes(card, 1, ESP_SLAVE_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);
context->tx_sent_buffers += buffer_used;
return ESP_OK;
}
esp_err_t esp_slave_get_packet(esp_slave_context_t *context, void* out_data, size_t size, size_t *out_length, uint32_t wait_ms)
{
sdmmc_card_t *card = context->card;
esp_err_t err;
esp_err_t ret = ESP_OK;
uint32_t len;
const uint32_t wait_ticks = wait_ms/portTICK_PERIOD_MS;
uint32_t pre = xTaskGetTickCount();
assert (size>0);
for (;;) {
err = esp_slave_get_rx_data_size(context, &len);
if (err == ESP_OK && len > 0) break;
if (err != ESP_OK && err != ESP_ERR_TIMEOUT) return err;
//not error and no data, retry ``timeout_cnt`` times.
uint32_t now = xTaskGetTickCount();
if (now-pre >= wait_ticks) return ESP_ERR_NOT_FOUND;
vTaskDelay(1);
}
ESP_LOGV(TAG, "get_packet: slave len=%d, max read size=%d", len, size);
if (len > size) {
len = size;
ret = ESP_ERR_NOT_FINISHED;
}
uint8_t *start = out_data;
uint32_t len_remain = len;
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(card, 1, ESP_SLAVE_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(card, 1, ESP_SLAVE_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;
} while(len_remain!=0);
context->rx_got_bytes += len;
*out_length = len;
return ret;
}
esp_err_t esp_slave_get_tx_buffer_num(esp_slave_context_t *context, uint32_t* tx_num)
{
uint32_t len;
esp_err_t err;
ESP_LOGV(TAG, "get_tx_buffer_num");
err = esp_slave_read_bytes(context, HOST_SLC0HOST_TOKEN_RDATA_REG, (uint8_t*)&len, 4);
if (err != ESP_OK) return err;
len = (len>>16)&TX_BUFFER_MASK;
len = (len + TX_BUFFER_MAX - context->tx_sent_buffers)%TX_BUFFER_MAX;
*tx_num = len;
return ESP_OK;
}
esp_err_t esp_slave_get_rx_data_size(esp_slave_context_t *context, uint32_t* rx_size)
{
uint32_t len;
esp_err_t err;
ESP_LOGV(TAG, "get_rx_data_size: got_bytes: %d", context->rx_got_bytes);
err = esp_slave_read_bytes(context, HOST_SLCHOST_PKT_LEN_REG, (uint8_t*)&len, 4);
if (err != ESP_OK) return err;
len &= RX_BYTE_MASK;
len = (len + RX_BYTE_MAX - context->rx_got_bytes)%RX_BYTE_MAX;
*rx_size = len;
return ESP_OK;
}
esp_err_t esp_slave_clear_intr(esp_slave_context_t *context, uint32_t intr_mask)
{
ESP_LOGV(TAG, "clear_intr: %08X", intr_mask);
return esp_slave_write_bytes(context, HOST_SLC0HOST_INT_CLR_REG, (uint8_t*)&intr_mask, 4);
}
esp_err_t esp_slave_get_intr(esp_slave_context_t *context, uint32_t *intr_raw, uint32_t *intr_st)
{
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= esp_slave_read_bytes(context, HOST_SLC0HOST_INT_RAW_REG, (uint8_t*)intr_raw, 4);
if (r != ESP_OK) return r;
}
if (intr_st != NULL) {
r = esp_slave_read_bytes(context, HOST_SLC0HOST_INT_ST_REG, (uint8_t*)intr_st, 4);
if (r != ESP_OK) return r;
}
return ESP_OK;
}
esp_err_t esp_slave_set_intr_ena(esp_slave_context_t *context, uint32_t ena_mask)
{
ESP_LOGV(TAG, "set_intr_ena: %08X", ena_mask);
return esp_slave_write_bytes(context, HOST_SLC0HOST_INT_ENA_REG, (uint8_t*)&ena_mask, 4);
}
esp_err_t esp_slave_get_intr_ena(esp_slave_context_t *context, uint32_t *ena_mask_o)
{
ESP_LOGV(TAG, "get_intr_ena");
esp_err_t ret = esp_slave_read_bytes(context, HOST_SLC0HOST_INT_ENA_REG, (uint8_t*)ena_mask_o, 4);
ESP_LOGV(TAG, "ena: %08X", *ena_mask_o);
return ret;
}
esp_err_t esp_slave_write_reg(esp_slave_context_t *context, uint8_t addr, uint8_t value, uint8_t* value_o)
{
ESP_LOGV(TAG, "write_reg: %08X", value);
// addrress over range
if (addr >= 64) return ESP_ERR_INVALID_ARG;
// reserved for interrupts
if (addr >= 28 && addr <= 31) return ESP_ERR_INVALID_ARG;
return esp_slave_write_byte(context, HOST_SLCHOST_CONF_W_REG(addr), value, value_o);
}
esp_err_t esp_slave_read_reg(esp_slave_context_t *context, uint8_t add, uint8_t *value_o)
{
ESP_LOGV(TAG, "read_reg");
// address over range
if (add >= 64) return ESP_ERR_INVALID_ARG;
esp_err_t ret = esp_slave_read_byte(context, HOST_SLCHOST_CONF_W_REG(add), value_o);
ESP_LOGV(TAG, "reg: %08X", *value_o);
return ret;
}
esp_err_t esp_slave_send_slave_intr(esp_slave_context_t *context, uint8_t intr_mask)
{
ESP_LOGV(TAG, "send_slave_intr: %02x", intr_mask);
return esp_slave_write_byte(context, HOST_SLCHOST_CONF_W7_REG+0, intr_mask, NULL);
}

View File

@ -1,233 +0,0 @@
// Copyright 2015-2018 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 "sdmmc_cmd.h"
#include "driver/sdmmc_defs.h"
#include "soc/sdio_slave_periph.h"
/*
* NOTE: This component is for example purpose only. Assertion fails if any of
* the preconditions (connections, grounding, slave data preparation, etc.) is
* not met.
* Please do check and handle the return value in your real product.
*/
#define ESP_ERR_NOT_FINISHED 0x201
/** Context used by the ``esp_slave`` component.
*/
typedef struct {
sdmmc_card_t* card; ///< Initialized sdmmc_cmd card
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.
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.
size_t tx_sent_buffers; ///< Counter hold the amount of buffers already sent to ESP32 slave. Should be set to 0 when initialization.
size_t rx_got_bytes; ///< Counter hold the amount of bytes already received from ESP32 slave. Should be set to 0 when initialization.
} esp_slave_context_t;
/** Initialize ``esp_slave_context_t`` by this macro.
*/
#define ESP_SLAVE_DEFAULT_CONTEXT(card) (esp_slave_context_t){\
.card = card, \
.block_size = 0x200, \
.buffer_size = 128, \
.tx_sent_buffers = 0, \
.rx_got_bytes = 0, \
}
/** SDIO Initialize process of a ESP32 slave device.
*
* @param context Context of the ``esp_slave`` component. Send to other functions later.
*
* @return
* - ESP_OK if success
* - One of the error codes from SDMMC host controller
*/
esp_err_t esp_slave_init_io(esp_slave_context_t *context);
/** Wait for interrupt of a ESP32 slave device.
*
* @param context Context of the ``esp_slave`` component.
*
* @return
* - ESP_OK if success
* - One of the error codes from SDMMC host controller
*/
esp_err_t esp_slave_wait_for_ioready(esp_slave_context_t *context);
/** Get buffer num for the host to send data to the slave. The buffers are size of ``buffer_size``.
*
* @param context Context of the component.
* @param tx_num Output of buffer num that host can send data to ESP32 slave.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
*/
esp_err_t esp_slave_get_tx_buffer_num(esp_slave_context_t *context, uint32_t* tx_num);
/** Get amount of data the ESP32 slave preparing to send to host.
*
* @param context Context of the component.
* @param rx_size Output of data size to read from slave.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
*/
esp_err_t esp_slave_get_rx_data_size(esp_slave_context_t *context, uint32_t* rx_size);
/** Reset the counters of this component. Usually you don't need to do this unless you know the slave is reset.
*
* @param context Context of the component.
*/
inline static void esp_slave_reset_cnt(esp_slave_context_t *context)
{
context->rx_got_bytes = 0;
context->tx_sent_buffers = 0;
}
/** Send a packet to the ESP32 slave. The slave receive the packet into buffers whose size is ``buffer_size`` in the context.
*
* @param context 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 esp_slave_send_packet(esp_slave_context_t *context, const void* start, size_t length, uint32_t wait_ms);
/** Get a packet from ESP32 slave.
*
* @param context 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[out] out_length Output of length the data actually received from slave.
* @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 esp_slave_get_packet(esp_slave_context_t *context, void* out_data, size_t size, size_t *out_length, uint32_t wait_ms);
/** wait for an interrupt of the slave
*
* @param context Context of the component.
* @param wait Ticks to wait.
*
* @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.
*/
inline static esp_err_t esp_slave_wait_int(esp_slave_context_t *context, TickType_t wait)
{
return sdmmc_io_wait_int(context->card, wait);
}
/** Clear interrupt bits of ESP32 slave. All the bits set in the mask will be cleared, while other bits will stay the same.
*
* @param context Context of the component.
* @param intr_mask Mask of interrupt bits to clear.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
*/
esp_err_t esp_slave_clear_intr(esp_slave_context_t *context, uint32_t intr_mask);
/** Get interrupt bits of ESP32 slave.
*
* @param context 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.
*
* @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 esp_slave_get_intr(esp_slave_context_t *context, uint32_t *intr_raw, uint32_t *intr_st);
/** 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 context Context of the component.
* @param ena_mask Mask of the interrupt bits to enable.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
*/
esp_err_t esp_slave_set_intr_ena(esp_slave_context_t *context, uint32_t ena_mask);
/** Get interrupt enable bits of ESP32 slave.
*
* @param context Context of the component.
* @param ena_mask_o Output of interrupt bit enable mask.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
*/
esp_err_t esp_slave_get_intr_ena(esp_slave_context_t *context, uint32_t *ena_mask_o);
/** Write general purpose R/W registers (8-bit) of ESP32 slave.
*
* @param context 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.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Address not valid.
* - One of the error codes from SDMMC host controller
*/
esp_err_t esp_slave_write_reg(esp_slave_context_t *context, uint8_t addr, uint8_t value, uint8_t* value_o);
/** Read general purpose R/W registers (8-bit) of ESP32 slave.
*
* @param context 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.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Address not valid.
* - One of the error codes from SDMMC host controller
*/
esp_err_t esp_slave_read_reg(esp_slave_context_t *context, uint8_t add, uint8_t *value_o);
/** Send interrupts to slave. Each bit of the interrupt will be triggered.
*
* @param context Context of the component.
* @param intr_mask Mask of interrupt bits to send to slave.
*
* @return
* - ESP_OK Success
* - One of the error codes from SDMMC host controller
*/
esp_err_t esp_slave_send_slave_intr(esp_slave_context_t *context, uint8_t intr_mask);

View File

@ -33,25 +33,21 @@ menu "Example Configuration"
If the example does not work, please try disabling the HS mode.
choice EXAMPLE_SLAVE
prompt "Id of Slave used in Espressif master-slave board."
prompt "GPIO to control slave EN in Espressif master-slave board."
default EXAMPLE_SLAVE_NONE
help
If Espressif master-slave board is used, select which slave is used.
If Espressif master-slave board is used, select the correct GPIO to control slave's EN.
config EXAMPLE_SLAVE_NONE
bool "Not using Espressif master-slave board."
config EXAMPLE_SLAVE_B1
bool "Using slave B1"
config EXAMPLE_SLAVE_B2
bool "Using slave B2"
config EXAMPLE_SLAVE_B3
bool "Using slave B3"
endchoice
config EXAMPLE_SLAVE_PWR_NEGTIVE_ACTIVE
bool "Slave power control pin is negtive active, otherwise postive active"
depends on !EXAMPLE_SLAVE_NONE
default y
default n
help
Slave power control pin is negtive active, otherwise postive active

View File

@ -1,4 +1,4 @@
/* SDIO example, host (uses sdmmc host driver)
/* SDIO example, host (uses sdmmc_host/sdspi_host driver)
This example code is in the Public Domain (or CC0 licensed, at your option.)
@ -20,26 +20,18 @@
#include "soc/sdio_slave_periph.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_slave.h"
#include "esp_serial_slave_link/essl_sdio.h"
#include "sdkconfig.h"
#include "driver/sdmmc_host.h"
#include "driver/sdspi_host.h"
#define TIMEOUT_MAX UINT32_MAX
/*
* For SDIO master-slave board, we have 3 pins controlling power of 3 different
* slaves individially. We only enable one at a time.
*/
#define GPIO_B1 5
#define GPIO_B2 18
#define GPIO_B3 19
#define GPIO_B1 21
#if CONFIG_EXAMPLE_SLAVE_B1
#define SLAVE_PWR_GPIO GPIO_B1
#elif CONFIG_EXAMPLE_SLAVE_B2
#define SLAVE_PWR_GPIO GPIO_B2
#elif CONFIG_EXAMPLE_SLAVE_B3
#define SLAVE_PWR_GPIO GPIO_B3
#endif
/*
@ -85,6 +77,7 @@
#define WRITE_BUFFER_LEN 4096
#define READ_BUFFER_LEN 4096
#define SLAVE_BUFFER_SIZE 128
static const char TAG[] = "example_host";
@ -103,21 +96,21 @@ typedef enum {
} example_job_t;
//host use this to inform the slave it should reset its counters
esp_err_t slave_reset(esp_slave_context_t *context)
esp_err_t slave_reset(essl_handle_t handle)
{
esp_err_t ret;
ESP_LOGI(TAG, "send reset to slave...");
ret = esp_slave_write_reg(context, 0, JOB_RESET, NULL);
ret = essl_write_reg(handle, 0, JOB_RESET, NULL, TIMEOUT_MAX);
if (ret != ESP_OK) {
return ret;
}
ret = esp_slave_send_slave_intr(context, BIT(SLAVE_INTR_NOTIFY));
ret = essl_send_slave_intr(handle, BIT(SLAVE_INTR_NOTIFY), TIMEOUT_MAX);
if (ret != ESP_OK) {
return ret;
}
vTaskDelay(500 / portTICK_RATE_MS);
ret = esp_slave_wait_for_ioready(context);
ret = essl_wait_for_ready(handle, TIMEOUT_MAX);
ESP_LOGI(TAG, "slave io ready");
return ret;
}
@ -166,7 +159,7 @@ static esp_err_t print_sdio_cis_information(sdmmc_card_t* card)
}
//host use this to initialize the slave card as well as SDIO registers
esp_err_t slave_init(esp_slave_context_t *context)
esp_err_t slave_init(essl_handle_t* handle)
{
esp_err_t err;
/* Probe */
@ -247,8 +240,14 @@ esp_err_t slave_init(esp_slave_context_t *context)
gpio_pullup_en(13);
gpio_pulldown_dis(13);
*context = ESP_SLAVE_DEFAULT_CONTEXT(card);
esp_err_t ret = esp_slave_init_io(context);
essl_sdio_config_t ser_config = {
.card = card,
.recv_buffer_size = SLAVE_BUFFER_SIZE,
};
err = essl_sdio_init_dev(handle, &ser_config);
ESP_ERROR_CHECK(err);
esp_err_t ret = essl_init(*handle, TIMEOUT_MAX);
ESP_ERROR_CHECK(ret);
ret = print_sdio_cis_information(card);
@ -267,7 +266,7 @@ void slave_power_on(void)
level_active = 1;
#endif
gpio_config_t cfg = {
.pin_bit_mask = BIT64(GPIO_B1) | BIT64(GPIO_B2) | BIT64(GPIO_B3),
.pin_bit_mask = BIT64(GPIO_B1),
.mode = GPIO_MODE_DEF_OUTPUT,
.pull_up_en = false,
.pull_down_en = false,
@ -275,8 +274,6 @@ void slave_power_on(void)
};
gpio_config(&cfg);
gpio_set_level(GPIO_B1, !level_active);
gpio_set_level(GPIO_B2, !level_active);
gpio_set_level(GPIO_B3, !level_active);
vTaskDelay(100);
gpio_set_level(SLAVE_PWR_GPIO, level_active);
@ -288,18 +285,18 @@ void slave_power_on(void)
DMA_ATTR uint8_t rcv_buffer[READ_BUFFER_LEN];
//try to get an interrupt from the slave and handle it, return if none.
esp_err_t process_event(esp_slave_context_t *context)
esp_err_t process_event(essl_handle_t handle)
{
esp_err_t ret = esp_slave_wait_int(context, 0);
esp_err_t ret = essl_wait_int(handle, 0);
if (ret == ESP_ERR_TIMEOUT) {
return ret;
}
ESP_ERROR_CHECK(ret);
uint32_t intr_raw, intr_st;
ret = esp_slave_get_intr(context, &intr_raw, &intr_st);
ret = essl_get_intr(handle, &intr_raw, &intr_st, TIMEOUT_MAX);
ESP_ERROR_CHECK(ret);
ret = esp_slave_clear_intr(context, intr_raw);
ret = essl_clear_intr(handle, intr_raw, TIMEOUT_MAX);
ESP_ERROR_CHECK(ret);
ESP_LOGD(TAG, "intr: %08X", intr_raw);
@ -314,7 +311,7 @@ esp_err_t process_event(esp_slave_context_t *context)
ESP_LOGD(TAG, "new packet coming");
while (1) {
size_t size_read = READ_BUFFER_LEN;
ret = esp_slave_get_packet(context, rcv_buffer, READ_BUFFER_LEN, &size_read, wait_ms);
ret = essl_get_packet(handle, rcv_buffer, READ_BUFFER_LEN, &size_read, wait_ms);
if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "interrupt but no data can be read");
break;
@ -334,32 +331,32 @@ esp_err_t process_event(esp_slave_context_t *context)
}
//tell the slave to do a job
static inline esp_err_t slave_inform_job(esp_slave_context_t *context, example_job_t job)
static inline esp_err_t slave_inform_job(essl_handle_t handle, example_job_t job)
{
esp_err_t ret;
ret = esp_slave_write_reg(context, SLAVE_REG_JOB, job, NULL);
ret = essl_write_reg(handle, SLAVE_REG_JOB, job, NULL, TIMEOUT_MAX);
ESP_ERROR_CHECK(ret);
ret = esp_slave_send_slave_intr(context, BIT(SLAVE_INTR_NOTIFY));
ret = essl_send_slave_intr(handle, BIT(SLAVE_INTR_NOTIFY), TIMEOUT_MAX);
ESP_ERROR_CHECK(ret);
return ret;
}
//tell the slave to write registers by write one of them, and read them back
void job_write_reg(esp_slave_context_t *context, int value)
void job_write_reg(essl_handle_t handle, int value)
{
esp_err_t ret;
uint8_t reg_read[64];
uint8_t reg_read[60];
ESP_LOGI(TAG, "========JOB: write slave reg========");
ret = esp_slave_write_reg(context, SLAVE_REG_VALUE, value, NULL);
ret = essl_write_reg(handle, SLAVE_REG_VALUE, value, NULL, TIMEOUT_MAX);
ESP_ERROR_CHECK(ret);
ret = slave_inform_job(context, JOB_WRITE_REG);
ret = slave_inform_job(handle, JOB_WRITE_REG);
ESP_ERROR_CHECK(ret);
vTaskDelay(10);
for (int i = 0; i < 64; i++) {
for (int i = 0; i < 60; i++) {
ESP_LOGD(TAG, "reading register %d", i);
ret = esp_slave_read_reg(context, i, &reg_read[i]);
ret = essl_read_reg(handle, i, &reg_read[i], TIMEOUT_MAX);
ESP_ERROR_CHECK(ret);
}
@ -374,7 +371,7 @@ int packet_len[] = {6, 12, 1024, 512, 3, 513, 517};
DMA_ATTR uint8_t send_buffer[READ_BUFFER_LEN];
//send packets to the slave (they will return and be handled by the interrupt handler)
void job_fifo(esp_slave_context_t *context)
void job_fifo(essl_handle_t handle)
{
for (int i = 0; i < READ_BUFFER_LEN; i++) {
send_buffer[i] = 0x46 + i * 5;
@ -392,7 +389,7 @@ void job_fifo(esp_slave_context_t *context)
for (int i = 0; i < sizeof(packet_len) / sizeof(int); i++) {
const int wait_ms = 50;
int length = packet_len[i];
ret = esp_slave_send_packet(context, send_buffer + pointer, length, wait_ms);
ret = essl_send_packet(handle, send_buffer + pointer, length, wait_ms);
if (ret == ESP_ERR_TIMEOUT) {
ESP_LOGD(TAG, "several packets are expected to timeout.");
} else {
@ -404,15 +401,15 @@ void job_fifo(esp_slave_context_t *context)
}
//inform the slave to send interrupts to host (the interrupts will be handled in the interrupt handler)
void job_getint(esp_slave_context_t *context)
void job_getint(essl_handle_t handle)
{
ESP_LOGI(TAG, "========JOB: get interrupts from slave========");
slave_inform_job(context, JOB_SEND_INT);
slave_inform_job(handle, JOB_SEND_INT);
}
void app_main(void)
{
esp_slave_context_t context;
essl_handle_t handle;
esp_err_t err;
//enable the power if on espressif SDIO master-slave board
@ -420,23 +417,23 @@ void app_main(void)
ESP_LOGI(TAG, "host ready, start initializing slave...");
err = slave_init(&context);
err = slave_init(&handle);
ESP_ERROR_CHECK(err);
err = slave_reset(&context);
err = slave_reset(handle);
ESP_ERROR_CHECK(err);
uint32_t start, end;
job_write_reg(&context, 10);
job_write_reg(handle, 10);
int times = 2;
while (1) {
job_getint(&context);
job_getint(handle);
start = xTaskGetTickCount();
while (1) {
process_event(&context);
process_event(handle);
vTaskDelay(1);
end = xTaskGetTickCount();
if ((end - start) * 1000 / CONFIG_FREERTOS_HZ > 5000) {
@ -449,11 +446,11 @@ void app_main(void)
};
while (1) {
job_fifo(&context);
job_fifo(handle);
start = xTaskGetTickCount();
while (1) {
process_event(&context);
process_event(handle);
vTaskDelay(1);
end = xTaskGetTickCount();
if ((end - start) * 1000 / CONFIG_FREERTOS_HZ > 2000) {

View File

@ -492,6 +492,21 @@ UT_043:
- UT_T1_32kXTAL
- psram
UT_044:
extends: .unit_test_template
parallel: 4
tags:
- ESP32_IDF
- UT_SDIO
UT_045:
extends: .unit_test_template
tags:
- ESP32_IDF
- UT_SDIO
- psram
nvs_compatible_test:
extends: .test_template
artifacts: