mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
feat(sdio): add hw_test example for signal integrity checks
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(sdio_host)
|
||||
@@ -0,0 +1,20 @@
|
||||
| Supported Targets | ESP32 | ESP32-S3 | ESP32-S31 |
|
||||
| ----------------- | ----- | -------- | --------- |
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Getting the following error
|
||||
|
||||
> `E (2562) sdmmc_io: sdmmc_io_read_byte: sdmmc_io_rw_direct (read 0x2) returned 0x107`
|
||||
|
||||
Kindly activate the debug log from `idf.py menuconfig -> Component config -> Log output -> Default log verbosity` option and verify the card's IO type status. If the card does not possess IO capabilities, it will encounter issues while attempting to read IO buffers.
|
||||
|
||||
To identify the card type, please inspect the following lines in the debug log.
|
||||
|
||||
```
|
||||
D (659) sdmmc_io: sdmmc_init_io: io_send_op_cond (1) returned 0x106; not IO card
|
||||
D (729) sdmmc_init: sdmmc_card_init: card type is SD
|
||||
```
|
||||
|
||||
See README.md in the parent folder
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "app_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,133 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||
|
||||
choice EXAMPLE_SDIO_INTERFACE
|
||||
prompt "Interface to communicate with slave"
|
||||
default EXAMPLE_SDIO_OVER_SDMMC
|
||||
help
|
||||
There are two interfaces to communicate with the SDIO slave: SDMMC and SPI.
|
||||
The SDMMC interface is 8 times faster than the SPI interface. However not all Espressif chips have this
|
||||
interface.
|
||||
|
||||
Alternatively you can use the SPI interface.
|
||||
|
||||
config EXAMPLE_SDIO_OVER_SDMMC
|
||||
bool "SDMMC host"
|
||||
depends on SOC_SDMMC_HOST_SUPPORTED
|
||||
|
||||
config EXAMPLE_SDIO_OVER_SPI
|
||||
bool "SPI"
|
||||
depends on IDF_TARGET_ESP32
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_SDIO_4BIT
|
||||
bool "Host tries using 4-bit mode to communicate with slave"
|
||||
default n
|
||||
depends on !EXAMPLE_SDIO_OVER_SPI
|
||||
help
|
||||
If this is set, the host tries using 4-bit mode to communicate with
|
||||
slave. If failed, the communication falls back to 1-bit mode.
|
||||
|
||||
If this is not set, the host uses 1-bit mode. However, CMD1 is still
|
||||
mandatory for interrupts.
|
||||
|
||||
Note that 4-bit mode is not compatible (by default) if the slave is
|
||||
using 3.3V flash which requires a pull-down on the MTDI pin.
|
||||
|
||||
config EXAMPLE_SDIO_HIGHSPEED
|
||||
bool "Host tries using HS mode to communicate with slave"
|
||||
default y
|
||||
depends on EXAMPLE_SDIO_OVER_SDMMC
|
||||
help
|
||||
If this is set, the host tries using high-speed mode to communicate
|
||||
with slave. If the slave doesn't support high-speed mode, the
|
||||
communication falls back to default-speed mode. If this is not set,
|
||||
the host uses DS mode.
|
||||
|
||||
If the example does not work, please try disabling the HS mode.
|
||||
|
||||
config EXAMPLE_SDIO_HOST_DELAY
|
||||
int "SDIO Host Delay"
|
||||
range 0 5
|
||||
default 0
|
||||
help
|
||||
SDIO Host delay phase
|
||||
|
||||
config EXAMPLE_ADJUSTABLE_PIN
|
||||
bool
|
||||
default EXAMPLE_SDIO_OVER_SPI || SOC_SDMMC_USE_GPIO_MATRIX
|
||||
|
||||
config EXAMPLE_PIN_CMD
|
||||
int "CMD (MOSI) GPIO number"
|
||||
depends on EXAMPLE_ADJUSTABLE_PIN
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 4 if IDF_TARGET_ESP32P4
|
||||
default 15
|
||||
|
||||
config EXAMPLE_PIN_CLK
|
||||
int "CLK GPIO number"
|
||||
depends on EXAMPLE_ADJUSTABLE_PIN
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 33 if IDF_TARGET_ESP32P4
|
||||
default 14
|
||||
|
||||
config EXAMPLE_PIN_D0
|
||||
int "D0 (MISO) GPIO number"
|
||||
depends on EXAMPLE_ADJUSTABLE_PIN
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 32 if IDF_TARGET_ESP32P4
|
||||
default 2
|
||||
|
||||
config EXAMPLE_PIN_D1
|
||||
int "D1 (INTR) GPIO number"
|
||||
depends on EXAMPLE_ADJUSTABLE_PIN
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 23 if IDF_TARGET_ESP32P4
|
||||
default 4
|
||||
|
||||
config EXAMPLE_PIN_D2
|
||||
int "D2 GPIO number"
|
||||
depends on EXAMPLE_ADJUSTABLE_PIN
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 53 if IDF_TARGET_ESP32P4
|
||||
default 12
|
||||
|
||||
config EXAMPLE_PIN_D3
|
||||
int "D3 (CS) GPIO number"
|
||||
depends on EXAMPLE_ADJUSTABLE_PIN
|
||||
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||
default 5 if IDF_TARGET_ESP32P4
|
||||
default 13
|
||||
|
||||
choice EXAMPLE_SLAVE
|
||||
prompt "GPIO to control slave EN in Espressif master-slave board."
|
||||
default EXAMPLE_SLAVE_NONE
|
||||
help
|
||||
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"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_SLAVE_PWR_NEGTIVE_ACTIVE
|
||||
bool "Slave power control pin is negative active, otherwise positive active"
|
||||
depends on !EXAMPLE_SLAVE_NONE
|
||||
default n
|
||||
help
|
||||
Slave power control pin is negative active, otherwise positive active
|
||||
|
||||
config EXAMPLE_NO_INTR_LINE
|
||||
bool "The host is not connected to the interrupt line (DAT1) of slave"
|
||||
default n
|
||||
help
|
||||
If this is set, the host example will not check the interrupt line but poll slave
|
||||
registers to see whether the slave has interrupts for the host.
|
||||
|
||||
Working without the interrupt line may increase the CPU load of the host, and do harm
|
||||
to the response speed to slave events, though can save 1 GPIO for other purposes in
|
||||
non-4-bit mode.
|
||||
|
||||
endmenu
|
||||
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
|
||||
#include "esp_serial_slave_link/essl_sdio.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
|
||||
#define TIMEOUT_MAX UINT32_MAX
|
||||
|
||||
#define GPIO_B1 21
|
||||
|
||||
#if CONFIG_EXAMPLE_SLAVE_B1
|
||||
#define SLAVE_PWR_GPIO GPIO_B1
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_ADJUSTABLE_PIN
|
||||
#define PIN_CLK CONFIG_EXAMPLE_PIN_CLK
|
||||
#define PIN_CMD CONFIG_EXAMPLE_PIN_CMD
|
||||
#define PIN_D0 CONFIG_EXAMPLE_PIN_D0
|
||||
#define PIN_D1 CONFIG_EXAMPLE_PIN_D1
|
||||
#define PIN_D2 CONFIG_EXAMPLE_PIN_D2
|
||||
#define PIN_D3 CONFIG_EXAMPLE_PIN_D3
|
||||
#else
|
||||
// For ESP32 only, when not using SDMMC host
|
||||
#include "soc/sdmmc_pins.h"
|
||||
#define PIN_CLK SDMMC_SLOT1_IOMUX_PIN_NUM_CLK
|
||||
#define PIN_CMD SDMMC_SLOT1_IOMUX_PIN_NUM_CMD
|
||||
#define PIN_D0 SDMMC_SLOT1_IOMUX_PIN_NUM_D0
|
||||
#define PIN_D1 SDMMC_SLOT1_IOMUX_PIN_NUM_D1
|
||||
#define PIN_D2 SDMMC_SLOT1_IOMUX_PIN_NUM_D2
|
||||
#define PIN_D3 SDMMC_SLOT1_IOMUX_PIN_NUM_D3
|
||||
#endif
|
||||
|
||||
/*
|
||||
sdio host example.
|
||||
|
||||
This example is supposed to work together with the sdio slave example. It uses the pins as follows:
|
||||
|
||||
* Host Slave
|
||||
* IO14 CLK
|
||||
* IO15 CMD
|
||||
* IO2 D0
|
||||
* IO4 D1
|
||||
* IO12 D2
|
||||
* IO13 D3
|
||||
|
||||
This is the only pins that can be used in standard ESP modules. The other set of pins (6, 11, 7, 8, 9, 10)
|
||||
are occupied by the spi bus communicating with the flash.
|
||||
|
||||
Protocol Above the ESP slave service:
|
||||
- Interrupts:
|
||||
0 is used to notify the slave to read the register 0.
|
||||
|
||||
- Registers:
|
||||
- 0 is the register to hold tasks. Bits:
|
||||
- 0: the slave should reset.
|
||||
- 1: the slave should send interrupts.
|
||||
- 2: the slave should write the shared registers according to the value in register 1.
|
||||
- 1 is the register to hold test value.
|
||||
- other registers will be written by the slave for testing.
|
||||
|
||||
- FIFO:
|
||||
The receiving FIFO is size of 256 bytes.
|
||||
When the host writes something to slave recv FIFO, the slave should return it as is to the sending FIFO.
|
||||
|
||||
The example works as following process:
|
||||
|
||||
1. reset the slave.
|
||||
2. tell the slave to write registers and read them back.
|
||||
3. tell the slave to send interrupts to the host.
|
||||
4. send data to slave FIFO and read them back.
|
||||
5. loop step 4.
|
||||
*/
|
||||
|
||||
#define WRITE_BUFFER_LEN 4096
|
||||
#define READ_BUFFER_LEN 4096
|
||||
#define SLAVE_BUFFER_SIZE 128
|
||||
|
||||
static const char TAG[] = "example_host";
|
||||
|
||||
#define SLAVE_INTR_NOTIFY 0
|
||||
|
||||
#define SLAVE_REG_JOB 0
|
||||
#define SLAVE_REG_VALUE 1
|
||||
|
||||
typedef enum {
|
||||
JOB_IDLE = 0,
|
||||
JOB_RESET = 1,
|
||||
JOB_SEND_INT = 2,
|
||||
JOB_WRITE_REG = 4,
|
||||
} example_job_t;
|
||||
|
||||
//host use this to inform the slave it should reset its counters
|
||||
esp_err_t slave_reset(essl_handle_t handle)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ESP_LOGI(TAG, "send reset to slave...");
|
||||
ret = essl_write_reg(handle, 0, JOB_RESET, NULL, TIMEOUT_MAX);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = essl_send_slave_intr(handle, BIT(SLAVE_INTR_NOTIFY), TIMEOUT_MAX);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||
ret = essl_wait_for_ready(handle, TIMEOUT_MAX);
|
||||
ESP_LOGI(TAG, "slave io ready");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_SDIO_OVER_SPI
|
||||
static void gpio_d2_set_high(void)
|
||||
{
|
||||
gpio_config_t d2_config = {
|
||||
.pin_bit_mask = BIT64(PIN_D2),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_ENABLE,
|
||||
};
|
||||
gpio_config(&d2_config);
|
||||
gpio_set_level(PIN_D2, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t print_sdio_cis_information(sdmmc_card_t* card)
|
||||
{
|
||||
const size_t cis_buffer_size = 256;
|
||||
uint8_t cis_buffer[cis_buffer_size];
|
||||
size_t cis_data_len = 1024; //specify maximum searching range to avoid infinite loop
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
ret = sdmmc_io_get_cis_data(card, cis_buffer, cis_buffer_size, &cis_data_len);
|
||||
if (ret == ESP_ERR_INVALID_SIZE) {
|
||||
int temp_buf_size = cis_data_len;
|
||||
uint8_t* temp_buf = malloc(temp_buf_size);
|
||||
assert(temp_buf);
|
||||
|
||||
ESP_LOGW(TAG, "CIS data longer than expected, temporary buffer allocated.");
|
||||
ret = sdmmc_io_get_cis_data(card, temp_buf, temp_buf_size, &cis_data_len);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
sdmmc_io_print_cis_info(temp_buf, cis_data_len, NULL);
|
||||
|
||||
free(temp_buf);
|
||||
} else if (ret == ESP_OK) {
|
||||
sdmmc_io_print_cis_info(cis_buffer, cis_data_len, NULL);
|
||||
} else {
|
||||
//including ESP_ERR_NOT_FOUND
|
||||
ESP_LOGE(TAG, "failed to get the entire CIS data.");
|
||||
abort();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
//host use this to initialize the slave card as well as SDIO registers
|
||||
esp_err_t slave_init(essl_handle_t* handle)
|
||||
{
|
||||
esp_err_t err;
|
||||
/* Probe */
|
||||
#ifndef CONFIG_EXAMPLE_SDIO_OVER_SPI
|
||||
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
|
||||
#ifdef CONFIG_EXAMPLE_SDIO_4BIT
|
||||
ESP_LOGI(TAG, "Probe using SD 4-bit...");
|
||||
config.flags = SDMMC_HOST_FLAG_4BIT;
|
||||
#else
|
||||
ESP_LOGI(TAG, "Probe using SD 1-bit...");
|
||||
config.flags = SDMMC_HOST_FLAG_1BIT;
|
||||
#endif
|
||||
config.input_delay_phase = CONFIG_EXAMPLE_SDIO_HOST_DELAY;
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_SDIO_HIGHSPEED
|
||||
config.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
#else
|
||||
config.max_freq_khz = SDMMC_FREQ_DEFAULT;
|
||||
#endif
|
||||
|
||||
// Set this flag to allocate aligned buffer of 512 bytes to meet DMA's requirements for CMD53 byte mode. Mandatory
|
||||
// when any buffer is behind the cache, or not aligned to 4 byte boundary.
|
||||
config.flags |= SDMMC_HOST_FLAG_ALLOC_ALIGNED_BUF;
|
||||
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
/* Note: For small devkits there may be no pullups on the board.
|
||||
This enables the internal pullups to help evaluate the driver quickly.
|
||||
However the internal pullups are not sufficient and not reliable,
|
||||
please make sure external pullups are connected to the bus in your
|
||||
real design.
|
||||
*/
|
||||
//slot_config.flags = SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||
|
||||
//Initialize all pins to avoid them floating
|
||||
//Set slot width to 4 to ignore other pin config on S3, which support at most 8 lines
|
||||
slot_config.width = 4;
|
||||
#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
|
||||
slot_config.clk = PIN_CLK;
|
||||
slot_config.cmd = PIN_CMD;
|
||||
slot_config.d0 = PIN_D0;
|
||||
slot_config.d1 = PIN_D1;
|
||||
slot_config.d2 = PIN_D2;
|
||||
slot_config.d3 = PIN_D3;
|
||||
#endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
|
||||
|
||||
err = sdmmc_host_init();
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
err = sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config);
|
||||
ESP_ERROR_CHECK(err);
|
||||
#else //over SPI
|
||||
sdspi_device_config_t dev_config = SDSPI_DEVICE_CONFIG_DEFAULT();
|
||||
dev_config.gpio_cs = PIN_D3;
|
||||
dev_config.gpio_int = PIN_D1;
|
||||
|
||||
err = gpio_install_isr_service(0);
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
spi_bus_config_t bus_config = {
|
||||
.mosi_io_num = PIN_CMD,
|
||||
.miso_io_num = PIN_D0,
|
||||
.sclk_io_num = PIN_CLK,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = 4000,
|
||||
};
|
||||
err = spi_bus_initialize(dev_config.host_id, &bus_config, SPI_DMA_CH_AUTO);
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
sdspi_dev_handle_t sdspi_handle;
|
||||
err = sdspi_host_init();
|
||||
ESP_ERROR_CHECK(err);
|
||||
err = sdspi_host_init_device(&dev_config, &sdspi_handle);
|
||||
ESP_ERROR_CHECK(err);
|
||||
ESP_LOGI(TAG, "Probe using SPI...");
|
||||
|
||||
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
|
||||
config.slot = sdspi_handle;
|
||||
//we have to pull up all the slave pins even when the pin is not used
|
||||
gpio_d2_set_high();
|
||||
#endif //over SPI
|
||||
|
||||
sdmmc_card_t *card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
|
||||
if (card == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
for (;;) {
|
||||
if (sdmmc_card_init(&config, card) == ESP_OK) {
|
||||
break;
|
||||
}
|
||||
ESP_LOGW(TAG, "slave init failed, retry...");
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
sdmmc_card_print_info(stdout, card);
|
||||
|
||||
gpio_pullup_en(PIN_CMD);
|
||||
gpio_pulldown_dis(PIN_CMD);
|
||||
gpio_pullup_en(PIN_CLK);
|
||||
gpio_pulldown_dis(PIN_CLK);
|
||||
gpio_pullup_en(PIN_D0);
|
||||
gpio_pulldown_dis(PIN_D0);
|
||||
gpio_pullup_en(PIN_D1);
|
||||
gpio_pulldown_dis(PIN_D1);
|
||||
gpio_pullup_en(PIN_D2);
|
||||
gpio_pulldown_dis(PIN_D2);
|
||||
gpio_pullup_en(PIN_D3);
|
||||
gpio_pulldown_dis(PIN_D3);
|
||||
|
||||
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);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void slave_power_on(void)
|
||||
{
|
||||
#ifdef SLAVE_PWR_GPIO
|
||||
int level_active;
|
||||
#ifdef CONFIG_EXAMPLE_SLAVE_PWR_NEGTIVE_ACTIVE
|
||||
level_active = 0;
|
||||
#else
|
||||
level_active = 1;
|
||||
#endif
|
||||
gpio_config_t cfg = {
|
||||
.pin_bit_mask = BIT64(GPIO_B1),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&cfg);
|
||||
gpio_set_level(GPIO_B1, !level_active);
|
||||
|
||||
vTaskDelay(100);
|
||||
gpio_set_level(SLAVE_PWR_GPIO, level_active);
|
||||
vTaskDelay(100);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
DMA_ATTR uint8_t rcv_buffer[READ_BUFFER_LEN];
|
||||
|
||||
static esp_err_t get_intr(essl_handle_t handle, uint32_t* out_raw, uint32_t* out_st)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
#ifndef CONFIG_EXAMPLE_NO_INTR_LINE
|
||||
ret = essl_wait_int(handle, 0);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = essl_get_intr(handle, out_raw, out_st, TIMEOUT_MAX);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = essl_clear_intr(handle, *out_raw, TIMEOUT_MAX);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ESP_LOGD(TAG, "intr: %08"PRIX32, *out_raw);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
//try to get an interrupt from the slave and handle it, return if none.
|
||||
esp_err_t process_event(essl_handle_t handle)
|
||||
{
|
||||
uint32_t intr_raw, intr_st;
|
||||
esp_err_t ret = get_intr(handle, &intr_raw, &intr_st);
|
||||
if (ret == ESP_ERR_TIMEOUT) {
|
||||
return ret;
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (intr_raw & BIT(i)) {
|
||||
ESP_LOGI(TAG, "host int: %d", i);
|
||||
}
|
||||
}
|
||||
|
||||
const int wait_ms = 50;
|
||||
if (intr_raw & ESSL_SDIO_DEF_ESP32.new_packet_intr_mask) {
|
||||
ESP_LOGD(TAG, "new packet coming");
|
||||
while (1) {
|
||||
size_t size_read = READ_BUFFER_LEN;
|
||||
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;
|
||||
} else if (ret != ESP_OK && ret != ESP_ERR_NOT_FINISHED) {
|
||||
ESP_LOGE(TAG, "rx packet error: %08X", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "receive data, size: %d", size_read);
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, rcv_buffer, size_read, ESP_LOG_INFO);
|
||||
if (ret == ESP_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
//tell the slave to do a job
|
||||
static inline esp_err_t slave_inform_job(essl_handle_t handle, example_job_t job)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = essl_write_reg(handle, SLAVE_REG_JOB, job, NULL, TIMEOUT_MAX);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
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(essl_handle_t handle, int value)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t reg_read[60];
|
||||
ESP_LOGI(TAG, "========JOB: write slave reg========");
|
||||
ret = essl_write_reg(handle, SLAVE_REG_VALUE, value, NULL, TIMEOUT_MAX);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = slave_inform_job(handle, JOB_WRITE_REG);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
vTaskDelay(10);
|
||||
for (int i = 0; i < 60; i++) {
|
||||
ESP_LOGD(TAG, "reading register %d", i);
|
||||
ret = essl_read_reg(handle, i, ®_read[i], TIMEOUT_MAX);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "read registers:");
|
||||
ESP_LOG_BUFFER_HEXDUMP(TAG, reg_read, 60, ESP_LOG_INFO);
|
||||
}
|
||||
|
||||
//the slave only load 16 buffers a time
|
||||
//so first 5 packets (use 1+1+8+4+1=15 buffers) are sent, the others (513, 517) failed (timeout)
|
||||
int packet_len[] = {6, 12, 1024, 512, 3, 513, 517};
|
||||
//the sending buffer should be word aligned
|
||||
DRAM_DMA_ALIGNED_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(essl_handle_t handle)
|
||||
{
|
||||
esp_err_t ret;
|
||||
int data_index = 0;
|
||||
|
||||
ESP_LOGI(TAG, "========JOB: send fifos========");
|
||||
/* CAUTION: This example shows that we can send random length of packet to the slave.
|
||||
* However it takes time of two transactions if the length is not multiples of 4 bytes.
|
||||
* e.g. sending 6 bytes is done by sending 4 + 2 bytes each transaction.
|
||||
* Try to avoid unaligned packets if possible to get higher efficiency.
|
||||
*/
|
||||
for (int i = 0; i < sizeof(packet_len) / sizeof(int); i++) {
|
||||
//Prepare data to send. The length can be random, but data should start at the 32-bit boundary.
|
||||
int length = packet_len[i];
|
||||
for (int i = 0; i < length; i ++) {
|
||||
send_buffer[i] = 0x46 + (data_index + i) * 5;
|
||||
}
|
||||
data_index += (length + 3) & (~3); //get different data next time
|
||||
|
||||
const int wait_ms = 50;
|
||||
ret = essl_send_packet(handle, send_buffer, length, wait_ms);
|
||||
if (ret == ESP_ERR_TIMEOUT || ret == ESP_ERR_NOT_FOUND) {
|
||||
ESP_LOGD(TAG, "slave not ready to receive packet %d", i); // And there are several packets expected to timeout.
|
||||
} else {
|
||||
ESP_ERROR_CHECK(ret);
|
||||
ESP_LOGI(TAG, "send packet length: %d", length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//inform the slave to send interrupts to host (the interrupts will be handled in the interrupt handler)
|
||||
void job_getint(essl_handle_t handle)
|
||||
{
|
||||
ESP_LOGI(TAG, "========JOB: get interrupts from slave========");
|
||||
slave_inform_job(handle, JOB_SEND_INT);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
essl_handle_t handle;
|
||||
esp_err_t err;
|
||||
|
||||
//enable the power if on espressif SDIO master-slave board
|
||||
slave_power_on();
|
||||
|
||||
ESP_LOGI(TAG, "host ready, start initializing slave...");
|
||||
|
||||
err = slave_init(&handle);
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
err = slave_reset(handle);
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
uint32_t start, end;
|
||||
|
||||
job_write_reg(handle, 10);
|
||||
|
||||
int times = 2;
|
||||
|
||||
while (1) {
|
||||
job_getint(handle);
|
||||
start = xTaskGetTickCount();
|
||||
while (1) {
|
||||
process_event(handle);
|
||||
vTaskDelay(1);
|
||||
end = xTaskGetTickCount();
|
||||
if ((end - start) * 1000 / CONFIG_FREERTOS_HZ > 5000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (--times == 0) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
while (1) {
|
||||
job_fifo(handle);
|
||||
|
||||
start = xTaskGetTickCount();
|
||||
while (1) {
|
||||
process_event(handle);
|
||||
vTaskDelay(1);
|
||||
end = xTaskGetTickCount();
|
||||
if ((end - start) * 1000 / CONFIG_FREERTOS_HZ > 2000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
dependencies:
|
||||
espressif/esp_serial_slave_link: "^1.0.1"
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_EXAMPLE_SLAVE_B1=y
|
||||
@@ -0,0 +1,2 @@
|
||||
## On P4-SDMMC + C5 SDIO test runner environment, hardware delay needs to be considered
|
||||
CONFIG_EXAMPLE_SDIO_HOST_DELAY=2
|
||||
Reference in New Issue
Block a user