From da977149f67487c023766974696cb1e8a5bc9117 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 6 Dec 2016 16:33:24 -0800 Subject: [PATCH 01/11] panic handlers: Print the PC address where abort() was called, don't dump registers --- components/esp32/cpu_util.c | 12 +++++ components/esp32/include/soc/cpu.h | 9 ++++ components/esp32/panic.c | 75 +++++++++++++++++------------- components/newlib/syscalls.c | 9 ---- 4 files changed, 63 insertions(+), 42 deletions(-) diff --git a/components/esp32/cpu_util.c b/components/esp32/cpu_util.c index cff61ab796..e3b4ef8f16 100644 --- a/components/esp32/cpu_util.c +++ b/components/esp32/cpu_util.c @@ -42,3 +42,15 @@ void IRAM_ATTR esp_cpu_unstall(int cpu_id) CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_STALL_PROCPU_C0_M); } } + +bool IRAM_ATTR esp_cpu_in_ocd_debug_mode() +{ +#if CONFIG_ESP32_DEBUG_OCDAWARE + int dcr; + int reg=0x10200C; //DSRSET register + asm("rer %0,%1":"=r"(dcr):"r"(reg)); + return (dcr&0x1); +#else + return false; // Always return false if "OCD aware" is disabled +#endif +} diff --git a/components/esp32/include/soc/cpu.h b/components/esp32/include/soc/cpu.h index 4457c81a22..b89ae2875f 100644 --- a/components/esp32/include/soc/cpu.h +++ b/components/esp32/include/soc/cpu.h @@ -94,4 +94,13 @@ void esp_cpu_stall(int cpu_id); */ void esp_cpu_unstall(int cpu_id); +/** + * @brief Returns true if a JTAG debugger is attached to CPU + * OCD (on chip debug) port. + * + * @note If "Make exception and panic handlers JTAG/OCD aware" + * is disabled, this function always returns false. + */ +bool esp_cpu_in_ocd_debug_mode(); + #endif diff --git a/components/esp32/panic.c b/components/esp32/panic.c index 0efe56fe01..3cdbfb3e39 100644 --- a/components/esp32/panic.c +++ b/components/esp32/panic.c @@ -36,7 +36,7 @@ /* Panic handlers; these get called when an unhandled exception occurs or the assembly-level task switching / interrupt code runs into an unrecoverable error. The default task stack - overflow handler also is in here. + overflow handler and abort handler are also in here. */ /* @@ -95,15 +95,29 @@ inline static void panicPutHex(int a) { } inline static void panicPutDec(int a) { } #endif - void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName ) { panicPutStr("***ERROR*** A stack overflow in task "); panicPutStr((char *)pcTaskName); panicPutStr(" has been detected.\r\n"); - configASSERT(0); + abort(); } +static bool abort_called; + +void abort() +{ +#if !CONFIG_ESP32_PANIC_SILENT_REBOOT + ets_printf("abort() was called at PC 0x%08x\n", (intptr_t)__builtin_return_address(0) - 3); +#endif + abort_called = true; + while(1) { + __asm__ ("break 0,0"); + *((int*) 0) = 0; + } +} + + static const char *edesc[] = { "IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError", "Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue", @@ -118,7 +132,7 @@ static const char *edesc[] = { }; -void commonErrorHandler(XtExcFrame *frame); +static void commonErrorHandler(XtExcFrame *frame); //The fact that we've panic'ed probably means the other CPU is now running wild, possibly //messing up the serial output, so we stall it here. @@ -127,19 +141,6 @@ static void haltOtherCore() esp_cpu_stall( xPortGetCoreID() == 0 ? 1 : 0 ); } -//Returns true when a debugger is attached using JTAG. -static int inOCDMode() -{ -#if CONFIG_ESP32_DEBUG_OCDAWARE - int dcr; - int reg = 0x10200C; //DSRSET register - asm("rer %0,%1":"=r"(dcr):"r"(reg)); - return (dcr & 0x1); -#else - return 0; //Always return no debugger is attached. -#endif -} - void panicHandler(XtExcFrame *frame) { int *regs = (int *)frame; @@ -165,7 +166,7 @@ void panicHandler(XtExcFrame *frame) panicPutStr(reason); panicPutStr(")\r\n"); - if (inOCDMode()) { + if (esp_cpu_in_ocd_debug_mode()) { asm("break.n 1"); } commonErrorHandler(frame); @@ -197,7 +198,7 @@ void xt_unhandled_exception(XtExcFrame *frame) } panicPutStr(" occurred on core "); panicPutDec(xPortGetCoreID()); - if (inOCDMode()) { + if (esp_cpu_in_ocd_debug_mode()) { panicPutStr(" at pc="); panicPutHex(regs[1]); panicPutStr(". Setting bp and returning..\r\n"); @@ -255,6 +256,7 @@ static inline bool stackPointerIsSane(uint32_t sp) { return !(sp < 0x3ffae010 || sp > 0x3ffffff0 || ((sp & 0xf) != 0)); } + static void putEntry(uint32_t pc, uint32_t sp) { if (pc & 0x80000000) { @@ -265,7 +267,8 @@ static void putEntry(uint32_t pc, uint32_t sp) panicPutStr(":0x"); panicPutHex(sp); } -void doBacktrace(XtExcFrame *frame) + +static void doBacktrace(XtExcFrame *frame) { uint32_t i = 0, pc = frame->pc, sp = frame->a1; panicPutStr("\nBacktrace:"); @@ -291,7 +294,7 @@ void doBacktrace(XtExcFrame *frame) We arrive here after a panic or unhandled exception, when no OCD is detected. Dump the registers to the serial port and either jump to the gdb stub, halt the CPU or reboot. */ -void commonErrorHandler(XtExcFrame *frame) +static void commonErrorHandler(XtExcFrame *frame) { int *regs = (int *)frame; int x, y; @@ -304,21 +307,28 @@ void commonErrorHandler(XtExcFrame *frame) //Feed the watchdogs, so they will give us time to print out debug info reconfigureAllWdts(); - panicPutStr("Register dump:\r\n"); + /* only dump registers for 'real' crashes, if crashing via abort() + the register window is no longer useful. + */ + if (!abort_called) { + panicPutStr("Register dump:\r\n"); - for (x = 0; x < 24; x += 4) { - for (y = 0; y < 4; y++) { - if (sdesc[x + y][0] != 0) { - panicPutStr(sdesc[x + y]); - panicPutStr(": 0x"); - panicPutHex(regs[x + y + 1]); - panicPutStr(" "); + for (x = 0; x < 24; x += 4) { + for (y = 0; y < 4; y++) { + if (sdesc[x + y][0] != 0) { + panicPutStr(sdesc[x + y]); + panicPutStr(": 0x"); + panicPutHex(regs[x + y + 1]); + panicPutStr(" "); + } } + panicPutStr("\r\n"); } - panicPutStr("\r\n"); } + /* With windowed ABI backtracing is easy, let's do it. */ doBacktrace(frame); + #if CONFIG_ESP32_PANIC_GDBSTUB disableAllWdts(); panicPutStr("Entering gdb stub now.\r\n"); @@ -339,8 +349,7 @@ void commonErrorHandler(XtExcFrame *frame) void esp_set_breakpoint_if_jtag(void *fn) { - if (!inOCDMode()) { - return; + if (esp_cpu_in_ocd_debug_mode()) { + setFirstBreakpoint((uint32_t)fn); } - setFirstBreakpoint((uint32_t)fn); } diff --git a/components/newlib/syscalls.c b/components/newlib/syscalls.c index 3b2fbf62ca..74182d07f2 100644 --- a/components/newlib/syscalls.c +++ b/components/newlib/syscalls.c @@ -22,15 +22,6 @@ #include "esp_attr.h" #include "freertos/FreeRTOS.h" -void IRAM_ATTR abort() -{ - do - { - __asm__ ("break 0,0"); - *((int*) 0) = 0; - } while(true); -} - void* IRAM_ATTR _malloc_r(struct _reent *r, size_t size) { return pvPortMalloc(size); From d245f016ea013b6c12e1ca4755a671eb57a1cc61 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 20 Dec 2016 18:02:47 +1100 Subject: [PATCH 02/11] esptool: Add new options to reset before/after, detect flash size --- components/bootloader/Makefile.projbuild | 5 ++- components/esptool_py/Kconfig.projbuild | 57 ++++++++++++++++++++++++ components/esptool_py/Makefile.projbuild | 9 +++- components/esptool_py/esptool | 2 +- 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 35be94e4a3..9e7b17b65f 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -50,11 +50,14 @@ else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH # One time flashing requires user to run esptool.py command themselves, # and warning is printed about inability to reflash. +# +# The flashing command is deliberately printed without an auto-reset +# step, so the device doesn't immediately reset to flash itself. bootloader: $(BOOTLOADER_BIN) @echo $(SEPARATOR) @echo "Bootloader built. One-time flash command is:" - @echo "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)" + @echo "$(subst hard_reset,no_reset,$(ESPTOOLPY_WRITE_FLASH)) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)" @echo $(SEPARATOR) @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" diff --git a/components/esptool_py/Kconfig.projbuild b/components/esptool_py/Kconfig.projbuild index 8bab51225e..dd2077cbb3 100644 --- a/components/esptool_py/Kconfig.projbuild +++ b/components/esptool_py/Kconfig.projbuild @@ -121,4 +121,61 @@ config ESPTOOLPY_FLASHSIZE default "8MB" if ESPTOOLPY_FLASHSIZE_8MB default "16MB" if ESPTOOLPY_FLASHSIZE_16MB +config ESPTOOLPY_FLASHSIZE_DETECT + bool "Detect flash size when flashing bootloader" + default y + help + If this option is set, 'make flash' targets will automatically detect + the flash size and update the bootloader image when flashing. + +choice ESPTOOLPY_BEFORE + prompt "Before flashing" + default ESPTOOLPY_BEFORE_RESET + help + Configure whether esptool.py should reset the ESP32 before flashing. + + Automatic resetting depends on the RTS & DTR signals being + wired from the serial port to the ESP32. Most USB development + boards do this internally. + + The "Reset with ESP32R0 Windows workaround" option works + around an automatic reset bug in hardware, when using Windows + with some development boards. This fix only works if you're + using a silicon revision 0 ESP32. + +config ESPTOOLPY_BEFORE_RESET + bool "Reset to bootloader" +config ESPTOOLPY_BEFORE_NORESET + bool "No reset" +config ESPTOOLPY_BEFORE_ESP32R0 + bool "Reset with ESP32R0 Windows workaround" +endchoice + +config ESPTOOLPY_BEFORE + string + default "default_reset" if ESPTOOLPY_BEFORE_RESET + default "no_reset" if ESPTOOLPY_BEFORE_NORESET + default "esp32r0" if ESPTOOLPY_BEFORE_ESP32R0 + +choice ESPTOOLPY_AFTER + prompt "After flashing" + default ESPTOOLPY_AFTER_RESET + help + Configure whether esptool.py should reset the ESP32 after flashing. + + Automatic resetting depends on the RTS & DTR signals being + wired from the serial port to the ESP32. Most USB development + boards do this internally. + +config ESPTOOLPY_AFTER_RESET + bool "Reset after flashing" +config ESPTOOLPY_AFTER_NORESET + bool "Stay in bootloader" +endchoice + +config ESPTOOLPY_AFTER + string + default "hard_reset" if ESPTOOLPY_AFTER_RESET + default "no_reset" if ESPTOOLPY_AFTER_NORESET + endmenu diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index b5992a4d96..88b5894731 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -13,7 +13,7 @@ PYTHON ?= $(call dequote,$(CONFIG_PYTHON)) # ESPTOOLPY_SRC := $(COMPONENT_PATH)/esptool/esptool.py ESPTOOLPY := $(PYTHON) $(ESPTOOLPY_SRC) --chip esp32 -ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) +ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) --before $(CONFIG_ESPTOOLPY_BEFORE) --after $(CONFIG_ESPTOOLPY_AFTER) # Supporting esptool command line tools ESPEFUSEPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espefuse.py @@ -21,10 +21,15 @@ ESPSECUREPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espsecure.py export ESPSECUREPY # is used in bootloader_support component ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) +ifdef CONFIG_ESPTOOLPY_FLASHSIZE_DETECT +ESPTOOL_WRITE_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size detect +else +ESPTOOL_WRITE_FLASH_OPTIONS := $(ESPTOOL_FLASH_OPTIONS) +endif ESPTOOL_ELF2IMAGE_OPTIONS := -ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS) +ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_WRITE_FLASH_OPTIONS) ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index adc914b91a..fe69994270 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit adc914b91ac6d2cfd1ace56307b4374eb9439e14 +Subproject commit fe69994270e2a450aad3e94a409b58460b1a214f From 76e61ded3055c8933c9616688a1241bfdaaab1cc Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 13:15:01 +1100 Subject: [PATCH 03/11] bootloader: Call esp_partition_table_basic_verify() as part of standard boot Was previously only verified during flash encryption. --- .../bootloader/src/main/bootloader_start.c | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 7dd753b3f8..e5fda9cf53 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -40,6 +40,7 @@ #include "esp_image_format.h" #include "esp_secure_boot.h" #include "esp_flash_encrypt.h" +#include "esp_flash_partitions.h" #include "bootloader_flash.h" #include "bootloader_config.h" @@ -116,16 +117,14 @@ bool load_partition_table(bootloader_state_t* bs) { const esp_partition_info_t *partitions; const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ - const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t); char *partition_usage; - - ESP_LOGI(TAG, "Partition Table:"); - ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + esp_err_t err; + int num_partitions; #ifdef CONFIG_SECURE_BOOT_ENABLED if(esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying partition table signature..."); - esp_err_t err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to verify partition table signature."); return false; @@ -141,17 +140,21 @@ bool load_partition_table(bootloader_state_t* bs) } ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions); - for(int i = 0; i < MAX_PARTITIONS; i++) { + err = esp_partition_table_basic_verify(partitions, true, &num_partitions); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify partition table"); + return false; + } + + ESP_LOGI(TAG, "Partition Table:"); + ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + + for(int i = 0; i < num_partitions; i++) { const esp_partition_info_t *partition = &partitions[i]; ESP_LOGD(TAG, "load partition table entry 0x%x", (intptr_t)partition); ESP_LOGD(TAG, "type=%x subtype=%x", partition->type, partition->subtype); partition_usage = "unknown"; - if (partition->magic != ESP_PARTITION_MAGIC) { - /* invalid partition definition indicates end-of-table */ - break; - } - /* valid partition table */ switch(partition->type) { case PART_TYPE_APP: /* app partition */ From 3783e28f0e57bf47ba12c67df9e0f09143b17abd Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 13:16:22 +1100 Subject: [PATCH 04/11] bootloader: Check all partitions fit inside configured flash size --- .../bootloader_support/src/flash_partitions.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/components/bootloader_support/src/flash_partitions.c b/components/bootloader_support/src/flash_partitions.c index ed427df1a6..b60968f969 100644 --- a/components/bootloader_support/src/flash_partitions.c +++ b/components/bootloader_support/src/flash_partitions.c @@ -13,32 +13,43 @@ // limitations under the License. #include "esp_flash_partitions.h" #include "esp_log.h" +#include "rom/spi_flash.h" static const char *TAG = "flash_parts"; esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions) { int num_parts; + uint32_t chip_size = g_rom_flashchip.chip_size; *num_partitions = 0; for(num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) { const esp_partition_info_t *part = &partition_table[num_parts]; - if(part->magic == 0xFFFF - && part->type == PART_TYPE_END - && part->subtype == PART_SUBTYPE_END) { + if (part->magic == 0xFFFF + && part->type == PART_TYPE_END + && part->subtype == PART_SUBTYPE_END) { /* TODO: check md5 */ ESP_LOGD(TAG, "partition table verified, %d entries", num_parts); *num_partitions = num_parts; return ESP_OK; } - if(part->magic != ESP_PARTITION_MAGIC) { + if (part->magic != ESP_PARTITION_MAGIC) { if (log_errors) { ESP_LOGE(TAG, "partition %d invalid magic number 0x%x", num_parts, part->magic); } return ESP_ERR_INVALID_STATE; } + + const esp_partition_pos_t *pos = &part->pos; + if (pos->offset > chip_size || pos->offset + pos->size > chip_size) { + if (log_errors) { + ESP_LOGE(TAG, "partition %d invalid - offset 0x%x size 0x%x exceeds flash chip size 0x%x", + num_parts, pos->offset, pos->size, chip_size); + } + return ESP_ERR_INVALID_SIZE; + } } if (log_errors) { From 79d6d9f7019b5d33ff7cb4ebedcbf49457be25c2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 15:20:49 +1100 Subject: [PATCH 05/11] CI build_examples: Correctly detect example build failures "pipefail" regression when fail-on-warnings was added... --- make/build_examples.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/make/build_examples.sh b/make/build_examples.sh index f1b37b7b4b..dc10d95ab2 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -30,10 +30,11 @@ for example in ${IDF_PATH}/examples/*; do # build non-verbose first BUILDLOG=$(mktemp -t examplebuild.XXXX.log) ( + set -o pipefail # so result of make all isn't lost when piping to tee set -e make clean defconfig - make all 2>&1 | tee $BUILDLOG - ) || (RESULT=$?; make V=1) # only build verbose if there's an error + make $* all 2>&1 | tee $BUILDLOG + ) || { RESULT=$?; make V=1; } # only build verbose if there's an error popd EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) From eb8324e2c280837aecde5d454ba52d663d3c77a8 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 16:12:48 +1100 Subject: [PATCH 06/11] examples: Fix build errors that weren't being caught by CI --- examples/12_blufi/components/blufi/blufi.c | 1 + examples/12_blufi/main/demo_main.c | 14 +++++++------- .../{14_ethernet => 17_ethernet}/main/tlk110_phy.h | 0 3 files changed, 8 insertions(+), 7 deletions(-) rename examples/{14_ethernet => 17_ethernet}/main/tlk110_phy.h (100%) diff --git a/examples/12_blufi/components/blufi/blufi.c b/examples/12_blufi/components/blufi/blufi.c index 5a1c543b50..ce473667a4 100644 --- a/examples/12_blufi/components/blufi/blufi.c +++ b/examples/12_blufi/components/blufi/blufi.c @@ -30,6 +30,7 @@ #include "bt_trace.h" #include "bt_types.h" +#include "bta_api.h" #include "blufi.h" diff --git a/examples/12_blufi/main/demo_main.c b/examples/12_blufi/main/demo_main.c index f1c3807741..90992313c2 100644 --- a/examples/12_blufi/main/demo_main.c +++ b/examples/12_blufi/main/demo_main.c @@ -46,15 +46,15 @@ const int CONNECTED_BIT = BIT0; static wifi_config_t sta_config; static char tmp_ssid[33]; -static char tmp_passwd[33]; +static char tmp_passwd[65]; static bool confirm = false; void wifi_set_blue_config(char *ssid, char *passwd) { - memset(tmp_ssid, 0, 33); - memset(tmp_passwd, 0, 33); - strcpy(tmp_ssid, ssid); - strcpy(tmp_passwd, passwd); + memset(tmp_ssid, 0, sizeof(tmp_ssid)); + memset(tmp_passwd, 0, sizeof(tmp_passwd)); + strlcpy(tmp_ssid, ssid, sizeof(tmp_ssid)); + strlcpy(tmp_passwd, passwd, sizeof(tmp_passwd)); confirm = true; LOG_DEBUG("confirm true\n"); } @@ -105,8 +105,8 @@ void wifiTestTask(void *pvParameters) if (confirm) { confirm = false; - strcpy(sta_config.sta.ssid, tmp_ssid); - strcpy(sta_config.sta.password, tmp_passwd); + memcpy(sta_config.sta.ssid, tmp_ssid, sizeof(sta_config.sta.ssid)); + memcpy(sta_config.sta.password, tmp_passwd, sizeof(sta_config.sta.password)); sta_config.sta.bssid_set = 0; ret = esp_wifi_disconnect(); diff --git a/examples/14_ethernet/main/tlk110_phy.h b/examples/17_ethernet/main/tlk110_phy.h similarity index 100% rename from examples/14_ethernet/main/tlk110_phy.h rename to examples/17_ethernet/main/tlk110_phy.h From 6b87419d420427ef4cec012a784b046731b27714 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 16:13:07 +1100 Subject: [PATCH 07/11] CI build_examples: Don't stop on first failed example, print failure summary --- make/build_examples.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/make/build_examples.sh b/make/build_examples.sh index dc10d95ab2..ab59208795 100755 --- a/make/build_examples.sh +++ b/make/build_examples.sh @@ -11,11 +11,10 @@ EXAMPLE_NUM=1 RESULT=0 +FAILED_EXAMPLES="" RESULT_WARNINGS=22 # magic number result code for "warnings found" -set -e - for example in ${IDF_PATH}/examples/*; do [ -f ${example}/Makefile ] || continue echo "Building ${example} as ${EXAMPLE_NUM}..." @@ -34,13 +33,13 @@ for example in ${IDF_PATH}/examples/*; do set -e make clean defconfig make $* all 2>&1 | tee $BUILDLOG - ) || { RESULT=$?; make V=1; } # only build verbose if there's an error + ) || { RESULT=$?; FAILED_EXAMPLES+=" ${example}"; make V=1; } # only build verbose if there's an error popd EXAMPLE_NUM=$(( $EXAMPLE_NUM + 1 )) - if [ $RESULT -eq 0 ] && grep -q ": warning:" $BUILDLOG; then - echo "Build will fail, due to warnings in this example" - RESULT=$RESULT_WARNINGS + if grep -q ": warning:" $BUILDLOG; then + [ $RESULT -eq 0 ] && RESULT=$RESULT_WARNINGS + FAILED_EXAMPLES+=" ${example} (warnings)" fi rm -f $BUILDLOG @@ -50,5 +49,7 @@ if [ $RESULT -eq $RESULT_WARNINGS ]; then echo "Build would have passed, except for warnings." fi +[ $RESULT -eq 0 ] || echo "Failed examples: $FAILED_EXAMPLES" + exit $RESULT From 9c7cc86793e6c710554942b61556b7382100fb9a Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Fri, 16 Dec 2016 23:41:04 +0800 Subject: [PATCH 08/11] 1. modify i2c_set_pin function 2. update example comments and other minor changes 3. rename API: i2c_cmd_link_create/i2c_cmd_link_delete (+4 squashed commits) Squashed commits: [2e0ac3e] 1. coding style: add one space after condition key words. 2. modify i2c.h, use gpio_num_t instead of int, improve comments of return values 3. add i2c index in index.rst 4. add readme for i2c example [4991d92] update i2c.doc [88b672e] driver: i2c 1. add mux and spin lock to run in a thread-safe way. 2. modify example code [4eb15fe] driver: i2c code 1. add i2c master code 2. add i2c slave code 3. add i2c example code 4. add DRAM_ATTR for I2C array --- components/driver/i2c.c | 1037 +++++++++++++++++++++ components/driver/include/driver/i2c.h | 514 ++++++++++ components/esp32/include/soc/i2c_reg.h | 2 + components/esp32/include/soc/i2c_struct.h | 6 +- docs/api/i2c.rst | 82 ++ docs/index.rst | 1 + examples/18_i2c/Makefile | 9 + examples/18_i2c/README.md | 29 + examples/18_i2c/main/component.mk | 3 + examples/18_i2c/main/i2c_test.c | 303 ++++++ 10 files changed, 1983 insertions(+), 3 deletions(-) create mode 100644 components/driver/i2c.c create mode 100644 components/driver/include/driver/i2c.h create mode 100644 docs/api/i2c.rst create mode 100644 examples/18_i2c/Makefile create mode 100644 examples/18_i2c/README.md create mode 100644 examples/18_i2c/main/component.mk create mode 100644 examples/18_i2c/main/i2c_test.c diff --git a/components/driver/i2c.c b/components/driver/i2c.c new file mode 100644 index 0000000000..311f01516c --- /dev/null +++ b/components/driver/i2c.c @@ -0,0 +1,1037 @@ +// Copyright 2015-2016 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 +#include +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_intr.h" +#include "esp_log.h" +#include "malloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "soc/dport_reg.h" +#include "soc/i2c_struct.h" +#include "soc/i2c_reg.h" +#include "driver/i2c.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" + +static const char* I2C_TAG = "i2c"; +#define I2C_CHECK(a, str, ret) if(!(a)) { \ + ESP_LOGE(I2C_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret); \ + } + +static portMUX_TYPE i2c_spinlock[I2C_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; +/* DRAM_ATTR is required to avoid I2C array placed in flash, due to accessed from ISR */ +static DRAM_ATTR i2c_dev_t* const I2C[I2C_NUM_MAX] = { &I2C0, &I2C1 }; + + +#define I2C_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux) +#define I2C_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux) +#define I2C_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux) +#define I2C_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + +#define I2C_DRIVER_ERR_STR "i2c driver install error" +#define I2C_DRIVER_MALLOC_ERR_STR "i2c driver malloc error" +#define I2C_NUM_ERROR_STR "i2c number error" +#define I2C_ADDR_ERROR_STR "i2c null address error" +#define I2C_DRIVER_NOT_INSTALL_ERR_STR "i2c driver not installed" +#define I2C_SLAVE_BUFFER_LEN_ERR_STR "i2c buffer size too short for slave mode" +#define I2C_EVT_QUEUE_ERR_STR "i2c evt queue error" +#define I2C_SEM_ERR_STR "i2c semaphore error" +#define I2C_BUF_ERR_STR "i2c ringbuffer error" +#define I2C_MASTER_MODE_ERR_STR "Only allowed in master mode" +#define I2C_MODE_SLAVE_ERR_STR "Only allowed in slave mode" +#define I2C_CMD_MALLOC_ERR_STR "i2c command link malloc error" +#define I2C_TRANS_MODE_ERR_STR "i2c trans mode error" +#define I2C_MODE_ERR_STR "i2c mode error" +#define I2C_SDA_IO_ERR_STR "sda gpio number error" +#define I2C_SCL_IO_ERR_STR "scl gpio number error" +#define I2C_CMD_LINK_INIT_ERR_STR "i2c command link error" +#define I2C_GPIO_PULLUP_ERR_STR "this i2c pin do not support internal pull-up" +#define I2C_FIFO_FULL_THRESH_VAL (28) +#define I2C_FIFO_EMPTY_THRESH_VAL (5) + +typedef struct { + uint8_t byte_num; /*!< cmd byte number */ + uint8_t ack_en; /*!< ack check enable */ + uint8_t ack_exp; /*!< expected ack level to get */ + uint8_t ack_val; /*!< ack value to send */ + uint8_t* data; /*!< data address */ + uint8_t byte_cmd; /*!< to save cmd for one byte command mode */ + i2c_opmode_t op_code; /*!< haredware cmd type */ +}i2c_cmd_t; + +typedef struct i2c_cmd_link{ + i2c_cmd_t cmd; /*!< command in current cmd link */ + struct i2c_cmd_link *next; /*!< next cmd link */ +} i2c_cmd_link_t; + +typedef struct { + i2c_cmd_link_t* head; /*!< head of the command link */ + i2c_cmd_link_t* cur; /*!< last node of the command link */ + i2c_cmd_link_t* free; /*!< the first node to free of the command link */ +} i2c_cmd_desc_t; + +typedef enum { + I2C_STATUS_READ, /*!< read status for current master command */ + I2C_STATUS_WRITE, /*!< write status for current master command */ + I2C_STATUS_IDLE, /*!< idle status for current master command */ + I2C_STATUS_ACK_ERROR, /*!< ack error status for current master command */ + I2C_STATUS_DONE, /*!< I2C command done */ +} i2c_status_t; + +typedef struct { + int i2c_num; /*!< I2C port number */ + int mode; /*!< I2C mode, master or slave */ + intr_handle_t intr_handle; /*!< I2C interrupt handle*/ + + int cmd_idx; /*!< record current command index, for master mode */ + int status; /*!< record current command status, for master mode */ + int rx_cnt; /*!< record current read index, for master mode */ + uint8_t data_buf[I2C_FIFO_LEN]; /*!< a buffer to store i2c fifo data */ + i2c_cmd_desc_t cmd_link; /*!< I2C command link */ + xSemaphoreHandle cmd_sem; /*!< semaphore to sync command status */ + xSemaphoreHandle cmd_mux; /*!< semaphore to lock command process */ + size_t tx_fifo_remain; /*!< tx fifo remain length, for master mode */ + size_t rx_fifo_remain; /*!< rx fifo remain length, for master mode */ + + xSemaphoreHandle slv_rx_mux; /*!< slave rx buffer mux */ + xSemaphoreHandle slv_tx_mux; /*!< slave tx buffer mux */ + size_t rx_buf_length; /*!< rx buffer length */ + RingbufHandle_t rx_ring_buf; /*!< rx ringbuffer handler of slave mode */ + size_t tx_buf_length; /*!< tx buffer length */ + RingbufHandle_t tx_ring_buf; /*!< tx ringbuffer handler of slave mode */ +} i2c_obj_t; + +static i2c_obj_t *p_i2c_obj[I2C_NUM_MAX] = {0}; +static void i2c_isr_handler_default(void* arg); +static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num); + +/* + For i2c master mode, we don't need to use a buffer for the data, the APIs will execute the master commands +and return after all of the commands have been sent out or when error occurs. So when we send master commands, +we should free or modify the source data only after the i2c_master_cmd_begin function returns. + For i2c slave mode, we need a data buffer to stash the sending and receiving data, because the hardware fifo +has only 32 bytes. +*/ + +esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_buf_len, size_t slv_tx_buf_len, + int intr_alloc_flags) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(mode == I2C_MODE_MASTER || ( slv_rx_buf_len > 100 || slv_tx_buf_len > 100 ), I2C_SLAVE_BUFFER_LEN_ERR_STR, + ESP_ERR_INVALID_ARG); + uint32_t intr_mask = 0; + if (p_i2c_obj[i2c_num] == NULL) { + p_i2c_obj[i2c_num] = (i2c_obj_t*) calloc(1, sizeof(i2c_obj_t)); + if (p_i2c_obj[i2c_num] == NULL) { + ESP_LOGE(I2C_TAG, I2C_DRIVER_MALLOC_ERR_STR); + return ESP_FAIL; + } + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + p_i2c->i2c_num = i2c_num; + p_i2c->mode = mode; + p_i2c->cmd_idx = 0; + p_i2c->rx_cnt = 0; + p_i2c->status = I2C_STATUS_IDLE; + + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + + if (mode == I2C_MODE_SLAVE) { + //we only use ringbuffer for slave mode. + if (slv_rx_buf_len > 0) { + p_i2c->rx_ring_buf = xRingbufferCreate(slv_rx_buf_len, RINGBUF_TYPE_BYTEBUF); + if (p_i2c->rx_ring_buf == NULL) { + ESP_LOGE(I2C_TAG, I2C_BUF_ERR_STR); + goto err; + } + p_i2c->rx_buf_length = slv_rx_buf_len; + } else { + p_i2c->tx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + } + if (slv_tx_buf_len > 0) { + p_i2c->tx_ring_buf = xRingbufferCreate(slv_tx_buf_len, RINGBUF_TYPE_BYTEBUF); + if (p_i2c->tx_ring_buf == NULL) { + ESP_LOGE(I2C_TAG, I2C_BUF_ERR_STR); + goto err; + } + p_i2c->tx_buf_length = slv_tx_buf_len; + } else { + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + p_i2c->slv_rx_mux = xSemaphoreCreateMutex(); + p_i2c->slv_tx_mux = xSemaphoreCreateMutex(); + if (p_i2c->slv_rx_mux == NULL || p_i2c->slv_rx_mux == NULL) { + ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); + goto err; + } + intr_mask |= ( I2C_RXFIFO_FULL_INT_ENA_M | I2C_TRANS_COMPLETE_INT_ENA_M ); + } else { + //semaphore to sync sending process, because we only have 32 bytes for hardware fifo. + p_i2c->cmd_sem = xSemaphoreCreateBinary(); + p_i2c->cmd_mux = xSemaphoreCreateMutex(); + if (p_i2c->cmd_sem == NULL || p_i2c->cmd_mux == NULL) { + ESP_LOGE(I2C_TAG, I2C_SEM_ERR_STR); + goto err; + } + //command link + p_i2c->cmd_link.cur = NULL; + p_i2c->cmd_link.head = NULL; + p_i2c->cmd_link.free = NULL; + + p_i2c->tx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + } else { + ESP_LOGE(I2C_TAG, I2C_DRIVER_ERR_STR); + return ESP_FAIL; + } + //hook isr handler + i2c_isr_register(i2c_num, i2c_isr_handler_default, p_i2c_obj[i2c_num], intr_alloc_flags, &p_i2c_obj[i2c_num]->intr_handle); + intr_mask |= ( I2C_TRANS_COMPLETE_INT_ENA_M | + I2C_TRANS_START_INT_ENA_M | + I2C_ARBITRATION_LOST_INT_ENA_M | + I2C_ACK_ERR_INT_ENA_M | + I2C_RXFIFO_OVF_INT_ENA_M | + I2C_SLAVE_TRAN_COMP_INT_ENA_M ); + SET_PERI_REG_MASK(I2C_INT_ENA_REG(i2c_num), intr_mask); + return ESP_OK; + + err: + //Some error has happened. Free/destroy all allocated things and return ESP_FAIL. + if (p_i2c_obj[i2c_num]) { + if (p_i2c_obj[i2c_num]->rx_ring_buf) { + vRingbufferDelete(p_i2c_obj[i2c_num]->rx_ring_buf); + p_i2c_obj[i2c_num]->rx_ring_buf = NULL; + p_i2c_obj[i2c_num]->rx_buf_length = 0; + } + if (p_i2c_obj[i2c_num]->tx_ring_buf) { + vRingbufferDelete(p_i2c_obj[i2c_num]->tx_ring_buf); + p_i2c_obj[i2c_num]->tx_ring_buf = NULL; + p_i2c_obj[i2c_num]->tx_buf_length = 0; + } + if (p_i2c_obj[i2c_num]->cmd_sem) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_sem); + } + if (p_i2c_obj[i2c_num]->cmd_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->cmd_mux); + } + if (p_i2c_obj[i2c_num]->slv_rx_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->slv_rx_mux); + } + if (p_i2c_obj[i2c_num]->slv_tx_mux) { + vSemaphoreDelete(p_i2c_obj[i2c_num]->slv_tx_mux); + } + } + free(p_i2c_obj[i2c_num]); + return ESP_FAIL; +} + +esp_err_t i2c_driver_delete(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + if (p_i2c->cmd_mux) { + xSemaphoreTake(p_i2c->cmd_mux, portMAX_DELAY); + vSemaphoreDelete(p_i2c->cmd_mux); + } + if (p_i2c->cmd_sem) { + vSemaphoreDelete(p_i2c->cmd_sem); + } + if (p_i2c->slv_rx_mux) { + vSemaphoreDelete(p_i2c->slv_rx_mux); + } + if (p_i2c->slv_tx_mux) { + vSemaphoreDelete(p_i2c->slv_tx_mux); + } + + if (p_i2c->rx_ring_buf) { + vRingbufferDelete(p_i2c->rx_ring_buf); + p_i2c->rx_ring_buf = NULL; + p_i2c->rx_buf_length = 0; + } + if (p_i2c->tx_ring_buf) { + vRingbufferDelete(p_i2c->tx_ring_buf); + p_i2c->tx_ring_buf = NULL; + p_i2c->tx_buf_length = 0; + } + uint32_t intr_mask = I2C_MASTER_TRAN_COMP_INT_ENA_M | + I2C_TIME_OUT_INT_ENA_M | + I2C_TRANS_COMPLETE_INT_ENA_M | + I2C_TRANS_START_INT_ENA_M | + I2C_TX_SEND_EMPTY_INT_ENA_M | + I2C_ARBITRATION_LOST_INT_ENA_M | + I2C_ACK_ERR_INT_ENA_M | + I2C_RXFIFO_OVF_INT_ENA_M | + I2C_RX_REC_FULL_INT_ENA_M | + I2C_SLAVE_TRAN_COMP_INT_ENA_M; + CLEAR_PERI_REG_MASK(I2C_INT_ENA_REG(i2c_num), intr_mask); + esp_intr_free(p_i2c->intr_handle); + p_i2c->intr_handle = NULL; + free(p_i2c_obj[i2c_num]); + p_i2c_obj[i2c_num] = NULL; + return ESP_OK; +} + +esp_err_t i2c_reset_tx_fifo(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->fifo_conf.tx_fifo_rst = 1; + I2C[i2c_num]->fifo_conf.tx_fifo_rst = 0; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_reset_rx_fifo(i2c_port_t i2c_num) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->fifo_conf.rx_fifo_rst = 1; + I2C[i2c_num]->fifo_conf.rx_fifo_rst = 0; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +static void i2c_isr_handler_default(void* arg) +{ + i2c_obj_t* p_i2c = (i2c_obj_t*) arg; + int i2c_num = p_i2c->i2c_num; + uint32_t status = I2C[i2c_num]->int_status.val; + int idx = 0; + portBASE_TYPE HPTaskAwoken = pdFALSE; + while (status != 0) { + status = I2C[i2c_num]->int_status.val; + if (status & I2C_TX_SEND_EMPTY_INT_ST_M) { + I2C[i2c_num]->int_clr.tx_send_empty = 1; + } else if (status & I2C_RX_REC_FULL_INT_ST_M) { + I2C[i2c_num]->int_clr.rx_rec_full = 1; + } else if (status & I2C_ACK_ERR_INT_ST_M) { + I2C[i2c_num]->int_clr.ack_err = 1; + if (p_i2c->mode == I2C_MODE_MASTER) { + p_i2c_obj[i2c_num]->status = I2C_STATUS_ACK_ERROR; + I2C[i2c_num]->int_clr.ack_err = 1; + //get error ack value from slave device, stop the commands + i2c_master_cmd_begin_static(i2c_num); + } + } else if (status & I2C_TRANS_START_INT_ST_M) { + I2C[i2c_num]->int_clr.trans_start = 1; + } else if (status & I2C_TIME_OUT_INT_ST_M) { + I2C[i2c_num]->int_clr.time_out = 1; + } else if (status & I2C_TRANS_COMPLETE_INT_ST_M) { + I2C[i2c_num]->int_clr.trans_complete = 1; + if (p_i2c->mode == I2C_MODE_SLAVE) { + int rx_fifo_cnt = I2C[i2c_num]->status_reg.rx_fifo_cnt; + for (idx = 0; idx < rx_fifo_cnt; idx++) { + p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data; + } + xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_clr.rx_fifo_full = 1; + } else { + if (p_i2c->status != I2C_STATUS_ACK_ERROR) { + i2c_master_cmd_begin_static(i2c_num); + } + } + } else if (status & I2C_MASTER_TRAN_COMP_INT_ST_M) { + I2C[i2c_num]->int_clr.master_tran_comp = 1; + } else if (status & I2C_ARBITRATION_LOST_INT_ST_M) { + I2C[i2c_num]->int_clr.arbitration_lost = 1; + } else if (status & I2C_SLAVE_TRAN_COMP_INT_ST_M) { + I2C[i2c_num]->int_clr.slave_tran_comp = 1; + } else if (status & I2C_END_DETECT_INT_ST_M) { + I2C[i2c_num]->int_ena.end_detect = 0; + I2C[i2c_num]->int_clr.end_detect = 1; + i2c_master_cmd_begin_static(i2c_num); + } else if (status & I2C_RXFIFO_OVF_INT_ST_M) { + I2C[i2c_num]->int_clr.rx_fifo_ovf = 1; + } else if (status & I2C_TXFIFO_EMPTY_INT_ST_M) { + int tx_fifo_rem = I2C_FIFO_LEN - I2C[i2c_num]->status_reg.tx_fifo_cnt; + size_t size = 0; + uint8_t *data = (uint8_t*) xRingbufferReceiveUpToFromISR(p_i2c->tx_ring_buf, &size, tx_fifo_rem); + if (data) { + for (idx = 0; idx < size; idx++) { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), data[idx]); + } + vRingbufferReturnItemFromISR(p_i2c->tx_ring_buf, data, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_ena.tx_fifo_empty = 1; + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + } else { + I2C[i2c_num]->int_ena.tx_fifo_empty = 0; + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + } + } else if (status & I2C_RXFIFO_FULL_INT_ST_M) { + int rx_fifo_cnt = I2C[i2c_num]->status_reg.rx_fifo_cnt; + for (idx = 0; idx < rx_fifo_cnt; idx++) { + p_i2c->data_buf[idx] = I2C[i2c_num]->fifo_data.data; + } + xRingbufferSendFromISR(p_i2c->rx_ring_buf, p_i2c->data_buf, rx_fifo_cnt, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + I2C[i2c_num]->int_clr.rx_fifo_full = 1; + } else { + I2C[i2c_num]->int_clr.val = status; + } + } +} + +esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, i2c_trans_mode_t rx_trans_mode) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(tx_trans_mode < I2C_DATA_MODE_MAX, I2C_TRANS_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(rx_trans_mode < I2C_DATA_MODE_MAX, I2C_TRANS_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->ctr.rx_lsb_first = rx_trans_mode; //set rx data msb first + I2C[i2c_num]->ctr.tx_lsb_first = tx_trans_mode; //set tx data msb first + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, i2c_trans_mode_t *rx_trans_mode) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + if (tx_trans_mode) { + *tx_trans_mode = I2C[i2c_num]->ctr.tx_lsb_first; + } + if (rx_trans_mode) { + *rx_trans_mode = I2C[i2c_num]->ctr.rx_lsb_first; + } + return ESP_OK; +} + +esp_err_t i2c_param_config(i2c_port_t i2c_num, i2c_config_t* i2c_conf) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(i2c_conf != NULL, I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(i2c_conf->mode < I2C_MODE_MAX, I2C_MODE_ERR_STR, ESP_ERR_INVALID_ARG); + + esp_err_t ret = i2c_set_pin(i2c_num, i2c_conf->sda_io_num, i2c_conf->scl_io_num, + i2c_conf->sda_pullup_en, i2c_conf->scl_pullup_en, i2c_conf->mode); + if (ret != ESP_OK) { + return ret; + } + if (i2c_num == I2C_NUM_0) { + periph_module_enable(PERIPH_I2C0_MODULE); + } else if (i2c_num == I2C_NUM_1) { + periph_module_enable(PERIPH_I2C1_MODULE); + } + + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->ctr.rx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set rx data msb first + I2C[i2c_num]->ctr.tx_lsb_first = I2C_DATA_MODE_MSB_FIRST; //set tx data msb first + I2C[i2c_num]->ctr.ms_mode = i2c_conf->mode; //mode for master or slave + I2C[i2c_num]->ctr.sda_force_out = 1; // set open-drain output mode + I2C[i2c_num]->ctr.scl_force_out = 1; // set open-drain output mode + I2C[i2c_num]->ctr.sample_scl_level = 0; //sample at high level of clock + + if (i2c_conf->mode == I2C_MODE_SLAVE) { //slave mode + I2C[i2c_num]->slave_addr.addr = i2c_conf->slave.slave_addr; + I2C[i2c_num]->slave_addr.en_10bit = i2c_conf->slave.addr_10bit_en; + I2C[i2c_num]->fifo_conf.nonfifo_en = 0; + I2C[i2c_num]->fifo_conf.fifo_addr_cfg_en = 0; + I2C[i2c_num]->fifo_conf.rx_fifo_full_thrhd = I2C_FIFO_FULL_THRESH_VAL; + I2C[i2c_num]->fifo_conf.tx_fifo_empty_thrhd = I2C_FIFO_EMPTY_THRESH_VAL; + I2C[i2c_num]->int_ena.rx_fifo_full = 1; + I2C[i2c_num]->ctr.trans_start = 0; + } else { + I2C[i2c_num]->fifo_conf.nonfifo_en = 0; + } + //set frequency + int half_cycle = ( I2C_APB_CLK_FREQ / i2c_conf->master.clk_speed ) / 2; + I2C[i2c_num]->scl_low_period.period = half_cycle - 1; + I2C[i2c_num]->scl_high_period.period = ( I2C_APB_CLK_FREQ / i2c_conf->master.clk_speed ) - half_cycle - 1; + //set timing for start signal + I2C[i2c_num]->scl_start_hold.time = half_cycle; + I2C[i2c_num]->scl_rstart_setup.time = half_cycle; + //set timing for stop signal + I2C[i2c_num]->scl_stop_hold.time = half_cycle; + I2C[i2c_num]->scl_stop_setup.time = half_cycle; + //set timing for data + I2C[i2c_num]->sda_hold.time = half_cycle / 2; + I2C[i2c_num]->sda_sample.time = half_cycle / 2; + //set timeout of receving data + I2C[i2c_num]->timeout.tout = 200000; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->scl_high_period.period = high_period; + I2C[i2c_num]->scl_low_period.period = low_period; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2s_get_period(i2c_port_t i2c_num, int* high_period, int* low_period) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (high_period) { + *high_period = I2C[i2c_num]->scl_high_period.period; + } + if (low_period) { + *low_period = I2C[i2c_num]->scl_low_period.period; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->scl_start_hold.time = hold_time; + I2C[i2c_num]->scl_rstart_setup.time = setup_time; + return ESP_OK; +} + +esp_err_t i2c_get_start_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (hold_time) { + *hold_time = I2C[i2c_num]->scl_start_hold.time; + } + if (setup_time) { + *setup_time = I2C[i2c_num]->scl_rstart_setup.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->scl_stop_hold.time = hold_time; + I2C[i2c_num]->scl_stop_setup.time = setup_time; + return ESP_OK; +} + +esp_err_t i2c_get_stop_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (setup_time) { + *setup_time = I2C[i2c_num]->scl_stop_setup.time; + } + if (hold_time) { + *hold_time = I2C[i2c_num]->scl_stop_hold.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C[i2c_num]->sda_hold.time = hold_time; + I2C[i2c_num]->sda_sample.time = sample_time; + return ESP_OK; +} + +esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + if (sample_time) { + *sample_time = I2C[i2c_num]->sda_sample.time; + } + if (hold_time) { + *hold_time = I2C[i2c_num]->sda_hold.time; + } + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + return ESP_OK; +} + +esp_err_t i2c_isr_register(i2c_port_t i2c_num, void (*fn)(void*), void * arg, int intr_alloc_flags, intr_handle_t *handle) +{ + I2C_CHECK(i2c_num < I2C_NUM_MAX, I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(fn != NULL, I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + esp_err_t ret; + switch (i2c_num) { + case I2C_NUM_1: + ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + break; + case I2C_NUM_0: + default: + ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); + break; + } + return ret; +} + +esp_err_t i2c_isr_free(intr_handle_t handle) +{ + return esp_intr_free(handle); +} + +esp_err_t i2c_set_pin(i2c_port_t i2c_num, gpio_num_t sda_io_num, gpio_num_t scl_io_num, gpio_pullup_t sda_pullup_en, gpio_pullup_t scl_pullup_en, i2c_mode_t mode) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(((GPIO_IS_VALID_OUTPUT_GPIO(sda_io_num))), I2C_SDA_IO_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((GPIO_IS_VALID_OUTPUT_GPIO(scl_io_num)) || + (GPIO_IS_VALID_GPIO(scl_io_num) && mode == I2C_MODE_SLAVE), + I2C_SCL_IO_ERR_STR, + ESP_ERR_INVALID_ARG); + I2C_CHECK((sda_pullup_en == GPIO_PULLUP_ENABLE && GPIO_IS_VALID_OUTPUT_GPIO(sda_io_num)) || + sda_pullup_en == GPIO_PULLUP_DISABLE, I2C_GPIO_PULLUP_ERR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK((scl_pullup_en == GPIO_PULLUP_ENABLE && GPIO_IS_VALID_OUTPUT_GPIO(scl_io_num)) || + scl_pullup_en == GPIO_PULLUP_DISABLE, I2C_GPIO_PULLUP_ERR_STR, ESP_ERR_INVALID_ARG); + + int sda_in_sig, sda_out_sig, scl_in_sig, scl_out_sig; + switch (i2c_num) { + case I2C_NUM_1: + sda_out_sig = I2CEXT1_SDA_OUT_IDX; + sda_in_sig = I2CEXT1_SDA_IN_IDX; + scl_out_sig = I2CEXT1_SCL_OUT_IDX; + scl_in_sig = I2CEXT1_SCL_IN_IDX; + break; + case I2C_NUM_0: + default: + sda_out_sig = I2CEXT0_SDA_OUT_IDX; + sda_in_sig = I2CEXT0_SDA_IN_IDX; + scl_out_sig = I2CEXT0_SCL_OUT_IDX; + scl_in_sig = I2CEXT0_SCL_IN_IDX; + break; + } + if (sda_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[sda_io_num], PIN_FUNC_GPIO); + gpio_set_direction(sda_io_num, GPIO_MODE_INPUT_OUTPUT_OD); + if (sda_pullup_en == GPIO_PULLUP_ENABLE) { + gpio_set_pull_mode(sda_io_num, GPIO_PULLUP_ONLY); + } else { + gpio_set_pull_mode(sda_io_num, GPIO_FLOATING); + } + gpio_matrix_out(sda_io_num, sda_out_sig, 0, 0); + gpio_matrix_in(sda_io_num, sda_in_sig, 0); + } + + if (scl_io_num >= 0) { + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[scl_io_num], PIN_FUNC_GPIO); + if (mode == I2C_MODE_MASTER) { + gpio_set_direction(scl_io_num, GPIO_MODE_INPUT_OUTPUT_OD); + gpio_matrix_out(scl_io_num, scl_out_sig, 0, 0); + } else { + gpio_set_direction(scl_io_num, GPIO_MODE_INPUT); + } + if (scl_pullup_en == GPIO_PULLUP_ENABLE) { + gpio_set_pull_mode(scl_io_num, GPIO_PULLUP_ONLY); + } else { + gpio_set_pull_mode(scl_io_num, GPIO_FLOATING); + } + gpio_matrix_in(scl_io_num, scl_in_sig, 0); + } + return ESP_OK; +} + +i2c_cmd_handle_t i2c_cmd_link_create() +{ + i2c_cmd_desc_t* cmd_desc = (i2c_cmd_desc_t*) calloc(1, sizeof(i2c_cmd_desc_t)); + return (i2c_cmd_handle_t) cmd_desc; +} + +void i2c_cmd_link_delete(i2c_cmd_handle_t cmd_handle) +{ + if (cmd_handle == NULL) { + return; + } + i2c_cmd_desc_t* cmd = (i2c_cmd_desc_t*) cmd_handle; + while (cmd->free) { + i2c_cmd_link_t* ptmp = cmd->free; + cmd->free = cmd->free->next; + free(ptmp); + } + cmd->cur = NULL; + cmd->free = NULL; + cmd->head = NULL; + free(cmd_handle); + return; +} + +static esp_err_t i2c_cmd_link_append(i2c_cmd_handle_t cmd_handle, i2c_cmd_t* cmd) +{ + i2c_cmd_desc_t* cmd_desc = (i2c_cmd_desc_t*) cmd_handle; + if (cmd_desc->head == NULL) { + cmd_desc->head = (i2c_cmd_link_t*) malloc(sizeof(i2c_cmd_link_t)); + if (cmd_desc->head == NULL) { + ESP_LOGE(I2C_TAG, I2C_CMD_MALLOC_ERR_STR); + goto err; + } + cmd_desc->cur = cmd_desc->head; + cmd_desc->free = cmd_desc->head; + } else { + cmd_desc->cur->next = (i2c_cmd_link_t*) malloc(sizeof(i2c_cmd_link_t)); + if (cmd_desc->cur->next == NULL) { + ESP_LOGE(I2C_TAG, I2C_CMD_MALLOC_ERR_STR); + goto err; + } + cmd_desc->cur = cmd_desc->cur->next; + } + memcpy((uint8_t*) &cmd_desc->cur->cmd, (uint8_t*) cmd, sizeof(i2c_cmd_t)); + cmd_desc->cur->next = NULL; + return ESP_OK; + + err: + return ESP_FAIL; +} + +esp_err_t i2c_master_start(i2c_cmd_handle_t cmd_handle) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 0; + cmd.data = NULL; + cmd.op_code = I2C_CMD_RESTART; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_stop(i2c_cmd_handle_t cmd_handle) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 0; + cmd.data = NULL; + cmd.op_code = I2C_CMD_STOP; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_write(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, bool ack_en) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + uint8_t len_tmp; + int data_offset = 0; + esp_err_t ret; + while (data_len > 0) { + len_tmp = data_len > 0xff ? 0xff : data_len; + data_len -= len_tmp; + i2c_cmd_t cmd; + cmd.ack_en = ack_en; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = len_tmp; + cmd.op_code = I2C_CMD_WRITE; + cmd.data = data + data_offset; + ret = i2c_cmd_link_append(cmd_handle, &cmd); + data_offset += len_tmp; + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +esp_err_t i2c_master_write_byte(i2c_cmd_handle_t cmd_handle, uint8_t data, bool ack_en) +{ + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = ack_en; + cmd.ack_exp = 0; + cmd.ack_val = 0; + cmd.byte_num = 1; + cmd.op_code = I2C_CMD_WRITE; + cmd.data = NULL; + cmd.byte_cmd = data; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +esp_err_t i2c_master_read(i2c_cmd_handle_t cmd_handle, uint8_t* data, size_t data_len, int ack) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + int len_tmp; + int data_offset = 0; + esp_err_t ret; + while (data_len > 0) { + len_tmp = data_len > 0xff ? 0xff : data_len; + data_len -= len_tmp; + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = ack & 0x1; + cmd.byte_num = len_tmp; + cmd.op_code = I2C_CMD_READ; + cmd.data = data + data_offset; + ret = i2c_cmd_link_append(cmd_handle, &cmd); + data_offset += len_tmp; + if (ret != ESP_OK) { + return ret; + } + } + return ESP_OK; +} + +esp_err_t i2c_master_read_byte(i2c_cmd_handle_t cmd_handle, uint8_t* data, int ack) +{ + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + i2c_cmd_t cmd; + cmd.ack_en = 0; + cmd.ack_exp = 0; + cmd.ack_val = ack & 0x1; + cmd.byte_num = 1; + cmd.op_code = I2C_CMD_READ; + cmd.data = data; + return i2c_cmd_link_append(cmd_handle, &cmd); +} + +static void IRAM_ATTR i2c_master_cmd_begin_static(i2c_port_t i2c_num) +{ + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portBASE_TYPE HPTaskAwoken = pdFALSE; + //This should never happen + if (p_i2c->mode == I2C_MODE_SLAVE) { + return; + } + if (p_i2c->status == I2C_STATUS_DONE) { + return; + } else if (p_i2c->status == I2C_STATUS_ACK_ERROR) { + I2C[i2c_num]->int_ena.end_detect = 0; + I2C[i2c_num]->int_clr.end_detect = 1; + I2C[i2c_num]->int_ena.master_tran_comp = 0; + xSemaphoreGiveFromISR(p_i2c->cmd_sem, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + return; + } else if (p_i2c->status == I2C_STATUS_READ) { + i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd; + while (p_i2c->rx_cnt-- > 0) { + *cmd->data++ = READ_PERI_REG(I2C_DATA_APB_REG(i2c_num)); + } + if (cmd->byte_num > 0) { + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + } else { + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + } + } + if (p_i2c->cmd_link.head == NULL) { + p_i2c->cmd_link.cur = NULL; + xSemaphoreGiveFromISR(p_i2c->cmd_sem, &HPTaskAwoken); + if (HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + return; + } + while (p_i2c->cmd_link.head) { + i2c_cmd_t *cmd = &p_i2c->cmd_link.head->cmd; + I2C[i2c_num]->command[p_i2c->cmd_idx].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_en = cmd->ack_en; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_exp = cmd->ack_exp; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_val = cmd->ack_val; + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = cmd->byte_num; + I2C[i2c_num]->command[p_i2c->cmd_idx].op_code = cmd->op_code; + if (cmd->op_code == I2C_CMD_WRITE) { + //TODO: to reduce interrupt number + if (cmd->data) { + while (p_i2c->tx_fifo_remain > 0 && cmd->byte_num > 0) { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), *cmd->data++); + p_i2c->tx_fifo_remain--; + cmd->byte_num--; + } + } else { + WRITE_PERI_REG(I2C_DATA_APB_REG(i2c_num), cmd->byte_cmd); + p_i2c->tx_fifo_remain--; + cmd->byte_num--; + } + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num -= cmd->byte_num; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + if (cmd->byte_num > 0) { + } else { + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + } + p_i2c->status = I2C_STATUS_WRITE; + break; + } else if(cmd->op_code == I2C_CMD_READ) { + //TODO: to reduce interrupt number + p_i2c->rx_cnt = cmd->byte_num > p_i2c->rx_fifo_remain ? p_i2c->rx_fifo_remain : cmd->byte_num; + cmd->byte_num -= p_i2c->rx_cnt; + I2C[i2c_num]->command[p_i2c->cmd_idx].byte_num = p_i2c->rx_cnt; + I2C[i2c_num]->command[p_i2c->cmd_idx].ack_val = cmd->ack_val; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].val = 0; + I2C[i2c_num]->command[p_i2c->cmd_idx + 1].op_code = I2C_CMD_END; + p_i2c->status = I2C_STATUS_READ; + break; + } else { + } + p_i2c->cmd_idx++; + p_i2c->cmd_link.head = p_i2c->cmd_link.head->next; + if (p_i2c->cmd_link.head == NULL || p_i2c->cmd_idx >= 15) { + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->cmd_idx = 0; + p_i2c->status = I2C_STATUS_IDLE; + break; + } + } + I2C[i2c_num]->int_clr.end_detect = 1; + I2C[i2c_num]->int_clr.master_tran_comp = 1; + I2C[i2c_num]->int_ena.end_detect = 1; + I2C[i2c_num]->int_ena.master_tran_comp = 1; + I2C[i2c_num]->ctr.trans_start = 0; + I2C[i2c_num]->ctr.trans_start = 1; + return; +} + +esp_err_t i2c_master_cmd_begin(i2c_port_t i2c_num, i2c_cmd_handle_t cmd_handle, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_ERR_INVALID_ARG); + I2C_CHECK(p_i2c_obj[i2c_num] != NULL, I2C_DRIVER_NOT_INSTALL_ERR_STR, ESP_ERR_INVALID_STATE); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_MASTER, I2C_MASTER_MODE_ERR_STR, ESP_ERR_INVALID_STATE); + I2C_CHECK(cmd_handle != NULL, I2C_CMD_LINK_INIT_ERR_STR, ESP_ERR_INVALID_ARG); + + esp_err_t ret; + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + portBASE_TYPE res = xSemaphoreTake(p_i2c->cmd_mux, ticks_to_wait); + if (res == pdFALSE) { + return ESP_ERR_TIMEOUT; + } + xSemaphoreTake(p_i2c->cmd_sem, 0); + i2c_reset_tx_fifo(i2c_num); + i2c_reset_rx_fifo(i2c_num); + i2c_cmd_desc_t* cmd = (i2c_cmd_desc_t*) cmd_handle; + p_i2c->cmd_link.free = cmd->free; + p_i2c->cmd_link.cur = cmd->cur; + p_i2c->cmd_link.head = cmd->head; + p_i2c->status = I2C_STATUS_IDLE; + p_i2c->cmd_idx = 0; + p_i2c->rx_cnt = 0; + p_i2c->tx_fifo_remain = I2C_FIFO_LEN; + p_i2c->rx_fifo_remain = I2C_FIFO_LEN; + i2c_reset_tx_fifo(i2c_num); + i2c_reset_rx_fifo(i2c_num); + + //start send commands, at most 32 bytes one time, isr handler will process the remaining commands. + i2c_master_cmd_begin_static(i2c_num); + ticks_to_wait = ticks_end - xTaskGetTickCount(); + res = xSemaphoreTake(p_i2c->cmd_sem, ticks_to_wait); + if (res == pdFALSE) { + ret = ESP_ERR_TIMEOUT; + } else if (p_i2c->status == I2C_STATUS_ACK_ERROR) { + ret = ESP_FAIL; + } else { + ret = ESP_OK; + } + p_i2c->status = I2C_STATUS_DONE; + xSemaphoreGive(p_i2c->cmd_mux); + return ret; +} + +int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + + portBASE_TYPE res; + int cnt = 0; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + + res = xSemaphoreTake(p_i2c->slv_tx_mux, ticks_to_wait); + if (res == pdFALSE) { + return 0; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + res = xRingbufferSend(p_i2c->tx_ring_buf, data, size, ticks_to_wait); + if (res == pdFALSE) { + cnt = 0; + } else { + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->int_clr.tx_fifo_empty = 1; + I2C[i2c_num]->int_ena.tx_fifo_empty = 1; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + cnt = size; + } + xSemaphoreGive(p_i2c->slv_tx_mux); + return cnt; +} + +static int i2c_slave_read(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait) +{ + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + size_t size; + uint8_t* pdata = (uint8_t*) xRingbufferReceiveUpTo(p_i2c->rx_ring_buf, &size, ticks_to_wait, max_size); + if (pdata && size > 0) { + memcpy(data, pdata, size); + vRingbufferReturnItem(p_i2c->rx_ring_buf, pdata); + } + return size; +} + +int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait) +{ + I2C_CHECK(( i2c_num < I2C_NUM_MAX ), I2C_NUM_ERROR_STR, ESP_FAIL); + I2C_CHECK((data != NULL), I2C_ADDR_ERROR_STR, ESP_FAIL); + I2C_CHECK(p_i2c_obj[i2c_num]->mode == I2C_MODE_SLAVE, I2C_MODE_SLAVE_ERR_STR, ESP_FAIL); + + i2c_obj_t* p_i2c = p_i2c_obj[i2c_num]; + portBASE_TYPE res; + portTickType ticks_end = xTaskGetTickCount() + ticks_to_wait; + res = xSemaphoreTake(p_i2c->slv_rx_mux, ticks_to_wait); + if (res == pdFALSE) { + return 0; + } + ticks_to_wait = ticks_end - xTaskGetTickCount(); + int cnt = i2c_slave_read(i2c_num, data, max_size, ticks_to_wait); + if (cnt > 0) { + I2C_ENTER_CRITICAL(&i2c_spinlock[i2c_num]); + I2C[i2c_num]->int_ena.rx_fifo_full = 1; + I2C_EXIT_CRITICAL(&i2c_spinlock[i2c_num]); + ticks_to_wait = ticks_end - xTaskGetTickCount(); + if (cnt < max_size && ticks_to_wait > 0) { + cnt += i2c_slave_read(i2c_num, data + cnt, max_size - cnt, ticks_to_wait); + } + } else { + cnt = 0; + } + xSemaphoreGive(p_i2c->slv_rx_mux); + return cnt; +} + + + + diff --git a/components/driver/include/driver/i2c.h b/components/driver/include/driver/i2c.h new file mode 100644 index 0000000000..69b80b1e67 --- /dev/null +++ b/components/driver/include/driver/i2c.h @@ -0,0 +1,514 @@ +// Copyright 2015-2016 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. + +#ifndef _DRIVER_I2C_H_ +#define _DRIVER_I2C_H_ + + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include "esp_err.h" +#include "esp_intr_alloc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/ringbuf.h" +#include "driver/gpio.h" + +#define I2C_APB_CLK_FREQ APB_CLK_FREQ /*!< I2C source clock is APB clock, 80MHz */ +#define I2C_FIFO_LEN (32) /*!< I2C hardware fifo length */ +typedef enum{ + I2C_MODE_SLAVE = 0, /*!< I2C slave mode */ + I2C_MODE_MASTER, /*!< I2C master mode */ + I2C_MODE_MAX, +}i2c_mode_t; + +typedef enum { + I2C_MASTER_WRITE = 0, /*!< I2C write data */ + I2C_MASTER_READ, /*!< I2C read data */ +} i2c_rw_t; + +typedef enum { + I2C_DATA_MODE_MSB_FIRST = 0, /*!< I2C data msb first */ + I2C_DATA_MODE_LSB_FIRST = 1, /*!< I2C data lsb first */ + I2C_DATA_MODE_MAX +} i2c_trans_mode_t; + +typedef enum{ + I2C_CMD_RESTART = 0, /*!=0) The number of data bytes that pushed to the I2C slave buffer. + */ +int i2c_slave_write_buffer(i2c_port_t i2c_num, uint8_t* data, int size, portBASE_TYPE ticks_to_wait); + +/** + * @brief I2C slave read data from internal buffer. When I2C slave receive data, isr will copy received data + * from hardware rx fifo to internal ringbuffer. Then users can read from internal ringbuffer. + * @note + * Only call this function in I2C slave mode + * + * @param i2c_num I2C port number + * @param data data pointer to write into internal buffer + * @param max_size Maximum data size to read + * @param ticks_to_wait Maximum waiting ticks + * + * @return + * - ESP_FAIL(-1) Parameter error + * - Others(>=0) The number of data bytes that read from I2C slave buffer. + */ +int i2c_slave_read_buffer(i2c_port_t i2c_num, uint8_t* data, size_t max_size, portBASE_TYPE ticks_to_wait); + +/** + * @brief set I2C master clock period + * + * @param i2c_num I2C port number + * @param high_period clock cycle number during SCL is high level, high_period is a 14 bit value + * @param low_period clock cycle number during SCL is low level, low_period is a 14 bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_period(i2c_port_t i2c_num, int high_period, int low_period); + +/** + * @brief get I2C master clock period + * + * @param i2c_num I2C port number + * @param high_period pointer to get clock cycle number during SCL is high level, will get a 14 bit value + * @param low_period pointer to get clock cycle number during SCL is low level, will get a 14 bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2s_get_period(i2c_port_t i2c_num, int* high_period, int* low_period); + +/** + * @brief set I2C master start signal timing + * + * @param i2c_num I2C port number + * @param setup_time clock number between the falling-edge of SDA and rising-edge of SCL for start mark, it's a 10-bit value. + * @param hold_time clock num between the falling-edge of SDA and falling-edge of SCL for start mark, it's a 10-bit value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_start_timing(i2c_port_t i2c_num, int setup_time, int hold_time); + +/** + * @brief get I2C master start signal timing + * + * @param i2c_num I2C port number + * @param setup_time pointer to get setup time + * @param hold_time pointer to get hold time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_start_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time); + +/** + * @brief set I2C master stop signal timing + * + * @param i2c_num I2C port number + * @param setup_time clock num between the rising-edge of SCL and the rising-edge of SDA, it's a 10-bit value. + * @param hold_time clock number after the STOP bit's rising-edge, it's a 14-bit value. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_stop_timing(i2c_port_t i2c_num, int setup_time, int hold_time); + +/** + * @brief get I2C master stop signal timing + * + * @param i2c_num I2C port number + * @param setup_time pointer to get setup time. + * @param hold_time pointer to get hold time. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_stop_timing(i2c_port_t i2c_num, int* setup_time, int* hold_time); + +/** + * @brief set I2C data signal timing + * + * @param i2c_num I2C port number + * @param sample_time clock number I2C used to sample data on SDA after the rising-edge of SCL, it's a 10-bit value + * @param hold_time clock number I2C used to hold the data after the falling-edge of SCL, it's a 10-bit value + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_data_timing(i2c_port_t i2c_num, int sample_time, int hold_time); + +/** + * @brief get I2C data signal timing + * + * @param i2c_num I2C port number + * @param sample_time pointer to get sample time + * @param hold_time pointer to get hold time + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_data_timing(i2c_port_t i2c_num, int* sample_time, int* hold_time); + +/** + * @brief set I2C data transfer mode + * + * @param i2c_num I2C port number + * @param tx_trans_mode I2C sending data mode + * @param rx_trans_mode I2C receving data mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_set_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t tx_trans_mode, i2c_trans_mode_t rx_trans_mode); + +/** + * @brief get I2C data transfer mode + * + * @param i2c_num I2C port number + * @param tx_trans_mode pointer to get I2C sending data mode + * @param rx_trans_mode pointer to get I2C receiving data mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t i2c_get_data_mode(i2c_port_t i2c_num, i2c_trans_mode_t *tx_trans_mode, i2c_trans_mode_t *rx_trans_mode); + +#ifdef __cplusplus +} +#endif + +#endif /*_DRIVER_I2C_H_*/ diff --git a/components/esp32/include/soc/i2c_reg.h b/components/esp32/include/soc/i2c_reg.h index d5c9858a92..9693ca5b59 100644 --- a/components/esp32/include/soc/i2c_reg.h +++ b/components/esp32/include/soc/i2c_reg.h @@ -261,6 +261,8 @@ #define I2C_RXFIFO_FULL_THRHD_V 0x1F #define I2C_RXFIFO_FULL_THRHD_S 0 +#define I2C_DATA_APB_REG(i) (0x60013000 + (i) * 0x14000 + 0x001c) + #define I2C_DATA_REG(i) (REG_I2C_BASE(i) + 0x001c) /* I2C_FIFO_RDATA : RO ;bitpos:[7:0] ;default: 8'b0 ; */ /*description: The register represent the byte data read from rxfifo when use apb fifo access*/ diff --git a/components/esp32/include/soc/i2c_struct.h b/components/esp32/include/soc/i2c_struct.h index a29a9c5286..ea50d6bee5 100644 --- a/components/esp32/include/soc/i2c_struct.h +++ b/components/esp32/include/soc/i2c_struct.h @@ -16,7 +16,7 @@ typedef volatile struct { union { struct { - uint32_t scl_low_period:14; /*This register is used to configure the low level width of SCL clock.*/ + uint32_t period:14; /*This register is used to configure the low level width of SCL clock.*/ uint32_t reserved14: 18; }; uint32_t val; @@ -58,7 +58,7 @@ typedef volatile struct { } status_reg; union { struct { - uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data.*/ + uint32_t tout: 20; /*This register is used to configure the max clock number of receiving a data, unit: APB clock cycle.*/ uint32_t reserved20:12; }; uint32_t val; @@ -282,7 +282,7 @@ typedef volatile struct { uint32_t reserved_f4; uint32_t date; /**/ uint32_t reserved_fc; - uint32_t fifo_start_addr; /*This the start address for ram when use apb nonfifo access.*/ + uint32_t ram_data[32]; /*This the start address for ram when use apb nonfifo access.*/ } i2c_dev_t; extern i2c_dev_t I2C0; extern i2c_dev_t I2C1; diff --git a/docs/api/i2c.rst b/docs/api/i2c.rst new file mode 100644 index 0000000000..1186e0583b --- /dev/null +++ b/docs/api/i2c.rst @@ -0,0 +1,82 @@ +I2C +=========== + +Overview +-------- + +ESP32 has two I2C controllers which can be set as master mode or slave mode. + +Application Example +------------------- + +I2C master and slave example: `examples/18_i2c `_. + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `driver/include/driver/i2c.h `_ + +Macros +^^^^^^ + +.. doxygendefine:: I2C_APB_CLK_FREQ +.. doxygendefine:: I2C_FIFO_LEN + +Type Definitions +^^^^^^^^^^^^^^^^ + +.. doxygentypedef:: i2c_cmd_handle_t + +Enumerations +^^^^^^^^^^^^ + +.. doxygenenum:: i2c_mode_t +.. doxygenenum:: i2c_rw_t +.. doxygenenum:: i2c_trans_mode_t +.. doxygenenum:: i2c_opmode_t +.. doxygenenum:: i2c_port_t +.. doxygenenum:: i2c_addr_mode_t + +Structures +^^^^^^^^^^ + +.. doxygenstruct:: i2c_config_t + :members: + + +Functions +^^^^^^^^^ + +.. doxygenfunction:: i2c_driver_install +.. doxygenfunction:: i2c_driver_delete +.. doxygenfunction:: i2c_param_config +.. doxygenfunction:: i2c_reset_tx_fifo +.. doxygenfunction:: i2c_reset_rx_fifo +.. doxygenfunction:: i2c_isr_register +.. doxygenfunction:: i2c_isr_free +.. doxygenfunction:: i2c_set_pin +.. doxygenfunction:: i2c_master_start +.. doxygenfunction:: i2c_master_write_byte +.. doxygenfunction:: i2c_master_write +.. doxygenfunction:: i2c_master_read_byte +.. doxygenfunction:: i2c_master_read +.. doxygenfunction:: i2c_master_stop +.. doxygenfunction:: i2c_master_cmd_begin +.. doxygenfunction:: i2c_slave_write_buffer +.. doxygenfunction:: i2c_slave_read +.. doxygenfunction:: i2c_set_period +.. doxygenfunction:: i2s_get_period +.. doxygenfunction:: i2c_set_start_timing +.. doxygenfunction:: i2c_get_start_timing +.. doxygenfunction:: i2c_set_stop_timing +.. doxygenfunction:: i2c_get_stop_timing +.. doxygenfunction:: i2c_set_data_timing +.. doxygenfunction:: i2c_get_data_timing +.. doxygenfunction:: i2c_set_data_mode +.. doxygenfunction:: i2c_get_data_mode +.. doxygenfunction:: i2c_cmd_link_create +.. doxygenfunction:: i2c_cmd_link_delete + diff --git a/docs/index.rst b/docs/index.rst index ba25b49691..9c2b0643f4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -107,6 +107,7 @@ Contents: LED Control Remote Control Timer + I2C Pulse Counter Sigma-delta Modulation SPI Flash and Partition APIs diff --git a/examples/18_i2c/Makefile b/examples/18_i2c/Makefile new file mode 100644 index 0000000000..d575e29b9d --- /dev/null +++ b/examples/18_i2c/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 := i2c + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/18_i2c/README.md b/examples/18_i2c/README.md new file mode 100644 index 0000000000..7a88894bf1 --- /dev/null +++ b/examples/18_i2c/README.md @@ -0,0 +1,29 @@ +# I2C Example + + +* This example will show you how to use I2C module by running two tasks on i2c bus: + + * read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance. + * Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip. + +* Pin assignment: + + * slave : + * GPIO25 is assigned as the data signal of i2c slave port + * GPIO26 is assigned as the clock signal of i2c slave port + * master: + * GPIO18 is assigned as the data signal of i2c master port + * GPIO19 is assigned as the clock signal of i2c master port + +* Connection: + + * connect GPIO18 with GPIO25 + * connect GPIO19 with GPIO26 + * connect sda/scl of sensor with GPIO18/GPIO19 + * no need to add external pull-up resistors, driver will enable internal pull-up resistors. + +* Test items: + + * read the sensor data, if connected. + * i2c master(ESP32) will write data to i2c slave(ESP32). + * i2c master(ESP32) will read data from i2c slave(ESP32). diff --git a/examples/18_i2c/main/component.mk b/examples/18_i2c/main/component.mk new file mode 100644 index 0000000000..44bd2b5273 --- /dev/null +++ b/examples/18_i2c/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/18_i2c/main/i2c_test.c b/examples/18_i2c/main/i2c_test.c new file mode 100644 index 0000000000..2ef6d75b2f --- /dev/null +++ b/examples/18_i2c/main/i2c_test.c @@ -0,0 +1,303 @@ +/* i2c - Example + + For other examples please check: + https://github.com/espressif/esp-idf/tree/master/examples + + 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 "driver/i2c.h" + +/** + * TEST CODE BRIEF + * + * This example will show you how to use I2C module by running two tasks on i2c bus: + * + * - read external i2c sensor, here we use a BH1750 light sensor(GY-30 module) for instance. + * - Use one I2C port(master mode) to read or write the other I2C port(slave mode) on one ESP32 chip. + * + * Pin assignment: + * + * - slave : + * GPIO25 is assigned as the data signal of i2c slave port + * GPIO26 is assigned as the clock signal of i2c slave port + * - master: + * GPIO18 is assigned as the data signal of i2c master port + * GPIO19 is assigned as the clock signal of i2c master port + * + * Connection: + * + * - connect GPIO18 with GPIO25 + * - connect GPIO19 with GPIO26 + * - connect sda/scl of sensor with GPIO18/GPIO19 + * - no need to add external pull-up resistors, driver will enable internal pull-up resistors. + * + * Test items: + * + * - read the sensor data, if connected. + * - i2c master(ESP32) will write data to i2c slave(ESP32). + * - i2c master(ESP32) will read data from i2c slave(ESP32). + */ + +#define DATA_LENGTH 512 /*! 1) { + i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); + } + i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/** + * @brief Test code to write esp-i2c-slave + * Master device write data to slave(both esp32), + * the data will be stored in slave buffer. + * We can read them out from slave buffer. + * + * ___________________________________________________________________ + * | start | slave_addr + wr_bit + ack | write n bytes + ack | stop | + * --------|---------------------------|----------------------|------| + * + */ +esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t* data_wr, size_t size) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, ( ESP_SLAVE_ADDR << 1 ) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/** + * @brief test code to write esp-i2c-slave + * + * 1. set mode + * _________________________________________________________________ + * | start | slave_addr + wr_bit + ack | write 1 byte + ack | stop | + * --------|---------------------------|---------------------|------| + * 2. wait more than 24 ms + * 3. read data + * ______________________________________________________________________________________ + * | start | slave_addr + rd_bit + ack | read 1 byte + ack | read 1 byte + nack | stop | + * --------|---------------------------|--------------------|--------------------|------| + */ +esp_err_t i2c_master_sensor_test(i2c_port_t i2c_num, uint8_t* data_h, uint8_t* data_l) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write_byte(cmd, BH1750_CMD_START, ACK_CHECK_EN); + i2c_master_stop(cmd); + int ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if (ret == ESP_FAIL) { + return ret; + } + vTaskDelay(30 / portTICK_RATE_MS); + + cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | READ_BIT, ACK_CHECK_EN); + i2c_master_read_byte(cmd, data_h, ACK_VAL); + i2c_master_read_byte(cmd, data_l, NACK_VAL); + i2c_master_stop(cmd); + ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS); + i2c_cmd_link_delete(cmd); + if (ret == ESP_FAIL) { + return ESP_FAIL; + } + return ESP_OK; +} + +/** + * @brief i2c master initialization + */ +void i2c_master_init() +{ + int i2c_master_port = I2C_MASTER_NUM; + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = I2C_MASTER_SDA_IO; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_io_num = I2C_MASTER_SCL_IO; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = I2C_MASTER_FREQ_HZ; + i2c_param_config(i2c_master_port, &conf); + i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0); +} + +/** + * @brief i2c slave initialization + */ +void i2c_slave_init() +{ + int i2c_slave_port = I2C_SLAVE_NUM; + i2c_config_t conf_slave; + conf_slave.sda_io_num = I2C_SLAVE_SDA_IO; + conf_slave.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf_slave.scl_io_num = I2C_SLAVE_SCL_IO; + conf_slave.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf_slave.mode = I2C_MODE_SLAVE; + conf_slave.slave.addr_10bit_en = 0; + conf_slave.slave.slave_addr = ESP_SLAVE_ADDR; + i2c_param_config(i2c_slave_port, &conf_slave); + i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0); +} + +/** + * @brief test function to show buffer + */ +void disp_buf(uint8_t* buf, int len) +{ + int i; + for (i = 0; i < len; i++) { + printf("%02x ", buf[i]); + if (( i + 1 ) % 16 == 0) { + printf("\n"); + } + } + printf("\n"); +} + +void i2c_test_task(void* arg) +{ + int i = 0; + int ret; + uint32_t task_idx = (uint32_t) arg; + uint8_t* data = (uint8_t*) malloc(DATA_LENGTH); + uint8_t* data_wr = (uint8_t*) malloc(DATA_LENGTH); + uint8_t* data_rd = (uint8_t*) malloc(DATA_LENGTH); + uint8_t sensor_data_h, sensor_data_l; + + while (1) { + ret = i2c_master_sensor_test( I2C_MASTER_NUM, &sensor_data_h, &sensor_data_l); + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER READ SENSOR( BH1750 )\n", task_idx); + printf("*******************\n"); + if (ret == ESP_OK) { + printf("data_h: %02x\n", sensor_data_h); + printf("data_l: %02x\n", sensor_data_l); + printf("sensor val: %f\n", ( sensor_data_h << 8 | sensor_data_l ) / 1.2); + } else { + printf("No ack, sensor not connected...skip...\n"); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + + //--------------------------------------------------- + for (i = 0; i < DATA_LENGTH; i++) { + data[i] = i; + } + size_t d_size = i2c_slave_write_buffer(I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); + if (d_size == 0) { + printf("i2c slave tx buffer full\n"); + ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, DATA_LENGTH); + } else { + ret = i2c_master_read_slave(I2C_MASTER_NUM, data_rd, RW_TEST_LENGTH); + } + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER READ FROM SLAVE\n", task_idx); + printf("*******************\n"); + printf("====TASK[%d] Slave buffer data ====\n", task_idx); + disp_buf(data, d_size); + if (ret == ESP_OK) { + printf("====TASK[%d] Master read ====\n", task_idx); + disp_buf(data_rd, d_size); + } else { + printf("Master read slave error, IO not connected...\n"); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + //--------------------------------------------------- + int size; + for (i = 0; i < DATA_LENGTH; i++) { + data_wr[i] = i + 10; + } + //we need to fill the slave buffer so that master can read later + ret = i2c_master_write_slave( I2C_MASTER_NUM, data_wr, RW_TEST_LENGTH); + if (ret == ESP_OK) { + size = i2c_slave_read_buffer( I2C_SLAVE_NUM, data, RW_TEST_LENGTH, 1000 / portTICK_RATE_MS); + } + xSemaphoreTake(print_mux, portMAX_DELAY); + printf("*******************\n"); + printf("TASK[%d] MASTER WRITE TO SLAVE\n", task_idx); + printf("*******************\n"); + printf("----TASK[%d] Master write ----\n", task_idx); + disp_buf(data_wr, RW_TEST_LENGTH); + if (ret == ESP_OK) { + printf("----TASK[%d] Slave read: [%d] bytes ----\n", task_idx, size); + disp_buf(data, size); + } else { + printf("TASK[%d] Master write slave error, IO not connected....\n", task_idx); + } + xSemaphoreGive(print_mux); + vTaskDelay(( DELAY_TIME_BETWEEN_ITEMS_MS * ( task_idx + 1 ) ) / portTICK_RATE_MS); + } +} + +void app_main() +{ + print_mux = xSemaphoreCreateMutex(); + i2c_slave_init(); + i2c_master_init(); + + xTaskCreate(i2c_test_task, "i2c_test_task_0", 1024 * 2, (void* ) 0, 10, NULL); + xTaskCreate(i2c_test_task, "i2c_test_task_1", 1024 * 2, (void* ) 1, 10, NULL); +} + From 03551ec2da506dd6471de566803505e48c64e2db Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 12:19:02 +1100 Subject: [PATCH 09/11] build system: Add 'make monitor' target from arduino-esp32 Originally added to arduino-esp32 by @me-no-dev. It's so useful, we want it in esp-idf as well! :) --- README.md | 10 ++++++ components/esptool_py/Kconfig.projbuild | 40 ++++++++++++++++++++++++ components/esptool_py/Makefile.projbuild | 8 +++++ make/project.mk | 1 + 4 files changed, 59 insertions(+) diff --git a/README.md b/README.md index f3007fdb88..32c65bb820 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,16 @@ This will flash the entire project (app, bootloader and partition table) to a ne You don't need to run `make all` before running `make flash`, `make flash` will automatically rebuild anything which needs it. +# Viewing Serial Output + +The `make monitor` target will use the already-installed [miniterm](http://pyserial.readthedocs.io/en/latest/tools.html#module-serial.tools.miniterm) (a part of pyserial) to display serial output from the ESP32 on the terminal console. + +Exit miniterm by typing Ctrl-]. + +To flash and monitor output in one pass, you can run: + +`make flash monitor` + # Compiling & Flashing Just the App After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table: diff --git a/components/esptool_py/Kconfig.projbuild b/components/esptool_py/Kconfig.projbuild index dd2077cbb3..edff88c55d 100644 --- a/components/esptool_py/Kconfig.projbuild +++ b/components/esptool_py/Kconfig.projbuild @@ -178,4 +178,44 @@ config ESPTOOLPY_AFTER default "hard_reset" if ESPTOOLPY_AFTER_RESET default "no_reset" if ESPTOOLPY_AFTER_NORESET +choice MONITOR_BAUD + prompt "'make monitor' baud rate" + default MONITOR_BAUD_115200B + help + Baud rate to use when running 'make monitor' to view serial output + from a running chip. + + Can override by setting the MONITORBAUD environment variable. + +config MONITOR_BAUD_9600B + bool "9600 bps" +config MONITOR_BAUD_57600B + bool "57600 bps" +config MONITOR_BAUD_115200B + bool "115200 bps" +config MONITOR_BAUD_230400B + bool "230400 bps" +config MONITOR_BAUD_921600B + bool "921600 bps" +config MONITOR_BAUD_2MB + bool "2 Mbps" +config MONITOR_BAUD_OTHER + bool "Custom baud rate" + +endchoice + +config MONITOR_BAUD_OTHER_VAL + int "Custom baud rate value" if MONITOR_BAUD_OTHER + default 115200 + +config MONITOR_BAUD + int + default 9600 if MONITOR_BAUD_9600B + default 57600 if MONITOR_BAUD_57600B + default 115200 if MONITOR_BAUD_115200B + default 230400 if MONITOR_BAUD_230400B + default 921600 if MONITOR_BAUD_921600B + default 2000000 if MONITOR_BAUD_2MB + default MONITOR_BAUD_OTHER_VAL if MONITOR_BAUD_OTHER + endmenu diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 88b5894731..e249d14432 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -67,4 +67,12 @@ erase_flash: @echo "Erasing entire flash..." $(ESPTOOLPY_SERIAL) erase_flash +MONITORBAUD ?= $(CONFIG_MONITOR_BAUD) + +# note: if you want to run miniterm from command line, can simply run +# miniterm.py on the console. The '$(PYTHON) -m serial.tools.miniterm' +# is to allow for the $(PYTHON) variable overriding the python path. +monitor: $(call prereq_if_explicit,%flash) + $(PYTHON) -m serial.tools.miniterm --rts 0 --dtr 0 --raw $(ESPPORT) $(MONITORBAUD) + .PHONY: erase_flash diff --git a/make/project.mk b/make/project.mk index a5e487b85c..e2a93ce119 100644 --- a/make/project.mk +++ b/make/project.mk @@ -30,6 +30,7 @@ help: @echo "make clean - Remove all build output" @echo "make size - Display the memory footprint of the app" @echo "make erase_flash - Erase entire flash contents" + @echo "make monitor - Display serial output on terminal console" @echo "" @echo "make app - Build just the app" @echo "make app-flash - Flash just the app" From 4f7314a760377e9947af2b57d655b5d797e6cf27 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 12:19:39 +1100 Subject: [PATCH 10/11] README: Add a note about parallel builds --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 32c65bb820..5290aa7981 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,16 @@ After the initial flash, you may just want to build and flash just your app, not (There's no downside to reflashing the bootloader and partition table each time, if they haven't changed.) +# Parallel Builds + +esp-idf supports compiling multiple files in parallel, so all of the above commands can be run as `make -jN` where `N` is the number of parallel make processes to run (generally N should be equal to or one more than the number of CPU cores in your system.) + +Multiple make functions can be combined into one. For example: to build the app & bootloader using 5 jobs in parallel, then flash everything, and then display serial output from the ESP32 run: + +``` +make -j5 flash monitor +``` + # The Partition Table Once you've compiled your project, the "build" directory will contain a binary file with a name like "my_app.bin". This is an ESP32 image binary that can be loaded by the bootloader. From 5e96070c27ecd0901c4501666d1f5b274adadf94 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 30 Dec 2016 10:38:21 +1100 Subject: [PATCH 11/11] linker script: Remove KEEP from RAM-resident sections Reduce RAM usage when not all data/bss sections in source files were used. --- components/esp32/ld/esp32.common.ld | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index aafafbb495..09b7634445 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -99,7 +99,7 @@ SECTIONS *(.sbss2.*) *(.gnu.linkonce.sb2.*) *(.dynbss) - KEEP(*(.bss)) + *(.bss) *(.bss.*) *(.share.mem) *(.gnu.linkonce.b.*) @@ -111,17 +111,17 @@ SECTIONS .dram0.data : { _data_start = ABSOLUTE(.); - KEEP(*(.data)) - KEEP(*(.data.*)) - KEEP(*(.gnu.linkonce.d.*)) - KEEP(*(.data1)) - KEEP(*(.sdata)) - KEEP(*(.sdata.*)) - KEEP(*(.gnu.linkonce.s.*)) - KEEP(*(.sdata2)) - KEEP(*(.sdata2.*)) - KEEP(*(.gnu.linkonce.s2.*)) - KEEP(*(.jcr)) + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) *(.dram1 .dram1.*) *libesp32.a:panic.o(.rodata .rodata.*) _data_end = ABSOLUTE(.);