Merge branch 'feature/emmc_example' into 'master'

SDMMC Host: added an example to communicate with an eMMC chip

Closes IDF-7157, IDF-4739, and IDFGH-6901

See merge request espressif/esp-idf!21760
This commit is contained in:
Armando (Dou Yiwen) 2023-04-17 11:53:55 +08:00
commit ef64e4e5b3
18 changed files with 518 additions and 23 deletions

View File

@ -514,6 +514,14 @@ pytest_examples_esp32h2_adc:
- build_pytest_examples_esp32h2
tags: [ esp32h2, adc ]
example_test_pytest_esp32s3_emmc:
extends:
- .pytest_examples_dir_template
- .rules:test:example_test-esp32s3
needs:
- build_pytest_examples_esp32s3
tags: [ esp32s3, emmc ]
.pytest_components_dir_template:
extends: .pytest_template
variables:

View File

@ -115,36 +115,66 @@ esp_err_t sdmmc_host_reset(void)
static void sdmmc_host_set_clk_div(int div)
{
// Set frequency to 160MHz / div
// div = p + 1
// duty cycle = (h + 1)/(p + 1) (should be = 1/2)
/**
* Set frequency to 160MHz / div
*
* n: counter resets at div_factor_n.
* l: negedge when counter equals div_factor_l.
* h: posedge when counter equals div_factor_h.
*
* We set the duty cycle to 1/2
*/
#if CONFIG_IDF_TARGET_ESP32
assert (div > 1 && div <= 16);
int p = div - 1;
int h = div - 1;
int l = div / 2 - 1;
SDMMC.clock.div_factor_h = h;
SDMMC.clock.div_factor_l = l;
SDMMC.clock.div_factor_n = h;
// Set phases for in/out clocks
// 180 degree phase on input and output clocks
SDMMC.clock.phase_dout = 4;
SDMMC.clock.phase_din = 4;
SDMMC.clock.phase_core = 0;
#elif CONFIG_IDF_TARGET_ESP32S3
assert (div > 1 && div <= 16);
int l = div - 1;
int h = div / 2 - 1;
SDMMC.clock.div_factor_p = p;
SDMMC.clock.div_factor_h = h;
SDMMC.clock.div_factor_m = p;
SDMMC.clock.div_factor_l = l;
SDMMC.clock.div_factor_n = l;
// Make sure 160 MHz source clock is used
#if SOC_SDMMC_SUPPORT_XTAL_CLOCK
SDMMC.clock.clk_sel = 1;
#endif
#if SOC_SDMMC_USE_GPIO_MATRIX
// 90 degree phase on input and output clocks
const int inout_clock_phase = 1;
#else
// 180 degree phase on input and output clocks
const int inout_clock_phase = 4;
#endif
// Set phases for in/out clocks
SDMMC.clock.phase_dout = inout_clock_phase;
SDMMC.clock.phase_din = inout_clock_phase;
SDMMC.clock.phase_core = 0;
/* 90 deg. delay for cclk_out to satisfy large hold time for SDR12 (up to 25MHz) and SDR25 (up to 50MHz) modes.
* Whether this delayed clock will be used depends on use_hold_reg bit in CMD structure,
* determined when sending out the command.
*/
SDMMC.clock.phase_dout = 1;
SDMMC.clock.phase_din = 0;
#endif //CONFIG_IDF_TARGET_ESP32S3
// Wait for the clock to propagate
esp_rom_delay_us(10);
}
static inline int s_get_host_clk_div(void)
{
#if CONFIG_IDF_TARGET_ESP32
return SDMMC.clock.div_factor_h + 1;
#elif CONFIG_IDF_TARGET_ESP32S3
return SDMMC.clock.div_factor_l + 1;
#endif
}
static void sdmmc_host_input_clk_disable(void)
{
SDMMC.clock.val = 0;
@ -300,7 +330,7 @@ esp_err_t sdmmc_host_get_real_freq(int slot, int* real_freq_khz)
return ESP_ERR_INVALID_ARG;
}
int host_div = SDMMC.clock.div_factor_p + 1;
int host_div = s_get_host_clk_div();
int card_div = slot == 0 ? SDMMC.clkdiv.div0 : SDMMC.clkdiv.div1;
*real_freq_khz = sdmmc_host_calc_freq(host_div, card_div);
@ -317,6 +347,9 @@ esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg) {
if (cmd.data_expected && cmd.rw && (SDMMC.wrtprt.cards & BIT(slot)) != 0) {
return ESP_ERR_INVALID_STATE;
}
/* Outputs should be synchronized to cclk_out */
cmd.use_hold_reg = 1;
int64_t t0 = esp_timer_get_time();
while (SDMMC.cmd.start_command == 1) {
if (esp_timer_get_time() - t0 > SDMMC_HOST_START_CMD_TIMEOUT_US) {

View File

@ -305,7 +305,6 @@ static sdmmc_hw_cmd_t make_hw_cmd(sdmmc_command_t* cmd)
if (cmd->flags & SCF_RSP_CRC) {
res.check_response_crc = 1;
}
res.use_hold_reg = 1;
if (cmd->data) {
res.data_expected = 1;
if ((cmd->flags & SCF_CMD_READ) == 0) {

View File

@ -379,9 +379,9 @@ typedef volatile struct sdmmc_dev_s {
uint32_t phase_dout: 3; ///< phase of data output clock (0x0: 0, 0x1: 90, 0x4: 180, 0x6: 270)
uint32_t phase_din: 3; ///< phase of data input clock
uint32_t phase_core: 3; ///< phase of the clock to SDMMC peripheral
uint32_t div_factor_p: 4; ///< controls clock period; it will be (div_factor_p + 1) / 160MHz
uint32_t div_factor_h: 4; ///< controls length of high pulse; it will be (div_factor_h + 1) / 160MHz
uint32_t div_factor_m: 4; ///< should be equal to div_factor_p
uint32_t div_factor_l: 4; ///< controls clock period; it will be (div_factor_l + 1) / 160MHz
uint32_t div_factor_n: 4; ///< should be equal to div_factor_l
uint32_t reserved21: 11;
};
uint32_t val;

View File

@ -376,9 +376,9 @@ typedef volatile struct sdmmc_dev_s {
uint32_t phase_dout: 3; ///< phase of data output clock (0x0: 0, 0x1: 90, 0x4: 180, 0x6: 270)
uint32_t phase_din: 3; ///< phase of data input clock
uint32_t phase_core: 3; ///< phase of the clock to SDMMC peripheral
uint32_t div_factor_p: 4; ///< controls clock period; it will be (div_factor_p + 1) / 160MHz
uint32_t div_factor_h: 4; ///< controls length of high pulse; it will be (div_factor_h + 1) / 160MHz
uint32_t div_factor_m: 4; ///< should be equal to div_factor_p
uint32_t div_factor_l: 4; ///< controls clock period; it will be (div_factor_l + 1) / 160MHz
uint32_t div_factor_n: 4; ///< should be equal to div_factor_l
uint32_t reserved1 : 2;
uint32_t clk_sel : 1; ///< clock source select (0: XTAL, 1: 160 MHz from PLL)
uint32_t reserved24: 8;

View File

@ -110,6 +110,7 @@ ENV_MARKERS = {
'external_flash': 'external flash memory connected via VSPI (FSPI)',
'sdcard_sdmode': 'sdcard running in SD mode',
'sdcard_spimode': 'sdcard running in SPI mode',
'emmc': 'eMMC card',
'MSPI_F8R8': 'runner with Octal Flash and Octal PSRAM',
'MSPI_F4R8': 'runner with Quad Flash and Octal PSRAM',
'MSPI_F4R4': 'runner with Quad Flash and Quad PSRAM',

View File

@ -6,6 +6,11 @@ examples/storage/custom_flash_driver:
temporary: true
reason: target esp32c2 is not supported yet
examples/storage/emmc:
enable:
- if: IDF_TARGET == "esp32s3"
reason: only support on esp32s3
examples/storage/ext_flash_fatfs:
disable:
- if: IDF_TARGET in ["esp32c2", "esp32c6", "esp32h2"]

View File

@ -0,0 +1,6 @@
# 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.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(emmc)

View File

@ -0,0 +1,141 @@
| Supported Targets | ESP32-S3 |
| ----------------- | -------- |
# eMMC chip example (with SDMMC Host)
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use an eMMC chip with an ESP device. Example does the following steps:
1. Use an "all-in-one" `esp_vfs_fat_sdmmc_mount` function to:
- initialize SDMMC peripheral,
- probe and initialize an eMMC chip,
- mount FAT filesystem using FATFS library (and format card, if the filesystem cannot be mounted),
- register FAT filesystem in VFS, enabling C standard library and POSIX functions to be used.
2. Print information about the eMMC chip, such as name, type, capacity, and maximum supported frequency.
3. Create a file using `fopen` and write to it using `fprintf`.
4. Rename the file. Before renaming, check if destination file already exists using `stat` function, and remove it using `unlink` function.
5. Open renamed file for reading, read back the line, and print it to the terminal.
This example supports eMMC chips.
## Hardware
This example requires an ESP32-S3 development board and an eMMC chip. Default connections can be found in next chapter. The standard ESP32S3 devkits do not have an eMMC integrated. Considering the signal integrity issues, we suggesting you make your custom PCB board with ESP32-S3 module and an eMMC chip. The signal integrity issues will be more important, when the bus is working under higher speeds. This includes but not limited to increasing the frequency, switching to DDR mode. We will talk a bit more about signal integrity issues in a following chapter.
It is possible to connect an eMMC breakout adapter, but note that cable connections will decrease the signal integrity, leading communication timing violations. When in this condition, you may need to use a lower clock frequency, or switch to SDR mode.
Pull-up resistors is needed. As the SD specification and the eMMC datasheet clarify, minimum 10k pull-up resistors are required for the bus IOs to protect the IOs against bus floating issue. Note these pull-up resistors are needed, even if the pin is not used (For example, you use 1-line-mode, the pull-up resistor is still required for the D1 pin).
### Pin assignments for ESP32-S3
On ESP32-S3, SDMMC peripheral is connected to GPIO pins using GPIO matrix. This allows arbitrary GPIOs to be used to connect an SD card. In this example, GPIOs can be configured in two ways:
1. Using menuconfig: Run `idf.py menuconfig` in the project directory and open "eMMC Example Configuration" menu.
2. In the source code: See the initialization of ``sdmmc_slot_config_t slot_config`` structure in the example code.
The table below lists the default pin assignments.
ESP32-S3 pin | eMMC pin | Notes
--------------|-------------|------------
GPIO34 | CLK | 10k pullup
GPIO33 | CMD | 10k pullup
GPIO37 | D0 | 10k pullup
GPIO38 | D1 | not used in 1-line mode; but card's D1 pin must have a 10k pullup
GPIO39 | D2 | not used in 1-line mode; but card's D2 pin must have a 10k pullup
GPIO36 | D3 | not used in 1-line mode, but card's D3 pin must have a 10k pullup
GPIO35 | D4 | not used in 1/4-line mode, but card's D4 pin must have a 10k pullup
GPIO40 | D5 | not used in 1/4-line mode, but card's D5 pin must have a 10k pullup
GPIO42 | D6 | not used in 1/4-line mode, but card's D6 pin must have a 10k pullup
GPIO41 | D7 | not used in 1/4-line mode, but card's D7 pin must have a 10k pullup
### Line modes
By default, this example uses 4 line SD mode, utilizing 6 pins: CLK, CMD, D0 - D3. It is possible to use 1-line mode (CLK, CMD, D0) or 8-line mode (CLK, CMD, D0 - D7) by changing "eMMC bus width" in the example configuration menu (see `CONFIG_EXAMPLE_EMMC_BUS_WIDTH_1/8`).
### Signal integrity
The signal integrity talking about in this chapter is mainly about is in time domain.
#### The background of this issue
- Voltage level switching needs time, and this is influenced by the pin load, connected capacitors and resistors, and other noises.
- Clock signal rises and falls will be measured by the eMMC by its minimum input high voltage (VIH) and maximum input low voltage (VIL).
- Input CMD and Data signals' rises and falls will be measured by the eMMC by its minimum input high voltage (VIH) and maximum input low voltage (VIL).
- Output CMD and Data signals' rises and falls will be measured by the eMMC by its mininum output high voltage (VOH) and maximum output low voltage (VOL).
Therefore when using cables to connect your eMMC and the ESP chip, the time that a pin voltage level increases to the VIL / VOL, or the time that a pin voltage level decreases to the VIH / VOH will be longer.
#### Requirements
- Input setup time (tSU), the data signal change should happen earlier than a valid clock edge, and should be earlier at least tSU time.
- Input hold time (tH), after the data signal changes, its status should hold at least tH time.
Both the tSU and the tH are usually in the unit of nanosecond (ns). When in DDR mode, the timing requirements is even more strict, as both the clock posedge and the negedge are valid.
The eMMC chip datasheet should specify the requirements of the tSU and the tH.
## How to use example
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with serial port name.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example output
Here is an example console output. In this case a 128MB SDSC card was connected, and `EXAMPLE_FORMAT_IF_MOUNT_FAILED` menuconfig option enabled. Card was unformatted, so the initial mount has failed. Card was then partitioned, formatted, and mounted again.
```
I (308) example: Initializing eMMC
I (318) example: Using SDMMC peripheral
I (318) example: Mounting filesystem
I (328) gpio: GPIO[34]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (338) gpio: GPIO[33]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (348) gpio: GPIO[37]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (348) gpio: GPIO[38]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (358) gpio: GPIO[39]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (368) gpio: GPIO[36]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (428) gpio: GPIO[36]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (428) example: Filesystem mounted
Name: IS008GP
Type: MMC
Speed: 40.00 MHz (limit: 40.00 MHz), DDR
Size: 7456MB
CSD: ver=3, sector_size=512, capacity=15269888 read_bl_len=9
EXT CSD: bus_width=4
I (438) example: Opening file /eMMC/hello.txt
I (448) example: File written
I (468) example: Renaming file /eMMC/hello.txt to /eMMC/foo.txt
I (468) example: Reading file /eMMC/foo.txt
I (468) example: Read from file: 'Hello IS008GP!'
I (468) example: Card unmounted
```
## Troubleshooting
### Card fails to initialize with `sdmmc_check_scr: send_scr returned 0xffffffff` error
Connections between the eMMC chip and the ESP chip are too long for the frequency used. Try using shorter connections, or try reducing the clock speed of the bus.
### Failure to mount filesystem
```
example: Failed to mount filesystem. If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.
```
The example will be able to mount only cards formatted using FAT32 filesystem. If the card is formatted as exFAT or some other filesystem, you have an option to format it in the example code. Enable the `EXAMPLE_FORMAT_IF_MOUNT_FAILED` menuconfig option, then build and flash the example.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "emmc_example_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,78 @@
menu "eMMC Example Configuration"
config EXAMPLE_FORMAT_IF_MOUNT_FAILED
bool "Format the card if mount failed"
default n
help
If this config item is set, format_if_mount_failed will be set to true and the card will be formatted if
the mount has failed.
choice EXAMPLE_EMMC_BUS_WIDTH
prompt "eMMC bus width"
default EXAMPLE_EMMC_BUS_WIDTH_4
help
Select the bus width of the MMC interface.
config EXAMPLE_EMMC_BUS_WIDTH_8
bool "8 lines (D0 - D7)"
config EXAMPLE_EMMC_BUS_WIDTH_4
bool "4 lines (D0 - D3)"
config EXAMPLE_EMMC_BUS_WIDTH_1
bool "1 line (D0)"
endchoice
if SOC_SDMMC_USE_GPIO_MATRIX
config EXAMPLE_PIN_CMD
int "CMD GPIO number"
default 33 if IDF_TARGET_ESP32S3
config EXAMPLE_PIN_CLK
int "CLK GPIO number"
default 34 if IDF_TARGET_ESP32S3
config EXAMPLE_PIN_D0
int "D0 GPIO number"
default 37 if IDF_TARGET_ESP32S3
if EXAMPLE_EMMC_BUS_WIDTH_4 || EXAMPLE_EMMC_BUS_WIDTH_8
config EXAMPLE_PIN_D1
int "D1 GPIO number"
default 38 if IDF_TARGET_ESP32S3
config EXAMPLE_PIN_D2
int "D2 GPIO number"
default 39 if IDF_TARGET_ESP32S3
config EXAMPLE_PIN_D3
int "D3 GPIO number"
default 36 if IDF_TARGET_ESP32S3
endif # EXAMPLE_EMMC_BUS_WIDTH_4 || EXAMPLE_EMMC_BUS_WIDTH_8
if EXAMPLE_EMMC_BUS_WIDTH_8
config EXAMPLE_PIN_D4
int "D4 GPIO number"
default 35 if IDF_TARGET_ESP32S3
config EXAMPLE_PIN_D5
int "D5 GPIO number"
default 40 if IDF_TARGET_ESP32S3
config EXAMPLE_PIN_D6
int "D6 GPIO number"
default 42 if IDF_TARGET_ESP32S3
config EXAMPLE_PIN_D7
int "D7 GPIO number"
default 41 if IDF_TARGET_ESP32S3
endif # EXAMPLE_EMMC_BUS_WIDTH_8
endif # SOC_SDMMC_USE_GPIO_MATRIX
endmenu

View File

@ -0,0 +1,172 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* eMMC and FAT filesystem example.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
// This example uses SDMMC peripheral to communicate with an eMMC.
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h"
static const char *TAG = "example";
#define MOUNT_POINT "/eMMC"
void app_main(void)
{
esp_err_t ret;
// Options for mounting the filesystem.
// If format_if_mount_failed is set to true, eMMC will be partitioned and
// formatted in case when mounting fails.
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
.format_if_mount_failed = true,
#else
.format_if_mount_failed = false,
#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
.max_files = 5,
.allocation_unit_size = 16 * 1024
};
sdmmc_card_t *card = NULL;
const char mount_point[] = MOUNT_POINT;
ESP_LOGI(TAG, "Initializing eMMC");
// Use settings defined above to initialize eMMC and mount FAT filesystem.
// Note: esp_vfs_fat_sdmmc_mount is all-in-one convenience functions.
// Please check its source code and implement error recovery when developing
// production applications.
ESP_LOGI(TAG, "Using SDMMC peripheral");
// By default, eMMC frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz)
// For setting a specific frequency, use host.max_freq_khz (range 400kHz - 52MHz for SDMMC)
// Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000;
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
host.max_freq_khz = SDMMC_FREQ_52M;
// This initializes the slot without card detect (CD) and write protect (WP) signals.
// Other fields will be initialized to zero
sdmmc_slot_config_t slot_config = {
.cd = SDMMC_SLOT_NO_CD,
.wp = SDMMC_SLOT_NO_WP,
};
// Set bus width to use:
#if CONFIG_EXAMPLE_EMMC_BUS_WIDTH_8
slot_config.width = 8;
#elif CONFIG_EXAMPLE_EMMC_BUS_WIDTH_4
slot_config.width = 4;
#else
slot_config.width = 1;
#endif
// Set bus IOs
#if CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
#if CONFIG_EXAMPLE_EMMC_BUS_WIDTH_4 || CONFIG_EXAMPLE_EMMC_BUS_WIDTH_8
slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;
#endif // CONFIG_EXAMPLE_EMMC_BUS_WIDTH_4 || CONFIG_EXAMPLE_EMMC_BUS_WIDTH_8
#if CONFIG_EXAMPLE_EMMC_BUS_WIDTH_8
slot_config.d4 = CONFIG_EXAMPLE_PIN_D4;
slot_config.d5 = CONFIG_EXAMPLE_PIN_D5;
slot_config.d6 = CONFIG_EXAMPLE_PIN_D6;
slot_config.d7 = CONFIG_EXAMPLE_PIN_D7;
#endif //CONFIG_EXAMPLE_EMMC_BUS_WIDTH_8
#endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
// Enable internal pullups on enabled pins. The internal pullups
// are insufficient however, please make sure 10k external pullups are
// connected on the bus. This is for debug / example purpose only.
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
ESP_LOGI(TAG, "Mounting filesystem");
ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount filesystem. "
"If you want the eMMC to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
} else {
ESP_LOGE(TAG, "Failed to initialize the eMMC (%s). "
"Make sure eMMC lines have pull-up resistors in place.", esp_err_to_name(ret));
}
return;
}
ESP_LOGI(TAG, "Filesystem mounted");
// Card has been initialized, print its properties
sdmmc_card_print_info(stdout, card);
// Use POSIX and C standard library functions to work with files:
// First create a file.
const char *file_hello = MOUNT_POINT"/hello.txt";
ESP_LOGI(TAG, "Opening file %s", file_hello);
FILE *f = fopen(file_hello, "w");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for writing");
return;
}
fprintf(f, "Hello %s!\n", card->cid.name);
fclose(f);
ESP_LOGI(TAG, "File written");
const char *file_foo = MOUNT_POINT"/foo.txt";
// Check if destination file exists before renaming
struct stat st;
if (stat(file_foo, &st) == 0) {
// Delete it if it exists
unlink(file_foo);
}
// Rename original file
ESP_LOGI(TAG, "Renaming file %s to %s", file_hello, file_foo);
if (rename(file_hello, file_foo) != 0) {
ESP_LOGE(TAG, "Rename failed");
return;
}
// Open renamed file for reading
ESP_LOGI(TAG, "Reading file %s", file_foo);
f = fopen(file_foo, "r");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for reading");
return;
}
// Read a line from file
char line[64];
fgets(line, sizeof(line), f);
fclose(f);
// Strip newline
char *pos = strchr(line, '\n');
if (pos) {
*pos = '\0';
}
ESP_LOGI(TAG, "Read from file: '%s'", line);
// All done, unmount partition and disable SDMMC peripheral
esp_vfs_fat_sdcard_unmount(mount_point, card);
ESP_LOGI(TAG, "Card unmounted");
}

View File

@ -0,0 +1,46 @@
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import logging
import re
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32s3
@pytest.mark.emmc
@pytest.mark.parametrize(
'config',
[
'1line',
'4line',
'8line',
],
indirect=True,
)
def test_examples_sd_card_sdmmc(dut: Dut) -> None:
dut.expect('example: Initializing eMMC', timeout=20)
dut.expect('example: Using SDMMC peripheral', timeout=10)
# Provide enough time for possible SD card formatting
dut.expect('Filesystem mounted', timeout=60)
# These lines are matched separately because of ASCII color codes in the output
name = dut.expect(re.compile(rb'Name: (\w+)\r'), timeout=10).group(1).decode()
_type = dut.expect(re.compile(rb'Type: (\S+)'), timeout=10).group(1).decode()
speed = dut.expect(re.compile(rb'Speed: (\S+)'), timeout=10).group(1).decode()
size = dut.expect(re.compile(rb'Size: (\S+)'), timeout=10).group(1).decode()
logging.info('Card {} {} {}MHz {} found'.format(name, _type, speed, size))
message_list = ('Opening file /eMMC/hello.txt',
'File written',
'Renaming file /eMMC/hello.txt to /eMMC/foo.txt',
'Reading file /eMMC/foo.txt',
"Read from file: 'Hello {}!'".format(name),
'Card unmounted')
for msg in message_list:
dut.expect(msg, timeout=10)

View File

@ -0,0 +1 @@
CONFIG_EXAMPLE_EMMC_BUS_WIDTH_1=y

View File

@ -0,0 +1 @@
CONFIG_EXAMPLE_EMMC_BUS_WIDTH_4=y

View File

@ -0,0 +1 @@
CONFIG_EXAMPLE_EMMC_BUS_WIDTH_8=y

View File

@ -0,0 +1 @@
CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED=y

View File

@ -40,4 +40,4 @@ def test_examples_sd_card_sdmmc(dut: Dut) -> None:
'Card unmounted')
for msg in message_list:
dut.expect(msg, timeout=20)
dut.expect(msg, timeout=60)