mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
sdio_example: use the new esp_ser component and new evboard
This commit is contained in:
parent
d158fa3f5c
commit
17b7c9036f
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user