From 0f1041cc044e1daa2e4896c8887681b8657bb853 Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Mon, 30 Dec 2019 13:31:12 +0800 Subject: [PATCH 1/2] example: move spi master example to a subfolder --- .../peripherals/spi_master/{ => lcd}/CMakeLists.txt | 0 examples/peripherals/spi_master/{ => lcd}/Makefile | 0 examples/peripherals/spi_master/{ => lcd}/README.md | 0 .../{ => lcd}/components/tjpgd/CMakeLists.txt | 0 .../{ => lcd}/components/tjpgd/component.mk | 0 .../{ => lcd}/components/tjpgd/include/tjpgd.h | 0 .../{ => lcd}/components/tjpgd/src/tjpgd.c | 0 .../spi_master/{ => lcd}/main/CMakeLists.txt | 0 .../spi_master/{ => lcd}/main/Kconfig.projbuild | 0 .../spi_master/{ => lcd}/main/component.mk | 0 .../spi_master/{ => lcd}/main/decode_image.c | 0 .../spi_master/{ => lcd}/main/decode_image.h | 8 ++++++++ .../peripherals/spi_master/{ => lcd}/main/image.jpg | Bin .../spi_master/{ => lcd}/main/pretty_effect.c | 0 .../spi_master/{ => lcd}/main/pretty_effect.h | 8 ++++++++ .../{ => lcd}/main/spi_master_example_main.c | 0 16 files changed, 16 insertions(+) rename examples/peripherals/spi_master/{ => lcd}/CMakeLists.txt (100%) rename examples/peripherals/spi_master/{ => lcd}/Makefile (100%) rename examples/peripherals/spi_master/{ => lcd}/README.md (100%) rename examples/peripherals/spi_master/{ => lcd}/components/tjpgd/CMakeLists.txt (100%) rename examples/peripherals/spi_master/{ => lcd}/components/tjpgd/component.mk (100%) rename examples/peripherals/spi_master/{ => lcd}/components/tjpgd/include/tjpgd.h (100%) rename examples/peripherals/spi_master/{ => lcd}/components/tjpgd/src/tjpgd.c (100%) rename examples/peripherals/spi_master/{ => lcd}/main/CMakeLists.txt (100%) rename examples/peripherals/spi_master/{ => lcd}/main/Kconfig.projbuild (100%) rename examples/peripherals/spi_master/{ => lcd}/main/component.mk (100%) rename examples/peripherals/spi_master/{ => lcd}/main/decode_image.c (100%) rename examples/peripherals/spi_master/{ => lcd}/main/decode_image.h (62%) rename examples/peripherals/spi_master/{ => lcd}/main/image.jpg (100%) rename examples/peripherals/spi_master/{ => lcd}/main/pretty_effect.c (100%) rename examples/peripherals/spi_master/{ => lcd}/main/pretty_effect.h (67%) rename examples/peripherals/spi_master/{ => lcd}/main/spi_master_example_main.c (100%) diff --git a/examples/peripherals/spi_master/CMakeLists.txt b/examples/peripherals/spi_master/lcd/CMakeLists.txt similarity index 100% rename from examples/peripherals/spi_master/CMakeLists.txt rename to examples/peripherals/spi_master/lcd/CMakeLists.txt diff --git a/examples/peripherals/spi_master/Makefile b/examples/peripherals/spi_master/lcd/Makefile similarity index 100% rename from examples/peripherals/spi_master/Makefile rename to examples/peripherals/spi_master/lcd/Makefile diff --git a/examples/peripherals/spi_master/README.md b/examples/peripherals/spi_master/lcd/README.md similarity index 100% rename from examples/peripherals/spi_master/README.md rename to examples/peripherals/spi_master/lcd/README.md diff --git a/examples/peripherals/spi_master/components/tjpgd/CMakeLists.txt b/examples/peripherals/spi_master/lcd/components/tjpgd/CMakeLists.txt similarity index 100% rename from examples/peripherals/spi_master/components/tjpgd/CMakeLists.txt rename to examples/peripherals/spi_master/lcd/components/tjpgd/CMakeLists.txt diff --git a/examples/peripherals/spi_master/components/tjpgd/component.mk b/examples/peripherals/spi_master/lcd/components/tjpgd/component.mk similarity index 100% rename from examples/peripherals/spi_master/components/tjpgd/component.mk rename to examples/peripherals/spi_master/lcd/components/tjpgd/component.mk diff --git a/examples/peripherals/spi_master/components/tjpgd/include/tjpgd.h b/examples/peripherals/spi_master/lcd/components/tjpgd/include/tjpgd.h similarity index 100% rename from examples/peripherals/spi_master/components/tjpgd/include/tjpgd.h rename to examples/peripherals/spi_master/lcd/components/tjpgd/include/tjpgd.h diff --git a/examples/peripherals/spi_master/components/tjpgd/src/tjpgd.c b/examples/peripherals/spi_master/lcd/components/tjpgd/src/tjpgd.c similarity index 100% rename from examples/peripherals/spi_master/components/tjpgd/src/tjpgd.c rename to examples/peripherals/spi_master/lcd/components/tjpgd/src/tjpgd.c diff --git a/examples/peripherals/spi_master/main/CMakeLists.txt b/examples/peripherals/spi_master/lcd/main/CMakeLists.txt similarity index 100% rename from examples/peripherals/spi_master/main/CMakeLists.txt rename to examples/peripherals/spi_master/lcd/main/CMakeLists.txt diff --git a/examples/peripherals/spi_master/main/Kconfig.projbuild b/examples/peripherals/spi_master/lcd/main/Kconfig.projbuild similarity index 100% rename from examples/peripherals/spi_master/main/Kconfig.projbuild rename to examples/peripherals/spi_master/lcd/main/Kconfig.projbuild diff --git a/examples/peripherals/spi_master/main/component.mk b/examples/peripherals/spi_master/lcd/main/component.mk similarity index 100% rename from examples/peripherals/spi_master/main/component.mk rename to examples/peripherals/spi_master/lcd/main/component.mk diff --git a/examples/peripherals/spi_master/main/decode_image.c b/examples/peripherals/spi_master/lcd/main/decode_image.c similarity index 100% rename from examples/peripherals/spi_master/main/decode_image.c rename to examples/peripherals/spi_master/lcd/main/decode_image.c diff --git a/examples/peripherals/spi_master/main/decode_image.h b/examples/peripherals/spi_master/lcd/main/decode_image.h similarity index 62% rename from examples/peripherals/spi_master/main/decode_image.h rename to examples/peripherals/spi_master/lcd/main/decode_image.h index 4c9faae365..eb02eab5ee 100644 --- a/examples/peripherals/spi_master/main/decode_image.h +++ b/examples/peripherals/spi_master/lcd/main/decode_image.h @@ -1,3 +1,11 @@ +/* + 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 #include "esp_err.h" diff --git a/examples/peripherals/spi_master/main/image.jpg b/examples/peripherals/spi_master/lcd/main/image.jpg similarity index 100% rename from examples/peripherals/spi_master/main/image.jpg rename to examples/peripherals/spi_master/lcd/main/image.jpg diff --git a/examples/peripherals/spi_master/main/pretty_effect.c b/examples/peripherals/spi_master/lcd/main/pretty_effect.c similarity index 100% rename from examples/peripherals/spi_master/main/pretty_effect.c rename to examples/peripherals/spi_master/lcd/main/pretty_effect.c diff --git a/examples/peripherals/spi_master/main/pretty_effect.h b/examples/peripherals/spi_master/lcd/main/pretty_effect.h similarity index 67% rename from examples/peripherals/spi_master/main/pretty_effect.h rename to examples/peripherals/spi_master/lcd/main/pretty_effect.h index 00f000c048..f4fbe70fbe 100644 --- a/examples/peripherals/spi_master/main/pretty_effect.h +++ b/examples/peripherals/spi_master/lcd/main/pretty_effect.h @@ -1,3 +1,11 @@ +/* + 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 #include "esp_err.h" diff --git a/examples/peripherals/spi_master/main/spi_master_example_main.c b/examples/peripherals/spi_master/lcd/main/spi_master_example_main.c similarity index 100% rename from examples/peripherals/spi_master/main/spi_master_example_main.c rename to examples/peripherals/spi_master/lcd/main/spi_master_example_main.c From f53812d27a83e32feb670be756cb41d94faebd5f Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Wed, 25 Dec 2019 17:32:12 +0800 Subject: [PATCH 2/2] spi: add eeprom example --- .../spi_master/hd_eeprom/CMakeLists.txt | 6 + .../peripherals/spi_master/hd_eeprom/Makefile | 9 + .../spi_master/hd_eeprom/README.md | 24 ++ .../components/eeprom/CMakeLists.txt | 4 + .../hd_eeprom/components/eeprom/component.mk | 6 + .../hd_eeprom/components/eeprom/linker.lf | 24 ++ .../hd_eeprom/components/eeprom/spi_eeprom.c | 324 ++++++++++++++++++ .../hd_eeprom/components/eeprom/spi_eeprom.h | 123 +++++++ .../spi_master/hd_eeprom/main/CMakeLists.txt | 5 + .../hd_eeprom/main/Kconfig.projbuild | 32 ++ .../spi_master/hd_eeprom/main/component.mk | 4 + .../hd_eeprom/main/spi_eeprom_main.c | 113 ++++++ 12 files changed, 674 insertions(+) create mode 100644 examples/peripherals/spi_master/hd_eeprom/CMakeLists.txt create mode 100644 examples/peripherals/spi_master/hd_eeprom/Makefile create mode 100644 examples/peripherals/spi_master/hd_eeprom/README.md create mode 100644 examples/peripherals/spi_master/hd_eeprom/components/eeprom/CMakeLists.txt create mode 100644 examples/peripherals/spi_master/hd_eeprom/components/eeprom/component.mk create mode 100644 examples/peripherals/spi_master/hd_eeprom/components/eeprom/linker.lf create mode 100644 examples/peripherals/spi_master/hd_eeprom/components/eeprom/spi_eeprom.c create mode 100644 examples/peripherals/spi_master/hd_eeprom/components/eeprom/spi_eeprom.h create mode 100644 examples/peripherals/spi_master/hd_eeprom/main/CMakeLists.txt create mode 100644 examples/peripherals/spi_master/hd_eeprom/main/Kconfig.projbuild create mode 100644 examples/peripherals/spi_master/hd_eeprom/main/component.mk create mode 100644 examples/peripherals/spi_master/hd_eeprom/main/spi_eeprom_main.c diff --git a/examples/peripherals/spi_master/hd_eeprom/CMakeLists.txt b/examples/peripherals/spi_master/hd_eeprom/CMakeLists.txt new file mode 100644 index 0000000000..dca4c246ee --- /dev/null +++ b/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) diff --git a/examples/peripherals/spi_master/hd_eeprom/Makefile b/examples/peripherals/spi_master/hd_eeprom/Makefile new file mode 100644 index 0000000000..9eb43967c8 --- /dev/null +++ b/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 + diff --git a/examples/peripherals/spi_master/hd_eeprom/README.md b/examples/peripherals/spi_master/hd_eeprom/README.md new file mode 100644 index 0000000000..4b1f4458a7 --- /dev/null +++ b/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. \ No newline at end of file diff --git a/examples/peripherals/spi_master/hd_eeprom/components/eeprom/CMakeLists.txt b/examples/peripherals/spi_master/hd_eeprom/components/eeprom/CMakeLists.txt new file mode 100644 index 0000000000..298fb3b395 --- /dev/null +++ b/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 ".") + diff --git a/examples/peripherals/spi_master/hd_eeprom/components/eeprom/component.mk b/examples/peripherals/spi_master/hd_eeprom/components/eeprom/component.mk new file mode 100644 index 0000000000..edc7523e30 --- /dev/null +++ b/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" diff --git a/examples/peripherals/spi_master/hd_eeprom/components/eeprom/linker.lf b/examples/peripherals/spi_master/hd_eeprom/components/eeprom/linker.lf new file mode 100644 index 0000000000..b6287c6603 --- /dev/null +++ b/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) \ No newline at end of file diff --git a/examples/peripherals/spi_master/hd_eeprom/components/eeprom/spi_eeprom.c b/examples/peripherals/spi_master/hd_eeprom/components/eeprom/spi_eeprom.c new file mode 100644 index 0000000000..9b50d89369 --- /dev/null +++ b/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 +#include "esp_log.h" +#include +#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; +} diff --git a/examples/peripherals/spi_master/hd_eeprom/components/eeprom/spi_eeprom.h b/examples/peripherals/spi_master/hd_eeprom/components/eeprom/spi_eeprom.h new file mode 100644 index 0000000000..295a7e0a8d --- /dev/null +++ b/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 \ No newline at end of file diff --git a/examples/peripherals/spi_master/hd_eeprom/main/CMakeLists.txt b/examples/peripherals/spi_master/hd_eeprom/main/CMakeLists.txt new file mode 100644 index 0000000000..30c1638dad --- /dev/null +++ b/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 ".") + diff --git a/examples/peripherals/spi_master/hd_eeprom/main/Kconfig.projbuild b/examples/peripherals/spi_master/hd_eeprom/main/Kconfig.projbuild new file mode 100644 index 0000000000..3dcb4a7017 --- /dev/null +++ b/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 diff --git a/examples/peripherals/spi_master/hd_eeprom/main/component.mk b/examples/peripherals/spi_master/hd_eeprom/main/component.mk new file mode 100644 index 0000000000..b4fa72791c --- /dev/null +++ b/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.) diff --git a/examples/peripherals/spi_master/hd_eeprom/main/spi_eeprom_main.c b/examples/peripherals/spi_master/hd_eeprom/main/spi_eeprom_main.c new file mode 100644 index 0000000000..3130c27c39 --- /dev/null +++ b/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 +#include +#include +#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); + } +}