Ver código fonte

spi: add eeprom example

Michael (XIAO Xufeng) 6 anos atrás
pai
commit
f53812d27a

+ 6 - 0
examples/peripherals/spi_master/hd_eeprom/CMakeLists.txt

@@ -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.5)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(spi_eeprom)

+ 9 - 0
examples/peripherals/spi_master/hd_eeprom/Makefile

@@ -0,0 +1,9 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := spi_eeprom
+
+include $(IDF_PATH)/make/project.mk
+

+ 24 - 0
examples/peripherals/spi_master/hd_eeprom/README.md

@@ -0,0 +1,24 @@
+## SPI master half duplex EEPROM example
+
+ This code demonstrates how to use the SPI master half duplex mode to read/write a AT93C46D
+ EEPROM (8-bit mode). There is also an Kconfig option `EXAMPLE_USE_SPI1_PINS` allowing use the
+ SPI1 (bus with code Flash connected on official modules).
+
+### Connections
+
+For different chip and host used, the connections may be different.
+
+|      | ESP32 | ESP32 | ESP32S2 |
+| ---- | ----- | ----- | ------- |
+| Host | SPI1  | HSPI  | FSPI    |
+| VCC  | 3.3V  | 3.3V  | 3.3V    |
+| GND  | GND   | GND   | GND     |
+| DO   | 7     | 18    | 37      |
+| DI   | 8     | 23    | 35      |
+| SK   | 6     | 19    | 36      |
+| CS   | 13    | 13    | 34      |
+| ORG  | GND   | GND   | GND     |
+
+### Notes
+
+If you meet timeout issues, please check your connections.

+ 4 - 0
examples/peripherals/spi_master/hd_eeprom/components/eeprom/CMakeLists.txt

@@ -0,0 +1,4 @@
+idf_component_register(SRCS "spi_eeprom.c"
+                    LDFRAGMENTS "linker.lf"
+                    INCLUDE_DIRS ".")
+

+ 6 - 0
examples/peripherals/spi_master/hd_eeprom/components/eeprom/component.mk

@@ -0,0 +1,6 @@
+#
+# Main Makefile. This is basically the same as a component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+
+COMPONENT_ADD_LDFRAGMENTS += "linker.lf"

+ 24 - 0
examples/peripherals/spi_master/hd_eeprom/components/eeprom/linker.lf

@@ -0,0 +1,24 @@
+# This example supports running on the SPI1 bus, which is shared with SPI flash accessed by the
+# cache. When doing transaction on SPI1 bus, data cannot be fetched from the flash, so all the data
+# used during this time should be put into the internal RAM.
+
+[mapping:eeprom]
+archive: libeeprom.a
+entries:
+    * (noflash)
+
+[mapping:ext_driver]
+archive: libdriver.a
+entries:
+    # gpio_set_level, gpio_get_level, gpio_context, _gpio_hal, etc...
+    gpio (noflash)
+
+[mapping:ext_soc]
+archive: libsoc.a
+entries:
+    gpio_hal (noflash)
+
+[mapping:ext_newlib]
+archive: libnewlib.a
+entries:
+    time:usleep (noflash)

+ 324 - 0
examples/peripherals/spi_master/hd_eeprom/components/eeprom/spi_eeprom.c

@@ -0,0 +1,324 @@
+/*
+    This code demonstrates how to use the SPI master half duplex mode to read/write a AT932C46D
+    EEPROM (8-bit mode).
+
+   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.
+*/
+
+#include "spi_eeprom.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/semphr.h"
+#include "driver/gpio.h"
+#include <unistd.h>
+#include "esp_log.h"
+#include <sys/param.h>
+#include "sdkconfig.h"
+
+
+#define EEPROM_BUSY_TIMEOUT_MS  5
+
+#define EEPROM_CLK_FREQ         (1*1000*1000)   //When powered by 3.3V, EEPROM max freq is 1MHz
+#define EEPROM_INPUT_DELAY_NS   ((1000*1000*1000/EEPROM_CLK_FREQ)/2+20)
+
+#define ADDR_MASK   0x7f
+
+#define CMD_EWDS    0x200
+#define CMD_WRAL    0x200
+#define CMD_ERAL    0x200
+#define CMD_EWEN    0x200
+#define CMD_CKBS    0x000
+#define CMD_READ    0x300
+#define CMD_ERASE   0x380
+#define CMD_WRITE   0x280
+
+#define ADD_EWDS    0x00
+#define ADD_WRAL    0x20
+#define ADD_ERAL    0x40
+#define ADD_EWEN    0x60
+
+/// Context (config and data) of the spi_eeprom
+struct eeprom_context_t{
+    eeprom_config_t cfg;        ///< Configuration by the caller.
+    spi_device_handle_t spi;    ///< SPI device handle
+    xSemaphoreHandle ready_sem; ///< Semaphore for ready signal
+};
+
+typedef struct eeprom_context_t eeprom_context_t;
+
+static const char TAG[] = "eeprom";
+
+
+// Workaround: The driver depends on some data in the flash and cannot be placed to DRAM easily for
+// now. Using the version in LL instead.
+#define gpio_set_level  gpio_set_level_patch
+#include "hal/gpio_ll.h"
+static inline esp_err_t gpio_set_level_patch(gpio_num_t gpio_num, uint32_t level)
+{
+    gpio_ll_set_level(&GPIO, gpio_num, level);
+    return ESP_OK;
+}
+
+
+static esp_err_t eeprom_simple_cmd(eeprom_context_t *ctx, uint16_t cmd)
+{
+    spi_transaction_t t = {
+        .cmd = cmd,
+        .user = ctx
+    };
+    return spi_device_polling_transmit(ctx->spi, &t);
+}
+
+static esp_err_t eeprom_wait_done(eeprom_context_t* ctx)
+{
+    //have to keep cs low for 250ns
+    usleep(1);
+    //clear signal
+    if (ctx->cfg.intr_used) {
+        xSemaphoreTake(ctx->ready_sem, 0);
+        gpio_set_level(ctx->cfg.cs_io, 1);
+        gpio_intr_enable(ctx->cfg.miso_io);
+
+        //Max processing time is 5ms, tick=1 may happen very soon, set to 2 at least
+        uint32_t tick_to_wait = MAX(EEPROM_BUSY_TIMEOUT_MS / portTICK_PERIOD_MS, 2);
+        BaseType_t ret = xSemaphoreTake(ctx->ready_sem, tick_to_wait);
+        gpio_intr_disable(ctx->cfg.miso_io);
+        gpio_set_level(ctx->cfg.cs_io, 0);
+
+        if (ret != pdTRUE) return ESP_ERR_TIMEOUT;
+    } else {
+        bool timeout = true;
+        gpio_set_level(ctx->cfg.cs_io, 1);
+        for (int i = 0; i < EEPROM_BUSY_TIMEOUT_MS * 1000; i ++) {
+            if (gpio_get_level(ctx->cfg.miso_io)) {
+                timeout = false;
+                break;
+            }
+            usleep(1);
+        }
+        gpio_set_level(ctx->cfg.cs_io, 0);
+        if (timeout) return ESP_ERR_TIMEOUT;
+    }
+    return ESP_OK;
+}
+
+static void cs_high(spi_transaction_t* t)
+{
+    ESP_EARLY_LOGV(TAG, "cs high %d.", ((eeprom_context_t*)t->user)->cfg.cs_io);
+    gpio_set_level(((eeprom_context_t*)t->user)->cfg.cs_io, 1);
+}
+
+static void cs_low(spi_transaction_t* t)
+{
+    gpio_set_level(((eeprom_context_t*)t->user)->cfg.cs_io, 0);
+    ESP_EARLY_LOGV(TAG, "cs low %d.", ((eeprom_context_t*)t->user)->cfg.cs_io);
+}
+
+void ready_rising_isr(void* arg)
+{
+    eeprom_context_t* ctx = (eeprom_context_t*)arg;
+    xSemaphoreGive(ctx->ready_sem);
+    ESP_EARLY_LOGV(TAG, "ready detected.");
+}
+
+esp_err_t spi_eeprom_deinit(eeprom_context_t* ctx)
+{
+    spi_bus_remove_device(ctx->spi);
+    if (ctx->cfg.intr_used) {
+        vSemaphoreDelete(ctx->ready_sem);
+    }
+    free(ctx);
+    return ESP_OK;
+}
+
+esp_err_t spi_eeprom_init(const eeprom_config_t *cfg, eeprom_context_t** out_ctx)
+{
+    esp_err_t err = ESP_OK;
+    if (cfg->intr_used && cfg->host == SPI1_HOST) {
+        ESP_LOGE(TAG, "interrupt cannot be used on SPI1 host.");
+        return ESP_ERR_INVALID_ARG;
+    }
+
+    eeprom_context_t* ctx = (eeprom_context_t*)malloc(sizeof(eeprom_context_t));
+    if (!ctx) return ESP_ERR_NO_MEM;
+
+    *ctx = (eeprom_context_t) {
+        .cfg = *cfg,
+    };
+
+    spi_device_interface_config_t devcfg={
+        .command_bits = 10,
+        .clock_speed_hz = EEPROM_CLK_FREQ,
+        .mode = 0,          //SPI mode 0
+        /*
+         * The timing requirements to read the busy signal from the EEPROM cannot be easily emulated
+         * by SPI transactions. We need to control CS pin by SW to check the busy signal manually.
+         */
+        .spics_io_num = -1,
+        .queue_size = 1,
+        .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_POSITIVE_CS,
+        .pre_cb = cs_high,
+        .post_cb = cs_low,
+        .input_delay_ns = EEPROM_INPUT_DELAY_NS,  //the EEPROM output the data half a SPI clock behind.
+    };
+    //Attach the EEPROM to the SPI bus
+    err = spi_bus_add_device(ctx->cfg.host, &devcfg, &ctx->spi);
+    if  (err != ESP_OK) {
+        goto cleanup;
+    }
+
+    gpio_set_level(ctx->cfg.cs_io, 0);
+    gpio_config_t cs_cfg = {
+        .pin_bit_mask = BIT64(ctx->cfg.cs_io),
+        .mode = GPIO_MODE_OUTPUT,
+    };
+    gpio_config(&cs_cfg);
+
+    if (ctx->cfg.intr_used) {
+        ctx->ready_sem = xSemaphoreCreateBinary();
+        if (ctx->ready_sem == NULL) {
+            err = ESP_ERR_NO_MEM;
+            goto cleanup;
+        }
+
+        gpio_set_intr_type(ctx->cfg.miso_io, GPIO_INTR_POSEDGE);
+        err = gpio_isr_handler_add(ctx->cfg.miso_io, ready_rising_isr, ctx);
+        if (err != ESP_OK) {
+            goto cleanup;
+        }
+        gpio_intr_disable(ctx->cfg.miso_io);
+    }
+    *out_ctx = ctx;
+    return ESP_OK;
+
+cleanup:
+    if (ctx->spi) {
+        spi_bus_remove_device(ctx->spi);
+        ctx->spi = NULL;
+    }
+    if (ctx->ready_sem) {
+        vSemaphoreDelete(ctx->ready_sem);
+        ctx->ready_sem = NULL;
+    }
+    free(ctx);
+    return err;
+}
+
+esp_err_t spi_eeprom_read(eeprom_context_t* ctx, uint8_t addr, uint8_t* out_data)
+{
+    spi_transaction_t t = {
+        .cmd = CMD_READ | (addr & ADDR_MASK),
+        .rxlength = 8,
+        .flags = SPI_TRANS_USE_RXDATA,
+        .user = ctx,
+    };
+    esp_err_t err = spi_device_polling_transmit(ctx->spi, &t);
+    if (err!= ESP_OK) return err;
+
+    *out_data = t.rx_data[0];
+    return ESP_OK;
+}
+
+esp_err_t spi_eeprom_erase(eeprom_context_t* ctx, uint8_t addr)
+{
+    esp_err_t err;
+    err = spi_device_acquire_bus(ctx->spi, portMAX_DELAY);
+    if (err != ESP_OK) return err;
+
+    err = eeprom_simple_cmd(ctx, CMD_ERASE | (addr & ADDR_MASK));
+
+    if (err == ESP_OK) {
+        err = eeprom_wait_done(ctx);
+    }
+
+    spi_device_release_bus(ctx->spi);
+    return err;
+}
+
+esp_err_t spi_eeprom_write(eeprom_context_t* ctx, uint8_t addr, uint8_t data)
+{
+    esp_err_t err;
+    err = spi_device_acquire_bus(ctx->spi, portMAX_DELAY);
+    if (err != ESP_OK) return err;
+
+    spi_transaction_t t = {
+        .cmd = CMD_WRITE | (addr & ADDR_MASK),
+        .length = 8,
+        .flags = SPI_TRANS_USE_TXDATA,
+        .tx_data = {data},
+        .user = ctx,
+    };
+    err = spi_device_polling_transmit(ctx->spi, &t);
+
+    if (err == ESP_OK) {
+        err = eeprom_wait_done(ctx);
+    }
+
+    spi_device_release_bus(ctx->spi);
+    return err;
+}
+
+esp_err_t spi_eeprom_write_enable(eeprom_context_t* ctx)
+{
+    return eeprom_simple_cmd(ctx, CMD_EWEN | ADD_EWEN);
+}
+
+esp_err_t spi_eeprom_write_disable(eeprom_context_t* ctx)
+{
+    return eeprom_simple_cmd(ctx, CMD_EWDS | ADD_EWDS);
+}
+
+esp_err_t spi_eeprom_erase_all(eeprom_context_t* ctx)
+{
+#if !CONFIG_EXAMPLE_5V_COMMANDS
+    //not supported in 3.3V VCC
+    ESP_LOGE(TAG, "erase all not supported by EEPROM under 3.3V VCC");
+    return ESP_ERR_NOT_SUPPORTED;
+#endif
+
+    esp_err_t err;
+    err = spi_device_acquire_bus(ctx->spi, portMAX_DELAY);
+    if (err != ESP_OK) return err;
+
+    err = eeprom_simple_cmd(ctx, CMD_ERAL | ADD_ERAL);
+
+    if (err == ESP_OK) {
+        err = eeprom_wait_done(ctx);
+    }
+
+    spi_device_release_bus(ctx->spi);
+    return err;
+}
+
+esp_err_t spi_eeprom_write_all(eeprom_context_t* ctx, uint8_t data)
+{
+#if !CONFIG_EXAMPLE_5V_COMMANDS
+    //not supported in 3.3V VCC
+    ESP_LOGE(TAG, "write all not supported by EEPROM under 3.3V VCC");
+    return ESP_ERR_NOT_SUPPORTED;
+#endif
+
+    esp_err_t err;
+    err = spi_device_acquire_bus(ctx->spi, portMAX_DELAY);
+    if (err != ESP_OK) return err;
+
+    spi_transaction_t t = {
+        .cmd = CMD_WRAL | ADD_WRAL,
+        .length = 8,
+        .flags = SPI_TRANS_USE_TXDATA,
+        .tx_data = {data},
+        .user = ctx,
+    };
+    err = spi_device_polling_transmit(ctx->spi, &t);
+
+    if (err == ESP_OK) {
+        err = eeprom_wait_done(ctx);
+    }
+
+    spi_device_release_bus(ctx->spi);
+    return err;
+}

+ 123 - 0
examples/peripherals/spi_master/hd_eeprom/components/eeprom/spi_eeprom.h

@@ -0,0 +1,123 @@
+/*
+    This code demonstrates how to use the SPI master half duplex mode to read/write a AT932C46D
+    EEPROM (8-bit mode).
+
+   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.
+*/
+
+#pragma once
+#include "driver/spi_master.h"
+#include "driver/gpio.h"
+#include "sdkconfig.h"
+
+/// Configurations of the spi_eeprom
+typedef struct {
+    spi_host_device_t host; ///< The SPI host used, set before calling `spi_eeprom_init()`
+    gpio_num_t cs_io;       ///< CS gpio number, set before calling `spi_eeprom_init()`
+    gpio_num_t miso_io;     ///< MISO gpio number, set before calling `spi_eeprom_init()`
+    bool intr_used;         ///< Whether to use polling or interrupt when waiting for write to be done. Set before calling `spi_eeprom_init()`.
+} eeprom_config_t;
+
+typedef struct eeprom_context_t* eeprom_handle_t;
+
+/**
+ * @brief Initialize the hardware.
+ *
+ * @param config Configuration of the EEPROM
+ * @param out_handle Output context of EEPROM communication.
+ * @return
+ *  - ESP_OK: on success
+ *  - ESP_ERR_INVALID_ARG: If the configuration in the context is incorrect.
+ *  - ESP_ERR_NO_MEM: if semaphore create failed.
+ *  - or other return value from `spi_bus_add_device()` or `gpio_isr_handler_add()`.
+ */
+esp_err_t spi_eeprom_init(const eeprom_config_t *config, eeprom_handle_t* out_handle);
+
+/**
+ * @brief Release the resources used by the EEPROM.
+ *
+ * @param handle Context of EEPROM communication.
+ * @return Always ESP_OK
+ */
+esp_err_t spi_eeprom_deinit(eeprom_handle_t handle);
+
+/**
+ * @brief Read a byte from the EEPROM.
+ *
+ * @param handle Context of EEPROM communication.
+ * @param addr      Address to read.
+ * @param out_data  Buffer to output the read data.
+ * @return return value from `spi_device_polling_transmit()`.
+ */
+esp_err_t spi_eeprom_read(eeprom_handle_t handle, uint8_t addr, uint8_t* out_data);
+
+/**
+ * @brief Erase a byte in the EEPROM.
+ *
+ * @param handle Context of EEPROM communication.
+ * @param addr  Address to erase.
+ * @return
+ *  - ESP_OK: on success
+ *  - ESP_ERR_TIMEOUT: if the EEPROM is not able to be ready before the time in the spec. This may mean that the connection is not correct.
+ *  - or return value from `spi_device_acquire_bus()` `spi_device_polling_transmit()`.
+ */
+esp_err_t spi_eeprom_erase(eeprom_handle_t handle, uint8_t addr);
+
+/**
+ * @brief Write a byte into the EEPROM
+ *
+ * @param handle Context of EEPROM communication.
+ * @param addr  Address to write.
+ * @param data  The byte to write.
+ * @return
+ *  - ESP_OK: on success
+ *  - ESP_ERR_TIMEOUT: if the EEPROM is not able to be ready before the time in the spec. This may mean that the connection is not correct.
+ *  - or return value from `spi_device_acquire_bus()` `spi_device_polling_transmit()`.
+ */
+esp_err_t spi_eeprom_write(eeprom_handle_t handle, uint8_t addr, uint8_t data);
+
+/**
+ * @brief Enable following write/erase to the EEPROM.
+ *
+ * @param handle Context of EEPROM communication.
+ * @return return value from `spi_device_polling_transmit()`.
+ */
+esp_err_t spi_eeprom_write_enable(eeprom_handle_t handle);
+
+/**
+ * @brief Disable following write/erase to the EEPROM.
+ *
+ * @param handle Context of EEPROM communication.
+ * @return return value from `spi_device_polling_transmit()`.
+ */
+esp_err_t spi_eeprom_write_disable(eeprom_handle_t handle);
+
+#if CONFIG_EXAMPLE_5V_COMMANDS
+/**
+ * @brief Erase all the memory in the EEPROM.
+ *
+ * @note This is only supported when EEPROM VCC is 5V.
+ * @param handle Context of EEPROM communication.
+ * @return
+ *  - ESP_OK: on success
+ *  - ESP_ERR_TIMEOUT: if the EEPROM is not able to be ready before the time in the spec. This may mean that the connection is not correct.
+ *  - or return value from `spi_device_acquire_bus()` `spi_device_polling_transmit()`.
+ */
+esp_err_t spi_eeprom_erase_all(eeprom_handle_t handle);
+
+/**
+ * @brief write all the memory in the EEPROM to the value given.
+ *
+ * @note This is only supported when EEPROM VCC is 5V.
+ * @param handle Context of EEPROM communication.
+ * @return
+ *  - ESP_OK: on success
+ *  - ESP_ERR_TIMEOUT: if the EEPROM is not able to be ready before the time in the spec. This may mean that the connection is not correct.
+ *  - or return value from `spi_device_acquire_bus()` `spi_device_polling_transmit()`.
+ */
+esp_err_t spi_eeprom_write_all(eeprom_handle_t handle, uint8_t data);
+#endif //CONFIG_EXAMPLE_5V_COMMANDS

+ 5 - 0
examples/peripherals/spi_master/hd_eeprom/main/CMakeLists.txt

@@ -0,0 +1,5 @@
+set(srcs "spi_eeprom_main.c")
+
+idf_component_register(SRCS ${srcs}
+                    INCLUDE_DIRS ".")
+

+ 32 - 0
examples/peripherals/spi_master/hd_eeprom/main/Kconfig.projbuild

@@ -0,0 +1,32 @@
+menu "Example Configuration"
+
+    config EXAMPLE_USE_SPI1_PINS
+        bool "The example runs on SPI1 pins or some other pins"
+        default n
+        depends on IDF_TARGET_ESP32
+        help
+            Enable this option will make the EEPROM use SPI1 pins, which is shared with the main
+            flash chip.
+
+            Currently this example hasn't supported SPI1 pins on other chips yet.
+
+    config EXAMPLE_INTR_USED
+        bool "Use the interrupt to detect the eeprom busy"
+        default y
+        depends on !EXAMPLE_USE_SPI1_PINS
+        help
+            Enable this option will allow the example to be blocked while the EEPROM is working
+            in progress, and unblocked by GPIO interrupt. Otherwise the example will keep polling
+            until the EEPROM is idle.
+
+    config EXAMPLE_5V_COMMANDS
+        bool "The EEPROM is supplied by 5V power"
+        default n
+        help
+            The Erase_All and Write_All commands of EEPROM are only supported when EEPROM is
+            supplied by 5V power. Enable this to use those two commands.
+
+            But please note that ESP chips don't support 5V IO, you need to add external
+            level-shifting circuits between ESP chip to the EEPROM.
+
+endmenu

+ 4 - 0
examples/peripherals/spi_master/hd_eeprom/main/component.mk

@@ -0,0 +1,4 @@
+#
+# Main Makefile. This is basically the same as a component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

+ 113 - 0
examples/peripherals/spi_master/hd_eeprom/main/spi_eeprom_main.c

@@ -0,0 +1,113 @@
+/* SPI Master Half Duplex EEPROM 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.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "driver/spi_master.h"
+#include "driver/gpio.h"
+
+#include "sdkconfig.h"
+#include "esp_log.h"
+#include "spi_eeprom.h"
+
+
+/*
+ This code demonstrates how to use the SPI master half duplex mode to read/write a AT932C46D EEPROM (8-bit mode).
+*/
+
+#ifdef CONFIG_IDF_TARGET_ESP32
+#  ifdef CONFIG_EXAMPLE_USE_SPI1_PINS
+#    define EEPROM_HOST    SPI1_HOST
+#    define DMA_CHAN    0
+// Use default pins, same as the flash chip.
+#    define PIN_NUM_MISO 7
+#    define PIN_NUM_MOSI 8
+#    define PIN_NUM_CLK  6
+#  else
+#    define EEPROM_HOST    HSPI_HOST
+#    define DMA_CHAN    2
+#    define PIN_NUM_MISO 18
+#    define PIN_NUM_MOSI 23
+#    define PIN_NUM_CLK  19
+#  endif
+
+#  define PIN_NUM_CS   13
+#elif defined CONFIG_IDF_TARGET_ESP32S2
+#  define EEPROM_HOST    SPI2_HOST
+#  define DMA_CHAN    EEPROM_HOST
+
+#  define PIN_NUM_MISO 37
+#  define PIN_NUM_MOSI 35
+#  define PIN_NUM_CLK  36
+#  define PIN_NUM_CS   34
+#endif
+
+static const char TAG[] = "main";
+
+void app_main(void)
+{
+    esp_err_t ret;
+#ifndef CONFIG_EXAMPLE_USE_SPI1_PINS
+    ESP_LOGI(TAG, "Initializing bus SPI%d...", EEPROM_HOST+1);
+    spi_bus_config_t buscfg={
+        .miso_io_num = PIN_NUM_MISO,
+        .mosi_io_num = PIN_NUM_MOSI,
+        .sclk_io_num = PIN_NUM_CLK,
+        .quadwp_io_num = -1,
+        .quadhd_io_num = -1,
+        .max_transfer_sz = 32,
+    };
+    //Initialize the SPI bus
+    ret = spi_bus_initialize(EEPROM_HOST, &buscfg, DMA_CHAN);
+    ESP_ERROR_CHECK(ret);
+#else
+    ESP_LOGI(TAG, "Attach to main flash bus...");
+#endif
+
+    eeprom_config_t eeprom_config = {
+        .cs_io = PIN_NUM_CS,
+        .host = EEPROM_HOST,
+        .miso_io = PIN_NUM_MISO,
+    };
+#ifdef CONFIG_EXAMPLE_INTR_USED
+    eeprom_config.intr_used = true;
+    gpio_install_isr_service(0);
+#endif
+
+    eeprom_handle_t eeprom_handle;
+
+    ESP_LOGI(TAG, "Initializing device...");
+    spi_eeprom_init(&eeprom_config, &eeprom_handle);
+
+    spi_eeprom_write_enable(eeprom_handle);
+
+    const char test_str[] = "Hello World!";
+    ESP_LOGI(TAG, "Write: %s", test_str);
+    for (int i = 0; i < sizeof(test_str); i++) {
+        // No need for this EEPROM to erase before write.
+        ret = spi_eeprom_write(eeprom_handle, i, test_str[i]);
+        ESP_ERROR_CHECK(ret);
+    }
+
+    uint8_t test_buf[32] = "";
+    for (int i = 0; i < sizeof(test_str); i++) {
+        ret = spi_eeprom_read(eeprom_handle, i, &test_buf[i]);
+        ESP_ERROR_CHECK(ret);
+    }
+    ESP_LOGI(TAG, "Read: %s", test_buf);
+
+    ESP_LOGI(TAG, "Example finished.");
+
+    while (1) {
+        // Add your main loop handling code here.
+        vTaskDelay(1);
+    }
+}