mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
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:
commit
38be5c140a
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
)
|
||||
|
769
components/driver/test/esp32/test_sdio.c
Normal file
769
components/driver/test/esp32/test_sdio.c
Normal 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);
|
@ -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
|
||||
|
9
components/esp_serial_slave_link/CMakeLists.txt
Normal file
9
components/esp_serial_slave_link/CMakeLists.txt
Normal 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"
|
||||
)
|
||||
|
@ -3,4 +3,5 @@
|
||||
#
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
COMPONENT_PRIV_INCLUDEDIRS := . include/esp_serial_slave_link
|
||||
|
244
components/esp_serial_slave_link/essl.c
Normal file
244
components/esp_serial_slave_link/essl.c
Normal 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);
|
||||
}
|
||||
|
45
components/esp_serial_slave_link/essl_internal.h
Normal file
45
components/esp_serial_slave_link/essl_internal.h
Normal 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;
|
455
components/esp_serial_slave_link/essl_sdio.c
Normal file
455
components/esp_serial_slave_link/essl_sdio.c
Normal 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;
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
@ -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 )
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
##
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
154
docs/en/api-reference/protocols/esp_serial_slave_link.rst
Normal file
154
docs/en/api-reference/protocols/esp_serial_slave_link.rst
Normal 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
|
@ -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.
|
||||
|
||||
|
@ -71,7 +71,7 @@ In particular, the driver does not set any bits in (1) I/O Enable and Int Enable
|
||||
For card configuration and data transfer, choose the pair of functions relevant to your case from the table below.
|
||||
|
||||
========================================================================= ================================= =================================
|
||||
Action Read Function Write Function
|
||||
Action Read Function Write Function
|
||||
========================================================================= ================================= =================================
|
||||
Read and write a single byte using IO_RW_DIRECT (CMD52) :cpp:func:`sdmmc_io_read_byte` :cpp:func:`sdmmc_io_write_byte`
|
||||
Read and write multiple bytes using IO_RW_EXTENDED (CMD53) in byte mode :cpp:func:`sdmmc_io_read_bytes` :cpp:func:`sdmmc_io_write_bytes`
|
||||
@ -82,6 +82,8 @@ SDIO interrupts can be enabled by the application using the function :cpp:func:`
|
||||
|
||||
If you want the application to wait until the SDIO interrupt occurs, use :cpp:func:`sdmmc_io_wait_int`.
|
||||
|
||||
There is a component ESSL (ESP Serial Slave Link) to use if you are communicating with an ESP32
|
||||
SDIO slave. See :doc:`/api-reference/protocols/esp_serial_slave_link` and example :example:`peripherals/sdio/host`.
|
||||
|
||||
Combo (memory + IO) cards
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -0,0 +1 @@
|
||||
.. include:: ../../../en/api-reference/protocols/esp_serial_slave_link.rst
|
@ -3,19 +3,20 @@
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:maxdepth: 1
|
||||
|
||||
mDNS <mdns>
|
||||
ESP-TLS <esp_tls>
|
||||
HTTP Client <esp_http_client>
|
||||
Websocket Client <esp_websocket_client>
|
||||
HTTP 服务器 <esp_http_server>
|
||||
HTTPS Server <esp_https_server>
|
||||
ICMP Echo <icmp_echo>
|
||||
ASIO <asio>
|
||||
ESP-MQTT <mqtt>
|
||||
Modbus slave <modbus>
|
||||
Local Control <esp_local_ctrl>
|
||||
mDNS <mdns>
|
||||
ESP-TLS <esp_tls>
|
||||
HTTP Client <esp_http_client>
|
||||
Websocket Client <esp_websocket_client>
|
||||
HTTP 服务器 <esp_http_server>
|
||||
HTTPS Server <esp_https_server>
|
||||
ICMP Echo <icmp_echo>
|
||||
ASIO <asio>
|
||||
ESP-MQTT <mqtt>
|
||||
Modbus slave <modbus>
|
||||
Local Control <esp_local_ctrl>
|
||||
ESP Serial Slave Link <esp_serial_slave_link>
|
||||
|
||||
此 API 部分的示例代码在 ESP-IDF 示例工程的 :example:`protocols` 目录下提供。
|
||||
|
||||
|
@ -84,7 +84,7 @@ better or disabling the HS mode in menuconfig.
|
||||
to pull down DAT2 line to set proper flash voltage. This conflicts with SDIO
|
||||
pullup requirements. Currently devkits using PICO-D4 and Wroom-32 series
|
||||
modules have this problem. You can either:
|
||||
|
||||
|
||||
- Use Wrover Kit v3 which integrates a Wrover module
|
||||
- Still use PICO-D4 or Wroom-32 Series modules as the slave, however:
|
||||
- Don't connect the DAT2 pin and leave it floating. This means
|
||||
@ -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.
|
||||
|
@ -1,3 +0,0 @@
|
||||
idf_component_register(SRCS "esp_slave.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES driver sdmmc)
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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, ®_read[i]);
|
||||
ret = essl_read_reg(handle, i, ®_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) {
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user