From d60ff9b6f6ce761ca2683246cf06126c574d186b Mon Sep 17 00:00:00 2001 From: Liu Han Date: Tue, 13 Dec 2016 09:52:29 +0800 Subject: [PATCH 01/59] components/tcpip_adapter: Fix set static IP address issue Set static IP address, clear current DNS servers. --- components/tcpip_adapter/tcpip_adapter_lwip.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index c20fea5059..8b51471390 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -25,6 +25,9 @@ #include "lwip/ip_addr.h" #include "lwip/ip6_addr.h" #include "lwip/nd6.h" +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ +#include "lwip/dns.h" +#endif #include "netif/wlanif.h" #include "netif/ethernetif.h" @@ -229,6 +232,12 @@ esp_err_t tcpip_adapter_set_ip_info(tcpip_adapter_if_t tcpip_if, tcpip_adapter_i if (status != TCPIP_ADAPTER_DHCP_STOPPED) { return ESP_ERR_TCPIP_ADAPTER_DHCP_NOT_STOPPED; } +#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */ + u8_t numdns = 0; + for (numdns = 0; numdns < DNS_MAX_SERVERS; numdns ++) { + dns_setserver(numdns, NULL); + } +#endif } ip4_addr_copy(esp_ip[tcpip_if].ip, ip_info->ip); From cbff82cc44c4052715ae3187455fb90a5c760ba1 Mon Sep 17 00:00:00 2001 From: Deomid Ryabkov Date: Wed, 14 Dec 2016 19:30:40 +0000 Subject: [PATCH 02/59] Allow gw to be null Allow interfaces to be configured without a default gateway, for local-only communication. In case of the AP interface, if gw is not set, do not offer it. --- components/lwip/apps/dhcpserver.c | 14 ++++++++------ components/tcpip_adapter/tcpip_adapter_lwip.c | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/components/lwip/apps/dhcpserver.c b/components/lwip/apps/dhcpserver.c index fcb27f0b05..94acc881e2 100644 --- a/components/lwip/apps/dhcpserver.c +++ b/components/lwip/apps/dhcpserver.c @@ -268,12 +268,14 @@ static u8_t *add_offer_options(u8_t *optptr) tcpip_adapter_get_ip_info(ESP_IF_WIFI_AP, &if_ip); - *optptr++ = DHCP_OPTION_ROUTER; - *optptr++ = 4; - *optptr++ = ip4_addr1(&if_ip.gw); - *optptr++ = ip4_addr2(&if_ip.gw); - *optptr++ = ip4_addr3(&if_ip.gw); - *optptr++ = ip4_addr4(&if_ip.gw); + if (!ip4_addr_isany_val(if_ip.gw)) { + *optptr++ = DHCP_OPTION_ROUTER; + *optptr++ = 4; + *optptr++ = ip4_addr1(&if_ip.gw); + *optptr++ = ip4_addr2(&if_ip.gw); + *optptr++ = ip4_addr3(&if_ip.gw); + *optptr++ = ip4_addr4(&if_ip.gw); + } } #ifdef USE_DNS diff --git a/components/tcpip_adapter/tcpip_adapter_lwip.c b/components/tcpip_adapter/tcpip_adapter_lwip.c index c20fea5059..7fb221f3a4 100644 --- a/components/tcpip_adapter/tcpip_adapter_lwip.c +++ b/components/tcpip_adapter/tcpip_adapter_lwip.c @@ -213,7 +213,7 @@ esp_err_t tcpip_adapter_set_ip_info(tcpip_adapter_if_t tcpip_if, tcpip_adapter_i tcpip_adapter_dhcp_status_t status; if (tcpip_if >= TCPIP_ADAPTER_IF_MAX || ip_info == NULL || - ip4_addr_isany_val(ip_info->ip) || ip4_addr_isany_val(ip_info->netmask) || ip4_addr_isany_val(ip_info->gw)) { + ip4_addr_isany_val(ip_info->ip) || ip4_addr_isany_val(ip_info->netmask)) { return ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS; } From 9d8e11020261749ea0e9c62cd2178facf914585f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 16 Dec 2016 11:30:37 +1100 Subject: [PATCH 03/59] Build system: Deal with the case where IDF_PATH contains ~ See github issue https://github.com/espressif/esp-idf/issues/118 (This is easier to work around in the build system than to document.) --- make/project.mk | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/make/project.mk b/make/project.mk index 0548002277..948bfda1f7 100644 --- a/make/project.mk +++ b/make/project.mk @@ -44,6 +44,22 @@ $(warning "esp-idf build system only supports GNU Make versions 3.81 or newer. Y endif endif +# make IDF_PATH an absolute path +# (works around the case where a shell character is embedded in the environment variable value.) +export IDF_PATH:=$(wildcard $(IDF_PATH)) + +ifndef IDF_PATH +$(error IDF_PATH variable is not set to a valid directory.) +endif + +ifneq ("$(IDF_PATH)","$(wildcard $(IDF_PATH))") +# due to the way make manages variables, this is hard to account for +# +# if you see this error, do the shell expansion in the shell ie +# make IDF_PATH=~/blah not make IDF_PATH="~/blah" +$(error If IDF_PATH is overriden on command line, it must be an absolute path with no embedded shell special characters) +endif + # disable built-in make rules, makes debugging saner MAKEFLAGS_OLD := $(MAKEFLAGS) MAKEFLAGS +=-rR @@ -345,7 +361,7 @@ $(foreach component,$(TEST_COMPONENT_PATHS),$(eval $(call GenerateComponentTarge app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) - + size: $(APP_ELF) $(SIZE) $(APP_ELF) From 461aab3e75fad8cbb9953d6e2ed732d92ddd44b3 Mon Sep 17 00:00:00 2001 From: Tian Zhong Xing Date: Fri, 16 Dec 2016 18:24:25 +0800 Subject: [PATCH 04/59] bootloader: fix error pointer to ota select info --- components/bootloader/src/main/bootloader_start.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 44de10e87d..ca5c55c08f 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -266,7 +266,7 @@ void bootloader_main() if (bs.ota_info.offset != 0) { // check if partition table has OTA info partition //ESP_LOGE("OTA info sector handling is not implemented"); - if (bs.ota_info.size < 2 * sizeof(esp_ota_select_entry_t)) { + if (bs.ota_info.size < 2 * SPI_SEC_SIZE) { ESP_LOGE(TAG, "ERROR: ota_info partition size %d is too small (minimum %d bytes)", bs.ota_info.size, sizeof(esp_ota_select_entry_t)); return; } @@ -275,10 +275,9 @@ void bootloader_main() ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", bs.ota_info.offset, bs.ota_info.size); return; } - sa = ota_select_map[0]; - sb = ota_select_map[1]; + memcpy(&sa, ota_select_map, sizeof(esp_ota_select_entry_t)); + memcpy(&sb, (uint8_t *)ota_select_map + SPI_SEC_SIZE, sizeof(esp_ota_select_entry_t)); bootloader_munmap(ota_select_map); - if(sa.ota_seq == 0xFFFFFFFF && sb.ota_seq == 0xFFFFFFFF) { // init status flash if (bs.factory.offset != 0) { // if have factory bin,boot factory bin From 7b02eae9e6b886fc5abb832a35086a88df79c90e Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 16 Dec 2016 20:01:15 +0800 Subject: [PATCH 05/59] ulp: add RD_REG, WR_REG, END instruction --- components/ulp/README.rst | 3 ++ components/ulp/include/esp32/ulp.h | 65 ++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/components/ulp/README.rst b/components/ulp/README.rst index 14c67d7109..875a01ef88 100644 --- a/components/ulp/README.rst +++ b/components/ulp/README.rst @@ -83,8 +83,11 @@ ULP coprocessor instruction defines .. doxygendefine:: I_DELAY .. doxygendefine:: I_HALT +.. doxygendefine:: I_END .. doxygendefine:: I_ST .. doxygendefine:: I_LD +.. doxygendefine:: I_WR_REG +.. doxygendefine:: I_RD_REG .. doxygendefine:: I_BL .. doxygendefine:: I_BGE .. doxygendefine:: I_BXR diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index c23d9cf9d1..18ad41ca6d 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -47,6 +47,10 @@ extern "C" { #define OPCODE_RD_REG 2 /*!< Instruction: read peripheral register (RTC_CNTL/RTC_IO/SARADC) (not implemented yet) */ +#define RD_REG_PERIPH_RTC_CNTL 0 /*!< Identifier of RTC_CNTL peripheral for RD_REG and WR_REG instructions */ +#define RD_REG_PERIPH_RTC_IO 1 /*!< Identifier of RTC_IO peripheral for RD_REG and WR_REG instructions */ +#define RD_REG_PERIPH_SARADC 2 /*!< Identifier of SARADC peripheral for RD_REG and WR_REG instructions */ + #define OPCODE_I2C 3 /*!< Instruction: read/write I2C (not implemented yet) */ #define OPCODE_DELAY 4 /*!< Instruction: delay (nop) for a given number of cycles */ @@ -191,8 +195,8 @@ typedef union { uint32_t addr : 8; /*!< Address within either RTC_CNTL, RTC_IO, or SARADC */ uint32_t periph_sel : 2; /*!< Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) */ uint32_t data : 8; /*!< 8 bits of data to write */ - uint32_t high : 5; /*!< High bit */ uint32_t low : 5; /*!< Low bit */ + uint32_t high : 5; /*!< High bit */ uint32_t opcode : 4; /*!< Opcode (OPCODE_WR_REG) */ } wr_reg; /*!< Format of WR_REG instruction */ @@ -200,10 +204,10 @@ typedef union { uint32_t addr : 8; /*!< Address within either RTC_CNTL, RTC_IO, or SARADC */ uint32_t periph_sel : 2; /*!< Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) */ uint32_t unused : 8; /*!< Unused */ - uint32_t high : 5; /*!< High bit */ uint32_t low : 5; /*!< Low bit */ + uint32_t high : 5; /*!< High bit */ uint32_t opcode : 4; /*!< Opcode (OPCODE_WR_REG) */ - } rd_reg; /*!< Format of WR_REG instruction */ + } rd_reg; /*!< Format of RD_REG instruction */ struct { uint32_t dreg : 2; /*!< Register where to store ADC result */ @@ -256,6 +260,8 @@ typedef union { } ulp_insn_t; +_Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should be 4 bytes"); + /** * Delay (nop) for a given number of cycles */ @@ -272,6 +278,59 @@ typedef union { .opcode = OPCODE_HALT } } +static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { + uint32_t ret = 3; + if (reg < DR_REG_RTCCNTL_BASE) { + assert(0 && "invalid register base"); + } else if (reg < DR_REG_RTCIO_BASE) { + ret = 0; + } else if (reg < DR_REG_SENS_BASE) { + ret = 1; + } else if (reg < DR_REG_RTCMEM0_BASE){ + ret = 2; + } else { + assert(0 && "invalid register base"); + } + return ret; +} + +/** + * Write literal value to a peripheral register + * + * reg[high_bit : low_bit] = val + */ +#define I_WR_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ + .addr = reg & 0xff, \ + .periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \ + .data = val, \ + .low = low_bit, \ + .high = high_bit, \ + .opcode = OPCODE_WR_REG } } + +/** + * Read from peripheral register into R0 + * + * R0 = reg[high_bit : low_bit] + */ +#define I_RD_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ + .addr = reg & 0xff, \ + .periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \ + .unused = 0, \ + .low = low_bit, \ + .high = high_bit, \ + .opcode = OPCODE_RD_REG } } + +/** + * End program. + * + * If wake == 1, wake up main CPU. + */ +#define I_END(wake) { .end = { \ + .wakeup = wake, \ + .unused = 0, \ + .sub_opcode = SUB_OPCODE_END, \ + .opcode = OPCODE_END } } + /** * Store value from register reg_val into RTC memory. * From 7a527896dcfbe7dc93752d9ef6c7ee8f3ce26f2a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 16 Dec 2016 20:25:38 +0800 Subject: [PATCH 06/59] ulp: use timer to start ULP, fix I_ANDI bug, add tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting the ULP using SENS_SAR_START_FORCE_REG doesn’t disable clock gating of RTC fast clock. When SoC goes into deep sleep mode, RTC fast clock gets gated, so ULP can no longer run. Instead, it has to be started using the timer (RTC_CNTL_ULP_CP_SLP_TIMER_EN bit). When ULP is enabled by the timer, clock also gets enabled. --- components/ulp/include/esp32/ulp.h | 2 +- components/ulp/test/test_ulp.c | 76 ++++++++++++++++++++++++++++++ components/ulp/ulp.c | 11 ++++- 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index 18ad41ca6d..a0903824c0 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -600,7 +600,7 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { #define I_ANDI(reg_dest, reg_src, imm_) { .alu_imm = { \ .dreg = reg_dest, \ .sreg = reg_src, \ - .imm = reg_imm_, \ + .imm = imm_, \ .unused = 0, \ .sel = ALU_SEL_AND, \ .sub_opcode = SUB_OPCODE_ALU_IMM, \ diff --git a/components/ulp/test/test_ulp.c b/components/ulp/test/test_ulp.c index f0000e1aa2..854eb3ee20 100644 --- a/components/ulp/test/test_ulp.c +++ b/components/ulp/test/test_ulp.c @@ -22,12 +22,14 @@ #include "esp_attr.h" #include "esp_err.h" #include "esp_log.h" +#include "esp_deep_sleep.h" #include "esp32/ulp.h" #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" #include "soc/sens_reg.h" +#include "driver/rtc_io.h" #include "sdkconfig.h" @@ -92,3 +94,77 @@ TEST_CASE("ulp branch test", "[ulp]") } TEST_ASSERT_EQUAL(0, RTC_SLOW_MEM[64]); } + +TEST_CASE("ulp wakeup test", "[ulp]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + const ulp_insn_t program[] = { + I_MOVI(R1, 1024), + M_LABEL(1), + I_DELAY(32000), + I_SUBI(R1, R1, 1), + M_BXZ(3), + I_RSHI(R3, R1, 5), // R3 = R1 / 32 + I_ST(R1, R3, 16), + M_BX(1), + M_LABEL(3), + I_MOVI(R2, 42), + I_MOVI(R3, 15), + I_ST(R2, R3, 0), + I_END(1) + }; + size_t size = sizeof(program)/sizeof(ulp_insn_t); + ulp_process_macros_and_load(0, program, &size); + ulp_run(0); + esp_deep_sleep_enable_ulp_wakeup(); + esp_deep_sleep_start(); +} + +TEST_CASE("ulp controls RTC_IO", "[ulp]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + const ulp_insn_t program[] = { + I_MOVI(R0, 0), // R0 is LED state + I_MOVI(R2, 16), // loop R2 from 16 down to 0 + M_LABEL(4), + I_SUBI(R2, R2, 1), + M_BXZ(6), + I_ADDI(R0, R0, 1), // R0 = (R0 + 1) % 2 + I_ANDI(R0, R0, 0x1), + M_BL(0, 1), // if R0 < 1 goto 0 + M_LABEL(1), + I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 1), // RTC_GPIO12 = 1 + M_BX(2), // goto 2 + M_LABEL(0), // 0: + I_WR_REG(RTC_GPIO_OUT_REG, 26, 27, 0), // RTC_GPIO12 = 0 + M_LABEL(2), // 2: + I_MOVI(R1, 100), // loop R1 from 100 down to 0 + M_LABEL(3), + I_SUBI(R1, R1, 1), + M_BXZ(5), + I_DELAY(32000), // delay for a while + M_BX(3), + M_LABEL(5), + M_BX(4), + M_LABEL(6), + I_END(1) // wake up the SoC + }; + const gpio_num_t led_gpios[] = { + GPIO_NUM_2, + GPIO_NUM_0, + GPIO_NUM_4 + }; + for (size_t i = 0; i < sizeof(led_gpios)/sizeof(led_gpios[0]); ++i) { + rtc_gpio_init(led_gpios[i]); + rtc_gpio_set_direction(led_gpios[i], RTC_GPIO_MODE_OUTPUT_ONLY); + rtc_gpio_set_level(led_gpios[i], 0); + } + size_t size = sizeof(program)/sizeof(ulp_insn_t); + ulp_process_macros_and_load(0, program, &size); + ulp_run(0); + esp_deep_sleep_enable_ulp_wakeup(); + esp_deep_sleep_start(); +} + diff --git a/components/ulp/ulp.c b/components/ulp/ulp.c index 228e3ff16d..60fa292f7c 100644 --- a/components/ulp/ulp.c +++ b/components/ulp/ulp.c @@ -263,8 +263,15 @@ esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* prog esp_err_t ulp_run(uint32_t entry_point) { - SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP_M); + // disable ULP timer + CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + // set entry point SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_PC_INIT_V, entry_point, SENS_PC_INIT_S); - SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_START_TOP_M); + // disable force start + CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP_M); + // make sure voltage is raised when RTC 8MCLK is enabled + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FOLW_8M); + // enable ULP timer + SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); return ESP_OK; } From b62f8b42d44edca43ae86119e6d1cfe898d84d8a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 16 Dec 2016 20:32:34 +0800 Subject: [PATCH 07/59] ulp: document peripherals accessible using RD_REG and WR_REG --- components/ulp/include/esp32/ulp.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index a0903824c0..f4e37e924a 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -49,7 +49,7 @@ extern "C" { #define RD_REG_PERIPH_RTC_CNTL 0 /*!< Identifier of RTC_CNTL peripheral for RD_REG and WR_REG instructions */ #define RD_REG_PERIPH_RTC_IO 1 /*!< Identifier of RTC_IO peripheral for RD_REG and WR_REG instructions */ -#define RD_REG_PERIPH_SARADC 2 /*!< Identifier of SARADC peripheral for RD_REG and WR_REG instructions */ +#define RD_REG_PERIPH_SENS 2 /*!< Identifier of SARADC peripheral for RD_REG and WR_REG instructions */ #define OPCODE_I2C 3 /*!< Instruction: read/write I2C (not implemented yet) */ @@ -277,17 +277,23 @@ _Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should .unused = 0, \ .opcode = OPCODE_HALT } } - +/** + * Map SoC peripheral register to periph_sel field of RD_REG and WR_REG + * instructions. + * + * @param reg peripheral register in RTC_CNTL_, RTC_IO_, SENS_ peripherals. + * @return periph_sel value for the peripheral to which this register belongs. + */ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { uint32_t ret = 3; if (reg < DR_REG_RTCCNTL_BASE) { assert(0 && "invalid register base"); } else if (reg < DR_REG_RTCIO_BASE) { - ret = 0; + ret = RD_REG_PERIPH_RTC_CNTL; } else if (reg < DR_REG_SENS_BASE) { - ret = 1; + ret = RD_REG_PERIPH_RTC_IO; } else if (reg < DR_REG_RTCMEM0_BASE){ - ret = 2; + ret = RD_REG_PERIPH_SENS; } else { assert(0 && "invalid register base"); } @@ -298,6 +304,7 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { * Write literal value to a peripheral register * * reg[high_bit : low_bit] = val + * This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers. */ #define I_WR_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ .addr = reg & 0xff, \ @@ -311,6 +318,7 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { * Read from peripheral register into R0 * * R0 = reg[high_bit : low_bit] + * This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers. */ #define I_RD_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ .addr = reg & 0xff, \ From 2c165aba366d4b22e7be02c723187795b91cf3e3 Mon Sep 17 00:00:00 2001 From: Deomid Ryabkov Date: Fri, 16 Dec 2016 22:27:45 +0000 Subject: [PATCH 08/59] uart_intr_config should return ESP_OK on success --- components/driver/uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/driver/uart.c b/components/driver/uart.c index 556e97baac..a2479f5db0 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -447,7 +447,7 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ } UART[uart_num]->int_ena.val = intr_conf->intr_enable_mask; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - return ESP_FAIL; + return ESP_OK; } //internal isr handler for default driver code. From d6fafd00dba4df5e75060939972582bcbf45af73 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 19 Dec 2016 13:06:21 +1100 Subject: [PATCH 09/59] Secure boot: Option for app & partition table signing to happen outside build system --- components/bootloader/Kconfig.projbuild | 29 +++++++++++++++++-- components/bootloader/Makefile.projbuild | 11 ++++++- components/bootloader_support/component.mk | 20 ++++++++++++- components/esptool_py/Makefile.projbuild | 6 ++-- components/partition_table/Makefile.projbuild | 2 +- docs/security/secure-boot.rst | 23 ++++++++++++++- make/component_wrapper.mk | 1 - make/project.mk | 12 ++++++++ 8 files changed, 94 insertions(+), 10 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 270369b2c6..798fcf0b23 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -69,9 +69,20 @@ config SECURE_BOOTLOADER_REFLASHABLE endchoice -config SECURE_BOOT_SIGNING_KEY - string "Secure boot signing key" +config SECURE_BOOT_BUILD_SIGNED_BINARIES + bool "Sign binaries during build" depends on SECURE_BOOT_ENABLED + default y + help + Once secure boot is enabled, bootloader will only boot if partition table and app image are signed. + + If enabled, these binary files are signed as part of the build process. The file named in "Secure boot private signing key" will be used to sign the image. + + If disabled, unsigned app/partition data will be built. They must be signed manually using espsecure.py (for example, on a remote signing server.) + +config SECURE_BOOT_SIGNING_KEY + string "Secure boot private signing key" + depends on SECURE_BOOT_BUILD_SIGNED_BINARIES default secure_boot_signing_key.pem help Path to the key file used to sign partition tables and app images for secure boot. Once secure boot is enabled, bootloader will only boot if partition table and app image are signed. @@ -85,6 +96,20 @@ config SECURE_BOOT_SIGNING_KEY See docs/security/secure-boot.rst for details. +config SECURE_BOOT_VERIFICATION_KEY + string "Secure boot public signature verification key" + depends on SECURE_BOOT_ENABLED && !SECURE_BOOT_BUILD_SIGNED_BINARIES + default signature_verification_key.bin + help + Path to a public key file used to verify signed images. This key is compiled into the bootloader, + and may also be used to verify signatures on OTA images after download. + + Key file is in raw binary format, and can be extracted from a + PEM formatted private key using the espsecure.py + extract_public_key command. + + See docs/security/secure-boot.rst for details. + config SECURE_BOOT_INSECURE bool "Allow potentially insecure options" depends on SECURE_BOOT_ENABLED diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 3f83803ad5..49bd934c61 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -65,8 +65,17 @@ else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin +ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES $(SECURE_BOOTLOADER_KEY): $(SECURE_BOOT_SIGNING_KEY) - $(Q) $(ESPSECUREPY) digest_private_key -k $< $@ + $(ESPSECUREPY) digest_private_key -k $< $@ +else +$(SECURE_BOOTLOADER_KEY): + @echo "No pre-generated key for a reflashable secure bootloader is available, due to signing configuration." + @echo "To generate one, you can use this command:" + @echo "espsecure.py generate_flash_encryption_key $@" + @echo "then re-run make." + exit 1 +endif bootloader: $(BOOTLOADER_DIGEST_BIN) @echo $(SEPARATOR) diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk index 1435dbb76b..6db815afff 100755 --- a/components/bootloader_support/component.mk +++ b/components/bootloader_support/component.mk @@ -17,8 +17,26 @@ ifdef CONFIG_SECURE_BOOT_ENABLED # this path is created relative to the component build directory SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin) -$(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) +ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES +# verification key derived from signing key. +$(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) $(SDKCONFIG_MAKEFILE) $(ESPSECUREPY) extract_public_key --keyfile $< $@ +else +# find the configured public key file +ORIG_SECURE_BOOT_VERIFICATION_KEY := $(call resolvepath,$(call dequote,$(CONFIG_SECURE_BOOT_VERIFICATION_KEY)),$(PROJECT_PATH)) + +$(ORIG_SECURE_BOOT_VERIFICATION_KEY): + @echo "Secure boot verification public key '$@' missing." + @echo "This can be extracted from the private signing key, see" + @echo "docs/security/secure-boot.rst for details." + exit 1 + +# copy it into the build dir, so the secure boot verification key has +# a predictable file name +$(SECURE_BOOT_VERIFICATION_KEY): $(ORIG_SECURE_BOOT_VERIFICATION_KEY) $(SDKCONFIG_MAKEFILE) + $(summary) CP $< $@ + cp $< $@ +endif COMPONENT_EXTRA_CLEAN += $(SECURE_BOOT_VERIFICATION_KEY) diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 54221f1795..3d8d5948fb 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -28,12 +28,12 @@ ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_CO ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) -ifdef CONFIG_SECURE_BOOT_ENABLED +ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES ifndef IS_BOOTLOADER_BUILD -# for secure boot, add a signing step to get from unsiged app to signed app +# for locally signed secure boot image, add a signing step to get from unsigned app to signed app APP_BIN_UNSIGNED := $(APP_BIN:.bin=-unsigned.bin) -$(APP_BIN): $(APP_BIN_UNSIGNED) $(SECURE_BOOT_SIGNING_KEY) +$(APP_BIN): $(APP_BIN_UNSIGNED) $(SECURE_BOOT_SIGNING_KEY) $(SDKCONFIG_MAKEFILE) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $< endif endif diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index dbc9d36053..5d1e726a86 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -21,7 +21,7 @@ PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(s PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin)) -ifdef CONFIG_SECURE_BOOT_ENABLED +ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN:.bin=-unsigned.bin) # add an extra signing step for secure partition table $(PARTITION_TABLE_BIN): $(PARTITION_TABLE_BIN_UNSIGNED) $(SDKCONFIG_MAKEFILE) $(SECURE_BOOT_SIGNING_KEY) diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 65b6bab48f..7aaf9abf81 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -25,7 +25,7 @@ This is a high level overview of the secure boot process. Step by step instructi 1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Secure Boot Configuration". -2. Secure Boot Configuration includes "Secure boot signing key", which is a file path. This file is a ECDSA public/private key pair in a PEM format file. +2. Secure Boot defaults to signing images and partition table data during the build process. The "Secure boot private signing key" config item is a file path to a ECDSA public/private key pair in a PEM format file. 3. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. @@ -119,6 +119,27 @@ openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key. Remember that the strength of the secure boot system depends on keeping the signing key private. +Remote Signing of Images +------------------------ + +For production builds, it can be good practice to use a remote signing server rather than have the signing key on the build machine (which is the default esp-idf secure boot configuration). The espsecure.py command line program can be used to sign app images & partition table data for secure boot, on a remote system. + +To use remote signing, disable the option "Sign binaries during build". The private signing key does not need to be present on the build system. However, the public (signature verification) key is required because it is compiled into the bootloader (and can be used to verify image signatures during OTA updates. + +To extract the public key from the private key:: + + espsecure.py extract_public_key --keyfile PRIVATE_SIGNING_KEY PUBLIC_VERIFICATION_KEY + +The path to the public signature verification key needs to be specified in the menuconfig under "Secure boot public signature verification key" in order to build the secure bootloader. + +After the app image and partition table are built, the build system will print signing steps using espsecure.py:: + + espsecure.py sign_data --keyfile PRIVATE_SIGNING_KEY BINARY_FILE + +The above command appends the image signature to the existing binary. You can use the --output argument to place the binary with signature appended into a separate file:: + + espsecure.py sign_data --keyfile PRIVATE_SIGNING_KEY --output SIGNED_BINARY_FILE BINARY_FILE + Secure Boot Best Practices -------------------------- diff --git a/make/component_wrapper.mk b/make/component_wrapper.mk index a84208267a..c750341c27 100644 --- a/make/component_wrapper.mk +++ b/make/component_wrapper.mk @@ -74,7 +74,6 @@ endif # Correspond to the files named in COMPONENT_EMBED_FILES & COMPONENT_EMBED_TXTFILES COMPONENT_EMBED_OBJS ?= $(addsuffix .bin.o,$(COMPONENT_EMBED_FILES)) $(addsuffix .txt.o,$(COMPONENT_EMBED_TXTFILES)) - # If we're called to compile something, we'll get passed the COMPONENT_INCLUDES # variable with all the include dirs from all the components in random order. This # means we can accidentally grab a header from another component before grabbing our own. diff --git a/make/project.mk b/make/project.mk index 0548002277..35be8a8615 100644 --- a/make/project.mk +++ b/make/project.mk @@ -142,6 +142,11 @@ include $(IDF_PATH)/make/common.mk all: ifdef CONFIG_SECURE_BOOT_ENABLED @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" +ifndef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES + @echo "App built but not signed. Sign app & partition data before flashing, via espsecure.py:" + @echo "espsecure.py sign_data --keyfile KEYFILE $(APP_BIN)" + @echo "espsecure.py sign_data --keyfile KEYFILE $(PARTITION_TABLE_BIN)" +endif @echo "To flash app & partition table, run 'make flash' or:" else @echo "To flash all build output, run 'make flash' or:" @@ -283,8 +288,15 @@ $(APP_ELF): $(foreach libcomp,$(COMPONENT_LIBRARIES),$(BUILD_DIR_BASE)/$(libcomp # Generation of $(APP_BIN) from $(APP_ELF) is added by the esptool # component's Makefile.projbuild app: $(APP_BIN) +ifeq ("$(CONFIG_SECURE_BOOT_ENABLED)$(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)","y") # secure boot enabled, but remote sign app image + @echo "App built but not signed. Signing step via espsecure.py:" + @echo "espsecure.py sign_data --keyfile KEYFILE $(APP_BIN)" + @echo "Then flash app command is:" + @echo $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN) +else @echo "App built. Default flash app command is:" @echo $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN) +endif all_binaries: $(APP_BIN) From 4853df9c83b915a5fa8b2d487176bc3a6b30eacf Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 19 Dec 2016 14:23:39 +0800 Subject: [PATCH 10/59] phy init data update This changes 54M target power from 16 dBm to 15 dBm, and bumps init_data version from 0 to 1. --- components/esp32/phy_init_data.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp32/phy_init_data.h b/components/esp32/phy_init_data.h index 206598f97c..ea6a761b35 100644 --- a/components/esp32/phy_init_data.h +++ b/components/esp32/phy_init_data.h @@ -27,7 +27,7 @@ static const char phy_init_magic_pre[] = PHY_INIT_MAGIC; * @brief Structure containing default recommended PHY initialization parameters. */ static const esp_phy_init_data_t phy_init_data= { - .param_ver_id = 0, + .param_ver_id = 1, .crystal_select = 3, .wifi_rx_gain_swp_step_1 = 0x05, .wifi_rx_gain_swp_step_2 = 0x04, @@ -75,7 +75,7 @@ static const esp_phy_init_data_t phy_init_data= { .target_power_qdb_1 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 76), .target_power_qdb_2 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 74), .target_power_qdb_3 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 68), - .target_power_qdb_4 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 64), + .target_power_qdb_4 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 60), .target_power_qdb_5 = LIMIT(CONFIG_ESP32_PHY_MAX_TX_POWER * 4, 0, 52), .target_power_index_mcs0 = 0, .target_power_index_mcs1 = 0, From bb584c43335617ee71a0f5564adbadd8f6abf372 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 19 Dec 2016 16:01:21 +0800 Subject: [PATCH 11/59] Fix exception handler to jump to correct vector instead of crashing to the double exception vector. --- components/freertos/xtensa_vectors.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/xtensa_vectors.S b/components/freertos/xtensa_vectors.S index f180705e70..7baae07ce0 100644 --- a/components/freertos/xtensa_vectors.S +++ b/components/freertos/xtensa_vectors.S @@ -713,7 +713,7 @@ _xt_user_exc: rsr a2, EXCCAUSE /* recover exc cause */ movi a3, _xt_exception_table - get_percpu_entry_for a3, a4 + get_percpu_entry_for a2, a4 addx4 a4, a2, a3 /* a4 = address of exception table entry */ l32i a4, a4, 0 /* a4 = handler address */ #ifdef __XTENSA_CALL0_ABI__ From 4854dcf0eb28860aaa94edbc5933c1cd0f23a7d2 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 19 Dec 2016 16:39:55 +0800 Subject: [PATCH 12/59] Get rid of old interrupt example code --- components/driver/include/driver/gpio.h | 9 +-------- components/driver/include/driver/uart.h | 5 +---- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index fba013fe8b..83d3806834 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -343,9 +343,6 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); /** * @brief register GPIO interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * * @param fn Interrupt handler function. * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) @@ -444,12 +441,8 @@ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); /** *----------EXAMPLE TO SET ISR HANDLER ---------------------- * @code{c} - * gpio_isr_register(gpio_intr_test,NULL, 0); //hook the isr handler for GPIO interrupt + * gpio_isr_register(gpio_intr_test, 0, NULL); //hook the isr handler for GPIO interrupt * @endcode - * @note - * 1. user should arrange the INUMs that used, better not to use a same INUM for different interrupt. - * 2. do not pick the INUM that already occupied by the system. - * 3. refer to soc.h to check which INUMs that can be used. */ /** *-------------EXAMPLE OF HANDLER FUNCTION-------------------* diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 34ca9fc401..e956308522 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -463,8 +463,6 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * @brief Install UART driver. * * UART ISR handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * @param rx_buffer_size UART RX ring buffer size @@ -595,7 +593,6 @@ esp_err_t uart_flush(uart_port_t uart_num); * @code{c} * //1. Setup UART * #include "freertos/queue.h" - * #define UART_INTR_NUM 17 //choose one interrupt number from soc.h * //a. Set UART parameter * int uart_num = 0; //uart port number * uart_config_t uart_config = { @@ -658,7 +655,7 @@ esp_err_t uart_flush(uart_port_t uart_num); * //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) * uart_set_pin(uart_num, 16, 17, 18, 19); * //Install UART driver( We don't need an event queue here) - * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, NULL, RINGBUF_TYPE_BYTEBUF); + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, NULL, 0); * uint8_t data[1000]; * while(1) { * //Read data from UART From 93e72649dcae4c81dd2c6f8eac2b54db95c3550d Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 19 Dec 2016 18:13:36 +0800 Subject: [PATCH 13/59] Fix memory debugging code --- components/freertos/heap_regions.c | 4 ++-- .../freertos/include/freertos/heap_regions_debug.h | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/components/freertos/heap_regions.c b/components/freertos/heap_regions.c index a7c9606036..5cece756c0 100644 --- a/components/freertos/heap_regions.c +++ b/components/freertos/heap_regions.c @@ -174,7 +174,7 @@ static void prvInsertBlockIntoFreeList( BlockLink_t *pxBlockToInsert ); /*-----------------------------------------------------------*/ /* The size of the structure placed at the beginning of each allocated memory -block must by correctly byte aligned. */ +block must be correctly byte aligned. */ static const uint32_t uxHeapStructSize = ( ( sizeof ( BlockLink_t ) + BLOCK_HEAD_LEN + BLOCK_TAIL_LEN + ( portBYTE_ALIGNMENT - 1 ) ) & ~portBYTE_ALIGNMENT_MASK ); /* Create a couple of list links to mark the start and end of the list. */ @@ -583,7 +583,7 @@ const HeapRegionTagged_t *pxHeapRegion; #if (configENABLE_MEMORY_DEBUG == 1) { - mem_debug_init(uxHeapStructSize, &xStart, pxEnd, &xMallocMutex, xBlockAllocatedBit); + mem_debug_init(uxHeapStructSize, &xStart, pxEnd, &xMallocMutex); mem_check_all(0); } #endif diff --git a/components/freertos/include/freertos/heap_regions_debug.h b/components/freertos/include/freertos/heap_regions_debug.h index 6ab4681f18..dca9531d7e 100644 --- a/components/freertos/include/freertos/heap_regions_debug.h +++ b/components/freertos/include/freertos/heap_regions_debug.h @@ -22,9 +22,10 @@ typedef struct { /* Please keep this definition same as BlockLink_t */ typedef struct _os_block_t { - struct _os_block_t *next; - size_t size; - unsigned int xtag; + struct _os_block_t *next; /*<< The next free block in the list. */ + int size: 24; /*<< The size of the free block. */ + int xtag: 7; /*<< Tag of this region */ + int xAllocated: 1; /*<< 1 if allocated */ }os_block_t; typedef struct { @@ -50,7 +51,7 @@ typedef struct _mem_dbg_ctl{ #define OS_BLOCK(_b) ((os_block_t*)((debug_block_t*)((char*)(_b) + BLOCK_HEAD_LEN))) #define DEBUG_BLOCK(_b) ((debug_block_t*)((char*)(_b) - BLOCK_HEAD_LEN)) #define HEAD_DOG(_b) ((_b)->head.dog) -#define TAIL_DOG(_b) (*(unsigned int*)((char*)(_b) + (((_b)->os_block.size & (~g_alloc_bit) ) - BLOCK_TAIL_LEN))) +#define TAIL_DOG(_b) (*(unsigned int*)((char*)(_b) + (((_b)->os_block.size ) - BLOCK_TAIL_LEN))) #define DOG_ASSERT()\ {\ From 99849ca2cc6895675ee5ebbe9ccdae9c3b3d9a25 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Mon, 19 Dec 2016 18:17:54 +0800 Subject: [PATCH 14/59] Fix timer example; it had too little stack resulting in a stack overflow --- examples/13_timer_group/main/timer_group.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/13_timer_group/main/timer_group.c b/examples/13_timer_group/main/timer_group.c index 9db471054d..37e22faafc 100644 --- a/examples/13_timer_group/main/timer_group.c +++ b/examples/13_timer_group/main/timer_group.c @@ -83,7 +83,7 @@ void IRAM_ATTR timer_group0_isr(void *para) uint32_t intr_status = TIMERG0.int_st_timers.val; timer_event_t evt; if((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) { - /*Timer0 is an example that don't reload counter value*/ + /*Timer0 is an example that doesn't reload counter value*/ TIMERG0.hw_timer[timer_idx].update = 1; /* We don't call a API here because they are not declared with IRAM_ATTR. @@ -197,9 +197,9 @@ void tg0_timer1_init() */ void app_main() { + timer_queue = xQueueCreate(10, sizeof(timer_event_t)); tg0_timer0_init(); tg0_timer1_init(); - timer_queue = xQueueCreate(10, sizeof(timer_event_t)); - xTaskCreate(timer_evt_task, "timer_evt_task", 1024, NULL, 5, NULL); + xTaskCreate(timer_evt_task, "timer_evt_task", 2048, NULL, 5, NULL); } From 1597f7a035fe213a3067eda4312cc4f7e896b962 Mon Sep 17 00:00:00 2001 From: Deomid Ryabkov Date: Mon, 19 Dec 2016 06:28:28 +0000 Subject: [PATCH 15/59] Add IRAM_ATTR to esp_intr_{enable,disable} So it's safe to invoke from ISR Signed-off-by: Jeroen Domburg --- components/esp32/intr_alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c index 57a6a97dea..9476a433d4 100644 --- a/components/esp32/intr_alloc.c +++ b/components/esp32/intr_alloc.c @@ -636,7 +636,7 @@ int esp_intr_get_cpu(intr_handle_t handle) //Muxing an interrupt source to interrupt 6, 7, 11, 15, 16 or 29 cause the interrupt to effectively be disabled. #define INT_MUX_DISABLED_INTNO 6 -esp_err_t esp_intr_enable(intr_handle_t handle) +esp_err_t IRAM_ATTR esp_intr_enable(intr_handle_t handle) { if (!handle) return ESP_ERR_INVALID_ARG; portENTER_CRITICAL(&spinlock); @@ -659,7 +659,7 @@ esp_err_t esp_intr_enable(intr_handle_t handle) return ESP_OK; } -esp_err_t esp_intr_disable(intr_handle_t handle) +esp_err_t IRAM_ATTR esp_intr_disable(intr_handle_t handle) { if (!handle) return ESP_ERR_INVALID_ARG; portENTER_CRITICAL(&spinlock); From 6a8a9a027280c3ecdd01a8018486945bded1bb66 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 20 Dec 2016 01:13:03 +0800 Subject: [PATCH 16/59] bugfix: ring buffer, fix api xRingbufferReceiveUpToFromISR --- components/freertos/ringbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/ringbuf.c b/components/freertos/ringbuf.c index 7074eb537d..e49b8e5ef4 100644 --- a/components/freertos/ringbuf.c +++ b/components/freertos/ringbuf.c @@ -600,7 +600,7 @@ void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, configASSERT(rb); configASSERT(rb->flags & flag_bytebuf); portENTER_CRITICAL_ISR(&rb->mux); - itemData=rb->getItemFromRingbufImpl(rb, item_size, 0); + itemData=rb->getItemFromRingbufImpl(rb, item_size, wanted_size); portEXIT_CRITICAL_ISR(&rb->mux); return (void*)itemData; } From 59e0f63d37d4c8206c84c2fd49d0e49eba1d28be Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 20 Dec 2016 10:00:04 +1100 Subject: [PATCH 17/59] Build system: Add `make erase_flash` target --- README.md | 6 ++++++ components/bootloader/Makefile.projbuild | 2 +- components/esptool_py/Makefile.projbuild | 9 +++++++-- docs/partition-tables.rst | 1 + make/project.mk | 3 ++- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c01e314f11..f3007fdb88 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,12 @@ In both cases the factory app is flashed at offset 0x10000. If you `make partiti For more details about partition tables and how to create custom variations, view the `docs/partition-tables.rst` file. +# Erasing Flash + +The `make flash` target does not erase the entire flash contents. However it is sometimes useful to set the device back to a totally erased state, particularly when making partition table changes or OTA app updates. To erase the entire flash, run `make erase_flash`. + +This can be combined with other targets, ie `make erase_flash flash` will erase everything and then re-flash the new app, bootloader and partition table. + # Resources * The [docs directory of the esp-idf repository](docs) contains source of [esp-idf](http://esp-idf.readthedocs.io/) documentation. diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 3f83803ad5..becb2003a8 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -43,7 +43,7 @@ bootloader: $(BOOTLOADER_BIN) ESPTOOL_ALL_FLASH_ARGS += $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN) -bootloader-flash: $(BOOTLOADER_BIN) +bootloader-flash: $(BOOTLOADER_BIN) | erase_flash $(ESPTOOLPY_WRITE_FLASH) 0x1000 $^ else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 54221f1795..460fb64a0f 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -43,17 +43,22 @@ APP_BIN_UNSIGNED ?= $(APP_BIN) $(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< -flash: all_binaries $(ESPTOOLPY_SRC) +flash: all_binaries $(ESPTOOLPY_SRC) | erase_flash @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." ifdef CONFIG_SECURE_BOOT_ENABLED @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" endif $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) -app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) +app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) | erase_flash @echo "Flashing app to serial port $(ESPPORT), offset $(CONFIG_APP_OFFSET)..." $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN) # Submodules normally added in component.mk, but can be added # at the project level as long as qualified path COMPONENT_SUBMODULES += $(COMPONENT_PATH)/esptool + +.PHONY: erase_flash +erase_flash: + @echo "Erasing entire flash..." + $(ESPTOOLPY_SERIAL) erase_flash diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index 55b8870857..d45a540c66 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -125,5 +125,6 @@ Flashing the partition table A manual flashing command is also printed as part of ``make partition_table``. +Note that updating the partition table doesn't erase data that may have been stored according to the old partition table. You can use ``make erase_flash`` (or ``esptool.py erase_flash``) to erase the entire flash contents. .. _secure boot: security/secure-boot.rst diff --git a/make/project.mk b/make/project.mk index 0548002277..1ffda9baea 100644 --- a/make/project.mk +++ b/make/project.mk @@ -26,9 +26,10 @@ help: @echo "make defconfig - Set defaults for all new configuration options" @echo "" @echo "make all - Build app, bootloader, partition table" - @echo "make flash - Flash all components to a fresh chip" + @echo "make flash - Flash app, bootloader, partition table to a chip" @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 "" @echo "make app - Build just the app" @echo "make app-flash - Flash just the app" From 198889ad26930ffa23a003effe4f266869be7994 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 20 Dec 2016 16:04:15 +1100 Subject: [PATCH 18/59] bootloader: Check if DRAM segments are going to collide with stack --- .../bootloader/src/main/bootloader_start.c | 33 +++++++++++++++++-- components/esp32/include/soc/cpu.h | 10 ++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 44de10e87d..bec45f7ce0 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -59,7 +59,7 @@ extern void Cache_Flush(int); void bootloader_main(); static void unpack_load_app(const esp_partition_pos_t *app_node); void print_flash_info(const esp_image_header_t* pfhdr); -void set_cache_and_start_app(uint32_t drom_addr, +static void set_cache_and_start_app(uint32_t drom_addr, uint32_t drom_load_addr, uint32_t drom_size, uint32_t irom_addr, @@ -365,7 +365,6 @@ void bootloader_main() unpack_load_app(&load_part_pos); } - static void unpack_load_app(const esp_partition_pos_t* partition) { esp_err_t err; @@ -413,6 +412,9 @@ static void unpack_load_app(const esp_partition_pos_t* partition) image_header.spi_size, (unsigned)image_header.entry_addr); + /* Important: From here on this function cannot access any global data (bss/data segments), + as loading the app image may overwrite these. + */ for (int segment = 0; segment < image_header.segment_count; segment++) { esp_image_segment_header_t segment_header; uint32_t data_offs; @@ -468,6 +470,31 @@ static void unpack_load_app(const esp_partition_pos_t* partition) segment_header.load_addr, segment_header.data_len, segment_header.data_len, (load)?"load":(map)?"map":""); if (load) { + intptr_t sp, start_addr, end_addr; + ESP_LOGV(TAG, "bootloader_mmap data_offs=%08x data_len=%08x", data_offs, segment_header.data_len); + + start_addr = segment_header.load_addr; + end_addr = start_addr + segment_header.data_len; + + /* Before loading segment, check it doesn't clobber + bootloader RAM... */ + + if (end_addr < 0x40000000) { + sp = (intptr_t)get_sp(); + if (end_addr > sp) { + ESP_LOGE(TAG, "Segment %d end address %08x overlaps bootloader stack %08x - can't load", + segment, end_addr, sp); + return; + } + if (end_addr > sp - 256) { + /* We don't know for sure this is the stack high water mark, so warn if + it seems like we may overflow. + */ + ESP_LOGW(TAG, "Segment %d end address %08x close to stack pointer %08x", + segment, end_addr, sp); + } + } + const void *data = bootloader_mmap(data_offs, segment_header.data_len); if(!data) { ESP_LOGE(TAG, "bootloader_mmap(0x%xc, 0x%x) failed", @@ -488,7 +515,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) image_header.entry_addr); } -void set_cache_and_start_app( +static void set_cache_and_start_app( uint32_t drom_addr, uint32_t drom_load_addr, uint32_t drom_size, diff --git a/components/esp32/include/soc/cpu.h b/components/esp32/include/soc/cpu.h index 4457c81a22..aa471a1739 100644 --- a/components/esp32/include/soc/cpu.h +++ b/components/esp32/include/soc/cpu.h @@ -26,6 +26,16 @@ #define WSR(reg, newval) asm volatile ("wsr %0, " #reg : : "r" (newval)); #define XSR(reg, swapval) asm volatile ("xsr %0, " #reg : "+r" (swapval)); +/** @brief Read current stack pointer address + * + */ +static inline void *get_sp() +{ + void *sp; + asm volatile ("mov %0, sp;" : "=r" (sp)); + return sp; +} + /* Return true if the CPU is in an interrupt context (PS.UM == 0) */ From 9dbdab5c9a820960f88f7ceea74ac1f5fc0e9a4a Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Mon, 19 Dec 2016 12:52:10 +0800 Subject: [PATCH 19/59] driver: uart 1. add uart rx buffer data length API 2. add uart pattern detect event 3. add uart example code 4. modify uart_isr_register 5. modify uart.rst 6. fix parity err event and frame err event. --- components/driver/include/driver/uart.h | 58 +++++++- components/driver/uart.c | 119 +++++++++------ docs/api/uart.rst | 4 +- examples/20_uart/Makefile | 9 ++ examples/20_uart/main/component.mk | 3 + examples/20_uart/main/uart_test.c | 183 ++++++++++++++++++++++++ 6 files changed, 326 insertions(+), 50 deletions(-) create mode 100644 examples/20_uart/Makefile create mode 100644 examples/20_uart/main/component.mk create mode 100644 examples/20_uart/main/uart_test.c diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index e956308522..c193fb0ef8 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -23,6 +23,7 @@ extern "C" { #include "soc/uart_reg.h" #include "soc/uart_struct.h" #include "esp_err.h" +#include "esp_intr_alloc.h" #include "driver/periph_ctrl.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -129,6 +130,7 @@ typedef enum { UART_PARITY_ERR, /*!< UART RX parity event*/ UART_DATA_BREAK, /*!< UART TX data and break event*/ UART_EVENT_MAX, /*!< UART event max index*/ + UART_PATTERN_DET, /*!< UART pattern detected */ } uart_event_type_t; /** @@ -139,6 +141,8 @@ typedef struct { size_t size; /*!< UART data size for UART_DATA event*/ } uart_event_t; +typedef intr_handle_t uart_isr_handle_t; + /** * @brief Set UART data bits. * @@ -372,12 +376,14 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); * @param arg parameter for handler function * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags); +esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags, uart_isr_handle_t *handle); /** @@ -463,9 +469,11 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * @brief Install UART driver. * * UART ISR handler will be attached to the same CPU core that this function is running on. + * Users should know that which CPU is running and then pick a INUM that is not used by system. + * We can find the information of INUM and interrupt level in soc.h. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param rx_buffer_size UART RX ring buffer size + * @param rx_buffer_size UART RX ring buffer size, rx_buffer_size should be greater than UART_FIFO_LEN. * @param tx_buffer_size UART TX ring buffer size. * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * @param queue_size UART event queue size/depth. @@ -586,6 +594,48 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp */ esp_err_t uart_flush(uart_port_t uart_num); +/** + * @brief UART get RX ring buffer cached data length + * + * @param uart_num UART port number. + * @param size Pointer of size_t to accept cached data length + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t uart_get_buffered_data_len(uart_port_t uart_num, size_t* size); + +/** + * @brief UART disable pattern detect function. + * Designed for applications like 'AT commands'. + * When the hardware detect a series of one same character, the interrupt will be triggered. + * + * @param uart_num UART port number. + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t uart_disable_pattern_det_intr(uart_port_t uart_num); + +/** + * @brief UART enable pattern detect function. + * Designed for applications like 'AT commands'. + * When the hardware detect a series of one same character, the interrupt will be triggered. + * + * @param uart_num UART port number. + * @param pattern_chr character of the pattern + * @param chr_num number of the character, 8bit value. + * @param chr_tout timeout of the interval between each pattern characters, 24bit value, unit is APB(80Mhz) clock cycle. + * @param post_idle idle time after the last pattern character, 24bit value, unit is APB(80Mhz) clock cycle. + * @param pre_idle idle time before the first pattern character, 24bit value, unit is APB(80Mhz) clock cycle. + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t uart_enable_pattern_det_intr(uart_port_t uart_num, char pattern_chr, uint8_t chr_num, int chr_tout, int post_idle, int pre_idle); /***************************EXAMPLE********************************** * * @@ -593,6 +643,7 @@ esp_err_t uart_flush(uart_port_t uart_num); * @code{c} * //1. Setup UART * #include "freertos/queue.h" + * #define UART_INTR_NUM 17 //choose one interrupt number from soc.h * //a. Set UART parameter * int uart_num = 0; //uart port number * uart_config_t uart_config = { @@ -655,7 +706,7 @@ esp_err_t uart_flush(uart_port_t uart_num); * //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19) * uart_set_pin(uart_num, 16, 17, 18, 19); * //Install UART driver( We don't need an event queue here) - * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, NULL, 0); + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 0, NULL, 0); * uint8_t data[1000]; * while(1) { * //Read data from UART @@ -689,7 +740,6 @@ esp_err_t uart_flush(uart_port_t uart_num); * ESP_LOGI(TAG,"data, len: %d", event.size); * int len = uart_read_bytes(uart_num, dtmp, event.size, 10); * ESP_LOGI(TAG, "uart read: %d", len); - uart_write_bytes(uart_num, (const char*)dtmp, len); * break; * //Event of HW FIFO overflow detected * case UART_FIFO_OVF: diff --git a/components/driver/uart.c b/components/driver/uart.c index 556e97baac..e85c54d8c4 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -59,6 +59,7 @@ typedef struct { QueueHandle_t xQueueUart; /*!< UART queue handler*/ intr_handle_t intr_handle; /*!< UART interrupt handle*/ //rx parameters + int rx_buffered_len; /*!< UART cached data length */ SemaphoreHandle_t rx_mux; /*!< UART RX data mutex*/ int rx_buf_size; /*!< RX ring buffer size */ RingbufHandle_t rx_ring_buf; /*!< RX ring buffer handler*/ @@ -260,22 +261,38 @@ esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask) return ESP_OK; } +esp_err_t uart_enable_pattern_det_intr(uart_port_t uart_num, char pattern_chr, uint8_t chr_num, int chr_tout, int post_idle, int pre_idle) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK(chr_tout >= 0 && chr_tout <= UART_RX_GAP_TOUT_V, "uart pattern set error\n", ESP_FAIL); + UART_CHECK(post_idle >= 0 && post_idle <= UART_POST_IDLE_NUM_V, "uart pattern set error\n", ESP_FAIL); + UART_CHECK(pre_idle >= 0 && pre_idle <= UART_PRE_IDLE_NUM_V, "uart pattern set error\n", ESP_FAIL); + UART[uart_num]->at_cmd_char.data = pattern_chr; + UART[uart_num]->at_cmd_char.char_num = chr_num; + UART[uart_num]->at_cmd_gaptout.rx_gap_tout = chr_tout; + UART[uart_num]->at_cmd_postcnt.post_idle_num = post_idle; + UART[uart_num]->at_cmd_precnt.pre_idle_num = pre_idle; + return uart_enable_intr_mask(uart_num, UART_AT_CMD_CHAR_DET_INT_ENA_M); +} + +esp_err_t uart_disable_pattern_det_intr(uart_port_t uart_num) +{ + return uart_disable_intr_mask(uart_num, UART_AT_CMD_CHAR_DET_INT_ENA_M); +} + esp_err_t uart_enable_rx_intr(uart_port_t uart_num) { - uart_enable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); - return ESP_OK; + return uart_enable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); } esp_err_t uart_disable_rx_intr(uart_port_t uart_num) { - uart_disable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); - return ESP_OK; + return uart_disable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA|UART_RXFIFO_TOUT_INT_ENA); } esp_err_t uart_disable_tx_intr(uart_port_t uart_num) { - uart_disable_intr_mask(uart_num, UART_TXFIFO_EMPTY_INT_ENA); - return ESP_OK; + return uart_disable_intr_mask(uart_num, UART_TXFIFO_EMPTY_INT_ENA); } esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) @@ -290,21 +307,21 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) return ESP_OK; } -esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags) +esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags, uart_isr_handle_t *handle) { int ret; UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); switch(uart_num) { case UART_NUM_1: - ret=esp_intr_alloc(ETS_UART1_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); + ret=esp_intr_alloc(ETS_UART1_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); break; case UART_NUM_2: - ret=esp_intr_alloc(ETS_UART2_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); + ret=esp_intr_alloc(ETS_UART2_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); break; case UART_NUM_0: default: - ret=esp_intr_alloc(ETS_UART0_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); + ret=esp_intr_alloc(ETS_UART0_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); break; } UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); @@ -595,6 +612,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) p_uart->rx_buffer_full_flg = true; uart_event.type = UART_BUFFER_FULL; } else { + UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); + p_uart->rx_buffered_len += p_uart->rx_stash_len; + UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_event.type = UART_DATA; } if(HPTaskAwoken == pdTRUE) { @@ -618,10 +638,10 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) } else if(uart_intr_status & UART_BRK_DET_INT_ST_M) { uart_reg->int_clr.brk_det = 1; uart_event.type = UART_BREAK; - } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M ) { + } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { uart_reg->int_clr.parity_err = 1; uart_event.type = UART_FRAME_ERR; - } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { + } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M) { uart_reg->int_clr.frm_err = 1; uart_event.type = UART_PARITY_ERR; } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { @@ -647,6 +667,9 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_ena.tx_brk_idle_done = 0; uart_reg->int_clr.tx_brk_idle_done = 1; UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]); + } else if(uart_intr_status & UART_AT_CMD_CHAR_DET_INT_ST_M) { + uart_reg->int_clr.at_cmd_char_det = 1; + uart_event.type = UART_PATTERN_DET; } else if(uart_intr_status & UART_TX_DONE_INT_ST_M) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); uart_reg->int_ena.tx_done = 0; @@ -656,8 +679,7 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) if(HPTaskAwoken == pdTRUE) { portYIELD_FROM_ISR() ; } - } - else { + } else { uart_reg->int_clr.val = uart_intr_status; /*simply clear all other intr status*/ uart_event.type = UART_EVENT_MAX; } @@ -833,6 +855,9 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp p_uart_obj[uart_num]->rx_cur_remain = size; } else { xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + p_uart_obj[uart_num]->rx_buffered_len -= copy_len; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); return copy_len; } } @@ -853,6 +878,9 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp if(p_uart_obj[uart_num]->rx_buffer_full_flg) { BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_data_buf, p_uart_obj[uart_num]->rx_stash_len, 1); if(res == pdTRUE) { + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + p_uart_obj[uart_num]->rx_buffered_len += p_uart_obj[uart_num]->rx_stash_len; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); p_uart_obj[uart_num]->rx_buffer_full_flg = false; uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); } @@ -860,9 +888,20 @@ int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickTyp } } xSemaphoreGive(p_uart_obj[uart_num]->rx_mux); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + p_uart_obj[uart_num]->rx_buffered_len -= copy_len; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); return copy_len; } +esp_err_t uart_get_buffered_data_len(uart_port_t uart_num, size_t* size) +{ + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + UART_CHECK((p_uart_obj[uart_num]), "uart driver error", ESP_FAIL); + *size = p_uart_obj[uart_num]->rx_buffered_len; + return ESP_OK; +} + esp_err_t uart_flush(uart_port_t uart_num) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); @@ -873,10 +912,13 @@ esp_err_t uart_flush(uart_port_t uart_num) //rx sem protect the ring buffer read related functions xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); - esp_intr_disable(p_uart->intr_handle); + uart_disable_rx_intr(p_uart_obj[uart_num]->uart_num); while(true) { if(p_uart->rx_head_ptr) { vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->rx_head_ptr); + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + p_uart_obj[uart_num]->rx_buffered_len -= p_uart->rx_cur_remain; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); p_uart->rx_ptr = NULL; p_uart->rx_cur_remain = 0; p_uart->rx_head_ptr = NULL; @@ -885,47 +927,33 @@ esp_err_t uart_flush(uart_port_t uart_num) if(data == NULL) { break; } + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + p_uart_obj[uart_num]->rx_buffered_len -= size; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); vRingbufferReturnItem(p_uart->rx_ring_buf, data); + if(p_uart_obj[uart_num]->rx_buffer_full_flg) { + BaseType_t res = xRingbufferSend(p_uart_obj[uart_num]->rx_ring_buf, p_uart_obj[uart_num]->rx_data_buf, p_uart_obj[uart_num]->rx_stash_len, 1); + if(res == pdTRUE) { + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + p_uart_obj[uart_num]->rx_buffered_len += p_uart_obj[uart_num]->rx_stash_len; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + p_uart_obj[uart_num]->rx_buffer_full_flg = false; + } + } } p_uart->rx_ptr = NULL; p_uart->rx_cur_remain = 0; p_uart->rx_head_ptr = NULL; - esp_intr_enable(p_uart->intr_handle); - xSemaphoreGive(p_uart->rx_mux); - - if(p_uart->tx_buf_size > 0) { - xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY); - esp_intr_disable(p_uart->intr_handle); - UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); - UART[uart_num]->int_ena.txfifo_empty = 0; - UART[uart_num]->int_clr.txfifo_empty = 1; - UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - do { - data = (uint8_t*) xRingbufferReceive(p_uart->tx_ring_buf, &size, (portTickType) 0); - if(data == NULL) { - break; - } - vRingbufferReturnItem(p_uart->rx_ring_buf, data); - } while(1); - p_uart->tx_brk_flg = 0; - p_uart->tx_brk_len = 0; - p_uart->tx_head = NULL; - p_uart->tx_len_cur = 0; - p_uart->tx_len_tot = 0; - p_uart->tx_ptr = NULL; - p_uart->tx_waiting_brk = 0; - p_uart->tx_waiting_fifo = false; - esp_intr_enable(p_uart->intr_handle); - xSemaphoreGive(p_uart->tx_mux); - } uart_reset_fifo(uart_num); + uart_enable_rx_intr(p_uart_obj[uart_num]->uart_num); + xSemaphoreGive(p_uart->rx_mux); return ESP_OK; } esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); - UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error", ESP_FAIL); + UART_CHECK((rx_buffer_size > UART_FIFO_LEN), "uart rx buffer length error(>128)", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); if(p_uart_obj[uart_num] == NULL) { @@ -946,6 +974,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->tx_brk_flg = 0; p_uart_obj[uart_num]->tx_brk_len = 0; p_uart_obj[uart_num]->tx_waiting_brk = 0; + p_uart_obj[uart_num]->rx_buffered_len = 0; if(uart_queue) { p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); @@ -971,7 +1000,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b ESP_LOGE(UART_TAG, "UART driver already installed"); return ESP_FAIL; } - uart_isr_register(uart_num, uart_rx_intr_handler_default, p_uart_obj[uart_num], intr_alloc_flags); + uart_isr_register(uart_num, uart_rx_intr_handler_default, p_uart_obj[uart_num], intr_alloc_flags, &p_uart_obj[uart_num]->intr_handle); uart_intr_config_t uart_intr = { .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M | UART_RXFIFO_TOUT_INT_ENA_M diff --git a/docs/api/uart.rst b/docs/api/uart.rst index 609816fd46..fa83309e2a 100644 --- a/docs/api/uart.rst +++ b/docs/api/uart.rst @@ -94,5 +94,7 @@ Functions .. doxygenfunction:: uart_write_bytes_with_break .. doxygenfunction:: uart_read_bytes .. doxygenfunction:: uart_flush - +.. doxygenfunction:: uart_get_buffered_data_len +.. doxygenfunction:: uart_disable_pattern_det_intr +.. doxygenfunction:: uart_enable_pattern_det_intr diff --git a/examples/20_uart/Makefile b/examples/20_uart/Makefile new file mode 100644 index 0000000000..4c523bd68a --- /dev/null +++ b/examples/20_uart/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 := uart + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/20_uart/main/component.mk b/examples/20_uart/main/component.mk new file mode 100644 index 0000000000..44bd2b5273 --- /dev/null +++ b/examples/20_uart/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/20_uart/main/uart_test.c b/examples/20_uart/main/uart_test.c new file mode 100644 index 0000000000..9a0d0003c3 --- /dev/null +++ b/examples/20_uart/main/uart_test.c @@ -0,0 +1,183 @@ +/* Uart 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 "esp_system.h" +#include "nvs_flash.h" +#include "driver/uart.h" +#include "freertos/queue.h" +#include "esp_log.h" +#include "soc/uart_struct.h" +static const char *TAG = "uart_example"; + +/** + * Test code brief + * This example shows how to configure uart settings and install uart driver. + * + * uart_evt_test() is an example that read and write data on UART0, and handler some of the special events. + * - port: UART0 + * - rx buffer: on + * - tx buffer: on + * - flow control: off + * - event queue: on + * - pin assignment: txd(default), rxd(default) + * + * uart_echo_test() is an example that read and write data on UART1, with hardware flow control turning on. + * - port: UART1 + * - rx buffer: on + * - tx buffer: off + * - flow control: on + * - event queue: off + * - pin assignment: txd(io4), rxd(io5), rts(18), cts(19) + */ + +#define BUF_SIZE (1024) +#define ECHO_TEST_TXD (4) +#define ECHO_TEST_RXD (5) +#define ECHO_TEST_RTS (18) +#define ECHO_TEST_CTS (19) + +QueueHandle_t uart0_queue; +void uart_task(void *pvParameters) +{ + int uart_num = (int) pvParameters; + uart_event_t event; + size_t buffered_size; + uint8_t* dtmp = (uint8_t*) malloc(BUF_SIZE); + for(;;) { + //Waiting for UART event. + if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) { + ESP_LOGI(TAG, "uart[%d] event:", uart_num); + switch(event.type) { + //Event of UART receving data + /*We'd better handler data event fast, there would be much more data events than + other types of events. If we take too much time on data event, the queue might + be full. + in this example, we don't process data in event, but read data outside.*/ + case UART_DATA: + uart_get_buffered_data_len(uart_num, &buffered_size); + ESP_LOGI(TAG, "data, len: %d; buffered len: %d", event.size, buffered_size); + break; + //Event of HW FIFO overflow detected + case UART_FIFO_OVF: + ESP_LOGI(TAG, "hw fifo overflow\n"); + //If fifo overflow happened, you should consider adding flow control for your application. + //We can read data out out the buffer, or directly flush the rx buffer. + uart_flush(uart_num); + break; + //Event of UART ring buffer full + case UART_BUFFER_FULL: + ESP_LOGI(TAG, "ring buffer full\n"); + //If buffer full happened, you should consider encreasing your buffer size + //We can read data out out the buffer, or directly flush the rx buffer. + uart_flush(uart_num); + break; + //Event of UART RX break detected + case UART_BREAK: + ESP_LOGI(TAG, "uart rx break\n"); + break; + //Event of UART parity check error + case UART_PARITY_ERR: + ESP_LOGI(TAG, "uart parity error\n"); + break; + //Event of UART frame error + case UART_FRAME_ERR: + ESP_LOGI(TAG, "uart frame error\n"); + break; + //UART_PATTERN_DET + case UART_PATTERN_DET: + ESP_LOGI(TAG, "uart pattern detected\n"); + break; + //Others + default: + ESP_LOGI(TAG, "uart event type: %d\n", event.type); + break; + } + } + } + free(dtmp); + dtmp = NULL; + vTaskDelete(NULL); +} + +void uart_evt_test() +{ + int uart_num = UART_NUM_0; + uart_config_t uart_config = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 122, + }; + //Set UART parameters + uart_param_config(uart_num, &uart_config); + //Set UART log level + esp_log_level_set(TAG, ESP_LOG_INFO); + //Install UART driver, and get the queue. + uart_driver_install(uart_num, BUF_SIZE * 2, BUF_SIZE * 2, 10, &uart0_queue, 0); + //Set UART pins,(-1: default pin, no change.) + //For UART0, we can just use the default pins. + //uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + //Set uart pattern detect function. + uart_enable_pattern_det_intr(uart_num, '+', 3, 10000, 10, 10); + //Create a task to handler UART event from ISR + xTaskCreate(uart_task, "uart_task", 2048, (void*)uart_num, 12, NULL); + //process data + uint8_t* data = (uint8_t*) malloc(BUF_SIZE); + do { + int len = uart_read_bytes(uart_num, data, BUF_SIZE, 100 / portTICK_RATE_MS); + if(len > 0) { + ESP_LOGI(TAG, "uart read : %d", len); + uart_write_bytes(uart_num, (const char*)data, len); + } + } while(1); +} + +//an example of echo test with hardware flow control on UART1 +void uart_echo_test() +{ + int uart_num = UART_NUM_1; + uart_config_t uart_config = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, + .rx_flow_ctrl_thresh = 122, + }; + //Configure UART1 parameters + uart_param_config(uart_num, &uart_config); + //Set UART1 pins(TX: IO4, RX: I05, RTS: IO18, CTS: IO19) + uart_set_pin(uart_num, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS); + //Install UART driver( We don't need an event queue here) + //In this example we don't even use a buffer for sending data. + uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0); + + uint8_t* data = (uint8_t*) malloc(BUF_SIZE); + while(1) { + //Read data from UART + int len = uart_read_bytes(uart_num, data, BUF_SIZE, 20 / portTICK_RATE_MS); + //Write data back to UART + uart_write_bytes(uart_num, (const char*) data, len); + } +} + +void app_main() +{ + //A uart read/write example without event queue; + xTaskCreate(uart_echo_test, "uart_echo_test", 1024, NULL, 10, NULL); + + //A uart example with event queue. + uart_evt_test(); +} From de8ecdd3c1c1ddbfc374d49fd645fd0543669201 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 10:31:20 +1100 Subject: [PATCH 20/59] build system: Fix Windows case when IDF_PATH contains colons (ie C:/) Closes github #166 https://github.com/espressif/esp-idf/issues/166 --- make/project.mk | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/make/project.mk b/make/project.mk index 948bfda1f7..1625bdca90 100644 --- a/make/project.mk +++ b/make/project.mk @@ -44,15 +44,16 @@ $(warning "esp-idf build system only supports GNU Make versions 3.81 or newer. Y endif endif -# make IDF_PATH an absolute path -# (works around the case where a shell character is embedded in the environment variable value.) -export IDF_PATH:=$(wildcard $(IDF_PATH)) +# make IDF_PATH a "real" absolute path +# * works around the case where a shell character is embedded in the environment variable value. +# * changes Windows-style C:/blah/ paths to MSYS/Cygwin style /c/blah +export IDF_PATH:=$(realpath $(wildcard $(IDF_PATH))) ifndef IDF_PATH $(error IDF_PATH variable is not set to a valid directory.) endif -ifneq ("$(IDF_PATH)","$(wildcard $(IDF_PATH))") +ifneq ("$(IDF_PATH)","$(realpath $(wildcard $(IDF_PATH)))") # due to the way make manages variables, this is hard to account for # # if you see this error, do the shell expansion in the shell ie @@ -60,6 +61,10 @@ ifneq ("$(IDF_PATH)","$(wildcard $(IDF_PATH))") $(error If IDF_PATH is overriden on command line, it must be an absolute path with no embedded shell special characters) endif +ifneq ("$(IDF_PATH)","$(subst :,,$(IDF_PATH))") +$(error IDF_PATH cannot contain colons. If overriding IDF_PATH on Windows, use Cygwin-style /c/dir instead of C:/dir) +endif + # disable built-in make rules, makes debugging saner MAKEFLAGS_OLD := $(MAKEFLAGS) MAKEFLAGS +=-rR From 2cffaf9cc841cbc5d2b495a326a2ba8bd3930d1b Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 22 Dec 2016 10:17:39 +0800 Subject: [PATCH 21/59] freertos: fix dual core issue This commit fixes: 1. xTaskGetCurrentTaskHandle may return wrong TCB when current task switch to a different core 2. Idle task may have problem when it terminate the task in both core --- components/freertos/tasks.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 16cce3b967..baaccb43e5 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -3496,26 +3496,27 @@ static void prvCheckTasksWaitingTermination( void ) /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called too often in the idle task. */ + taskENTER_CRITICAL(&xTaskQueueMutex); while( uxTasksDeleted > ( UBaseType_t ) 0U ) { - taskENTER_CRITICAL(&xTaskQueueMutex); + //taskENTER_CRITICAL(&xTaskQueueMutex); { xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); } - taskEXIT_CRITICAL(&xTaskQueueMutex); + //taskEXIT_CRITICAL(&xTaskQueueMutex); if( xListIsEmpty == pdFALSE ) { TCB_t *pxTCB; - taskENTER_CRITICAL(&xTaskQueueMutex); + //taskENTER_CRITICAL(&xTaskQueueMutex); { pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); --uxCurrentNumberOfTasks; --uxTasksDeleted; } - taskEXIT_CRITICAL(&xTaskQueueMutex); + //taskEXIT_CRITICAL(&xTaskQueueMutex); #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) { @@ -3535,7 +3536,8 @@ static void prvCheckTasksWaitingTermination( void ) { mtCOVERAGE_TEST_MARKER(); } - } + } + taskEXIT_CRITICAL(&xTaskQueueMutex); } #endif /* vTaskDelete */ } @@ -3806,10 +3808,12 @@ TCB_t *pxTCB; { TaskHandle_t xReturn; + vPortCPUAcquireMutex(&xTaskQueueMutex); /* A critical section is not required as this is not called from an interrupt and the current TCB will always be the same for any individual execution thread. */ xReturn = pxCurrentTCB[ xPortGetCoreID() ]; + vPortCPUReleaseMutex(&xTaskQueueMutex); return xReturn; } From 57817f7c53e4f4de41d5829f1a272f1e41e1406e Mon Sep 17 00:00:00 2001 From: Yinling Date: Thu, 24 Nov 2016 11:46:58 +0800 Subject: [PATCH 22/59] generate test result and commit to CI-test-result: 1. config git user name before commit 2. continue committing test result for failed jobs 3. update test result repository path 4. change escape key word in branch name, use '___' to escape key word '/' in report file name --- .gitlab-ci.yml | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7cb63be8c3..20970597e0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -149,11 +149,12 @@ test_build_system: test_report: stage: test_report + image: espressif/esp32-ci-env only: - master - triggers tags: - - test_report + - report variables: LOG_PATH: "$CI_PROJECT_DIR/$CI_BUILD_REF" TEST_CASE_FILE_PATH: "$CI_PROJECT_DIR/components/idf_test" @@ -165,12 +166,33 @@ test_report: - $LOG_PATH expire_in: 12 mos script: + # calc log path + - VER_NUM=`git rev-list HEAD | wc -l | awk '{print $1}'` + - SHA_ID=`echo $CI_BUILD_REF | cut -c 1-7` + - REVISION="${VER_NUM}_${SHA_ID}" + # replace / to _ in branch name + - ESCAPED_BRANCH_NAME=`echo $CI_BUILD_REF_NAME | sed 's/\//___/g'` + # result path and artifacts path + - RESULT_PATH="$CI_PROJECT_NAME/$ESCAPED_BRANCH_NAME/$REVISION" + - ARTIFACTS_PATH="$GITLAB_HTTP_SERVER/idf/esp-idf/builds/$CI_BUILD_ID/artifacts/browse/$CI_BUILD_REF" # clone test bench - git clone $GITLAB_SSH_SERVER/yinling/auto_test_script.git - cd auto_test_script # generate report - - python CITestReport.py -l $LOG_PATH -t $TEST_CASE_FILE_PATH -p $REPORT_PATH - + - python CITestReport.py -l $LOG_PATH -t $TEST_CASE_FILE_PATH -p $REPORT_PATH -r $RESULT_PATH -a $ARTIFACTS_PATH || FAIL=True + # commit to CI-test-result project + - git clone $GITLAB_SSH_SERVER/qa/CI-test-result.git + - rm -rf CI-test-result/RawData/$RESULT_PATH + - cp -R $CI_PROJECT_NAME CI-test-result/RawData + - cd CI-test-result + # config git user + - git config --global user.email "ci-test-result@espressif.com" + - git config --global user.name "ci-test-result" + # commit test result + - git add . + - git commit . -m "update test result for $CI_PROJECT_NAME/$CI_BUILD_REF_NAME/$CI_BUILD_REF, pipeline ID $CI_PIPELINE_ID" || exit 0 + - git push origin master + - test "${FAIL}" = "True" && exit 1 push_master_to_github: before_script: From 3a2fbda35c7bb0d1cbc99a59a6db32258be549f1 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 22 Dec 2016 10:51:40 +0800 Subject: [PATCH 23/59] freertos: minor change according to review comments --- components/freertos/tasks.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index baaccb43e5..f2bdf8ccb0 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -3499,24 +3499,20 @@ static void prvCheckTasksWaitingTermination( void ) taskENTER_CRITICAL(&xTaskQueueMutex); while( uxTasksDeleted > ( UBaseType_t ) 0U ) { - //taskENTER_CRITICAL(&xTaskQueueMutex); { xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); } - //taskEXIT_CRITICAL(&xTaskQueueMutex); if( xListIsEmpty == pdFALSE ) { TCB_t *pxTCB; - //taskENTER_CRITICAL(&xTaskQueueMutex); { pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); ( void ) uxListRemove( &( pxTCB->xGenericListItem ) ); --uxCurrentNumberOfTasks; --uxTasksDeleted; } - //taskEXIT_CRITICAL(&xTaskQueueMutex); #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) && ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS ) { @@ -3809,9 +3805,6 @@ TCB_t *pxTCB; TaskHandle_t xReturn; vPortCPUAcquireMutex(&xTaskQueueMutex); - /* A critical section is not required as this is not called from - an interrupt and the current TCB will always be the same for any - individual execution thread. */ xReturn = pxCurrentTCB[ xPortGetCoreID() ]; vPortCPUReleaseMutex(&xTaskQueueMutex); From 2f9772860a1ad0747b509eb41c48040fd16adaee Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 12:18:15 +1100 Subject: [PATCH 24/59] Examples: Add READMEs for examples which did not have them Closes github #128 https://github.com/espressif/esp-idf/issues/128 --- examples/11_rmt_nec_tx_rx/README.md | 6 ++++++ examples/13_timer_group/README.md | 3 +++ examples/16_pcnt/README.md | 14 ++++++++++++++ examples/16_pcnt/main/pcnt_test.c | 24 ++++++++++++------------ examples/19_sigmadelta/README.md | 7 +++++++ 5 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 examples/11_rmt_nec_tx_rx/README.md create mode 100644 examples/13_timer_group/README.md create mode 100644 examples/16_pcnt/README.md create mode 100644 examples/19_sigmadelta/README.md diff --git a/examples/11_rmt_nec_tx_rx/README.md b/examples/11_rmt_nec_tx_rx/README.md new file mode 100644 index 0000000000..d578600d01 --- /dev/null +++ b/examples/11_rmt_nec_tx_rx/README.md @@ -0,0 +1,6 @@ +# Example: rmt_nec_tx_rx + +This example uses the remote control (RMT) peripheral to transmit and receive codes for the NEC infrared remote protocol. + +Configuration (pin numbers, etc.) can be modified in top of the main/infrared_nec.c file. + diff --git a/examples/13_timer_group/README.md b/examples/13_timer_group/README.md new file mode 100644 index 0000000000..0be2c847a8 --- /dev/null +++ b/examples/13_timer_group/README.md @@ -0,0 +1,3 @@ +# Example: timer_group + +This example uses the timer group driver to generate timer interrupts at two specified alarm intervals. diff --git a/examples/16_pcnt/README.md b/examples/16_pcnt/README.md new file mode 100644 index 0000000000..a7019ea9e7 --- /dev/null +++ b/examples/16_pcnt/README.md @@ -0,0 +1,14 @@ +# Example: pcnt + +This example uses the pulse counter module (PCNT) to count the rising edges of pulses generated by the LED Controller module (LEDC). + +By default GPIO18 is used as output pin, GPIO4 is used as pulse input pin and GPIO5 is used as control input pin. This configuration (pin numbers, etc.) can be modified in top of the main/pcnt_test.c file. + +* Open serial port to view the message printed on your screen +* To do this test, you should connect GPIO18 with GPIO4 +* GPIO5 is the control signal, you can leave it floating with internal pulled up, or connect it to ground. HIGH = Count increases, LOW = count decreases. +* An interrupt is configured to trigger when the count reaches threshold values. +* The counter will reset when it reaches the limit values. + + + diff --git a/examples/16_pcnt/main/pcnt_test.c b/examples/16_pcnt/main/pcnt_test.c index b8489ecb2f..6089c8edd6 100644 --- a/examples/16_pcnt/main/pcnt_test.c +++ b/examples/16_pcnt/main/pcnt_test.c @@ -36,14 +36,14 @@ * When counter value reaches thresh1 or thresh0 value, it will trigger interrupt. * When counter value reaches l_lim value or h_lim value, counter value will be reset to zero and trigger interrupt. */ -#define PCNT_TEST_UNIT PCNT_UNIT_0 -#define PCNT_H_LIM_VAL (10) -#define PCNT_L_LIM_VAL (-10) -#define PCNT_THRESH1_VAL (5) -#define PCNT_THRESH0_VAL (-5) -#define PCNT_INPUT_SIG_IO (4) -#define PCNT_INPUT_CTRL_IO (5) -#define LEDC_OUPUT_IO (18) +#define PCNT_TEST_UNIT PCNT_UNIT_0 +#define PCNT_H_LIM_VAL 10 +#define PCNT_L_LIM_VAL -10 +#define PCNT_THRESH1_VAL 5 +#define PCNT_THRESH0_VAL -5 +#define PCNT_INPUT_SIG_IO 4 /* Pulse Input GPIO */ +#define PCNT_INPUT_CTRL_IO 5 /* Control GPIO HIGH=count up, LOW=count down */ +#define LEDC_OUTPUT_IO 18 /* Output GPIO */ xQueueHandle pcnt_evt_queue; /*A queue to handle pulse counter event*/ @@ -96,8 +96,8 @@ void IRAM_ATTR pcnt_intr_handler(void* arg) static void ledc_init(void) { ledc_channel_config_t ledc_channel; - /*use GPIO18 as output pin*/ - ledc_channel.gpio_num = LEDC_OUPUT_IO; + /*use LEDC_OUTPUT_IO as output pin*/ + ledc_channel.gpio_num = LEDC_OUTPUT_IO; /*LEDC high speed mode */ ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; /*use LEDC channel 1*/ @@ -125,9 +125,9 @@ static void ledc_init(void) static void pcnt_init(void) { pcnt_config_t pcnt_config = { - /*Set GPIO4 as pulse input gpio */ + /*Set PCNT_INPUT_SIG_IO as pulse input gpio */ .pulse_gpio_num = PCNT_INPUT_SIG_IO, - /*set gpio5 as control gpio */ + /*set PCNT_INPUT_CTRL_IO as control gpio */ .ctrl_gpio_num = PCNT_INPUT_CTRL_IO, /*Choose channel 0 */ .channel = PCNT_CHANNEL_0, diff --git a/examples/19_sigmadelta/README.md b/examples/19_sigmadelta/README.md new file mode 100644 index 0000000000..e8cac2c87b --- /dev/null +++ b/examples/19_sigmadelta/README.md @@ -0,0 +1,7 @@ +# Example: sigma_delta modulation + +This example uses the sigma_delta output modulation driver to generate modulated output on a GPIO. + +By default the GPIO output is 4, however you can edit this in the `sigmadelta_init()` function inside `main/sigmadelta_test.c`. + +If you connect an LED to the output GPIO, you will see it blinking slowly. From ab5915ff8b8cd9765b7814fe8f3acbe5912e03e5 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 12:28:08 +1100 Subject: [PATCH 25/59] spi_flash: Standardise argument types & names used for flash offsets Closes github #88: https://github.com/espressif/esp-idf/issues/88 --- components/spi_flash/flash_mmap.c | 2 +- components/spi_flash/include/esp_spi_flash.h | 26 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index 2165a784d1..15f75f3634 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -77,7 +77,7 @@ static void IRAM_ATTR spi_flash_mmap_init() } } -esp_err_t IRAM_ATTR spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, +esp_err_t IRAM_ATTR spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory, const void** out_ptr, spi_flash_mmap_handle_t* out_handle) { esp_err_t ret; diff --git a/components/spi_flash/include/esp_spi_flash.h b/components/spi_flash/include/esp_spi_flash.h index 91675088ae..bb3ec39b45 100644 --- a/components/spi_flash/include/esp_spi_flash.h +++ b/components/spi_flash/include/esp_spi_flash.h @@ -78,13 +78,13 @@ esp_err_t spi_flash_erase_range(size_t start_address, size_t size); * @note If source address is in DROM, this function will return * ESP_ERR_INVALID_ARG. * - * @param dest destination address in Flash. Must be a multiple of 4 bytes. - * @param src pointer to the source buffer. - * @param size length of data, in bytes. Must be a multiple of 4 bytes. + * @param dest_addr destination address in Flash. Must be a multiple of 4 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 4 bytes. * * @return esp_err_t */ -esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); +esp_err_t spi_flash_write(size_t dest_addr, const void *src, size_t size); /** @@ -97,24 +97,24 @@ esp_err_t spi_flash_write(size_t dest, const void *src, size_t size); * @note If source address is in DROM, this function will return * ESP_ERR_INVALID_ARG. * - * @param dest destination address in Flash. Must be a multiple of 32 bytes. - * @param src pointer to the source buffer. - * @param size length of data, in bytes. Must be a multiple of 32 bytes. + * @param dest_addr destination address in Flash. Must be a multiple of 32 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 32 bytes. * * @return esp_err_t */ -esp_err_t spi_flash_write_encrypted(size_t dest, const void *src, size_t size); +esp_err_t spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size); /** * @brief Read data from Flash. * - * @param src source address of the data in Flash. - * @param dest pointer to the destination buffer - * @param size length of data + * @param src_addr source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data * * @return esp_err_t */ -esp_err_t spi_flash_read(size_t src, void *dest, size_t size); +esp_err_t spi_flash_read(size_t src_addr, void *dest, size_t size); /** * @brief Enumeration which specifies memory space requested in an mmap call @@ -149,7 +149,7 @@ typedef uint32_t spi_flash_mmap_handle_t; * * @return ESP_OK on success, ESP_ERR_NO_MEM if pages can not be allocated */ -esp_err_t spi_flash_mmap(uint32_t src_addr, size_t size, spi_flash_mmap_memory_t memory, +esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory, const void** out_ptr, spi_flash_mmap_handle_t* out_handle); /** From 99f4c697ee16d69f1f02ce6e14e64a4e35fc2651 Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Thu, 22 Dec 2016 13:37:07 +0800 Subject: [PATCH 26/59] freertos: enable dual core by default --- components/freertos/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index f03da6bc01..b9db00e50b 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -3,7 +3,7 @@ menu "FreeRTOS" # This is actually also handled in the ESP32 startup code, not only in FreeRTOS. config FREERTOS_UNICORE bool "Run FreeRTOS only on first core" - default y + default n help This version of FreeRTOS normally takes control of all cores of the CPU. Select this if you only want to start it on the first core. From abb7668af7affbb0838d95e30789ae4f3657e420 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 16:32:19 +1100 Subject: [PATCH 27/59] build system: Fix bug where erase_flash was always invoked for flash Order-only prerequisites do not work for phony targets! --- components/bootloader/Makefile.projbuild | 2 +- components/esptool_py/Makefile.projbuild | 7 ++++--- make/common.mk | 14 ++++++++++++++ make/project_config.mk | 2 +- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 0af467de47..35be94e4a3 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -43,7 +43,7 @@ bootloader: $(BOOTLOADER_BIN) ESPTOOL_ALL_FLASH_ARGS += $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN) -bootloader-flash: $(BOOTLOADER_BIN) | erase_flash +bootloader-flash: $(BOOTLOADER_BIN) $(call prereq_if_explicit,erase_flash) $(ESPTOOLPY_WRITE_FLASH) 0x1000 $^ else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index d00158c7f9..b5992a4d96 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -43,14 +43,14 @@ APP_BIN_UNSIGNED ?= $(APP_BIN) $(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< -flash: all_binaries $(ESPTOOLPY_SRC) | erase_flash +flash: all_binaries $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." ifdef CONFIG_SECURE_BOOT_ENABLED @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" endif $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) -app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) | erase_flash +app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) @echo "Flashing app to serial port $(ESPPORT), offset $(CONFIG_APP_OFFSET)..." $(ESPTOOLPY_WRITE_FLASH) $(CONFIG_APP_OFFSET) $(APP_BIN) @@ -58,7 +58,8 @@ app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) | erase_flash # at the project level as long as qualified path COMPONENT_SUBMODULES += $(COMPONENT_PATH)/esptool -.PHONY: erase_flash erase_flash: @echo "Erasing entire flash..." $(ESPTOOLPY_SERIAL) erase_flash + +.PHONY: erase_flash diff --git a/make/common.mk b/make/common.mk index c0487d2737..605b8ab862 100644 --- a/make/common.mk +++ b/make/common.mk @@ -54,3 +54,17 @@ endef define resolvepath $(foreach dir,$(1),$(if $(filter /%,$(dir)),$(dir),$(subst //,/,$(2)/$(dir)))) endef + + +# macro to include a target only if it's on the list of targets that make +# was invoked with +# +# This allows you to have something like an "order-only phony prerequisite", +# ie a prerequisite that determines an order phony targets have to run in. +# +# Because normal order-only prerequisites don't work with phony targets. +# +# example $(call prereq_if_explicit,erase_flash) +define prereq_if_explicit +$(filter $(1),$(MAKECMDGOALS)) +endef diff --git a/make/project_config.mk b/make/project_config.mk index a32c74a380..187d1ac282 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -26,7 +26,7 @@ menuconfig: $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig $(KCONFIG_TOOL_ENV) $(KCONFIG_TOOL_DIR)/mconf $(IDF_PATH)/Kconfig ifeq ("$(wildcard $(SDKCONFIG))","") -ifeq ("$(filter defconfig,$(MAKECMDGOALS))","") +ifeq ("$(call prereq_if_explicit,defconfig)","") # if not configuration is present and defconfig is not a target, run makeconfig $(SDKCONFIG): menuconfig else From 794f7dd2940b200bcaa052a78b1f0a4adbf38f62 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Thu, 22 Dec 2016 12:08:15 +0800 Subject: [PATCH 28/59] bugfix: uart event mismatch 1. Fix bug of uart frame error and parity error interrupt mismatch in driver code, which will cause the corresponding interrupt can not be cleared correctly, and will finally cause a interrupt watch dog. 2. Add gpio pull-up for rx pin and cts pin. --- components/driver/uart.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/driver/uart.c b/components/driver/uart.c index e85c54d8c4..b9ebc4b5f0 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -387,6 +387,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r if(rx_io_num >= 0) { PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[rx_io_num], PIN_FUNC_GPIO); + gpio_set_pull_mode(rx_io_num, GPIO_PULLUP_ONLY); gpio_set_direction(rx_io_num, GPIO_MODE_INPUT); gpio_matrix_in(rx_io_num, rx_sig, 0); } @@ -397,6 +398,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r } if(cts_io_num >= 0) { PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[cts_io_num], PIN_FUNC_GPIO); + gpio_set_pull_mode(cts_io_num, GPIO_PULLUP_ONLY); gpio_set_direction(cts_io_num, GPIO_MODE_INPUT); gpio_matrix_in(cts_io_num, cts_sig, 0); } @@ -639,10 +641,10 @@ static void IRAM_ATTR uart_rx_intr_handler_default(void *param) uart_reg->int_clr.brk_det = 1; uart_event.type = UART_BREAK; } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) { - uart_reg->int_clr.parity_err = 1; + uart_reg->int_clr.frm_err = 1; uart_event.type = UART_FRAME_ERR; } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M) { - uart_reg->int_clr.frm_err = 1; + uart_reg->int_clr.parity_err = 1; uart_event.type = UART_PARITY_ERR; } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) { UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]); From e8b194d5e55ed5df252446ab5682f8921022db01 Mon Sep 17 00:00:00 2001 From: Malte Janduda Date: Thu, 22 Sep 2016 01:50:53 +0200 Subject: [PATCH 29/59] provide list of packages for homebrew --- docs/macos-setup.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/macos-setup.rst b/docs/macos-setup.rst index 53c6fe54c8..67a1fa9906 100644 --- a/docs/macos-setup.rst +++ b/docs/macos-setup.rst @@ -66,7 +66,9 @@ In any case, here are the steps to compile the toolchain yourself. sudo port install gsed gawk binutils gperf grep gettext ncurses - - with homebrew (*TODO: provide list of packages for homebrew*) + - with homebrew + + brew install gnu-sed gawk binutils gperf grep gettext ncurses Create a case-sensitive filesystem image:: From a1915f7f8744ba25e9778b5820163c7d1881447c Mon Sep 17 00:00:00 2001 From: Yinling Date: Fri, 23 Dec 2016 15:16:29 +0800 Subject: [PATCH 30/59] use gitlab repo for bt lib on non master barnch same as wifi lib, some bt lib might not be pushed to github yet for developping branches replace gitlab server address with variable --- .gitlab-ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7cb63be8c3..065b0512a6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,9 +14,10 @@ before_script: - chmod 600 ~/.ssh/id_rsa - echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - # if testing master branch, use github wifi libs. - # if testing other branches, use gitlab wifi libs (as maybe changes aren't merged to master yet) - - test "${CI_BUILD_REF_NAME}" = "master" || sed -i "s%https://github.com/espressif/esp32-wifi-lib%ssh://git@gitlab.espressif.cn:27227/idf/esp32-wifi-lib%" .gitmodules + # if testing master branch, use github wifi and bt libs. + # if testing other branches, use gitlab wifi and bt libs (as maybe changes aren't merged to master yet) + - test "${CI_BUILD_REF_NAME}" = "master" || sed -i "s%https://github.com/espressif/esp32-wifi-lib%${GITLAB_SSH_SERVER}/idf/esp32-wifi-lib%" .gitmodules + - test "${CI_BUILD_REF_NAME}" = "master" || sed -i "s%https://github.com/espressif/esp32-bt-lib%${GITLAB_SSH_SERVER}/idf/esp32-bt-lib%" .gitmodules # fetch all submodules - git submodule update --init --recursive From 5ddf6daa98585af35ffa926d5e939bf237aff88f Mon Sep 17 00:00:00 2001 From: shangke Date: Sun, 18 Dec 2016 21:18:37 +0800 Subject: [PATCH 31/59] feature/ethernet_driver: update ethernet driver 1. The transmitting mode of the packets from LWIP to MAC is changed from synchronous to asynchronous. 2. The receive buf mode : support pointer mode and copy mode. 3. Add get phy status func used to config mac register. --- components/ethernet/Kconfig | 20 +- components/ethernet/emac_common.h | 48 ++- components/ethernet/emac_dev.c | 27 +- components/ethernet/emac_dev.h | 46 ++- components/ethernet/emac_main.c | 379 +++++++++++++++------- components/ethernet/include/esp_eth.h | 41 ++- components/lwip/core/pbuf.c | 12 +- components/lwip/include/lwip/lwip/pbuf.h | 9 +- components/lwip/port/netif/ethernetif.c | 32 +- components/lwip/port/netif/wlanif.c | 3 +- examples/14_ethernet/main/tlk110_phy.h | 28 ++ examples/17_ethernet/main/ethernet_main.c | 59 +++- 12 files changed, 501 insertions(+), 203 deletions(-) create mode 100644 examples/14_ethernet/main/tlk110_phy.h diff --git a/components/ethernet/Kconfig b/components/ethernet/Kconfig index cbd3bfbe47..9eb897cbba 100644 --- a/components/ethernet/Kconfig +++ b/components/ethernet/Kconfig @@ -3,18 +3,32 @@ menuconfig ETHERNET default n help Enable this option to enable ethernet driver and show the menu with ethernet features. - config DMA_RX_BUF_NUM - int "Dma Rx Buf Num" + int "DMA Rx Buf Num" default 10 depends on ETHERNET help Dma rx buf num ,can not be 0 . config DMA_TX_BUF_NUM - int "Dma Tx Buf Num" + int "DMA Tx Buf Num" default 10 depends on ETHERNET help Dma tx Buf num ,can not be 0. + +config EMAC_L2_TO_L3_RX_BUF_MODE + bool "L2 To L3 RX BUF COPY MODE" + default n + depends on ETHERNET + help + Receive Buf user copy mode or pointer mode. + +config EMAC_TASK_PRIORITY + int "EMAC_TASK_PRIORITY" + default 20 + depends on ETHERNET + help + Emac task priority ,suggest 3 ~ 23. + diff --git a/components/ethernet/emac_common.h b/components/ethernet/emac_common.h index 48f71fd90f..774c287ad5 100644 --- a/components/ethernet/emac_common.h +++ b/components/ethernet/emac_common.h @@ -19,6 +19,7 @@ #include "esp_err.h" #include "emac_dev.h" +#include "esp_eth.h" #ifdef __cplusplus extern "C" { @@ -32,11 +33,6 @@ typedef struct { emac_par_t par; } emac_event_t; -enum emac_mode { - EMAC_MODE_RMII = 0, - EMAC_MDOE_MII, -}; - enum emac_runtime_status { EMAC_RUNTIME_NOT_INIT = 0, EMAC_RUNTIME_INIT, @@ -45,36 +41,37 @@ enum emac_runtime_status { }; enum { + SIG_EMAC_RX_UNAVAIL, SIG_EMAC_TX_DONE, SIG_EMAC_RX_DONE, - SIG_EMAC_TX, SIG_EMAC_START, SIG_EMAC_STOP, + SIG_EMAC_CHECK_LINK, SIG_EMAC_MAX }; -typedef void (*emac_phy_fun)(void); -typedef esp_err_t (*emac_tcpip_input_fun)(void *buffer, uint16_t len, void *eb); -typedef void (*emac_gpio_config_func)(void); - struct emac_config_data { - unsigned int phy_addr; - enum emac_mode mac_mode; + eth_phy_base_t phy_addr; + eth_mode_t mac_mode; struct dma_extended_desc *dma_etx; - unsigned int cur_tx; - unsigned int dirty_tx; - signed int cnt_tx; + uint32_t cur_tx; + uint32_t dirty_tx; + int32_t cnt_tx; struct dma_extended_desc *dma_erx; - unsigned int cur_rx; - unsigned int dirty_rx; - signed int cnt_rx; - unsigned int rx_need_poll; + uint32_t cur_rx; + uint32_t dirty_rx; + int32_t cnt_rx; + uint32_t rx_need_poll; bool phy_link_up; enum emac_runtime_status emac_status; uint8_t macaddr[6]; - emac_phy_fun phy_init; - emac_tcpip_input_fun emac_tcpip_input; - emac_gpio_config_func emac_gpio_config; + eth_phy_func phy_init; + eth_tcpip_input_func emac_tcpip_input; + eth_gpio_config_func emac_gpio_config; + eth_phy_check_link_func emac_phy_check_link; + eth_phy_check_init_func emac_phy_check_init; + eth_phy_get_speed_mode_func emac_phy_get_speed_mode; + eth_phy_get_duplex_mode_func emac_phy_get_duplex_mode; }; enum emac_post_type { @@ -103,18 +100,15 @@ struct emac_close_cmd { #if CONFIG_ETHERNET #define DMA_RX_BUF_NUM CONFIG_DMA_RX_BUF_NUM #define DMA_TX_BUF_NUM CONFIG_DMA_TX_BUF_NUM +#define EMAC_TASK_PRIORITY CONFIG_EMAC_TASK_PRIORITY #else #define DMA_RX_BUF_NUM 1 #define DMA_TX_BUF_NUM 1 +#define EMAC_TASK_PRIORITY 10 #endif #define DMA_RX_BUF_SIZE 1600 #define DMA_TX_BUF_SIZE 1600 -//lwip err -#define ERR_OK 0 -#define ERR_MEM -1 -#define ERR_IF -16 - #define EMAC_CMD_OK 0 #define EMAC_CMD_FAIL -1 diff --git a/components/ethernet/emac_dev.c b/components/ethernet/emac_dev.c index a8095fecba..244da08432 100644 --- a/components/ethernet/emac_dev.c +++ b/components/ethernet/emac_dev.c @@ -34,18 +34,6 @@ static const char *TAG = "emac"; -void emac_poll_tx_cmd(void) -{ - //write any to wake up dma - REG_WRITE(EMAC_DMATXPOLLDEMAND_REG, 1); -} - -void emac_poll_rx_cmd(void) -{ - //write any to wake up dma - REG_WRITE(EMAC_DMARXPOLLDEMAND_REG, 1); -} - void emac_enable_dma_tx(void) { REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_START_STOP_TRANSMISSION_COMMAND); @@ -66,15 +54,6 @@ void emac_disable_dma_rx(void) REG_CLR_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_START_STOP_RECEIVE); } -uint32_t emac_read_tx_cur_reg(void) -{ - return REG_READ(EMAC_DMATXCURRDESC_REG); -} - -uint32_t emac_read_rx_cur_reg(void) -{ - return REG_READ(EMAC_DMARXCURRDESC_REG); -} uint32_t emac_read_mac_version(void) { @@ -121,16 +100,18 @@ void emac_dma_init(void) REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_FORWARD_UNDERSIZED_GOOD_FRAMES); REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG, EMAC_OPERATE_SECOND_FRAME); REG_SET_FIELD(EMAC_DMABUSMODE_REG, EMAC_PROG_BURST_LEN, 4); + REG_SET_BIT(EMAC_DMAOPERATION_MODE_REG,EMAC_DMAOPERATION_MODE_REG); } void emac_mac_init(void) { - REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACRX); - REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACTX); REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX); REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACMIIGMII); REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); REG_SET_BIT(EMAC_GMACFRAMEFILTER_REG, EMAC_PROMISCUOUS_MODE); + + REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACRX); + REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACTX); } void emac_set_clk_rmii(void) diff --git a/components/ethernet/emac_dev.h b/components/ethernet/emac_dev.h index ede15271eb..bc04d8d9e2 100644 --- a/components/ethernet/emac_dev.h +++ b/components/ethernet/emac_dev.h @@ -49,14 +49,52 @@ uint32_t emac_read_mac_version(void); void emac_dma_init(void); void emac_mac_init(void); void emac_enable_dma_tx(void); -void emac_poll_tx_cmd(void); -uint32_t emac_read_tx_cur_reg(void); void emac_enable_dma_rx(void); -uint32_t emac_read_rx_cur_reg(void); -void emac_poll_rx_cmd(void); void emac_disable_dma_tx(void); void emac_disable_dma_rx(void); +uint32_t inline emac_read_tx_cur_reg(void) +{ + return REG_READ(EMAC_DMATXCURRDESC_REG); +} + +uint32_t inline emac_read_rx_cur_reg(void) +{ + return REG_READ(EMAC_DMARXCURRDESC_REG); +} + +void inline emac_poll_tx_cmd(void) +{ + //write any to wake up dma + REG_WRITE(EMAC_DMATXPOLLDEMAND_REG, 1); +} + +void inline emac_poll_rx_cmd(void) +{ + //write any to wake up dma + REG_WRITE(EMAC_DMARXPOLLDEMAND_REG, 1); +} + +void inline emac_disable_rx_intr(void) +{ + REG_CLR_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_INTERRUPT_ENABLE); +} + +void inline emac_enable_rx_intr(void) +{ + REG_SET_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_INTERRUPT_ENABLE); +} + +void inline emac_disable_rx_unavail_intr(void) +{ + REG_CLR_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_BUFFER_UNAVAILABLE_ENABLE); +} + +void inline emac_enable_rx_unavail_intr(void) +{ + REG_SET_BIT(EMAC_DMAINTERRUPT_EN_REG,EMAC_RECEIVE_BUFFER_UNAVAILABLE_ENABLE); +} + #ifdef __cplusplus } #endif diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index 3dc61d809c..ab2ca8964c 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -47,6 +47,8 @@ #include "freertos/semphr.h" #include "freertos/timers.h" +#include "lwip/err.h" + #define EMAC_EVT_QNUM 200 #define EMAC_SIG_MAX 50 @@ -63,7 +65,8 @@ static xTaskHandle emac_task_hdl; static xQueueHandle emac_xqueue; static uint8_t emac_sig_cnt[EMAC_SIG_MAX] = {0}; static TimerHandle_t emac_timer = NULL; - +static SemaphoreHandle_t emac_rx_xMutex = NULL; +static SemaphoreHandle_t emac_tx_xMutex = NULL; static const char *TAG = "emac"; static esp_err_t emac_ioctl(emac_sig_t sig, emac_par_t par); @@ -82,20 +85,24 @@ void esp_eth_get_mac(uint8_t mac[6]) static void emac_setup_tx_desc(struct dma_extended_desc *tx_desc , uint32_t size) { - tx_desc->basic.desc0 = EMAC_DESC_TX_OWN | EMAC_DESC_INT_COMPL | EMAC_DESC_LAST_SEGMENT | EMAC_DESC_FIRST_SEGMENT | EMAC_DESC_SECOND_ADDR_CHAIN; tx_desc->basic.desc1 = size & 0xfff; + tx_desc->basic.desc0 = EMAC_DESC_INT_COMPL | EMAC_DESC_LAST_SEGMENT | EMAC_DESC_FIRST_SEGMENT | EMAC_DESC_SECOND_ADDR_CHAIN; + tx_desc->basic.desc0 = EMAC_DESC_TX_OWN | EMAC_DESC_INT_COMPL | EMAC_DESC_LAST_SEGMENT | EMAC_DESC_FIRST_SEGMENT | EMAC_DESC_SECOND_ADDR_CHAIN; } static void emac_clean_tx_desc(struct dma_extended_desc *tx_desc) { - tx_desc->basic.desc0 = 0; tx_desc->basic.desc1 = 0; + tx_desc->basic.desc0 = 0; } -static void emac_clean_rx_desc(struct dma_extended_desc *rx_desc) +static void emac_clean_rx_desc(struct dma_extended_desc *rx_desc ,uint32_t buf_ptr) { - rx_desc->basic.desc0 = EMAC_DESC_RX_OWN; + if(buf_ptr != 0) { + rx_desc->basic.desc2 = buf_ptr; + } rx_desc->basic.desc1 = EMAC_DESC_RX_SECOND_ADDR_CHAIN | DMA_RX_BUF_SIZE; + rx_desc->basic.desc0 = EMAC_DESC_RX_OWN; } static void emac_set_tx_base_reg(void) @@ -156,18 +163,15 @@ static void emac_init_dma_chain(void) dma_phy = (uint32_t)(emac_config.dma_erx); p = emac_config.dma_erx; - for (i = 0; i < (DMA_TX_BUF_NUM - 1); i++ ) { + for (i = 0; i < (DMA_RX_BUF_NUM - 1); i++ ) { dma_phy += sizeof(struct dma_extended_desc); - emac_clean_rx_desc(p); + emac_clean_rx_desc(p, (uint32_t)(&emac_dma_rx_buf[0]) + (i * DMA_RX_BUF_SIZE)); p->basic.desc3 = dma_phy; - p->basic.desc2 = (uint32_t)(&emac_dma_rx_buf[0]) + (i * DMA_RX_BUF_SIZE); p++; } - p->basic.desc3 = (uint32_t)(emac_config.dma_erx); - p->basic.desc2 = (uint32_t)(&emac_dma_rx_buf[0]) + (i * DMA_RX_BUF_SIZE); - //init desc0 desc1 - emac_clean_rx_desc(p); + emac_clean_rx_desc(p, (uint32_t)(&emac_dma_rx_buf[0]) + (i * DMA_RX_BUF_SIZE)); + p->basic.desc3 = (uint32_t)(emac_config.dma_erx); } void esp_eth_smi_write(uint32_t reg_num, uint16_t value) @@ -207,18 +211,32 @@ static void emac_set_user_config_data(eth_config_t *config ) emac_config.phy_init = config->phy_init; emac_config.emac_tcpip_input = config->tcpip_input; emac_config.emac_gpio_config = config->gpio_config; + emac_config.emac_phy_check_link = config->phy_check_link; + emac_config.emac_phy_check_init = config->phy_check_init; + emac_config.emac_phy_get_speed_mode = config->phy_get_speed_mode; + emac_config.emac_phy_get_duplex_mode = config->phy_get_duplex_mode; +} + +static void emac_enable_intr() +{ + REG_WRITE(EMAC_DMAINTERRUPT_EN_REG, EMAC_INTR_ENABLE_BIT); +} + +static void emac_disable_intr() +{ + REG_WRITE(EMAC_DMAINTERRUPT_EN_REG, 0); } static esp_err_t emac_verify_args(void) { esp_err_t ret = ESP_OK; - if (emac_config.phy_addr > 31) { + if (emac_config.phy_addr > PHY31) { ESP_LOGE(TAG, "phy addr err"); ret = ESP_FAIL; } - if (emac_config.mac_mode != EMAC_MODE_RMII) { + if (emac_config.mac_mode != ETH_MODE_RMII) { ESP_LOGE(TAG, "mac mode err,now only support RMII"); ret = ESP_FAIL; } @@ -238,6 +256,26 @@ static esp_err_t emac_verify_args(void) ret = ESP_FAIL; } + if (emac_config.emac_phy_check_link == NULL) { + ESP_LOGE(TAG, "phy check link func is null"); + ret = ESP_FAIL; + } + + if (emac_config.emac_phy_check_init == NULL) { + ESP_LOGE(TAG, "phy check init func is null"); + ret = ESP_FAIL; + } + + if (emac_config.emac_phy_get_speed_mode == NULL) { + ESP_LOGE(TAG, "phy get speed mode func is null"); + ret = ESP_FAIL; + } + + if (emac_config.emac_phy_get_duplex_mode == NULL) { + ESP_LOGE(TAG, "phy get duplex mode func is null"); + ret = ESP_FAIL; + } + return ret; } @@ -255,7 +293,13 @@ static void emac_process_tx(void) { uint32_t cur_tx_desc = emac_read_tx_cur_reg(); - while (((uint32_t) & (emac_config.dma_etx[emac_config.dirty_tx].basic.desc0) != cur_tx_desc)) { + if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + return; + } + + xSemaphoreTakeRecursive( emac_tx_xMutex, ( TickType_t ) portMAX_DELAY ); + + while (((uint32_t) &(emac_config.dma_etx[emac_config.dirty_tx].basic.desc0) != cur_tx_desc)) { emac_clean_tx_desc(&(emac_config.dma_etx[emac_config.dirty_tx])); emac_config.dirty_tx = (emac_config.dirty_tx + 1) % DMA_TX_BUF_NUM; emac_config.cnt_tx --; @@ -263,11 +307,33 @@ static void emac_process_tx(void) if (emac_config.cnt_tx < 0) { ESP_LOGE(TAG, "emac tx chain err"); } + cur_tx_desc = emac_read_tx_cur_reg(); } + + xSemaphoreGiveRecursive( emac_tx_xMutex ); } +void esp_eth_free_rx_buf(void *buf) +{ + xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY ); + + emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.cur_rx]),(uint32_t) buf); + emac_config.cur_rx = (emac_config.cur_rx + 1) % DMA_RX_BUF_NUM; + emac_config.cnt_rx--; + if(emac_config.cnt_rx < 0) { + ESP_LOGE(TAG, "emac rx buf err!!\n"); + } + emac_poll_rx_cmd(); + + xSemaphoreGiveRecursive( emac_rx_xMutex ); +} + +#if CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE static void emac_process_rx(void) { + if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + return; + } uint32_t cur_rx_desc = emac_read_rx_cur_reg(); while (((uint32_t) & (emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc)) { @@ -275,17 +341,116 @@ static void emac_process_rx(void) emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); - emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx])); + emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]),(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; - if (emac_config.rx_need_poll != 0) { - emac_poll_rx_cmd(); - emac_config.rx_need_poll = 0; - } + //if open this ,one intr can do many intrs ? - //cur_rx_desc = emac_read_rx_cur_reg(); + cur_rx_desc = emac_read_rx_cur_reg(); } + + emac_enable_rx_intr(); } +static void emac_process_rx_unavail(void) +{ + if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + return; + } + + uint32_t dirty_cnt = 0; + while (dirty_cnt < DMA_RX_BUF_NUM) { + + if(emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 == EMAC_DESC_RX_OWN) { + break; + } + + dirty_cnt ++; + //copy data to lwip + emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), + (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + + emac_clean_rx_desc(&(emac_config.dma_erx[emac_config.dirty_rx]),(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2)); + emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; + } + emac_enable_rx_intr(); + emac_enable_rx_unavail_intr(); + emac_poll_rx_cmd(); +} + +#else +static void emac_process_rx_unavail(void) +{ + if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + return; + } + + xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY ); + + while (emac_config.cnt_rx < DMA_RX_BUF_NUM) { + + //copy data to lwip + emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), + (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + emac_config.cnt_rx++; + if(emac_config.cnt_rx > DMA_RX_BUF_NUM) { + ESP_LOGE(TAG, "emac rx unavail buf err !!\n"); + } + emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; + } + emac_enable_rx_intr(); + emac_enable_rx_unavail_intr(); + xSemaphoreGiveRecursive( emac_rx_xMutex ); +} + +static void emac_process_rx(void) +{ + if(emac_config.emac_status == EMAC_RUNTIME_STOP) { + return; + } + + uint32_t cur_rx_desc = emac_read_rx_cur_reg(); + + xSemaphoreTakeRecursive( emac_rx_xMutex, ( TickType_t ) portMAX_DELAY ); + + if(((uint32_t) &(emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc)) { + + while (((uint32_t) &(emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) != cur_rx_desc) && emac_config.cnt_rx < DMA_RX_BUF_NUM ) { + //copy data to lwip + emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), + (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + + emac_config.cnt_rx++; + + if(emac_config.cnt_rx > DMA_RX_BUF_NUM ) { + ESP_LOGE(TAG, "emac rx buf err!!\n"); + } + emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; + + cur_rx_desc = emac_read_rx_cur_reg(); + } + } else { + if(emac_config.cnt_rx < DMA_RX_BUF_NUM) { + if((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0 & EMAC_DESC_RX_OWN) == 0) { + while (emac_config.cnt_rx < DMA_RX_BUF_NUM) { + + //copy data to lwip + emac_config.emac_tcpip_input((void *)(emac_config.dma_erx[emac_config.dirty_rx].basic.desc2), + (((emac_config.dma_erx[emac_config.dirty_rx].basic.desc0) >> EMAC_DESC_FRAME_LENGTH_S) & EMAC_DESC_FRAME_LENGTH) , NULL); + emac_config.cnt_rx++; + if(emac_config.cnt_rx > DMA_RX_BUF_NUM) { + ESP_LOGE(TAG,"emac rx buf err!!!\n"); + } + + emac_config.dirty_rx = (emac_config.dirty_rx + 1) % DMA_RX_BUF_NUM; + } + } + } + } + emac_enable_rx_intr(); + xSemaphoreGiveRecursive( emac_rx_xMutex ); +} +#endif + //TODO other events need to do something static void IRAM_ATTR emac_process_intr(void *arg) { @@ -295,35 +460,37 @@ static void IRAM_ATTR emac_process_intr(void *arg) //clr intrs REG_WRITE(EMAC_DMASTATUS_REG, event); - if (event & EMAC_RECV_BUF_UNAVAIL) { - emac_config.rx_need_poll = 1; - } else if (event & EMAC_TRANS_INT) { - emac_post(SIG_EMAC_TX_DONE, 0); - } else if (event & EMAC_RECV_INT) { + if (event & EMAC_RECV_INT) { + emac_disable_rx_intr(); emac_post(SIG_EMAC_RX_DONE, 0); - } else { - //other events + } + + if (event & EMAC_RECV_BUF_UNAVAIL) { + emac_disable_rx_unavail_intr(); + emac_post(SIG_EMAC_RX_UNAVAIL,0); + } + + if (event & EMAC_TRANS_INT) { + emac_post(SIG_EMAC_TX_DONE, 0); } } -//ToDo: this should only be called once because this allocates the interrupt as well. -static void emac_enable_intr() +static void emac_check_phy_init(void) { - //init emac intr - esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL); - REG_WRITE(EMAC_DMAINTERRUPT_EN_REG, EMAC_INTR_ENABLE_BIT); -} + emac_config.emac_phy_check_init(); + if(emac_config.emac_phy_get_duplex_mode() == ETH_MDOE_FULLDUPLEX) { + REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX); + } else { + REG_CLR_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACDUPLEX); + } + if(emac_config.emac_phy_get_speed_mode() == ETH_SPEED_MODE_100M) { + REG_SET_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); + } else { + REG_CLR_BIT(EMAC_GMACCONFIG_REG, EMAC_GMACFESPEED); + } -static void emac_disable_intr() -{ - REG_WRITE(EMAC_DMAINTERRUPT_EN_REG, 0); + emac_mac_init(); } - -static bool emac_check_phy_link_status(void) -{ - return ((esp_eth_smi_read(1) & 0x4) == 0x4 ); -} - static void emac_process_link_updown(bool link_status) { system_event_t evt; @@ -331,10 +498,10 @@ static void emac_process_link_updown(bool link_status) emac_config.phy_link_up = link_status; if (link_status == true) { + emac_check_phy_init(); ESP_LOGI(TAG, "eth link_up!!!"); emac_enable_dma_tx(); emac_enable_dma_rx(); - ets_delay_us(200000); evt.event_id = SYSTEM_EVENT_ETH_CONNECTED; } else { ESP_LOGI(TAG, "eth link_down!!!"); @@ -356,26 +523,20 @@ static void emac_hw_init(void) //ipc TODO } -static esp_err_t emac_xmit(void *param) +esp_err_t esp_eth_tx(uint8_t *buf, uint16_t size) { - struct emac_post_cmd *post_cmd = (struct emac_post_cmd *)param; - struct emac_tx_cmd *cmd = (struct emac_tx_cmd *)(post_cmd->cmd); esp_err_t ret = ESP_OK; - void *buf = cmd->buf; - uint16_t size = cmd->size; - if (emac_config.emac_status != EMAC_RUNTIME_START || emac_config.emac_status == EMAC_RUNTIME_NOT_INIT) { ESP_LOGI(TAG, "tx netif close"); - cmd->err = ERR_IF; - ret = ESP_FAIL; + ret = ERR_IF; goto _exit; } - if (emac_config.cnt_tx == DMA_TX_BUF_NUM) { - ESP_LOGI(TAG, "tx buf full"); - cmd->err = ERR_MEM; - ret = ESP_FAIL; + xSemaphoreTakeRecursive( emac_tx_xMutex, ( TickType_t ) portMAX_DELAY ); + if (emac_config.cnt_tx == DMA_TX_BUF_NUM -1) { + ESP_LOGD(TAG, "tx buf full"); + ret = ERR_MEM; goto _exit; } @@ -390,26 +551,23 @@ static esp_err_t emac_xmit(void *param) _exit: - if (post_cmd->post_type == EMAC_POST_SYNC) { - xSemaphoreGive(emac_g_sem); - } - + xSemaphoreGiveRecursive( emac_tx_xMutex ); return ret; } static void emac_init_default_data(void) { - emac_config.rx_need_poll = 0; + memset((uint8_t *)&emac_config, 0,sizeof(struct emac_config_data)); } -void emac_link_check_func(void *pv_parameters) +void emac_process_link_check(void) { if (emac_config.emac_status != EMAC_RUNTIME_START || emac_config.emac_status == EMAC_RUNTIME_NOT_INIT) { return; } - if (emac_check_phy_link_status() == true ) { + if (emac_config.emac_phy_check_link() == true ) { if (emac_config.phy_link_up == false) { emac_process_link_updown(true); } @@ -420,9 +578,14 @@ void emac_link_check_func(void *pv_parameters) } } +void emac_link_check_func(void *pv_parameters) +{ + emac_post(SIG_EMAC_CHECK_LINK,0); +} + static bool emac_link_check_timer_init(void) { - emac_timer = xTimerCreate("emac_timer", (1000 / portTICK_RATE_MS), + emac_timer = xTimerCreate("emac_timer", (2000 / portTICK_RATE_MS), pdTRUE, (void *)rand(), emac_link_check_func); if (emac_timer == NULL) { return false; @@ -473,18 +636,11 @@ static void emac_start(void *param) emac_set_tx_base_reg(); emac_set_rx_base_reg(); - emac_mac_init(); emac_config.phy_init(); - //for test - //emac_wait_linkup(); - - //mmc not support - //ptp TODO - //enable emac intr emac_enable_intr(); emac_config.emac_status = EMAC_RUNTIME_START; @@ -573,7 +729,7 @@ esp_err_t esp_eth_disable(void) return close_cmd.err; } - if (emac_config.emac_status != EMAC_RUNTIME_NOT_INIT) { + if (emac_config.emac_status == EMAC_RUNTIME_START) { if (emac_ioctl(SIG_EMAC_STOP, (emac_par_t)(&post_cmd)) != 0) { close_cmd.err = EMAC_CMD_FAIL; } @@ -608,9 +764,6 @@ static esp_err_t emac_ioctl(emac_sig_t sig, emac_par_t par) case SIG_EMAC_TX_DONE: emac_process_tx(); break; - case SIG_EMAC_TX: - emac_xmit((void *)par); - break; case SIG_EMAC_START: emac_start((void *)par); break; @@ -626,29 +779,6 @@ static esp_err_t emac_ioctl(emac_sig_t sig, emac_par_t par) return ret; } -esp_err_t esp_eth_tx(uint8_t *buf, uint16_t size) -{ - struct emac_post_cmd post_cmd; - struct emac_tx_cmd tx_cmd; - - post_cmd.cmd = (void *)(&tx_cmd); - - if (emac_check_phy_link_status() == false) { - emac_process_link_updown(false); - tx_cmd.err = ERR_IF; - } else { - tx_cmd.buf = buf; - tx_cmd.size = size; - tx_cmd.err = ERR_OK; - - if (emac_ioctl(SIG_EMAC_TX, (emac_par_t)(&post_cmd)) != 0) { - tx_cmd.err = ERR_MEM; - } - } - - return tx_cmd.err; -} - void emac_task(void *pv) { emac_event_t e; @@ -662,18 +792,21 @@ void emac_task(void *pv) case SIG_EMAC_RX_DONE: emac_process_rx(); break; + case SIG_EMAC_RX_UNAVAIL: + emac_process_rx_unavail(); + break; case SIG_EMAC_TX_DONE: emac_process_tx(); break; - case SIG_EMAC_TX: - emac_xmit((void *)e.par); - break; case SIG_EMAC_START: emac_start((void *)e.par); break; case SIG_EMAC_STOP: emac_stop((void *)e.par); break; + case SIG_EMAC_CHECK_LINK: + emac_process_link_check(); + break; default: ESP_LOGE(TAG, "unexpect sig %d", e.sig); break; @@ -684,27 +817,35 @@ void emac_task(void *pv) esp_err_t IRAM_ATTR emac_post(emac_sig_t sig, emac_par_t par) { - portENTER_CRITICAL(&g_emac_mux); + if(sig <= SIG_EMAC_RX_DONE) { + if (emac_sig_cnt[sig]) { + return ESP_OK; + } else { + emac_sig_cnt[sig]++; + emac_event_t evt; + signed portBASE_TYPE ret; + evt.sig = sig; + evt.par = par; + portBASE_TYPE tmp; - if (emac_sig_cnt[sig] && sig != SIG_EMAC_TX) { - portEXIT_CRITICAL(&g_emac_mux); - return ESP_OK; + ret = xQueueSendFromISR(emac_xqueue, &evt, &tmp); + + if(tmp != pdFALSE) { + portYIELD_FROM_ISR(); + } + + if(ret != pdPASS) { + return ESP_FAIL; + } + } } else { emac_sig_cnt[sig]++; - portEXIT_CRITICAL(&g_emac_mux); emac_event_t evt; evt.sig = sig; evt.par = par; - if (sig <= SIG_EMAC_RX_DONE) { - portBASE_TYPE tmp; - if (xQueueSendFromISR(emac_xqueue, &evt, &tmp) != pdPASS) { - return ESP_FAIL; - } - } else { - if (xQueueSend(emac_xqueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { - return ESP_FAIL; - } + if (xQueueSend(emac_xqueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + return ESP_FAIL; } } @@ -737,7 +878,7 @@ esp_err_t esp_eth_init(eth_config_t *config) REG_SET_FIELD(EMAC_EX_PHYINF_CONF_REG, EMAC_EX_PHY_INTF_SEL, EMAC_EX_PHY_INTF_RMII); emac_dma_init(); - if (emac_config.mac_mode == EMAC_MODE_RMII) { + if (emac_config.mac_mode == ETH_MODE_RMII) { emac_set_clk_rmii(); } else { emac_set_clk_mii(); @@ -752,8 +893,12 @@ esp_err_t esp_eth_init(eth_config_t *config) //init task for emac emac_g_sem = xSemaphoreCreateBinary(); + emac_rx_xMutex = xSemaphoreCreateRecursiveMutex(); + emac_tx_xMutex = xSemaphoreCreateRecursiveMutex(); emac_xqueue = xQueueCreate(EMAC_EVT_QNUM, sizeof(emac_event_t)); - xTaskCreate(emac_task, "emacT", 2048 * 4, NULL, (19), &emac_task_hdl); + xTaskCreate(emac_task, "emacT", 2048, NULL, EMAC_TASK_PRIORITY, &emac_task_hdl); + + esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL); emac_reset(); emac_enable_clk(false); diff --git a/components/ethernet/include/esp_eth.h b/components/ethernet/include/esp_eth.h index b97289dd2b..2aae9d2024 100644 --- a/components/ethernet/include/esp_eth.h +++ b/components/ethernet/include/esp_eth.h @@ -22,15 +22,21 @@ extern "C" { #endif -typedef void (*eth_phy_fun)(void); -typedef esp_err_t (*eth_tcpip_input_fun)(void *buffer, uint16_t len, void *eb); -typedef void (*eth_gpio_config_func)(void); - typedef enum { ETH_MODE_RMII = 0, ETH_MDOE_MII, } eth_mode_t; +typedef enum { + ETH_SPEED_MODE_10M = 0, + ETH_SPEED_MODE_100M, +} eth_speed_mode_t; + +typedef enum { + ETH_MODE_HALFDUPLEX = 0, + ETH_MDOE_FULLDUPLEX, +} eth_duplex_mode_t; + typedef enum { PHY0 = 0, PHY1, @@ -66,6 +72,15 @@ typedef enum { PHY31, } eth_phy_base_t; +typedef bool (*eth_phy_check_link_func)(void); +typedef void (*eth_phy_check_init_func)(void); +typedef eth_speed_mode_t (*eth_phy_get_speed_mode_func)(void); +typedef eth_duplex_mode_t (*eth_phy_get_duplex_mode_func)(void); +typedef void (*eth_phy_func)(void); +typedef esp_err_t (*eth_tcpip_input_func)(void *buffer, uint16_t len, void *eb); +typedef void (*eth_gpio_config_func)(void); + + /** * @brief ethernet configuration * @@ -73,8 +88,12 @@ typedef enum { typedef struct { eth_phy_base_t phy_addr; /*!< phy base addr (0~31) */ eth_mode_t mac_mode; /*!< mac mode only support RMII now */ - eth_tcpip_input_fun tcpip_input; /*!< tcpip input func */ - eth_phy_fun phy_init; /*!< phy init func */ + eth_tcpip_input_func tcpip_input; /*!< tcpip input func */ + eth_phy_func phy_init; /*!< phy init func */ + eth_phy_check_link_func phy_check_link; /*!< phy check link func */ + eth_phy_check_init_func phy_check_init; /*!< phy check init func */ + eth_phy_get_speed_mode_func phy_get_speed_mode; /*!< phy check init func */ + eth_phy_get_duplex_mode_func phy_get_duplex_mode; /*!< phy check init func */ eth_gpio_config_func gpio_config; /*!< gpio config func */ } eth_config_t; @@ -159,6 +178,16 @@ void esp_eth_smi_write(uint32_t reg_num, uint16_t value); */ uint16_t esp_eth_smi_read(uint32_t reg_num); +/** + * @brief Free emac rx buf. + * + * @note buf can not be null,and it is tcpip input buf. + * + * @param[in] buf: start address of recevie packet data. + * + */ +void esp_eth_free_rx_buf(void *buf); + #ifdef __cplusplus } #endif diff --git a/components/lwip/core/pbuf.c b/components/lwip/core/pbuf.c index 29e24ef2b4..b954817492 100755 --- a/components/lwip/core/pbuf.c +++ b/components/lwip/core/pbuf.c @@ -80,6 +80,7 @@ #if ESP_LWIP #include "esp_wifi_internal.h" +#include "esp_eth.h" #endif #define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) @@ -351,7 +352,8 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) p->flags = 0; #if ESP_LWIP - p->eb = NULL; + p->user_buf = NULL; + p->user_flag = PBUF_USER_FLAG_OWNER_NULL; #endif LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); @@ -720,9 +722,13 @@ pbuf_free(struct pbuf *p) } else if (type == PBUF_ROM || type == PBUF_REF) { #if ESP_LWIP - if (type == PBUF_REF && p->eb != NULL ) esp_wifi_internal_free_rx_buffer(p->eb); + if (type == PBUF_REF && p->user_flag == PBUF_USER_FLAG_OWNER_WIFI ) { + esp_wifi_internal_free_rx_buffer(p->user_buf); + } + if (type == PBUF_REF && p->user_flag == PBUF_USER_FLAG_OWNER_ETH ) { + esp_eth_free_rx_buf(p->user_buf); + } #endif - memp_free(MEMP_PBUF, p); /* type == PBUF_RAM */ } else { diff --git a/components/lwip/include/lwip/lwip/pbuf.h b/components/lwip/include/lwip/lwip/pbuf.h index 1834c4e04c..d84aabaf81 100755 --- a/components/lwip/include/lwip/lwip/pbuf.h +++ b/components/lwip/include/lwip/lwip/pbuf.h @@ -105,6 +105,12 @@ typedef enum { /** indicates this pbuf includes a TCP FIN flag */ #define PBUF_FLAG_TCP_FIN 0x20U +#if ESP_LWIP +#define PBUF_USER_FLAG_OWNER_NULL 0 +#define PBUF_USER_FLAG_OWNER_WIFI 1 +#define PBUF_USER_FLAG_OWNER_ETH 2 +#endif + struct pbuf { /** next pbuf in singly linked pbuf chain */ struct pbuf *next; @@ -138,7 +144,8 @@ struct pbuf { u16_t ref; #if ESP_LWIP - void *eb; + void *user_buf; + u8_t user_flag; #endif }; diff --git a/components/lwip/port/netif/ethernetif.c b/components/lwip/port/netif/ethernetif.c index c6f08ec832..79f21f6b3f 100755 --- a/components/lwip/port/netif/ethernetif.c +++ b/components/lwip/port/netif/ethernetif.c @@ -113,7 +113,8 @@ ethernet_low_level_output(struct netif *netif, struct pbuf *p) esp_interface_t eth_if = tcpip_adapter_get_esp_if(netif); if (eth_if != ESP_IF_ETH) { - printf("eth_if=%d netif=%p pbuf=%p len=%d\n", eth_if, netif, p, p->len); + LWIP_DEBUGF(NETIF_DEBUG,("eth_if=%d netif=%p pbuf=%p len=%d\n", eth_if, netif, p, p->len)); + return ERR_IF; } @@ -134,7 +135,6 @@ ethernet_low_level_output(struct netif *netif, struct pbuf *p) } } - //printf("netif=%p pbuf=%p len=%d\n", netif, p, p->len); return esp_eth_tx(q->payload, pbuf_x_len); #else for(q = p; q != NULL; q = q->next) { @@ -160,7 +160,7 @@ ethernetif_input(struct netif *netif, void *buffer, uint16_t len) if(buffer== NULL || netif == NULL) goto _exit; - +#if CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); if (p == NULL) { //g_rx_alloc_pbuf_fail_cnt++; @@ -168,12 +168,28 @@ ethernetif_input(struct netif *netif, void *buffer, uint16_t len) } memcpy(p->payload, buffer, len); - /* full packet send to tcpip_thread to process */ - if (netif->input(p, netif) != ERR_OK) { - LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); - pbuf_free(p); +/* full packet send to tcpip_thread to process */ +if (netif->input(p, netif) != ERR_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); +} + +#else + p = pbuf_alloc(PBUF_RAW, len, PBUF_REF); + if (p == NULL){ + return; } - + p->payload = buffer; + p->user_flag = PBUF_USER_FLAG_OWNER_ETH; + p->user_buf = buffer; + + /* full packet send to tcpip_thread to process */ +if (netif->input(p, netif) != ERR_OK) { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + p->user_flag = PBUF_USER_FLAG_OWNER_NULL; + pbuf_free(p); +} +#endif _exit: ; } diff --git a/components/lwip/port/netif/wlanif.c b/components/lwip/port/netif/wlanif.c index efaa76a73f..fea163f8cc 100755 --- a/components/lwip/port/netif/wlanif.c +++ b/components/lwip/port/netif/wlanif.c @@ -176,7 +176,8 @@ wlanif_input(struct netif *netif, void *buffer, u16_t len, void* eb) return; } p->payload = buffer; - p->eb = eb; + p->user_buf = eb; + p->user_flag = PBUF_USER_FLAG_OWNER_WIFI; #endif /* full packet send to tcpip_thread to process */ diff --git a/examples/14_ethernet/main/tlk110_phy.h b/examples/14_ethernet/main/tlk110_phy.h new file mode 100644 index 0000000000..5f2ca644dc --- /dev/null +++ b/examples/14_ethernet/main/tlk110_phy.h @@ -0,0 +1,28 @@ +#define BASIC_MODE_STATUS_REG (0x1) +#define AUTO_NEGOTIATION_COMPLETE BIT(5) +#define LINK_STATUS BIT(2) + +#define PHY_IDENTIFIER_REG (0x2) +#define OUI_MSB_21TO6_DEF 0x2000 + +#define SOFTWARE_STAP_CONTROL_REG (0x9) +#define SW_STRAP_CONFIG_DONE BIT(15) +#define AUTO_MDIX_ENABLE BIT(14) +#define AUTO_NEGOTIATION_ENABLE BIT(13) +#define AN_1 BIT(12) +#define AN_0 BIT(11) +#define LED_CFG BIT(10) +#define RMII_ENHANCED_MODE BIT(9) + +#define PHY_STATUS_REG (0x10) +#define AUTO_NEGTIATION_STATUS BIT(4) +#define DUPLEX_STATUS BIT(2) +#define SPEED_STATUS BIT(1) + +#define CABLE_DIAGNOSTIC_CONTROL_REG (0x1e) +#define DIAGNOSTIC_DONE BIT(1) + +#define PHY_RESET_CONTROL_REG (0x1f) +#define SOFTWARE_RESET BIT(15) + + diff --git a/examples/17_ethernet/main/ethernet_main.c b/examples/17_ethernet/main/ethernet_main.c index 836e977f48..7e84a9badd 100644 --- a/examples/17_ethernet/main/ethernet_main.c +++ b/examples/17_ethernet/main/ethernet_main.c @@ -32,30 +32,64 @@ #include "tcpip_adapter.h" #include "nvs_flash.h" #include "driver/gpio.h" +#include "tlk110_phy.h" static const char *TAG = "eth_demo"; +#define DEFAULT_PHY_CONFIG (AUTO_MDIX_ENABLE|AUTO_NEGOTIATION_ENABLE|AN_1|AN_0|LED_CFG) + +void phy_tlk110_check_phy_init(void) +{ + while((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & AUTO_NEGOTIATION_COMPLETE ) != AUTO_NEGOTIATION_COMPLETE) + {}; + while((esp_eth_smi_read(PHY_STATUS_REG) & AUTO_NEGTIATION_STATUS ) != AUTO_NEGTIATION_STATUS) + {}; + while((esp_eth_smi_read(CABLE_DIAGNOSTIC_CONTROL_REG) & DIAGNOSTIC_DONE ) != DIAGNOSTIC_DONE) + {}; +} + +eth_speed_mode_t phy_tlk110_get_speed_mode(void) +{ + if((esp_eth_smi_read(PHY_STATUS_REG) & SPEED_STATUS ) != SPEED_STATUS) { + return ETH_SPEED_MODE_100M; + } else { + return ETH_SPEED_MODE_10M; + } +} + +eth_duplex_mode_t phy_tlk110_get_duplex_mode(void) +{ + if((esp_eth_smi_read(PHY_STATUS_REG) & DUPLEX_STATUS ) == DUPLEX_STATUS) { + return ETH_MDOE_FULLDUPLEX; + } else { + return ETH_MODE_HALFDUPLEX; + } +} + +bool phy_tlk110_check_phy_link_status(void) +{ + return ((esp_eth_smi_read(BASIC_MODE_STATUS_REG) & LINK_STATUS) == LINK_STATUS ); +} + void phy_tlk110_init(void) { - esp_eth_smi_write(0x1f, 0x8000); - ets_delay_us(20000); + esp_eth_smi_write(PHY_RESET_CONTROL_REG, SOFTWARE_RESET); - while (esp_eth_smi_read(0x2) != 0x2000) { + while (esp_eth_smi_read(PHY_IDENTIFIER_REG) != OUI_MSB_21TO6_DEF) { } - esp_eth_smi_write(0x9, 0x7400); - esp_eth_smi_write(0x9, 0xf400); - ets_delay_us(20000); + esp_eth_smi_write(SOFTWARE_STAP_CONTROL_REG, DEFAULT_PHY_CONFIG |SW_STRAP_CONFIG_DONE); + ets_delay_us(300); } void eth_gpio_config_rmii(void) { //txd0 to gpio19 ,can not change - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO19_U, 5); - //rx_en to gpio21 ,can not change - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO21_U, 5); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO19_U, FUNC_GPIO19_EMAC_TXD0); + //tx_en to gpio21 ,can not change + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO21_U, FUNC_GPIO21_EMAC_TX_EN); //txd1 to gpio22 , can not change - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO22_U, 5); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO22_U, FUNC_GPIO22_EMAC_TXD1); //rxd0 to gpio25 , can not change gpio_set_direction(25, GPIO_MODE_INPUT); //rxd1 to gpio26 ,can not change @@ -102,8 +136,13 @@ void app_main() config.phy_init = phy_tlk110_init; config.gpio_config = eth_gpio_config_rmii; config.tcpip_input = tcpip_adapter_eth_input; + config.phy_check_init = phy_tlk110_check_phy_init; + config.phy_check_link = phy_tlk110_check_phy_link_status; + config.phy_get_speed_mode = phy_tlk110_get_speed_mode; + config.phy_get_duplex_mode = phy_tlk110_get_duplex_mode; ret = esp_eth_init(&config); + if(ret == ESP_OK) { esp_eth_enable(); xTaskCreate(eth_task, "eth_task", 2048, NULL, (tskIDLE_PRIORITY + 2), NULL); From 15651b5923b61f4e113db1790f3d9a4e5fb73cdb Mon Sep 17 00:00:00 2001 From: XiaXiaotian Date: Mon, 26 Dec 2016 15:47:20 +0800 Subject: [PATCH 32/59] lwip: add ip frag and reassembly option in menuconfig --- components/lwip/Kconfig | 13 +++++++++++++ components/lwip/include/lwip/port/lwipopts.h | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 2e7e31a8a9..9e2b031674 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -49,6 +49,19 @@ config LWIP_DHCP_MAX_NTP_SERVERS First argument of sntp_setserver/sntp_setservername functions is limited to this value. +config LWIP_IP_FRAG + bool "Enable fragment outgoing IP packets" + default 0 + help + Enabling this option allows fragmenting outgoing IP packets if their size + exceeds MTU. + +config LWIP_IP_REASSEMBLY + bool "Enable reassembly incoming fragmented IP packets" + default 0 + help + Enabling this option allows reassemblying incoming fragmented IP packets. + endmenu diff --git a/components/lwip/include/lwip/port/lwipopts.h b/components/lwip/include/lwip/port/lwipopts.h index 8612eb11b0..5000d63ba9 100755 --- a/components/lwip/include/lwip/port/lwipopts.h +++ b/components/lwip/include/lwip/port/lwipopts.h @@ -154,14 +154,14 @@ * this option does not affect outgoing packet sizes, which can be controlled * via IP_FRAG. */ -#define IP_REASSEMBLY 0 +#define IP_REASSEMBLY CONFIG_LWIP_IP_REASSEMBLY /** * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note * that this option does not affect incoming packet sizes, which can be * controlled via IP_REASSEMBLY. */ -#define IP_FRAG 0 +#define IP_FRAG CONFIG_LWIP_IP_FRAG /** * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally) From d2e58193d235bcde15b1124af63358300412d31c Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Mon, 26 Dec 2016 19:04:41 +0800 Subject: [PATCH 33/59] add more protection for per-core data --- components/freertos/Kconfig | 6 ++- .../freertos/include/freertos/portmacro.h | 4 +- components/freertos/tasks.c | 54 +++++++++++++------ 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index b9db00e50b..859ece2c09 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -195,7 +195,11 @@ config FREERTOS_PORTMUX_DEBUG_RECURSIVE If enabled, additional debug information will be printed for recursive portMUX usage. - +config FREERTOS_INT_DISABLING_DURATION_DEBUG + bool "Debug interrupt disabling duration" + default n + help + If enabled, the longest interrupt disabling duration will be recorded. endif # FREERTOS_DEBUG_INTERNALS diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index f20a4a1e26..9c15eebf9a 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -213,7 +213,6 @@ portBASE_TYPE vPortCPUReleaseMutex(portMUX_TYPE *mux); #define portEXIT_CRITICAL_ISR(mux) vPortCPUReleaseMutex(mux) #endif - // Cleaner and preferred solution allows nested interrupts disabling and restoring via local registers or stack. // They can be called from interrupts too. //NOT SMP-COMPATIBLE! Use only if all you want is to disable the interrupts locally! @@ -225,6 +224,9 @@ static inline unsigned portENTER_CRITICAL_NESTED() { unsigned state = XTOS_SET_I #define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state) +#define portDisableINT() portENTER_CRITICAL_NESTED() +#define portEnableINT(state) portEXIT_CRITICAL_NESTED((state)) + /* * Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare * *mux to compare, and if it's the same, will set *mux to set. It will return the old value diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index f2bdf8ccb0..00df0df871 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -369,7 +369,7 @@ PRIVILEGED_DATA static portMUX_TYPE xTickCountMutex = portMUX_INITIALIZER_UNLOCK \ /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \ the same priority get an equal share of the processor time. */ \ - listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB[ xPortGetCoreID() ], &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); \ + listGET_OWNER_OF_NEXT_ENTRY( xTaskGetCurrentTaskHandle(), &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); \ } /* taskSELECT_HIGHEST_PRIORITY_TASK */ /*-----------------------------------------------------------*/ @@ -398,7 +398,7 @@ PRIVILEGED_DATA static portMUX_TYPE xTickCountMutex = portMUX_INITIALIZER_UNLOCK /* Find the highest priority queue that contains ready tasks. */ \ portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ - listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB[ xPortGetCoreID() ], &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + listGET_OWNER_OF_NEXT_ENTRY( xTaskGetCurrentTaskHandle(), &( pxReadyTasksLists[ uxTopPriority ] ) ); \ } /* taskSELECT_HIGHEST_PRIORITY_TASK() */ /*-----------------------------------------------------------*/ @@ -456,7 +456,7 @@ count overflows. */ * see if the parameter is NULL and returns a pointer to the appropriate TCB. */ /* ToDo: See if this still works for multicore. */ -#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) pxCurrentTCB[ xPortGetCoreID() ] : ( TCB_t * ) ( pxHandle ) ) +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) xTaskGetCurrentTaskHandle() : ( TCB_t * ) ( pxHandle ) ) /* The item value of the event list item is normally used to hold the priority of the task to which it belongs (coded to allow it to be held in reverse @@ -631,9 +631,11 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode */ void taskYIELD_OTHER_CORE( BaseType_t xCoreID, UBaseType_t uxPriority ) { + TCB_t *curTCB = xTaskGetCurrentTaskHandle(); BaseType_t i; + if (xCoreID != tskNO_AFFINITY) { - if ( pxCurrentTCB[ xCoreID ]->uxPriority < uxPriority ) { + if ( curTCB->uxPriority < uxPriority ) { vPortYieldOtherCore( xCoreID ); } } @@ -1039,6 +1041,7 @@ UBaseType_t x; static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID ) { + TCB_t *curTCB; BaseType_t i; /* Ensure interrupts don't access the task lists while the lists are being @@ -1111,6 +1114,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode portSETUP_TCB( pxNewTCB ); } + curTCB = pxCurrentTCB[ xPortGetCoreID() ]; taskEXIT_CRITICAL(&xTaskQueueMutex); if( xSchedulerRunning != pdFALSE ) @@ -1121,17 +1125,17 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode the other processor will keep running the task it's working on, and only switch to the newer task on a timer interrupt. */ //No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. - if( pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < pxNewTCB->uxPriority ) + if( curTCB->uxPriority < pxNewTCB->uxPriority ) { /* Scheduler is running. If the created task is of a higher priority than an executing task then it should run now. No mux here, uxPriority is mostly atomic and there's not really any harm if this check misfires. */ - if( tskCAN_RUN_HERE( xCoreID ) && pxCurrentTCB[ xPortGetCoreID() ]->uxPriority < pxNewTCB->uxPriority ) + if( tskCAN_RUN_HERE( xCoreID ) && curTCB->uxPriority < pxNewTCB->uxPriority ) { taskYIELD_IF_USING_PREEMPTION(); } - else if( xCoreID != xPortGetCoreID() ) { + else if( xCoreID != xPortGetCoreID() ) {//TODO taskYIELD_OTHER_CORE(xCoreID, pxNewTCB->uxPriority); } else @@ -1409,11 +1413,12 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode eTaskState eReturn; List_t *pxStateList; const TCB_t * const pxTCB = ( TCB_t * ) xTask; + TCB_t * curTCB = xTaskGetCurrentTaskHandle(); UNTESTED_FUNCTION(); configASSERT( pxTCB ); - if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] ) + if( pxTCB == curTCB ) { /* The task calling this function is querying its own state. */ eReturn = eRunning; @@ -1691,6 +1696,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode void vTaskSuspend( TaskHandle_t xTaskToSuspend ) { TCB_t *pxTCB; + TCB_t *curTCB; UNTESTED_FUNCTION(); taskENTER_CRITICAL(&xTaskQueueMutex); @@ -1723,10 +1729,11 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode } vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ); + curTCB = pxCurrentTCB[ xPortGetCoreID() ]; } taskEXIT_CRITICAL(&xTaskQueueMutex); - if( pxTCB == pxCurrentTCB[ xPortGetCoreID() ] ) + if( pxTCB == curTCB ) { if( xSchedulerRunning != pdFALSE ) { @@ -2034,7 +2041,7 @@ void vTaskEndScheduler( void ) //Return global reent struct if FreeRTOS isn't running, struct _reent* __getreent() { //No lock needed because if this changes, we won't be running anymore. - TCB_t *currTask=pxCurrentTCB[ xPortGetCoreID() ]; + TCB_t *currTask=xTaskGetCurrentTaskHandle(); if (currTask==NULL) { //No task running. Return global struct. return _GLOBAL_REENT; @@ -2052,7 +2059,11 @@ void vTaskSuspendAll( void ) BaseType_t. Please read Richard Barry's reply in the following link to a post in the FreeRTOS support forum before reporting this as a bug! - http://goo.gl/wu4acr */ + unsigned state; + + state = portDisableINT(); ++uxSchedulerSuspended[ xPortGetCoreID() ]; + portEnableINT(state); } /*----------------------------------------------------------*/ @@ -2595,7 +2606,7 @@ BaseType_t xSwitchRequired = pdFALSE; /* If xTask is NULL then we are setting our own task hook. */ if( xTask == NULL ) { - xTCB = ( TCB_t * ) pxCurrentTCB[ xPortGetCoreID() ]; + xTCB = ( TCB_t * ) xTaskGetCurrentTaskHandle(); } else { @@ -2626,7 +2637,7 @@ BaseType_t xSwitchRequired = pdFALSE; /* If xTask is NULL then we are calling our own task hook. */ if( xTask == NULL ) { - xTCB = ( TCB_t * ) pxCurrentTCB[ xPortGetCoreID() ]; + xTCB = ( TCB_t * ) xTaskGetCurrentTaskHandle(); } else { @@ -3387,8 +3398,8 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) { - pxTCB = prvGetTCBFromHandle( xTaskToSet ); taskENTER_CRITICAL(&xTaskQueueMutex); + pxTCB = prvGetTCBFromHandle( xTaskToSet ); pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; pxTCB->pvThreadLocalStoragePointersDelCallback[ xIndex ] = xDelCallback; taskEXIT_CRITICAL(&xTaskQueueMutex); @@ -3408,8 +3419,10 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters ) if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) { + taskENTER_CRITICAL(&xTaskQueueMutex); pxTCB = prvGetTCBFromHandle( xTaskToSet ); pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; + taskEXIT_CRITICAL(&xTaskQueueMutex); } } #endif /* configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS */ @@ -3803,10 +3816,11 @@ TCB_t *pxTCB; TaskHandle_t xTaskGetCurrentTaskHandle( void ) { TaskHandle_t xReturn; + unsigned state; - vPortCPUAcquireMutex(&xTaskQueueMutex); + state = portDisableINT(); xReturn = pxCurrentTCB[ xPortGetCoreID() ]; - vPortCPUReleaseMutex(&xTaskQueueMutex); + portEnableINT(state); return xReturn; } @@ -3832,7 +3846,9 @@ TCB_t *pxTCB; BaseType_t xTaskGetSchedulerState( void ) { BaseType_t xReturn; + unsigned state; + state = portDisableINT(); if( xSchedulerRunning == pdFALSE ) { xReturn = taskSCHEDULER_NOT_STARTED; @@ -3848,6 +3864,7 @@ TCB_t *pxTCB; xReturn = taskSCHEDULER_SUSPENDED; } } + portEnableINT(state); return xReturn; } @@ -4383,16 +4400,19 @@ TickType_t uxReturn; void *pvTaskIncrementMutexHeldCount( void ) { + TCB_t *curTCB; + /* If xSemaphoreCreateMutex() is called before any tasks have been created - then pxCurrentTCB will be NULL. */ + then xTaskGetCurrentTaskHandle() will be NULL. */ taskENTER_CRITICAL(&xTaskQueueMutex); if( pxCurrentTCB[ xPortGetCoreID() ] != NULL ) { ( pxCurrentTCB[ xPortGetCoreID() ]->uxMutexesHeld )++; } + curTCB = pxCurrentTCB[ xPortGetCoreID() ]; taskEXIT_CRITICAL(&xTaskQueueMutex); - return pxCurrentTCB[ xPortGetCoreID() ]; + return curTCB; } #endif /* configUSE_MUTEXES */ From d049fd392953d8aaf2c3cb4270aa8b401e4f016a Mon Sep 17 00:00:00 2001 From: Liu Zhi Fu Date: Tue, 27 Dec 2016 12:11:07 +0800 Subject: [PATCH 34/59] freertos: rework code based on review --- components/freertos/Kconfig | 6 ------ .../freertos/include/freertos/portmacro.h | 3 --- components/freertos/tasks.c | 20 ++++++++++--------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index 859ece2c09..d8b392d577 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -195,12 +195,6 @@ config FREERTOS_PORTMUX_DEBUG_RECURSIVE If enabled, additional debug information will be printed for recursive portMUX usage. -config FREERTOS_INT_DISABLING_DURATION_DEBUG - bool "Debug interrupt disabling duration" - default n - help - If enabled, the longest interrupt disabling duration will be recorded. - endif # FREERTOS_DEBUG_INTERNALS endmenu diff --git a/components/freertos/include/freertos/portmacro.h b/components/freertos/include/freertos/portmacro.h index 9c15eebf9a..7cae4b05b6 100644 --- a/components/freertos/include/freertos/portmacro.h +++ b/components/freertos/include/freertos/portmacro.h @@ -224,9 +224,6 @@ static inline unsigned portENTER_CRITICAL_NESTED() { unsigned state = XTOS_SET_I #define portCLEAR_INTERRUPT_MASK_FROM_ISR(state) portEXIT_CRITICAL_NESTED(state) -#define portDisableINT() portENTER_CRITICAL_NESTED() -#define portEnableINT(state) portEXIT_CRITICAL_NESTED((state)) - /* * Wrapper for the Xtensa compare-and-set instruction. This subroutine will atomically compare * *mux to compare, and if it's the same, will set *mux to set. It will return the old value diff --git a/components/freertos/tasks.c b/components/freertos/tasks.c index 00df0df871..159d96c5b2 100644 --- a/components/freertos/tasks.c +++ b/components/freertos/tasks.c @@ -1041,7 +1041,7 @@ UBaseType_t x; static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode, const BaseType_t xCoreID ) { - TCB_t *curTCB; + TCB_t *curTCB; BaseType_t i; /* Ensure interrupts don't access the task lists while the lists are being @@ -1119,6 +1119,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode if( xSchedulerRunning != pdFALSE ) { + taskENTER_CRITICAL(&xTaskQueueMutex); /* Scheduler is running. If the created task is of a higher priority than an executing task then it should run now. ToDo: This only works for the current core. If a task is scheduled on an other processor, @@ -1135,7 +1136,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode { taskYIELD_IF_USING_PREEMPTION(); } - else if( xCoreID != xPortGetCoreID() ) {//TODO + else if( xCoreID != xPortGetCoreID() ) { taskYIELD_OTHER_CORE(xCoreID, pxNewTCB->uxPriority); } else @@ -1147,6 +1148,7 @@ static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB, TaskFunction_t pxTaskCode { mtCOVERAGE_TEST_MARKER(); } + taskEXIT_CRITICAL(&xTaskQueueMutex); } else { @@ -2061,9 +2063,9 @@ void vTaskSuspendAll( void ) http://goo.gl/wu4acr */ unsigned state; - state = portDisableINT(); + state = portENTER_CRITICAL_NESTED(); ++uxSchedulerSuspended[ xPortGetCoreID() ]; - portEnableINT(state); + portEXIT_CRITICAL_NESTED(state); } /*----------------------------------------------------------*/ @@ -3818,9 +3820,9 @@ TCB_t *pxTCB; TaskHandle_t xReturn; unsigned state; - state = portDisableINT(); + state = portENTER_CRITICAL_NESTED(); xReturn = pxCurrentTCB[ xPortGetCoreID() ]; - portEnableINT(state); + portEXIT_CRITICAL_NESTED(state); return xReturn; } @@ -3848,7 +3850,7 @@ TCB_t *pxTCB; BaseType_t xReturn; unsigned state; - state = portDisableINT(); + state = portENTER_CRITICAL_NESTED(); if( xSchedulerRunning == pdFALSE ) { xReturn = taskSCHEDULER_NOT_STARTED; @@ -3864,7 +3866,7 @@ TCB_t *pxTCB; xReturn = taskSCHEDULER_SUSPENDED; } } - portEnableINT(state); + portEXIT_CRITICAL_NESTED(state); return xReturn; } @@ -4403,7 +4405,7 @@ TickType_t uxReturn; TCB_t *curTCB; /* If xSemaphoreCreateMutex() is called before any tasks have been created - then xTaskGetCurrentTaskHandle() will be NULL. */ + then pxCurrentTCB will be NULL. */ taskENTER_CRITICAL(&xTaskQueueMutex); if( pxCurrentTCB[ xPortGetCoreID() ] != NULL ) { From 6a39bc6996548aaedbbdb63178182c0076fa55fc Mon Sep 17 00:00:00 2001 From: Krzysztof Date: Tue, 27 Dec 2016 12:32:40 +0100 Subject: [PATCH 35/59] Clarification on documenting examples --- docs/api/template.rst | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/api/template.rst b/docs/api/template.rst index 6feb7ba271..3cc8713ba4 100644 --- a/docs/api/template.rst +++ b/docs/api/template.rst @@ -40,10 +40,15 @@ Application Example *INSTRUCTIONS* - 1. Provide one or more practical examples to demonstrate functionality of this API. - 2. Break down the code into parts and describe functionality of each part. - 3. Provide screenshots if applicable. - + 1. Prepare one or more practical examples to demonstrate functionality of this API. + 2. Each example should follow pattern of projects located in ``esp-idf/examples/`` folder. + 3. Place example in this folder complete with ``README.md`` file. + 4. Provide overview of demonstrated functionality in ``README.md``. + 5. With good overview reader should be able to understand what example does without opening the source code. + 6. Depending on complexity of example, break down description of code into parts and provide overview of functionality of each part. + 7. Include flow diagram and screenshots of application output if applicable. + 8. Finally add in this section synopsis of each example together with link to respective folder in ``esp-idf/examples/``. + API Reference ------------- From da977149f67487c023766974696cb1e8a5bc9117 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 6 Dec 2016 16:33:24 -0800 Subject: [PATCH 36/59] 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 c1a6d5511663560bbed058e0ecd4d3ec6a3da37e Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 12:34:11 +1100 Subject: [PATCH 37/59] WiFi interface: SSID and password fields should be uint8_t in all cases Closes github #40 https://github.com/espressif/esp-idf/issues/40 --- components/esp32/include/esp_wifi_types.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/esp32/include/esp_wifi_types.h b/components/esp32/include/esp_wifi_types.h index 583d7a6a91..ac7642829f 100755 --- a/components/esp32/include/esp_wifi_types.h +++ b/components/esp32/include/esp_wifi_types.h @@ -96,7 +96,7 @@ typedef enum { } wifi_second_chan_t; typedef struct { - char *ssid; /**< SSID of AP */ + uint8_t *ssid; /**< SSID of AP */ uint8_t *bssid; /**< MAC address of AP */ uint8_t channel; /**< channel, scan the specific channel */ bool show_hidden; /**< enable to scan AP whose SSID is hidden */ @@ -126,8 +126,8 @@ typedef enum { } wifi_bandwidth_t; typedef struct { - char ssid[32]; /**< SSID of ESP32 soft-AP */ - char password[64]; /**< Password of ESP32 soft-AP */ + uint8_t ssid[32]; /**< SSID of ESP32 soft-AP */ + uint8_t password[64]; /**< Password of ESP32 soft-AP */ uint8_t ssid_len; /**< Length of SSID. If softap_config.ssid_len==0, check the SSID until there is a termination character; otherwise, set the SSID length according to softap_config.ssid_len. */ uint8_t channel; /**< Channel of ESP32 soft-AP */ wifi_auth_mode_t authmode; /**< Auth mode of ESP32 soft-AP. Do not support AUTH_WEP in soft-AP mode */ @@ -137,8 +137,8 @@ typedef struct { } wifi_ap_config_t; typedef struct { - char ssid[32]; /**< SSID of target AP*/ - char password[64]; /**< password of target AP*/ + uint8_t ssid[32]; /**< SSID of target AP*/ + uint8_t password[64]; /**< password of target AP*/ bool bssid_set; /**< whether set MAC address of target AP or not. Generally, station_config.bssid_set needs to be 0; and it needs to be 1 only when users need to check the MAC address of the AP.*/ uint8_t bssid[6]; /**< MAC address of target AP*/ } wifi_sta_config_t; @@ -215,7 +215,7 @@ typedef struct { typedef struct { wifi_pkt_rx_ctrl_t rx_ctrl; - char payload[0]; /**< ieee80211 packet buff, The length of payload is described by sig_len */ + uint8_t payload[0]; /**< ieee80211 packet buff, The length of payload is described by sig_len */ } wifi_promiscuous_pkt_t; /** From 1e44f72e98ba03e27a014c8518037345062e1667 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 12:37:03 +1100 Subject: [PATCH 38/59] esp_wifi_init: Update comment about init event_q Closes github #28 https://github.com/espressif/esp-idf/issues/28 --- components/esp32/include/esp_wifi.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/components/esp32/include/esp_wifi.h b/components/esp32/include/esp_wifi.h index 68d06aae52..ac49764f1f 100755 --- a/components/esp32/include/esp_wifi.h +++ b/components/esp32/include/esp_wifi.h @@ -107,17 +107,15 @@ typedef struct { * WiFi NVS structure etc, this WiFi also start WiFi task * * @attention 1. This API must be called before all other WiFi API can be called - * @attention 2. Generally we should init event_q in *config, WiFi driver will post the event - * to this queue when event happens, such as, when station connects to WiFi, WiFi driver - * will post station connected event to this queue. If the queue is not initialized, WiFi - * will not post any events + * @attention 2. event_handler field in cfg should be set to a valid event handler function. + * In most cases, use the WIFI_INIT_CONFIG_DEFAULT macro which sets esp_event_send(). * * @param config provide WiFi init configuration * * @return * - ESP_OK: succeed * - ESP_ERR_WIFI_NO_MEM: out of memory - * - others: refer to error code esp_err.h + * - others: refer to error code esp_err.h */ esp_err_t esp_wifi_init(wifi_init_config_t *config); From ff1fa8a32340bff9ced31262857ddc66b17a6306 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:05:19 +1100 Subject: [PATCH 39/59] gpio driver: Fix gpio_set_level validation of gpio_num argument Closes #125 https://github.com/espressif/esp-idf/issues/125 --- components/driver/gpio.c | 2 +- components/driver/include/driver/gpio.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 1a38620dbb..3201372bc1 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -161,7 +161,7 @@ static esp_err_t gpio_output_enable(gpio_num_t gpio_num) esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level) { - GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error", ESP_ERR_INVALID_ARG); if (level) { if (gpio_num < 32) { GPIO.out_w1ts = (1 << gpio_num); diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 83d3806834..4f2a800724 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -269,7 +269,7 @@ esp_err_t gpio_intr_disable(gpio_num_t gpio_num); * * @return * - ESP_OK Success - * - GPIO_IS_VALID_GPIO GPIO number error + * - ESP_ERR_INVALID_ARG GPIO number error * */ esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level); From 41eca2c67ba276c3a85e5da094b6da5e2e569487 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:21:11 +1100 Subject: [PATCH 40/59] RMT: Don't require carrier_freq_hz to be non-zero if carrier_en unset Closes github #123 https://github.com/espressif/esp-idf/issues/123 --- components/driver/rmt.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/components/driver/rmt.c b/components/driver/rmt.c index e29f190024..a16f0f8fe5 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -380,10 +380,16 @@ esp_err_t rmt_config(rmt_config_t* rmt_param) uint8_t gpio_num = rmt_param->gpio_num; uint8_t mem_cnt = rmt_param->mem_block_num; int clk_div = rmt_param->clk_div; + uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz; + bool carrier_en = rmt_param->tx_config.carrier_en; RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK(GPIO_IS_VALID_GPIO(gpio_num), RMT_GPIO_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK((mem_cnt + channel <= 8 && mem_cnt > 0), RMT_MEM_CNT_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK((clk_div > 0), RMT_CLK_DIV_ERROR_STR, ESP_ERR_INVALID_ARG); + if (mode == RMT_MODE_TX) { + RMT_CHECK((!carrier_en || carrier_freq_hz > 0), "RMT carrier frequency can't be zero", ESP_ERR_INVALID_ARG); + } + periph_module_enable(PERIPH_RMT_MODULE); RMT.conf_ch[channel].conf0.div_cnt = clk_div; @@ -397,7 +403,6 @@ esp_err_t rmt_config(rmt_config_t* rmt_param) if(mode == RMT_MODE_TX) { uint32_t rmt_source_clk_hz = 0; - uint32_t carrier_freq_hz = rmt_param->tx_config.carrier_freq_hz; uint16_t carrier_duty_percent = rmt_param->tx_config.carrier_duty_percent; uint8_t carrier_level = rmt_param->tx_config.carrier_level; uint8_t idle_level = rmt_param->tx_config.idle_level; @@ -416,16 +421,23 @@ esp_err_t rmt_config(rmt_config_t* rmt_param) portEXIT_CRITICAL(&rmt_spinlock); /*Set carrier*/ - uint32_t duty_div, duty_h, duty_l; - duty_div = rmt_source_clk_hz / carrier_freq_hz; - duty_h = duty_div * carrier_duty_percent / 100; - duty_l = duty_div - duty_h; - RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; - RMT.carrier_duty_ch[channel].high = duty_h; - RMT.carrier_duty_ch[channel].low = duty_l; - RMT.conf_ch[channel].conf0.carrier_en = rmt_param->tx_config.carrier_en; + RMT.conf_ch[channel].conf0.carrier_en = carrier_en; + if (carrier_en) { + uint32_t duty_div, duty_h, duty_l; + duty_div = rmt_source_clk_hz / carrier_freq_hz; + duty_h = duty_div * carrier_duty_percent / 100; + duty_l = duty_div - duty_h; + RMT.conf_ch[channel].conf0.carrier_out_lv = carrier_level; + RMT.carrier_duty_ch[channel].high = duty_h; + RMT.carrier_duty_ch[channel].low = duty_l; + } else { + RMT.conf_ch[channel].conf0.carrier_out_lv = 0; + RMT.carrier_duty_ch[channel].high = 0; + RMT.carrier_duty_ch[channel].low = 0; + } ESP_LOGD(RMT_TAG, "Rmt Tx Channel %u|Gpio %u|Sclk_Hz %u|Div %u|Carrier_Hz %u|Duty %u", - channel, gpio_num, rmt_source_clk_hz, clk_div, carrier_freq_hz, carrier_duty_percent); + channel, gpio_num, rmt_source_clk_hz, clk_div, carrier_freq_hz, carrier_duty_percent); + } else if(RMT_MODE_RX == mode) { uint8_t filter_cnt = rmt_param->rx_config.filter_ticks_thresh; From 45571b3c3810f174ee315d603f54f7cd34f96d31 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:30:24 +1100 Subject: [PATCH 41/59] LEDC driver: Use ledc_channel_t for all channel arguments Closes github #54: https://github.com/espressif/esp-idf/issues/54 --- components/driver/include/driver/ledc.h | 6 +++--- components/driver/ledc.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index fb97c6c011..691379a3d8 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -253,7 +253,7 @@ int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel); * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error */ -esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t gradule_direction, +esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty, ledc_duty_direction_t gradule_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale); /** @@ -354,7 +354,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel); * - ESP_OK Success * */ -esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx); +esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t timer_idx); /***************************EXAMPLE********************************** * @@ -391,7 +391,7 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint * * ----------------EXAMPLE OF SETTING DUTY --- ----------------- * @code{c} - * uint32_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) + * ledc_channel_t ledc_channel = LEDC_CHANNEL_0; //LEDC channel(0-73) * uint32_t duty = 2000; //duty range is 0 ~ ((2**bit_num)-1) * LEDC_set_duty(LEDC_HIGH_SPEED_MODE, ledc_channel, duty); //set speed mode, channel, and duty. * ledc_update_duty(LEDC_HIGH_SPEED_MODE, ledc_channel); //after set duty, we need to call ledc_update_duty to update the settings. diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 77ca975969..c00cf26bb1 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -44,7 +44,7 @@ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_ return ESP_OK; } -static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num, uint32_t hpoint_val, uint32_t duty_val, +static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel_num, uint32_t hpoint_val, uint32_t duty_val, uint32_t duty_direction, uint32_t duty_num, uint32_t duty_cycle, uint32_t duty_scale) { portENTER_CRITICAL(&ledc_spinlock); @@ -58,7 +58,7 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num, return ESP_OK; } -esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx) +esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t timer_idx) { LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG); @@ -239,7 +239,7 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl portEXIT_CRITICAL(&ledc_spinlock); return ESP_OK; } -esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, +esp_err_t ledc_set_fade(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale) { LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG); From 6395081503069ea569d94eb706742e684a4269cd Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:43:42 +1100 Subject: [PATCH 42/59] uart driver: Set type of uart_driver_install queue param Closes github #91 https://github.com/espressif/esp-idf/issues/91 --- components/driver/include/driver/uart.h | 5 +++-- components/driver/uart.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index c193fb0ef8..c788374bdc 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -477,7 +477,8 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * @param tx_buffer_size UART TX ring buffer size. * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * @param queue_size UART event queue size/depth. - * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * @param uart_queue UART event queue handle (out param). On success, a new queue handle is written here to provide + * access to UART events. If set to NULL, driver will not use an event queue. * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. * @@ -485,7 +486,7 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags); +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t* uart_queue, int intr_alloc_flags); /** * @brief Uninstall UART driver. diff --git a/components/driver/uart.c b/components/driver/uart.c index e85c54d8c4..45f4e23945 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -950,7 +950,7 @@ esp_err_t uart_flush(uart_port_t uart_num) return ESP_OK; } -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags) +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t *uart_queue, int intr_alloc_flags) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_CHECK((rx_buffer_size > UART_FIFO_LEN), "uart rx buffer length error(>128)", ESP_FAIL); @@ -978,7 +978,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b if(uart_queue) { p_uart_obj[uart_num]->xQueueUart = xQueueCreate(queue_size, sizeof(uart_event_t)); - *((QueueHandle_t*) uart_queue) = p_uart_obj[uart_num]->xQueueUart; + *uart_queue = p_uart_obj[uart_num]->xQueueUart; ESP_LOGI(UART_TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_uart_obj[uart_num]->xQueueUart)); } else { p_uart_obj[uart_num]->xQueueUart = NULL; From 948a2ba23af4bee60849d5d90dc0f6f28bef6cc0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:44:50 +1100 Subject: [PATCH 43/59] uart driver: Remove invalid UART_BITRATE_115200 enum from example Closes github #92 https://github.com/espressif/esp-idf/issues/92 --- components/driver/include/driver/uart.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index c788374bdc..68d02a5e0a 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -648,7 +648,7 @@ esp_err_t uart_enable_pattern_det_intr(uart_port_t uart_num, char pattern_chr, u * //a. Set UART parameter * int uart_num = 0; //uart port number * uart_config_t uart_config = { - * .baud_rate = UART_BITRATE_115200, //baudrate + * .baud_rate = 115200, //baudrate * .data_bits = UART_DATA_8_BITS, //data bit mode * .parity = UART_PARITY_DISABLE, //parity mode * .stop_bits = UART_STOP_BITS_1, //stop bit mode From 9496fda66285c949fc258b11049832d1953d3c86 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 13:54:42 +1100 Subject: [PATCH 44/59] RMT driver: Rename rmt_set_evt_intr_en to rmt_set_tx_thr_intr_en Closes github #115: https://github.com/espressif/esp-idf/issues/115 --- components/driver/include/driver/rmt.h | 6 ++++-- components/driver/rmt.c | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 24df1ac8ed..36e33e732e 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -524,7 +524,9 @@ esp_err_t rmt_set_err_intr_en(rmt_channel_t channel, bool en); esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en); /** - * @brief Set RMT TX event interrupt enable + * @brief Set RMT TX threshold event interrupt enable + * + * Causes an interrupt when a threshold number of items have been transmitted. * * @param channel RMT channel (0 - 7) * @@ -536,7 +538,7 @@ esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en); * - ESP_ERR_INVALID_ARG Parameter error * - ESP_OK Success */ -esp_err_t rmt_set_evt_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh); +esp_err_t rmt_set_tx_thr_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh); /** * @brief Set RMT pins diff --git a/components/driver/rmt.c b/components/driver/rmt.c index a16f0f8fe5..d277ea00cd 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -341,7 +341,7 @@ esp_err_t rmt_set_tx_intr_en(rmt_channel_t channel, bool en) return ESP_OK; } -esp_err_t rmt_set_evt_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh) +esp_err_t rmt_set_tx_thr_intr_en(rmt_channel_t channel, bool en, uint16_t evt_thresh) { RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); RMT_CHECK(evt_thresh < 256, "RMT EVT THRESH ERR", ESP_ERR_INVALID_ARG); @@ -624,7 +624,7 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel) rmt_set_rx_intr_en(channel, 0); rmt_set_err_intr_en(channel, 0); rmt_set_tx_intr_en(channel, 0); - rmt_set_evt_intr_en(channel, 0, 0xffff); + rmt_set_tx_thr_intr_en(channel, 0, 0xffff); if(p_rmt_obj[channel]->tx_sem) { vSemaphoreDelete(p_rmt_obj[channel]->tx_sem); p_rmt_obj[channel]->tx_sem = NULL; @@ -697,7 +697,7 @@ esp_err_t rmt_write_items(rmt_channel_t channel, rmt_item32_t* rmt_item, int ite RMT.apb_conf.mem_tx_wrap_en = 1; len_rem -= item_block_len; RMT.conf_ch[channel].conf1.tx_conti_mode = 0; - rmt_set_evt_intr_en(channel, 1, item_sub_len); + rmt_set_tx_thr_intr_en(channel, 1, item_sub_len); p_rmt->tx_data = rmt_item + item_block_len; p_rmt->tx_len_rem = len_rem; p_rmt->tx_offset = 0; From 665dcc571280900ee206966fded16d39948ba3a2 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 14:20:00 +1100 Subject: [PATCH 45/59] linux docs: Add note about precompiled gdb on Arch Closes github #150: https://github.com/espressif/esp-idf/issues/150 --- docs/linux-setup.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/linux-setup.rst b/docs/linux-setup.rst index cf5e78b63d..20f460aa62 100644 --- a/docs/linux-setup.rst +++ b/docs/linux-setup.rst @@ -17,7 +17,6 @@ To compile with ESP-IDF you need to get the following packages: sudo pacman -S --needed gcc git make ncurses flex bison gperf python2-pyserial - Step 1: Download binary toolchain for the ESP32 ================================================== @@ -49,6 +48,16 @@ Alternatively, you may create an alias for the above command. This way you can g Then when you need the toolchain you can type ``get_esp32`` on the command line and the toolchain will be added to your ``PATH``. +Arch Linux Users +---------------- + +To run the precompiled gdb (xtensa-esp32-elf-gdb) in Arch Linux requires ncurses 5, but Arch uses ncurses 6. Backwards compatibility libraries are available in AUR_ for native and lib32 configurations: +- https://aur.archlinux.org/packages/ncurses5-compat-libs/ +- https://aur.archlinux.org/packages/lib32-ncurses5-compat-libs/ + +(Alternatively, use crosstool-NG to compile a gdb that links against ncurses 6.) + + Alternative Step 1: Compile the toolchain from source using crosstool-NG ======================================================================== @@ -156,3 +165,4 @@ Further reading If you'd like to use the Eclipse IDE instead of running ``make``, check out the Eclipse setup guide in this directory. +.. _AUR: https://wiki.archlinux.org/index.php/Arch_User_Repository From e6b09dc258a415c5cda5bdc282ffc02e59ed8821 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 14:47:14 +1100 Subject: [PATCH 46/59] FreeRTOS: Default to canary byte stack overflow checking Was mistakenly "none" due to name change not being propagated. Closes github issue #181: https://github.com/espressif/esp-idf/issues/181 --- components/freertos/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/freertos/Kconfig b/components/freertos/Kconfig index f03da6bc01..2489270c05 100644 --- a/components/freertos/Kconfig +++ b/components/freertos/Kconfig @@ -54,7 +54,7 @@ config FREERTOS_ASSERT_ON_UNTESTED_FUNCTION choice FREERTOS_CHECK_STACKOVERFLOW prompt "Check for stack overflow" - default FREERTOS_CHECK_STACKOVERFLOW_QUICK + default FREERTOS_CHECK_STACKOVERFLOW_CANARY help FreeRTOS can check for stack overflows in threads and trigger an user function called vApplicationStackOverflowHook when this happens. From 06e03ff52e0737ba6ecd6ec0e40a7d0ef8e48335 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Dec 2016 12:42:21 +1100 Subject: [PATCH 47/59] Replace backwards-compatible portTICK_RATE_MS with FreeRTOS v8+ portTICK_PERIOD_MS Closes github #51 https://github.com/espressif/esp-idf/issues/51 --- components/bt/bluedroid/btc/core/btc_task.c | 2 +- components/bt/bluedroid/hci/hci_hal_h4.c | 2 +- components/bt/bluedroid/hci/hci_layer.c | 2 +- components/bt/bluedroid/osi/osi_arch.c | 8 ++++---- components/bt/bluedroid/stack/btu/btu_task.c | 2 +- components/bt/bt.c | 2 +- components/esp32/test/test_intr_alloc.c | 16 ++++++++-------- components/ethernet/emac_main.c | 4 ++-- .../freertos/test/test_freertos_eventgroups.c | 4 ++-- components/lwip/port/freertos/sys_arch.c | 14 +++++++------- examples/01_hello_world/main/hello_world_main.c | 2 +- examples/02_blink/main/blink.c | 4 ++-- .../03_http_request/main/http_request_main.c | 10 +++++----- .../04_https_request/main/https_request_main.c | 2 +- examples/07_nvs_rw_value/main/nvs_rw_value.c | 2 +- examples/08_nvs_rw_blob/main/nvs_rw_blob.c | 4 ++-- examples/11_rmt_nec_tx_rx/main/infrared_nec.c | 2 +- examples/12_blufi/components/blufi/blufi_task.c | 2 +- examples/16_pcnt/main/pcnt_test.c | 2 +- examples/17_ethernet/main/ethernet_main.c | 4 ++-- examples/19_sigmadelta/main/sigmadelta_test.c | 2 +- 21 files changed, 46 insertions(+), 46 deletions(-) diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index b4ce0d95ca..773d7889bb 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -80,7 +80,7 @@ static bt_status_t btc_task_post(btc_msg_t *msg) return BT_STATUS_PARM_INVALID; } - if (xQueueSend(xBtcQueue, msg, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xBtcQueue, msg, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("Btc Post failed\n"); return BT_STATUS_BUSY; } diff --git a/components/bt/bluedroid/hci/hci_hal_h4.c b/components/bt/bluedroid/hci/hci_hal_h4.c index 922ee6ecc0..237226266a 100644 --- a/components/bt/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/bluedroid/hci/hci_hal_h4.c @@ -177,7 +177,7 @@ void hci_hal_h4_task_post(void) evt.sig = 0xff; evt.par = 0; - if (xQueueSend(xHciH4Queue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xHciH4Queue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("xHciH4Queue failed\n"); } } diff --git a/components/bt/bluedroid/hci/hci_layer.c b/components/bt/bluedroid/hci/hci_layer.c index e0f15e0ea9..d71690d4a6 100644 --- a/components/bt/bluedroid/hci/hci_layer.c +++ b/components/bt/bluedroid/hci/hci_layer.c @@ -146,7 +146,7 @@ void hci_host_task_post(void) evt.sig = 0xff; evt.par = 0; - if (xQueueSend(xHciHostQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xHciHostQueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("xHciHostQueue failed\n"); } } diff --git a/components/bt/bluedroid/osi/osi_arch.c b/components/bt/bluedroid/osi/osi_arch.c index e896efd871..d1d0185aec 100644 --- a/components/bt/bluedroid/osi/osi_arch.c +++ b/components/bt/bluedroid/osi/osi_arch.c @@ -137,9 +137,9 @@ osi_sem_wait(osi_sem_t *sem, uint32_t timeout) StartTime = xTaskGetTickCount(); if (timeout != 0) { - if (xSemaphoreTake(*sem, timeout / portTICK_RATE_MS) == pdTRUE) { + if (xSemaphoreTake(*sem, timeout / portTICK_PERIOD_MS) == pdTRUE) { EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -153,7 +153,7 @@ osi_sem_wait(osi_sem_t *sem, uint32_t timeout) while (xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE); EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -190,7 +190,7 @@ osi_now(void) void osi_delay_ms(uint32_t ms) { - vTaskDelay(ms / portTICK_RATE_MS); + vTaskDelay(ms / portTICK_PERIOD_MS); } diff --git a/components/bt/bluedroid/stack/btu/btu_task.c b/components/bt/bluedroid/stack/btu/btu_task.c index 5cca29dd83..4c640fad7c 100644 --- a/components/bt/bluedroid/stack/btu/btu_task.c +++ b/components/bt/bluedroid/stack/btu/btu_task.c @@ -338,7 +338,7 @@ void btu_task_post(uint32_t sig) evt.sig = sig; evt.par = 0; - if (xQueueSend(xBtuQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xBtuQueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("xBtuQueue failed\n"); } } diff --git a/components/bt/bt.c b/components/bt/bt.c index ef9a063d69..8d1fecb777 100644 --- a/components/bt/bt.c +++ b/components/bt/bt.c @@ -85,7 +85,7 @@ static int32_t IRAM_ATTR semphr_give_from_isr_wrapper(void *semphr, void *hptw) static int32_t IRAM_ATTR semphr_take_wrapper(void *semphr, uint32_t block_time_ms) { - return (int32_t)xSemaphoreTake(semphr, block_time_ms / portTICK_RATE_MS); + return (int32_t)xSemaphoreTake(semphr, block_time_ms / portTICK_PERIOD_MS); } static void *IRAM_ATTR mutex_create_wrapper(void) diff --git a/components/esp32/test/test_intr_alloc.c b/components/esp32/test/test_intr_alloc.c index 31991b4e41..329fc9a9e4 100644 --- a/components/esp32/test/test_intr_alloc.c +++ b/components/esp32/test/test_intr_alloc.c @@ -98,7 +98,7 @@ static void timer_test(int flags) { esp_intr_get_intno(inth[0]), esp_intr_get_intno(inth[1]), esp_intr_get_intno(inth[2]), esp_intr_get_intno(inth[3])); printf("Timer values on start: %d %d %d %d\n", count[0], count[1], count[2], count[3]); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); TEST_ASSERT(count[0]==0); TEST_ASSERT(count[1]!=0); @@ -110,7 +110,7 @@ static void timer_test(int flags) { esp_intr_disable(inth[1]); esp_intr_disable(inth[2]); for (x=0; x<4; x++) count[x]=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); TEST_ASSERT(count[0]!=0); TEST_ASSERT(count[1]==0); @@ -122,7 +122,7 @@ static void timer_test(int flags) { esp_intr_disable(inth[0]); esp_intr_disable(inth[3]); for (x=0; x<4; x++) count[x]=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); TEST_ASSERT(count[0]==0); TEST_ASSERT(count[1]!=0); @@ -152,18 +152,18 @@ void local_timer_test() printf("Int timer 1 intno %d\n", esp_intr_get_intno(ih)); xthal_set_ccompare(1, xthal_get_ccount()+8000000); int_timer_ctr=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr!=0); printf("Disabling int\n"); esp_intr_disable(ih); int_timer_ctr=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr==0); printf("Re-enabling\n"); esp_intr_enable(ih); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr!=0); @@ -173,12 +173,12 @@ void local_timer_test() r=esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED, int_timer_handler, NULL, &ih); TEST_ASSERT(r==ESP_OK); int_timer_ctr=0; - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr==0); printf("Re-enabling\n"); esp_intr_enable(ih); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); printf("Timer val after 1 sec: %d\n", int_timer_ctr); TEST_ASSERT(int_timer_ctr!=0); r=esp_intr_free(ih); diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index ab2ca8964c..20d428cf7b 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -585,7 +585,7 @@ void emac_link_check_func(void *pv_parameters) static bool emac_link_check_timer_init(void) { - emac_timer = xTimerCreate("emac_timer", (2000 / portTICK_RATE_MS), + emac_timer = xTimerCreate("emac_timer", (2000 / portTICK_PERIOD_MS), pdTRUE, (void *)rand(), emac_link_check_func); if (emac_timer == NULL) { return false; @@ -844,7 +844,7 @@ esp_err_t IRAM_ATTR emac_post(emac_sig_t sig, emac_par_t par) evt.sig = sig; evt.par = par; - if (xQueueSend(emac_xqueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(emac_xqueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { return ESP_FAIL; } } diff --git a/components/freertos/test/test_freertos_eventgroups.c b/components/freertos/test/test_freertos_eventgroups.c index 35a5cc4ed2..b17e127c28 100644 --- a/components/freertos/test/test_freertos_eventgroups.c +++ b/components/freertos/test/test_freertos_eventgroups.c @@ -34,7 +34,7 @@ static void task_event_group_call_response(void *param) printf("Task %d done\n", task_num); /* Delay is due to not-yet-fixed bug with deleting tasks at same time */ - vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelete(NULL); } @@ -85,7 +85,7 @@ static void task_test_sync(void *param) printf("Done %d = %x\n", task_num, after_done); /* Delay is due to not-yet-fixed bug with deleting tasks at same time */ - vTaskDelay(100 / portTICK_RATE_MS); + vTaskDelay(100 / portTICK_PERIOD_MS); vTaskDelete(NULL); } diff --git a/components/lwip/port/freertos/sys_arch.c b/components/lwip/port/freertos/sys_arch.c index 97ee32d1bd..7f96c9b859 100755 --- a/components/lwip/port/freertos/sys_arch.c +++ b/components/lwip/port/freertos/sys_arch.c @@ -164,9 +164,9 @@ sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) StartTime = xTaskGetTickCount(); if (timeout != 0) { - if (xSemaphoreTake(*sem, timeout / portTICK_RATE_MS) == pdTRUE) { + if (xSemaphoreTake(*sem, timeout / portTICK_PERIOD_MS) == pdTRUE) { EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -180,7 +180,7 @@ sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) while (xSemaphoreTake(*sem, portMAX_DELAY) != pdTRUE); EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -293,9 +293,9 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) sys_mutex_lock(&(*mbox)->lock); if (timeout != 0) { - if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), timeout / portTICK_RATE_MS)) { + if (pdTRUE == xQueueReceive((*mbox)->os_mbox, &(*msg), timeout / portTICK_PERIOD_MS)) { EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -323,7 +323,7 @@ sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout) } EndTime = xTaskGetTickCount(); - Elapsed = (EndTime - StartTime) * portTICK_RATE_MS; + Elapsed = (EndTime - StartTime) * portTICK_PERIOD_MS; if (Elapsed == 0) { Elapsed = 1; @@ -566,7 +566,7 @@ void sys_thread_sem_deinit(void) void sys_delay_ms(uint32_t ms) { - vTaskDelay(ms/portTICK_RATE_MS); + vTaskDelay(ms / portTICK_PERIOD_MS); } diff --git a/examples/01_hello_world/main/hello_world_main.c b/examples/01_hello_world/main/hello_world_main.c index 0e872522fa..c8b9f5f0c9 100644 --- a/examples/01_hello_world/main/hello_world_main.c +++ b/examples/01_hello_world/main/hello_world_main.c @@ -17,7 +17,7 @@ void hello_task(void *pvParameter) printf("Hello world!\n"); for (int i = 10; i >= 0; i--) { printf("Restarting in %d seconds...\n", i); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } printf("Restarting now.\n"); fflush(stdout); diff --git a/examples/02_blink/main/blink.c b/examples/02_blink/main/blink.c index 1e49e51b2f..f97572ac21 100644 --- a/examples/02_blink/main/blink.c +++ b/examples/02_blink/main/blink.c @@ -33,10 +33,10 @@ void blink_task(void *pvParameter) while(1) { /* Blink off (output low) */ gpio_set_level(BLINK_GPIO, 0); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); /* Blink on (output high) */ gpio_set_level(BLINK_GPIO, 1); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } } diff --git a/examples/03_http_request/main/http_request_main.c b/examples/03_http_request/main/http_request_main.c index 9fe1933373..3831ae65b9 100644 --- a/examples/03_http_request/main/http_request_main.c +++ b/examples/03_http_request/main/http_request_main.c @@ -115,7 +115,7 @@ static void http_get_task(void *pvParameters) if(err != 0 || res == NULL) { ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } @@ -129,7 +129,7 @@ static void http_get_task(void *pvParameters) if(s < 0) { ESP_LOGE(TAG, "... Failed to allocate socket."); freeaddrinfo(res); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } ESP_LOGI(TAG, "... allocated socket\r\n"); @@ -138,7 +138,7 @@ static void http_get_task(void *pvParameters) ESP_LOGE(TAG, "... socket connect failed errno=%d", errno); close(s); freeaddrinfo(res); - vTaskDelay(4000 / portTICK_RATE_MS); + vTaskDelay(4000 / portTICK_PERIOD_MS); continue; } @@ -148,7 +148,7 @@ static void http_get_task(void *pvParameters) if (write(s, REQUEST, strlen(REQUEST)) < 0) { ESP_LOGE(TAG, "... socket send failed"); close(s); - vTaskDelay(4000 / portTICK_RATE_MS); + vTaskDelay(4000 / portTICK_PERIOD_MS); continue; } ESP_LOGI(TAG, "... socket send success"); @@ -166,7 +166,7 @@ static void http_get_task(void *pvParameters) close(s); for(int countdown = 10; countdown >= 0; countdown--) { ESP_LOGI(TAG, "%d... ", countdown); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } ESP_LOGI(TAG, "Starting again!"); } diff --git a/examples/04_https_request/main/https_request_main.c b/examples/04_https_request/main/https_request_main.c index caf3f374a3..933d97ac83 100644 --- a/examples/04_https_request/main/https_request_main.c +++ b/examples/04_https_request/main/https_request_main.c @@ -362,7 +362,7 @@ static void https_get_task(void *pvParameters) for(int countdown = 10; countdown >= 0; countdown--) { ESP_LOGI(TAG, "%d...", countdown); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } ESP_LOGI(TAG, "Starting again!"); } diff --git a/examples/07_nvs_rw_value/main/nvs_rw_value.c b/examples/07_nvs_rw_value/main/nvs_rw_value.c index dac2d4077e..1b3e06b859 100644 --- a/examples/07_nvs_rw_value/main/nvs_rw_value.c +++ b/examples/07_nvs_rw_value/main/nvs_rw_value.c @@ -72,7 +72,7 @@ void app_main() // Restart module for (int i = 10; i >= 0; i--) { printf("Restarting in %d seconds...\n", i); - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); } printf("Restarting now.\n"); fflush(stdout); diff --git a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c index 7c13c15ba7..0d4b7db4ee 100644 --- a/examples/08_nvs_rw_blob/main/nvs_rw_blob.c +++ b/examples/08_nvs_rw_blob/main/nvs_rw_blob.c @@ -164,7 +164,7 @@ void app_main() */ while (1) { if (gpio_get_level(GPIO_NUM_0) == 0) { - vTaskDelay(1000 / portTICK_RATE_MS); + vTaskDelay(1000 / portTICK_PERIOD_MS); if(gpio_get_level(GPIO_NUM_0) == 0) { err = save_run_time(); if (err != ESP_OK) printf("Error (%d) saving run time blob to NVS!\n", err); @@ -173,6 +173,6 @@ void app_main() esp_restart(); } } - vTaskDelay(200 / portTICK_RATE_MS); + vTaskDelay(200 / portTICK_PERIOD_MS); } } diff --git a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c index d2f7b091fa..ea42502749 100644 --- a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c +++ b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c @@ -352,7 +352,7 @@ void rmt_nec_tx_task() rmt_wait_tx_done(channel); //before we free the data, make sure sending is already done. free(item); - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); } vTaskDelete(NULL); } diff --git a/examples/12_blufi/components/blufi/blufi_task.c b/examples/12_blufi/components/blufi/blufi_task.c index cda66c0511..75547c6432 100644 --- a/examples/12_blufi/components/blufi/blufi_task.c +++ b/examples/12_blufi/components/blufi/blufi_task.c @@ -69,7 +69,7 @@ static esp_err_t blufi_task_post(uint32_t sig, void *par, void *cb, void *arg) evt.cb = cb; evt.arg = arg; - if (xQueueSend(xBlufiTaskQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) { + if (xQueueSend(xBlufiTaskQueue, &evt, 10 / portTICK_PERIOD_MS) != pdTRUE) { LOG_ERROR("Blufi Post failed\n"); return ESP_FAIL; } diff --git a/examples/16_pcnt/main/pcnt_test.c b/examples/16_pcnt/main/pcnt_test.c index b8489ecb2f..0185f9b6f9 100644 --- a/examples/16_pcnt/main/pcnt_test.c +++ b/examples/16_pcnt/main/pcnt_test.c @@ -196,7 +196,7 @@ void app_main() portBASE_TYPE res; while(1) { - res = xQueueReceive(pcnt_evt_queue, &evt, 1000 / portTICK_RATE_MS); + res = xQueueReceive(pcnt_evt_queue, &evt, 1000 / portTICK_PERIOD_MS); if(res == pdTRUE) { pcnt_get_counter_value(PCNT_TEST_UNIT, &count); printf("Event PCNT unit[%d]; cnt: %d\n", evt.unit, count); diff --git a/examples/17_ethernet/main/ethernet_main.c b/examples/17_ethernet/main/ethernet_main.c index 7e84a9badd..fc4347f7d8 100644 --- a/examples/17_ethernet/main/ethernet_main.c +++ b/examples/17_ethernet/main/ethernet_main.c @@ -108,11 +108,11 @@ void eth_task(void *pvParameter) { tcpip_adapter_ip_info_t ip; memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); while (1) { - vTaskDelay(2000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_PERIOD_MS); if (tcpip_adapter_get_ip_info(ESP_IF_ETH, &ip) == 0) { ESP_LOGI(TAG, "\n~~~~~~~~~~~\n"); diff --git a/examples/19_sigmadelta/main/sigmadelta_test.c b/examples/19_sigmadelta/main/sigmadelta_test.c index 92bc6e0a83..60880311d5 100644 --- a/examples/19_sigmadelta/main/sigmadelta_test.c +++ b/examples/19_sigmadelta/main/sigmadelta_test.c @@ -46,7 +46,7 @@ void app_main() while(1) { sigmadelta_set_duty(SIGMADELTA_CHANNEL_0, duty); /*by changing delay time, you can change the blink frequency of LED. */ - vTaskDelay(10 / portTICK_RATE_MS); + vTaskDelay(10 / portTICK_PERIOD_MS); duty += inc; if(duty == 127 || duty == -127) inc = (-1) * inc; From ade7ee20920f733a682f5be6ed9e6705c5db5ceb Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Sat, 24 Dec 2016 20:45:57 +0800 Subject: [PATCH 48/59] gpio_driver: add per-pin interrupt handlers 1. add ISR handler apis so that users of different layers can hook their own isr handler on different GPIO. Audio project has different software layers, they need different gpio isr handler for layer instead of processing all GPIO interrupts in one handler. If this kind of calling a handler from isr is not proper, please kindly point out. 2. add gpio example code. 3. improve gpio.rst 4. add readme for gpio example Squashed commits: [278e50f] update: GPIO 1. coding style, add a space between conditional or loop keyword and an opening paren. 2. modify some return value and doc 3. use printf in example code Squashed commits: [efb23bb] minor change of comment --- components/driver/gpio.c | 134 +++++++++++++++++++++--- components/driver/include/driver/gpio.h | 128 ++++++++-------------- docs/api/gpio.rst | 28 +++-- examples/21_gpio/Makefile | 9 ++ examples/21_gpio/README.md | 20 ++++ examples/21_gpio/main/component.mk | 3 + examples/21_gpio/main/gpio_test.c | 114 ++++++++++++++++++++ 7 files changed, 330 insertions(+), 106 deletions(-) create mode 100644 examples/21_gpio/Makefile create mode 100644 examples/21_gpio/README.md create mode 100644 examples/21_gpio/main/component.mk create mode 100644 examples/21_gpio/main/gpio_test.c diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 3201372bc1..4e83705408 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -72,45 +72,59 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = { GPIO_PIN_REG_39 }; -esp_err_t gpio_pullup_en(gpio_num_t gpio_num) { +typedef struct { + gpio_isr_t fn; /*!< isr function */ + void* args; /*!< isr function args */ +} gpio_isr_func_t; + +static gpio_isr_func_t* gpio_isr_func = NULL; +static gpio_isr_handle_t gpio_isr_handle; +static portMUX_TYPE gpio_spinlock = portMUX_INITIALIZER_UNLOCKED; + +esp_err_t gpio_pullup_en(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { rtc_gpio_pullup_en(gpio_num); - }else{ + } else { REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU); } return ESP_OK; } -esp_err_t gpio_pullup_dis(gpio_num_t gpio_num) { +esp_err_t gpio_pullup_dis(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { rtc_gpio_pullup_dis(gpio_num); - }else{ + } else { REG_CLR_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU); } return ESP_OK; } -esp_err_t gpio_pulldown_en(gpio_num_t gpio_num) { +esp_err_t gpio_pulldown_en(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { rtc_gpio_pulldown_en(gpio_num); - }else{ - REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD); + } else { + REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD); } return ESP_OK; } -esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num) { +esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num) +{ GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); - if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ - rtc_gpio_pulldown_dis(gpio_num); - }else{ + if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) { + rtc_gpio_pulldown_dis(gpio_num); + } else { REG_CLR_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD); } return ESP_OK; } + esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type) { GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); @@ -323,6 +337,96 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) return ESP_OK; } +void IRAM_ATTR gpio_intr_service(void* arg) +{ + //GPIO intr process + uint32_t gpio_num = 0; + //read status to get interrupt status for GPIO0-31 + uint32_t gpio_intr_status; + gpio_intr_status = GPIO.status; + //read status1 to get interrupt status for GPIO32-39 + uint32_t gpio_intr_status_h; + gpio_intr_status_h = GPIO.status1.intr_st; + + if (gpio_isr_func == NULL) { + return; + } + do { + if (gpio_num < 32) { + if (gpio_intr_status & BIT(gpio_num)) { //gpio0-gpio31 + if (gpio_isr_func[gpio_num].fn != NULL) { + gpio_isr_func[gpio_num].fn(gpio_isr_func[gpio_num].args); + } + GPIO.status_w1tc = BIT(gpio_num); + } + } else { + if (gpio_intr_status_h & BIT(gpio_num - 32)) { + if (gpio_isr_func[gpio_num].fn != NULL) { + gpio_isr_func[gpio_num].fn(gpio_isr_func[gpio_num].args); + } + GPIO.status1_w1tc.intr_st = BIT(gpio_num - 32); + } + } + } while (++gpio_num < GPIO_PIN_COUNT); +} + +esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void* args) +{ + GPIO_CHECK(gpio_isr_func != NULL, "GPIO isr service is not installed, call gpio_install_isr_service() first", ESP_ERR_INVALID_STATE); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&gpio_spinlock); + gpio_intr_disable(gpio_num); + if (gpio_isr_func) { + gpio_isr_func[gpio_num].fn = isr_handler; + gpio_isr_func[gpio_num].args = args; + } + gpio_intr_enable(gpio_num); + portEXIT_CRITICAL(&gpio_spinlock); + return ESP_OK; +} + +esp_err_t gpio_isr_handler_remove(gpio_num_t gpio_num) +{ + GPIO_CHECK(gpio_isr_func != NULL, "GPIO isr service is not installed, call gpio_install_isr_service() first", ESP_ERR_INVALID_STATE); + GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); + portENTER_CRITICAL(&gpio_spinlock); + gpio_intr_disable(gpio_num); + if (gpio_isr_func) { + gpio_isr_func[gpio_num].fn = NULL; + gpio_isr_func[gpio_num].args = NULL; + } + portEXIT_CRITICAL(&gpio_spinlock); + return ESP_OK; +} + +esp_err_t gpio_install_isr_service(int intr_alloc_flags) +{ + GPIO_CHECK(gpio_isr_func == NULL, "GPIO isr service already installed", ESP_FAIL); + esp_err_t ret; + portENTER_CRITICAL(&gpio_spinlock); + gpio_isr_func = (gpio_isr_func_t*) calloc(GPIO_NUM_MAX, sizeof(gpio_isr_func_t)); + if (gpio_isr_func == NULL) { + ret = ESP_ERR_NO_MEM; + } else { + ret = gpio_isr_register(gpio_intr_service, NULL, intr_alloc_flags, &gpio_isr_handle); + } + portEXIT_CRITICAL(&gpio_spinlock); + return ret; +} + +void gpio_uninstall_isr_service() +{ + if (gpio_isr_func == NULL) { + return; + } + portENTER_CRITICAL(&gpio_spinlock); + esp_intr_free(gpio_isr_handle); + free(gpio_isr_func); + gpio_isr_func = NULL; + portEXIT_CRITICAL(&gpio_spinlock); + return; +} + esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, gpio_isr_handle_t *handle) { GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG); @@ -334,7 +438,7 @@ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type) { GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG); esp_err_t ret = ESP_OK; - if ((intr_type == GPIO_INTR_LOW_LEVEL) || (intr_type == GPIO_INTR_HIGH_LEVEL)) { + if (( intr_type == GPIO_INTR_LOW_LEVEL ) || ( intr_type == GPIO_INTR_HIGH_LEVEL )) { GPIO.pin[gpio_num].int_type = intr_type; GPIO.pin[gpio_num].wakeup_enable = 0x1; } else { diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 4f2a800724..1472ba8352 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -156,6 +156,7 @@ typedef enum { GPIO_NUM_37 = 37, /*!< GPIO37, input mode only */ GPIO_NUM_38 = 38, /*!< GPIO38, input mode only */ GPIO_NUM_39 = 39, /*!< GPIO39, input mode only */ + GPIO_NUM_MAX = 40, } gpio_num_t; typedef enum { @@ -205,9 +206,8 @@ typedef enum { } gpio_pull_mode_t; - +typedef void (*gpio_isr_t)(void*); typedef intr_handle_t gpio_isr_handle_t; -typedef void (*gpio_event_callback)(gpio_num_t gpio_intr_num); /** * @brief GPIO common configuration @@ -357,8 +357,6 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); */ esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, gpio_isr_handle_t *handle); - - /** * @brief Enable pull-up on GPIO. * @@ -403,93 +401,55 @@ esp_err_t gpio_pulldown_en(gpio_num_t gpio_num); */ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); +/** + * @brief Install a GPIO ISR service, so we can assign different ISR handler for different pins + * + * @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred) + * ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. + * + * @return + * - ESP_OK Success + * - ESP_FAIL Operation fail + * - ESP_ERR_NO_MEM No memory to install this service + */ +esp_err_t gpio_install_isr_service(int intr_alloc_flags); /** - * *************** ATTENTION ********************/ -/** - *@attention - * Each GPIO has its own separate configuration register, so we do not use - * a lock to serialize access to them. This works under the assumption that - * no situation will occur where two tasks try to configure the same GPIO - * pin simultaneously. It is up to the application developer to guarantee this. - */ + * @brief Un-install GPIO ISR service, free the resources. + */ +void gpio_uninstall_isr_service(); /** - *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * - * @code{c} - * gpio_config_t io_conf; - * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt - * io_conf.mode = GPIO_MODE_OUTPUT; //set as output mode - * io_conf.pin_bit_mask = GPIO_SEL_18 | GPIO_SEL_19; //bit mask of the pins that you want to set,e.g.GPIO18/19 - * io_conf.pull_down_en = 0; //disable pull-down mode - * io_conf.pull_up_en = 0; //disable pull-up mode - * gpio_config(&io_conf); //configure GPIO with the given settings - * @endcode - **/ + * @brief Add ISR handler for the corresponding GPIO. + * + * Interrupt handlers no longer need to be declared with IRAM_ATTR, unless you pass the ESP_INTR_FLAG_IRAM flag + * when allocating the ISR in gpio_install_isr_service(). + * This ISR handler will be called from an ISR. So there probably is some stack size limit, and this limit + * is smaller compared to a "raw" interrupt handler due to another level of indirection. + * + * @param gpio_num GPIO number + * @param isr_handler ISR handler function for the corresponding GPIO number. + * @param args parameter for ISR handler. + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Wrong state, the ISR service has not been initialized. + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_isr_handler_add(gpio_num_t gpio_num, gpio_isr_t isr_handler, void* args); /** - *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * - * @code{c} - * io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt - * io_conf.mode = GPIO_MODE_INPUT; //set as input - * io_conf.pin_bit_mask = GPIO_SEL_4 | GPIO_SEL_5; //bit mask of the pins that you want to set, e.g.,GPIO4/5 - * io_conf.pull_down_en = 0; //disable pull-down mode - * io_conf.pull_up_en = 1; //enable pull-up mode - * gpio_config(&io_conf); //configure GPIO with the given settings - * @endcode - */ -/** - *----------EXAMPLE TO SET ISR HANDLER ---------------------- - * @code{c} - * gpio_isr_register(gpio_intr_test, 0, NULL); //hook the isr handler for GPIO interrupt - * @endcode - */ -/** - *-------------EXAMPLE OF HANDLER FUNCTION-------------------* - * @code{c} - * #include "esp_attr.h" - * void IRAM_ATTR gpio_intr_test(void* arg) - * { - * //GPIO intr process - * ets_printf("in gpio_intr\n"); - * uint32_t gpio_num = 0; - * uint32_t gpio_intr_status = READ_PERI_REG(GPIO_STATUS_REG); //read status to get interrupt status for GPIO0-31 - * uint32_t gpio_intr_status_h = READ_PERI_REG(GPIO_STATUS1_REG);//read status1 to get interrupt status for GPIO32-39 - * SET_PERI_REG_MASK(GPIO_STATUS_W1TC_REG, gpio_intr_status); //Clear intr for gpio0-gpio31 - * SET_PERI_REG_MASK(GPIO_STATUS1_W1TC_REG, gpio_intr_status_h); //Clear intr for gpio32-39 - * do { - * if(gpio_num < 32) { - * if(gpio_intr_status & BIT(gpio_num)) { //gpio0-gpio31 - * ets_printf("Intr GPIO%d ,val: %d\n",gpio_num,gpio_get_level(gpio_num)); - * //This is an isr handler, you should post an event to process it in RTOS queue. - * } - * } else { - * if(gpio_intr_status_h & BIT(gpio_num - 32)) { - * ets_printf("Intr GPIO%d, val : %d\n",gpio_num,gpio_get_level(gpio_num)); - * //This is an isr handler, you should post an event to process it in RTOS queue. - * } - * } - * } while(++gpio_num < GPIO_PIN_COUNT); - * } - * @endcode - */ + * @brief Remove ISR handler for the corresponding GPIO. + * + * @param gpio_num GPIO number + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_STATE Wrong state, the ISR service has not been initialized. + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t gpio_isr_handler_remove(gpio_num_t gpio_num); -/** - *----EXAMPLE OF I2C CONFIG AND PICK SIGNAL FOR IO MATRIX---* - * @code{c} - * gpio_config_t io_conf; - * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt - * io_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; //set as output mode - * io_conf.pin_bit_mask = GPIO_SEL_21 | GPIO_SEL_22; //bit mask of the pins that you want to set,e.g.GPIO21/22 - * io_conf.pull_down_en = 0; //disable pull-down mode - * io_conf.pull_up_en = 1; //enable pull-up mode - * gpio_config(&io_conf); //configure GPIO with the given settings - * gpio_matrix_out(21, EXT_I2C_SCL_O_IDX, 0, 0); //set output signal for io_matrix - * gpio_matrix_out(22, EXT_I2C_SDA_O_IDX, 0, 0); //set output signal for io_matrix - * gpio_matrix_in( 22, EXT_I2C_SDA_I_IDX, 0); //set input signal for io_matrix - * @endcode - * - */ #ifdef __cplusplus } diff --git a/docs/api/gpio.rst b/docs/api/gpio.rst index 0cd4eca365..f331b9d265 100644 --- a/docs/api/gpio.rst +++ b/docs/api/gpio.rst @@ -4,20 +4,17 @@ GPIO Overview -------- -`Instructions`_ +The ESP32 chip features 40 physical GPIO pads. Some GPIO pads cannot be used or do not have the corresponding pin on the chip package(refer to technical reference manual ). Each pad can be used as a general purpose I/O or can be connected to an internal peripheral signal. +Note that GPIO6-11 are usually used for SPI flash. GPIO34-39 can only be set as input mode. Application Example ------------------- -`Instructions`_ +GPIO output and input interrupt example: `examples/21_gpio `_. API Reference ------------- -`Instructions`_ - -.. _Instructions: template.html - Header Files ^^^^^^^^^^^^ @@ -110,7 +107,8 @@ Macros Type Definitions ^^^^^^^^^^^^^^^^ -.. doxygentypedef:: gpio_event_callback +.. doxygentypedef:: gpio_isr_t +.. doxygentypedef:: gpio_isr_handle_t Enumerations ^^^^^^^^^^^^ @@ -122,6 +120,13 @@ Enumerations .. doxygenenum:: gpio_pulldown_t .. doxygenenum:: gpio_pull_mode_t +Structures +^^^^^^^^^^ + +.. doxygenstruct:: gpio_config_t + :members: + + Functions ^^^^^^^^^ @@ -136,3 +141,12 @@ Functions .. doxygenfunction:: gpio_wakeup_enable .. doxygenfunction:: gpio_wakeup_disable .. doxygenfunction:: gpio_isr_register +.. doxygenfunction:: gpio_pullup_en +.. doxygenfunction:: gpio_pullup_dis +.. doxygenfunction:: gpio_pulldown_en +.. doxygenfunction:: gpio_pulldown_dis +.. doxygenfunction:: gpio_install_isr_service +.. doxygenfunction:: gpio_uninstall_isr_service +.. doxygenfunction:: gpio_isr_handler_add +.. doxygenfunction:: gpio_isr_handler_remove + diff --git a/examples/21_gpio/Makefile b/examples/21_gpio/Makefile new file mode 100644 index 0000000000..0589a9f21d --- /dev/null +++ b/examples/21_gpio/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 := gpio + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/21_gpio/README.md b/examples/21_gpio/README.md new file mode 100644 index 0000000000..7436d77532 --- /dev/null +++ b/examples/21_gpio/README.md @@ -0,0 +1,20 @@ +# Example: GPIO + +###This test code shows how to configure gpio and how to use gpio interrupt. + + +####GPIO functions: + + * GPIO18: output + * GPIO19: output + * GPIO4: input, pulled up, interrupt from rising edge and falling edge + * GPIO5: input, pulled up, interrupt from rising edge. + +####Test: + * Connect GPIO18 with GPIO4 + * Connect GPIO19 with GPIO5 + * Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5 + + + + diff --git a/examples/21_gpio/main/component.mk b/examples/21_gpio/main/component.mk new file mode 100644 index 0000000000..44bd2b5273 --- /dev/null +++ b/examples/21_gpio/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/21_gpio/main/gpio_test.c b/examples/21_gpio/main/gpio_test.c new file mode 100644 index 0000000000..a5e9571bca --- /dev/null +++ b/examples/21_gpio/main/gpio_test.c @@ -0,0 +1,114 @@ +/* GPIO 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 "freertos/queue.h" +#include "driver/gpio.h" + +/** + * Brief: + * This test code shows how to configure gpio and how to use gpio interrupt. + * + * GPIO status: + * GPIO18: output + * GPIO19: output + * GPIO4: input, pulled up, interrupt from rising edge and falling edge + * GPIO5: input, pulled up, interrupt from rising edge. + * + * Test: + * Connect GPIO18 with GPIO4 + * Connect GPIO19 with GPIO5 + * Generate pulses on GPIO18/19, that triggers interrupt on GPIO4/5 + * + */ + +#define GPIO_OUTPUT_IO_0 18 +#define GPIO_OUTPUT_IO_1 19 +#define GPIO_OUTPUT_PIN_SEL ((1< Date: Thu, 29 Dec 2016 17:29:14 +0800 Subject: [PATCH 49/59] Add i2s driver --- components/driver/i2s.c | 807 +++++++++++++++++++++++++ components/driver/include/driver/i2s.h | 380 ++++++++++++ examples/22_i2s/Makefile | 8 + examples/22_i2s/main/app_main.c | 72 +++ examples/22_i2s/main/component.mk | 10 + 5 files changed, 1277 insertions(+) create mode 100644 components/driver/i2s.c create mode 100644 components/driver/include/driver/i2s.h create mode 100644 examples/22_i2s/Makefile create mode 100644 examples/22_i2s/main/app_main.c create mode 100644 examples/22_i2s/main/component.mk diff --git a/components/driver/i2s.c b/components/driver/i2s.c new file mode 100644 index 0000000000..d8e6198370 --- /dev/null +++ b/components/driver/i2s.c @@ -0,0 +1,807 @@ +// 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 "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/xtensa_api.h" + +#include "soc/dport_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/sens_reg.h" +#include "rom/lldesc.h" + +#include "driver/gpio.h" +#include "driver/i2s.h" + +#include "esp_intr.h" +#include "esp_err.h" +#include "esp_log.h" + +static const char* I2S_TAG = "I2S"; +#define I2S_CHECK(a, str, ret) if (!(a)) { \ + ESP_LOGE(I2S_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \ + return (ret); \ + } +#define I2S_BASE_CLK (2*APB_CLK_FREQ) +#define I2S_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_spinlock[i2s_num]) +#define I2S_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_spinlock[i2s_num]) +#define I2S_ENTER_CRITICAL() portENTER_CRITICAL(&i2s_spinlock[i2s_num]) +#define I2S_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock[i2s_num]) +#define gpio_matrix_out_check(a, b, c, d) if(a != -1) gpio_matrix_out(a, b, c, d) //if pin = -1, do not need to configure +#define gpio_matrix_in_check(a, b, c) if(a != -1) gpio_matrix_in(a, b, c) + + +/** + * @brief DMA buffer object + * + */ +typedef struct { + char **buf; + int buf_size; + int rw_pos; + void *curr_ptr; + SemaphoreHandle_t mux; + xQueueHandle queue; + lldesc_t **desc; +} i2s_dma_t; + +/** + * @brief I2S object instance + * + */ +typedef struct { + i2s_port_t i2s_num; /*!< I2S port number*/ + int queue_size; /*!< I2S event queue size*/ + QueueHandle_t i2s_queue; /*!< I2S queue handler*/ + int dma_buf_count; /*!< DMA buffer count, number of buffer*/ + int dma_buf_len; /*!< DMA buffer length, length of each buffer*/ + i2s_dma_t *rx; /*!< DMA Tx buffer*/ + i2s_dma_t *tx; /*!< DMA Rx buffer*/ + i2s_isr_handle_t i2s_isr_handle; /*!< I2S Interrupt handle*/ + int channel_num; /*!< Number of channels*/ + int bytes_per_sample; /*!< Bytes per sample*/ + i2s_mode_t mode; /*!< I2S Working mode*/ +} i2s_obj_t; + +static i2s_obj_t *p_i2s_obj[I2S_NUM_MAX] = {0}; +static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1}; +static portMUX_TYPE i2s_spinlock[I2S_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED}; +static esp_err_t i2s_reset_fifo(i2s_port_t i2s_num) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->conf.rx_fifo_reset = 1; + I2S[i2s_num]->conf.rx_fifo_reset = 0; + I2S[i2s_num]->conf.tx_fifo_reset = 1; + I2S[i2s_num]->conf.tx_fifo_reset = 0; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_clear_intr_status(i2s_port_t i2s_num, uint32_t clr_mask) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S[i2s_num]->int_clr.val = clr_mask; + return ESP_OK; +} + +esp_err_t i2s_enable_rx_intr(i2s_port_t i2s_num) +{ + + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.in_suc_eof = 1; + I2S[i2s_num]->int_ena.in_dscr_err = 1; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_disable_rx_intr(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.in_suc_eof = 0; + I2S[i2s_num]->int_ena.in_dscr_err = 0; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_disable_tx_intr(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.out_eof = 0; + I2S[i2s_num]->int_ena.out_dscr_err = 0; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_enable_tx_intr(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + I2S[i2s_num]->int_ena.out_eof = 1; + I2S[i2s_num]->int_ena.out_dscr_err = 1; + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +static esp_err_t i2s_isr_register(i2s_port_t i2s_num, uint8_t intr_alloc_flags, void (*fn)(void*), void * arg, i2s_isr_handle_t *handle) +{ + return esp_intr_alloc(ETS_I2S0_INTR_SOURCE + i2s_num, intr_alloc_flags, fn, arg, handle); +} + +static esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint8_t bits, bool fuzzy) +{ + int factor = (256%bits)? 384 : 256; // According to hardware codec requirement(supported 256fs or 384fs) + int clkmInteger, clkmDecimals, bck = 0; + float denom = (float)1 / 64; + int channel = 2; + + float clkmdiv = (float)I2S_BASE_CLK / (rate * factor); + if (clkmdiv > 256) { + ESP_LOGE(I2S_TAG, "clkmdiv is too large\r\n"); + return ESP_FAIL; + } + clkmInteger = clkmdiv; + clkmDecimals = (clkmdiv - clkmInteger) / denom; + float mclk = clkmInteger + denom * clkmDecimals; + bck = factor/(bits * channel); + + I2S[i2s_num]->clkm_conf.clka_en = 0; + I2S[i2s_num]->clkm_conf.clkm_div_a = 63; + I2S[i2s_num]->clkm_conf.clkm_div_b = clkmDecimals; + I2S[i2s_num]->clkm_conf.clkm_div_num = clkmInteger; + I2S[i2s_num]->sample_rate_conf.tx_bck_div_num = bck; + I2S[i2s_num]->sample_rate_conf.rx_bck_div_num = bck; + I2S[i2s_num]->sample_rate_conf.tx_bits_mod = bits; + I2S[i2s_num]->sample_rate_conf.rx_bits_mod = bits; + float real_rate = (float)(I2S_BASE_CLK / (bck * bits * clkmInteger)/2); + ESP_LOGI(I2S_TAG, "Req RATE: %d, real rate: %0.3f, BITS: %u, CLKM: %u, BCK: %u, MCLK: %0.3f, SCLK: %f, diva: %d, divb: %d", + rate, real_rate, bits, clkmInteger, bck, (float)I2S_BASE_CLK / mclk, real_rate *16*2, 64, clkmDecimals); + return ESP_OK; +} + +static void IRAM_ATTR i2s_intr_handler_default(void *arg) +{ + i2s_obj_t *p_i2s = (i2s_obj_t*) arg; + uint8_t i2s_num = p_i2s->i2s_num; + i2s_dev_t* i2s_reg = I2S[i2s_num]; + i2s_event_t i2s_event; + int dummy; + + portBASE_TYPE high_priority_task_awoken = 0; + + lldesc_t *finish_desc; + + if (i2s_reg->int_st.out_dscr_err || i2s_reg->int_st.in_dscr_err) { + ESP_LOGE(I2S_TAG, "out_dscr_err: %d or in_dscr_err:%d", i2s_reg->int_st.out_dscr_err == 1, i2s_reg->int_st.in_dscr_err == 1); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_DMA_ERROR; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + + if (i2s_reg->int_st.out_eof && p_i2s->tx) { + finish_desc = (lldesc_t*) i2s_reg->out_eof_des_addr; + // All buffers are empty. This means we have an underflow on our hands. + if (xQueueIsQueueFullFromISR(p_i2s->tx->queue)) { + xQueueReceiveFromISR(p_i2s->tx->queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->tx->queue, (void*)(&finish_desc->buf), &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_TX_DONE; + if (xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + + } + + if (i2s_reg->int_st.in_suc_eof && p_i2s->rx) { + // All buffers are full. This means we have an overflow. + finish_desc = (lldesc_t*) i2s_reg->in_eof_des_addr; + if (xQueueIsQueueFullFromISR(p_i2s->rx->queue)) { + xQueueReceiveFromISR(p_i2s->rx->queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->rx->queue, (void*)(&finish_desc->buf), &high_priority_task_awoken); + if (p_i2s->i2s_queue) { + i2s_event.type = I2S_EVENT_RX_DONE; + if (p_i2s->i2s_queue && xQueueIsQueueFullFromISR(p_i2s->i2s_queue)) { + xQueueReceiveFromISR(p_i2s->i2s_queue, &dummy, &high_priority_task_awoken); + } + xQueueSendFromISR(p_i2s->i2s_queue, (void * )&i2s_event, &high_priority_task_awoken); + } + } + if (high_priority_task_awoken == pdTRUE) { + portYIELD_FROM_ISR(); + } + + i2s_reg->int_clr.val = I2S[i2s_num]->int_st.val; +} + +static esp_err_t i2s_destroy_dma_queue(i2s_port_t i2s_num, i2s_dma_t *dma) +{ + int bux_idx; + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGE(I2S_TAG, "Not initialized yet"); + return ESP_FAIL; + } + if (dma == NULL) { + return ESP_FAIL; + } + for (bux_idx = 0; bux_idx < p_i2s_obj[i2s_num]->dma_buf_count; bux_idx++) { + if (dma->desc && dma->desc[bux_idx]) + free(dma->desc[bux_idx]); + if (dma->buf && dma->buf[bux_idx]) + free(dma->buf[bux_idx]); + } + if (dma->buf) + free(dma->buf); + if (dma->desc) + free(dma->desc); + vQueueDelete(dma->queue); + vSemaphoreDelete(dma->mux); + return ESP_OK; +} + +static i2s_dma_t *i2s_create_dma_queue(i2s_port_t i2s_num, int dma_buf_count, int dma_buf_len) +{ + int bux_idx; + int sample_size = p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + i2s_dma_t *dma = (i2s_dma_t*) malloc(sizeof(i2s_dma_t)); + if (dma == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc i2s_dma_t"); + return NULL; + } + memset(dma, 0, sizeof(i2s_dma_t)); + + dma->buf = (char **)malloc(sizeof(char*) * dma_buf_count); + if (dma->buf == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma buffer pointer"); + + return NULL; + } + memset(dma->buf, 0, sizeof(char*) * dma_buf_count); + + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->buf[bux_idx] = (char*) malloc(dma_buf_len * sample_size); + if (dma->buf[bux_idx] == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma buffer"); + i2s_destroy_dma_queue(i2s_num, dma); + return NULL; + } + ESP_LOGD(I2S_TAG, "Addr[%d] = %d", bux_idx, (int)dma->buf[bux_idx]); + memset(dma->buf[bux_idx], 0, dma_buf_len * sample_size); + } + + dma->desc = (lldesc_t**) malloc(sizeof(lldesc_t*) * dma_buf_count); + if (dma->desc == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma description"); + i2s_destroy_dma_queue(i2s_num, dma); + return NULL; + } + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->desc[bux_idx] = (lldesc_t*) malloc(sizeof(lldesc_t)); + if (dma->desc[bux_idx] == NULL) { + ESP_LOGE(I2S_TAG, "Error malloc dma description entry"); + i2s_destroy_dma_queue(i2s_num, dma); + return NULL; + } + } + + for (bux_idx = 0; bux_idx < dma_buf_count; bux_idx++) { + dma->desc[bux_idx]->owner = 1; + dma->desc[bux_idx]->eof = 1; + dma->desc[bux_idx]->sosf = 0; + dma->desc[bux_idx]->length = dma_buf_len * sample_size; + dma->desc[bux_idx]->size = dma_buf_len * sample_size; + dma->desc[bux_idx]->buf = (uint8_t *) dma->buf[bux_idx]; + dma->desc[bux_idx]->offset = 0; + dma->desc[bux_idx]->empty = (uint32_t)((bux_idx < (dma_buf_count - 1)) ? (dma->desc[bux_idx + 1]) : dma->desc[0]); + } + dma->queue = xQueueCreate(dma_buf_count - 1, sizeof(char*)); + dma->mux = xSemaphoreCreateMutex(); + dma->rw_pos = 0; + dma->buf_size = dma_buf_len * sample_size; + dma->curr_ptr = NULL; + ESP_LOGI(I2S_TAG, "DMA Malloc info, datalen=blocksize=%d, dma_buf_count=%d", dma_buf_len * sample_size, dma_buf_count); + return dma; +} + + +esp_err_t i2s_start(i2s_port_t i2s_num) +{ + //start DMA link + I2S_ENTER_CRITICAL(); + esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); + I2S[i2s_num]->int_clr.val = 0xFFFFFFFF; + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + ESP_LOGD(I2S_TAG, "I2S_MODE_TX"); + i2s_enable_tx_intr(i2s_num); + I2S[i2s_num]->out_link.start = 1; + I2S[i2s_num]->conf.tx_start = 1; + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + ESP_LOGD(I2S_TAG, "I2S_MODE_RX"); + i2s_enable_rx_intr(i2s_num); + I2S[i2s_num]->in_link.start = 1; + I2S[i2s_num]->conf.rx_start = 1; + } + esp_intr_enable(p_i2s_obj[i2s_num]->i2s_isr_handle); + I2S_EXIT_CRITICAL(); + return ESP_OK; +} + +esp_err_t i2s_stop(i2s_port_t i2s_num) +{ + I2S_ENTER_CRITICAL(); + esp_intr_disable(p_i2s_obj[i2s_num]->i2s_isr_handle); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + I2S[i2s_num]->out_link.stop = 1; + I2S[i2s_num]->conf.tx_start = 0; + i2s_disable_tx_intr(i2s_num); + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + I2S[i2s_num]->in_link.stop = 1; + I2S[i2s_num]->conf.rx_start = 0; + i2s_disable_rx_intr(i2s_num); + } + I2S_EXIT_CRITICAL(); + return 0; +} + +static esp_err_t configure_dac_pin(void) +{ + SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_DAC_DIG_FORCE_M); + SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_DAC_CLK_INV_M); + + SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_DAC_XPD_FORCE_M); + SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC_M); + + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RUE_M); + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_RDE_M); + + SET_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_DAC_XPD_FORCE_M); + SET_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC_M); + + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RUE_M); + CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_RDE_M); + return ESP_OK; +} + +esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (pin == NULL) { + return configure_dac_pin(); + } + if (pin->bck_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->bck_io_num)) { + ESP_LOGE(I2S_TAG, "bck_io_num error"); + return ESP_FAIL; + } + if (pin->ws_io_num != -1 && !GPIO_IS_VALID_GPIO(pin->ws_io_num)) { + ESP_LOGE(I2S_TAG, "ws_io_num error"); + return ESP_FAIL; + } + if (pin->data_out_num != -1 && !GPIO_IS_VALID_GPIO(pin->data_out_num)) { + ESP_LOGE(I2S_TAG, "data_out_num error"); + return ESP_FAIL; + } + if (pin->data_in_num != -1 && !GPIO_IS_VALID_GPIO(pin->data_in_num)) { + ESP_LOGE(I2S_TAG, "data_in_num error"); + return ESP_FAIL; + } + + int bck_sig = -1, ws_sig = -1, data_out_sig = -1, data_in_sig = -1; + //TX & RX + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + bck_sig = I2S0I_BCK_OUT_IDX; + ws_sig = I2S0I_WS_OUT_IDX; + data_in_sig = I2S0I_DATA_IN15_IDX; + if (i2s_num == I2S_NUM_1) { + bck_sig = I2S1I_BCK_OUT_IDX; + ws_sig = I2S1I_WS_OUT_IDX; + data_in_sig = I2S1I_DATA_IN15_IDX; + } + } + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + bck_sig = I2S0O_BCK_OUT_IDX; + ws_sig = I2S0O_WS_OUT_IDX; + data_out_sig = I2S0O_DATA_OUT23_IDX; + if (i2s_num == I2S_NUM_1) { + bck_sig = I2S1O_BCK_OUT_IDX; + ws_sig = I2S1O_WS_OUT_IDX; + data_out_sig = I2S1O_DATA_OUT23_IDX; + } + } + + gpio_matrix_out_check(pin->data_out_num, data_out_sig, 0, 0); + gpio_matrix_in_check(pin->data_in_num, data_in_sig, 0); + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_MASTER) { + gpio_matrix_out_check(pin->ws_io_num, ws_sig, 0, 0); + gpio_matrix_out_check(pin->bck_io_num, bck_sig, 0, 0); + } else if (p_i2s_obj[i2s_num]->mode & I2S_MODE_SLAVE) { + gpio_matrix_in_check(pin->ws_io_num, ws_sig, 0); + gpio_matrix_in_check(pin->bck_io_num, bck_sig, 0); + } + ESP_LOGE(I2S_TAG, "data: out %d, in: %d, ws: %d, bck: %d", data_out_sig, data_in_sig, ws_sig, bck_sig); + return ESP_OK; +} + +esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_CHECK((p_i2s_obj[i2s_num]->bytes_per_sample > 0), "bits_per_sample not set", ESP_FAIL); + return i2s_set_clk(i2s_num, rate, p_i2s_obj[i2s_num]->bytes_per_sample*8, 0); +} +static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_config) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_CHECK((i2s_config), "param null", ESP_FAIL); + if (i2s_num == I2S_NUM_1) { + periph_module_enable(PERIPH_I2S1_MODULE); + } else { + periph_module_enable(PERIPH_I2S0_MODULE); + } + // configure I2S data port interface. + i2s_reset_fifo(i2s_num); + + //reset i2s + I2S[i2s_num]->conf.tx_reset = 1; + I2S[i2s_num]->conf.tx_reset = 0; + I2S[i2s_num]->conf.rx_reset = 1; + I2S[i2s_num]->conf.rx_reset = 0; + + + //reset dma + I2S[i2s_num]->lc_conf.in_rst = 1; + I2S[i2s_num]->lc_conf.in_rst = 0; + I2S[i2s_num]->lc_conf.out_rst = 1; + I2S[i2s_num]->lc_conf.out_rst = 0; + + + //Enable and configure DMA + I2S[i2s_num]->lc_conf.check_owner = 0; + I2S[i2s_num]->lc_conf.out_loop_test = 0; + I2S[i2s_num]->lc_conf.out_auto_wrback = 0; + I2S[i2s_num]->lc_conf.out_data_burst_en = 0; + I2S[i2s_num]->lc_conf.outdscr_burst_en = 0; + I2S[i2s_num]->lc_conf.out_no_restart_clr = 0; + I2S[i2s_num]->lc_conf.indscr_burst_en = 0; + I2S[i2s_num]->lc_conf.out_eof_mode = 1; + + + I2S[i2s_num]->conf2.lcd_en = 0; + I2S[i2s_num]->conf2.camera_en = 0; + I2S[i2s_num]->pdm_conf.pcm2pdm_conv_en = 0; + I2S[i2s_num]->pdm_conf.pdm2pcm_conv_en = 0; + + I2S[i2s_num]->fifo_conf.dscr_en = 0; + p_i2s_obj[i2s_num]->channel_num = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1; + + I2S[i2s_num]->conf_chan.tx_chan_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1); // 0-two channel;1-right;2-left;3-righ;4-left + I2S[i2s_num]->fifo_conf.tx_fifo_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + I2S[i2s_num]->conf.tx_mono = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + + I2S[i2s_num]->conf_chan.rx_chan_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? i2s_config->channel_format : (i2s_config->channel_format >> 1); // 0-two channel;1-right;2-left;3-righ;4-left + I2S[i2s_num]->fifo_conf.rx_fifo_mod = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + I2S[i2s_num]->conf.rx_mono = i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 0 : 1; // 0-right&left channel;1-one channel + I2S[i2s_num]->fifo_conf.dscr_en = 1;//connect dma to fifo + + I2S[i2s_num]->conf.tx_start = 0; + I2S[i2s_num]->conf.rx_start = 0; + + if (i2s_config->mode & I2S_MODE_TX) { + I2S[i2s_num]->conf.tx_msb_right = 0; + I2S[i2s_num]->conf.tx_right_first = 0; + + I2S[i2s_num]->conf.tx_slave_mod = 0; // Master + I2S[i2s_num]->fifo_conf.tx_fifo_mod_force_en = 1;//? + + if (i2s_config->mode & I2S_MODE_SLAVE) { + I2S[i2s_num]->conf.tx_slave_mod = 1;//TX Slave + } + } + + if (i2s_config->mode & I2S_MODE_RX) { + I2S[i2s_num]->conf.rx_msb_right = 0; + I2S[i2s_num]->conf.rx_right_first = 0; + I2S[i2s_num]->conf.rx_slave_mod = 0; // Master + I2S[i2s_num]->fifo_conf.rx_fifo_mod_force_en = 1;//? + I2S[i2s_num]->rx_eof_num = (i2s_config->dma_buf_len); + if (i2s_config->mode & I2S_MODE_SLAVE) { + I2S[i2s_num]->conf.rx_slave_mod = 1;//RX Slave + } + } + + if (i2s_config->mode & I2S_MODE_DAC_BUILT_IN) { + I2S[i2s_num]->conf2.lcd_en = 1; + I2S[i2s_num]->conf.tx_right_first = 1; + I2S[i2s_num]->fifo_conf.tx_fifo_mod = 3; + } + + if (i2s_config->communication_format & I2S_COMM_FORMAT_I2S) { + I2S[i2s_num]->conf.tx_short_sync = 0; + I2S[i2s_num]->conf.rx_short_sync = 0; + I2S[i2s_num]->conf.tx_msb_shift = 1; + I2S[i2s_num]->conf.rx_msb_shift = 1; + if (i2s_config->communication_format & I2S_COMM_FORMAT_I2S_LSB) { + if (i2s_config->mode & I2S_MODE_TX) { + I2S[i2s_num]->conf.tx_msb_shift = 0; + } + if (i2s_config->mode & I2S_MODE_RX) { + I2S[i2s_num]->conf.rx_msb_shift = 0; + } + } + } + + if (i2s_config->communication_format & I2S_COMM_FORMAT_PCM) { + I2S[i2s_num]->conf.tx_msb_shift = 0; + I2S[i2s_num]->conf.rx_msb_shift = 0; + I2S[i2s_num]->conf.tx_short_sync = 0; + I2S[i2s_num]->conf.rx_short_sync = 0; + if (i2s_config->communication_format & I2S_COMM_FORMAT_PCM_SHORT) { + if (i2s_config->mode & I2S_MODE_TX) { + I2S[i2s_num]->conf.tx_short_sync = 1; + } + if (i2s_config->mode & I2S_MODE_RX) { + I2S[i2s_num]->conf.rx_short_sync = 1; + } + } + } + if ((p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) && (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX)) { + I2S[i2s_num]->conf.sig_loopback = 1; + } + i2s_set_clk(i2s_num, i2s_config->sample_rate, p_i2s_obj[i2s_num]->bytes_per_sample*8, 0); + return ESP_OK; +} + +esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num) +{ + int total_buffer_in_bytes = p_i2s_obj[i2s_num]->dma_buf_count * p_i2s_obj[i2s_num]->dma_buf_len * p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + const uint32_t zero_sample[2] = { 0 }; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + while (total_buffer_in_bytes > 0) { + i2s_push_sample(i2s_num, (const char*) zero_sample, 10); + total_buffer_in_bytes -= p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + } + return ESP_OK; +} + +esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void* i2s_queue) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + I2S_CHECK((i2s_config != NULL), "I2S configuration must not NULL", ESP_FAIL); + I2S_CHECK((i2s_config->dma_buf_count >= 2 && i2s_config->dma_buf_count <= 128), "I2S buffer count less than 128 and more than 2", ESP_FAIL); + I2S_CHECK((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 2048), "I2S buffer length at most 2048 and more than 8", ESP_FAIL); + if (p_i2s_obj[i2s_num] == NULL) { + p_i2s_obj[i2s_num] = (i2s_obj_t*) malloc(sizeof(i2s_obj_t)); + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGE(I2S_TAG, "Malloc I2S driver error"); + return ESP_FAIL; + } + + p_i2s_obj[i2s_num]->i2s_num = i2s_num; + p_i2s_obj[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; + p_i2s_obj[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; + p_i2s_obj[i2s_num]->i2s_queue = i2s_queue; + p_i2s_obj[i2s_num]->mode = i2s_config->mode; + p_i2s_obj[i2s_num]->bytes_per_sample = i2s_config->bits_per_sample/8; + + //initial dma + if (ESP_FAIL == i2s_isr_register(i2s_num, i2s_config->intr_alloc_flags, i2s_intr_handler_default, p_i2s_obj[i2s_num], &p_i2s_obj[i2s_num]->i2s_isr_handle)) { + free(p_i2s_obj[i2s_num]); + ESP_LOGE(I2S_TAG, "Register I2S Interrupt error"); + return ESP_FAIL; + } + i2s_stop(i2s_num); + i2s_param_config(i2s_num, i2s_config); + + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + p_i2s_obj[i2s_num]->tx = i2s_create_dma_queue(i2s_num, i2s_config->dma_buf_count, i2s_config->dma_buf_len); + if (p_i2s_obj[i2s_num]->tx == NULL) { + ESP_LOGE(I2S_TAG, "Failed to create tx dma buffer"); + i2s_driver_uninstall(i2s_num); + return ESP_FAIL; + } + I2S[i2s_num]->out_link.addr = (uint32_t) p_i2s_obj[i2s_num]->tx->desc[0]; + } + + if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + p_i2s_obj[i2s_num]->rx = i2s_create_dma_queue(i2s_num, i2s_config->dma_buf_count, i2s_config->dma_buf_len); + if (p_i2s_obj[i2s_num]->rx == NULL){ + ESP_LOGE(I2S_TAG, "Failed to create rx dma buffer"); + i2s_driver_uninstall(i2s_num); + return ESP_FAIL; + } + I2S[i2s_num]->in_link.addr = (uint32_t) p_i2s_obj[i2s_num]->rx->desc[0]; + } + + + if (i2s_queue) { + p_i2s_obj[i2s_num]->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); + *((QueueHandle_t*) i2s_queue) = p_i2s_obj[i2s_num]->i2s_queue; + ESP_LOGI(I2S_TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_i2s_obj[i2s_num]->i2s_queue)); + } else { + p_i2s_obj[i2s_num]->i2s_queue = NULL; + } + + i2s_start(i2s_num); + } else { + ESP_LOGE(I2S_TAG, "I2S driver already installed"); + return ESP_FAIL; + } + + return ESP_OK; +} + +esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) +{ + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num] == NULL) { + ESP_LOGI(I2S_TAG, "ALREADY NULL"); + return ESP_OK; + } + i2s_stop(i2s_num); + esp_intr_free(p_i2s_obj[i2s_num]->i2s_isr_handle); + + if (p_i2s_obj[i2s_num]->tx != NULL && p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) { + i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->tx); + p_i2s_obj[i2s_num]->tx = NULL; + } + if (p_i2s_obj[i2s_num]->rx != NULL && p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) { + i2s_destroy_dma_queue(i2s_num, p_i2s_obj[i2s_num]->rx); + p_i2s_obj[i2s_num]->rx = NULL; + } + + if (p_i2s_obj[i2s_num]->i2s_queue) { + vQueueDelete(p_i2s_obj[i2s_num]->i2s_queue); + p_i2s_obj[i2s_num]->i2s_queue = NULL; + } + + free(p_i2s_obj[i2s_num]); + p_i2s_obj[i2s_num] = NULL; + + if (i2s_num == I2S_NUM_0) { + periph_module_disable(PERIPH_I2S0_MODULE); + } else if (i2s_num == I2S_NUM_1) { + periph_module_disable(PERIPH_I2S1_MODULE); + } + return ESP_OK; +} + +int i2s_write_bytes(i2s_port_t i2s_num, const char *src, size_t size, TickType_t ticks_to_wait) +{ + char *data_ptr; + int bytes_can_write, bytes_writen = 0; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->tx == NULL) { + return 0; + } + xSemaphoreTake(p_i2s_obj[i2s_num]->tx->mux, (portTickType)portMAX_DELAY); + while (size > 0) { + if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + break; + } + p_i2s_obj[i2s_num]->tx->rw_pos = 0; + } + ESP_LOGD(I2S_TAG, "size: %d, rw_pos: %d, buf_size: %d, curr_ptr: %d", size, p_i2s_obj[i2s_num]->tx->rw_pos, p_i2s_obj[i2s_num]->tx->buf_size, (int)p_i2s_obj[i2s_num]->tx->curr_ptr); + data_ptr = (char*)p_i2s_obj[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; + bytes_can_write = p_i2s_obj[i2s_num]->tx->buf_size - p_i2s_obj[i2s_num]->tx->rw_pos; + if (bytes_can_write > size) { + bytes_can_write = size; + } + memcpy(data_ptr, src, bytes_can_write); + size -= bytes_can_write; + src += bytes_can_write; + p_i2s_obj[i2s_num]->tx->rw_pos += bytes_can_write; + bytes_writen += bytes_can_write; + } + xSemaphoreGive(p_i2s_obj[i2s_num]->tx->mux); + return bytes_writen; +} + +int i2s_read_bytes(i2s_port_t i2s_num, char* dest, size_t size, TickType_t ticks_to_wait) +{ + char *data_ptr; + int bytes_can_read, byte_read = 0; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->rx == NULL) { + return 0; + } + xSemaphoreTake(p_i2s_obj[i2s_num]->rx->mux, (portTickType)portMAX_DELAY); + while (size > 0) { + if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size || p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->rx->queue, &p_i2s_obj[i2s_num]->rx->curr_ptr, ticks_to_wait) == pdFALSE) { + break; + } + p_i2s_obj[i2s_num]->rx->rw_pos = 0; + } + data_ptr = (char*)p_i2s_obj[i2s_num]->rx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; + bytes_can_read = p_i2s_obj[i2s_num]->rx->buf_size - p_i2s_obj[i2s_num]->rx->rw_pos; + if (bytes_can_read > size) { + bytes_can_read = size; + } + memcpy(dest, data_ptr, bytes_can_read); + size -= bytes_can_read; + dest += bytes_can_read; + p_i2s_obj[i2s_num]->rx->rw_pos += bytes_can_read; + byte_read += bytes_can_read; + } + xSemaphoreGive(p_i2s_obj[i2s_num]->rx->mux); + return byte_read; +} +int i2s_push_sample(i2s_port_t i2s_num, const char *sample, TickType_t ticks_to_wait) +{ + int i, bytes_to_push = 0; + char *data_ptr; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->tx->rw_pos == p_i2s_obj[i2s_num]->tx->buf_size || p_i2s_obj[i2s_num]->tx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->tx->queue, &p_i2s_obj[i2s_num]->tx->curr_ptr, ticks_to_wait) == pdFALSE) { + return 0; + } + ESP_LOGD(I2S_TAG, "rw_pos: %d, buf_size: %d, curr_ptr: %d", p_i2s_obj[i2s_num]->tx->rw_pos, p_i2s_obj[i2s_num]->tx->buf_size, (int)p_i2s_obj[i2s_num]->tx->curr_ptr); + p_i2s_obj[i2s_num]->tx->rw_pos = 0; + } + data_ptr = (char*)p_i2s_obj[i2s_num]->tx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->tx->rw_pos; + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *data_ptr++ = *sample++; + bytes_to_push ++; + } + if (p_i2s_obj[i2s_num]->channel_num == 2) { + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *data_ptr++ = *sample++; + bytes_to_push ++; + } + } + + p_i2s_obj[i2s_num]->tx->rw_pos += p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + return bytes_to_push; +} + +int i2s_pop_sample(i2s_port_t i2s_num, char *sample, TickType_t ticks_to_wait) +{ + int i, bytes_to_pop = 0; + char *data_ptr; + I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_FAIL); + if (p_i2s_obj[i2s_num]->rx->rw_pos == p_i2s_obj[i2s_num]->rx->buf_size || p_i2s_obj[i2s_num]->rx->curr_ptr == NULL) { + if (xQueueReceive(p_i2s_obj[i2s_num]->rx->queue, &p_i2s_obj[i2s_num]->rx->curr_ptr, ticks_to_wait) == pdFALSE) { + return 0; + } + p_i2s_obj[i2s_num]->rx->rw_pos = 0; + } + data_ptr = (char*)p_i2s_obj[i2s_num]->rx->curr_ptr; + data_ptr += p_i2s_obj[i2s_num]->rx->rw_pos; + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *sample++ = *data_ptr++; + bytes_to_pop++; + } + if (p_i2s_obj[i2s_num]->channel_num == 2) { + for (i = 0; i < p_i2s_obj[i2s_num]->bytes_per_sample; i++) { + *sample++ = *data_ptr++; + bytes_to_pop++; + } + } + + p_i2s_obj[i2s_num]->rx->rw_pos += p_i2s_obj[i2s_num]->bytes_per_sample * p_i2s_obj[i2s_num]->channel_num; + return bytes_to_pop; +} diff --git a/components/driver/include/driver/i2s.h b/components/driver/include/driver/i2s.h new file mode 100644 index 0000000000..d44bb676a3 --- /dev/null +++ b/components/driver/include/driver/i2s.h @@ -0,0 +1,380 @@ +// 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_I2S_H_ +#define _DRIVER_I2S_H_ +#include "esp_err.h" +#include +#include "soc/gpio_reg.h" +#include "soc/soc.h" +#include "soc/i2s_struct.h" +#include "soc/i2s_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/io_mux_reg.h" +#include "rom/gpio.h" +#include "esp_attr.h" +#include "esp_intr_alloc.h" +#include "driver/periph_ctrl.h" +#include "freertos/semphr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define I2S_PIN_NO_CHANGE (-1) + + +/** + * @brief I2S bit width per sample. + * + */ +typedef enum { + I2S_BITS_PER_SAMPLE_8BIT = 8, /*!< I2S bits per sample: 8-bits*/ + I2S_BITS_PER_SAMPLE_16BIT = 16, /*!< I2S bits per sample: 16-bits*/ + I2S_BITS_PER_SAMPLE_24BIT = 24, /*!< I2S bits per sample: 24-bits*/ + I2S_BITS_PER_SAMPLE_32BIT = 32, /*!< I2S bits per sample: 32-bits*/ +} i2s_bits_per_sample_t; + +/** + * @brief I2S communication standard format + * + */ +typedef enum { + I2S_COMM_FORMAT_I2S = 0x01, /*!< I2S communication format I2S*/ + I2S_COMM_FORMAT_I2S_MSB = 0x02, /*!< I2S format MSB*/ + I2S_COMM_FORMAT_I2S_LSB = 0x04, /*!< I2S format LSB*/ + I2S_COMM_FORMAT_PCM = 0x08, /*!< I2S communication format PCM*/ + I2S_COMM_FORMAT_PCM_SHORT = 0x10, /*!< PCM Short*/ + I2S_COMM_FORMAT_PCM_LONG = 0x20, /*!< PCM Long*/ +} i2s_comm_format_t; + + +/** + * @brief I2S channel format type + */ +typedef enum { + I2S_CHANNEL_FMT_RIGHT_LEFT = 0x00, + I2S_CHANNEL_FMT_ALL_RIGHT, + I2S_CHANNEL_FMT_ALL_LEFT, + I2S_CHANNEL_FMT_ONLY_RIGHT, + I2S_CHANNEL_FMT_ONLY_LEFT, +} i2s_channel_fmt_t; + +/** + * @brief PDM sample rate ratio, measured in Hz. + * + */ +typedef enum { + PDM_SAMPLE_RATE_RATIO_64, + PDM_SAMPLE_RATE_RATIO_128, +} pdm_sample_rate_ratio_t; + +/** + * @brief PDM PCM convter enable/disable. + * + */ +typedef enum { + PDM_PCM_CONV_ENABLE, + PDM_PCM_CONV_DISABLE, +} pdm_pcm_conv_t; + + +/** + * @brief I2S Peripheral, 0 & 1. + * + */ +typedef enum { + I2S_NUM_0 = 0x0, /*!< I2S 0*/ + I2S_NUM_1 = 0x1, /*!< I2S 1*/ + I2S_NUM_MAX, +} i2s_port_t; + +/** + * @brief I2S Mode, defaut is I2S_MODE_MASTER | I2S_MODE_TX + * + */ +typedef enum { + I2S_MODE_MASTER = 1, + I2S_MODE_SLAVE = 2, + I2S_MODE_TX = 4, + I2S_MODE_RX = 8, + I2S_MODE_DAC_BUILT_IN = 16 +} i2s_mode_t; + +/** + * @brief I2S configuration parameters for i2s_param_config function + * + */ +typedef struct { + i2s_mode_t mode; /*!< I2S work mode*/ + int sample_rate; /*!< I2S sample rate*/ + i2s_bits_per_sample_t bits_per_sample; /*!< I2S bits per sample*/ + i2s_channel_fmt_t channel_format; /*!< I2S channel format */ + i2s_comm_format_t communication_format; /*!< I2S communication format */ + int intr_alloc_flags; /*!< Flags used to allocate the interrupt. One or multiple (ORred) ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info */ + int dma_buf_count; /*!< I2S DMA Buffer Count */ + int dma_buf_len; /*!< I2S DMA Buffer Length */ +} i2s_config_t; + +/** + * @brief I2S event types + * + */ +typedef enum { + I2S_EVENT_DMA_ERROR, + I2S_EVENT_TX_DONE, /*!< I2S DMA finish sent 1 buffer*/ + I2S_EVENT_RX_DONE, /*!< I2S DMA finish received 1 buffer*/ + I2S_EVENT_MAX, /*!< I2S event max index*/ +} i2s_event_type_t; + +/** + * @brief Event structure used in I2S event queue + * + */ +typedef struct { + i2s_event_type_t type; /*!< I2S event type */ + size_t size; /*!< I2S data size for I2S_DATA event*/ +} i2s_event_t; + +/** + * @brief I2S pin number for i2s_set_pin + * + */ +typedef struct { + int bck_io_num; /*!< BCK in out pin*/ + int ws_io_num; /*!< WS in out pin*/ + int data_out_num; /*!< DATA out pin*/ + int data_in_num; /*!< DATA in pin*/ +} i2s_pin_config_t; + +typedef intr_handle_t i2s_isr_handle_t; +/** + * @brief Set I2S pin number + * + * @note + * Internal signal can be output to multiple GPIO pads + * Only one GPIO pad can connect with input signal + * + * @param i2s_num I2S_NUM_0 or I2S_NUM_1 + * + * @param pin I2S Pin struct, or NULL for 2-channels, 8-bits DAC pin configuration (GPIO25 & GPIO26) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin); + +/** + * @brief i2s install and start driver + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param i2s_config I2S configurations - see i2s_config_t struct + * + * @param queue_size I2S event queue size/depth. + * + * @param i2s_queue I2S event queue handle, if set NULL, driver will not use an event queue. + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void* i2s_queue); + +/** + * @brief Uninstall I2S driver. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num); + +/** + * @brief i2s read data buffer to i2s dma buffer + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param src source address to write + * + * @param size size of data (size in bytes) + * + * @param ticks_to_wait Write timeout + * + * @return number of written bytes + */ +int i2s_write_bytes(i2s_port_t i2s_num, const char *src, size_t size, TickType_t ticks_to_wait); + +/** + * @brief i2s write data buffer to i2s dma buffer + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param dest destination address to read + * + * @param size size of data (size in bytes) + * + * @param ticks_to_wait Read timeout + * + * @return number of read bytes + */ +int i2s_read_bytes(i2s_port_t i2s_num, char* dest, size_t size, TickType_t ticks_to_wait); + +/** + * @brief i2s push 1 sample to i2s dma buffer, with the size parameter equal to one sample's size in bytes = bits_per_sample/8. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param sample destination address to write (depend on bits_per_sample, size of sample (in bytes) = 2*bits_per_sample/8) + * + * @param ticks_to_wait Push timeout + * + * @return number of push bytes + */ +int i2s_push_sample(i2s_port_t i2s_num, const char *sample, TickType_t ticks_to_wait); + +/** + * @brief Pop 1 sample to i2s dma buffer, with the size parameter equal to one sample's size in bytes = bits_per_sample/8. + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param sample destination address to write (depend on bits_per_sample, size of sample (in bytes) = 2*bits_per_sample/8) + * + * @param ticks_to_wait Pop timeout + * + * @return number of pop bytes + */ +int i2s_pop_sample(i2s_port_t i2s_num, char *sample, TickType_t ticks_to_wait); + + +/** + * @brief Set clock rate used for I2S RX and TX + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @param rate I2S clock (ex: 8000, 44100...) + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate); + +/** + * @brief Start driver + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * +* @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_start(i2s_port_t i2s_num); + +/** + * @brief Stop driver + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_stop(i2s_port_t i2s_num); + +/** + * @brief Set the TX DMA buffer contents to all zeroes + * + * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num); + +/***************************EXAMPLE********************************** + * + * + * ----------------EXAMPLE OF I2S SETTING --------------------- + * @code{c} + * + * #include "freertos/queue.h" + * #define I2S_INTR_NUM 17 //choose one interrupt number from soc.h + * int i2s_num = 0; //i2s port number + * i2s_config_t i2s_config = { + * .mode = I2S_MODE_MASTER | I2S_MODE_TX, + * .sample_rate = 44100, + * .bits_per_sample = 16, //16, 32 + * .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //format LEFT_RIGHT + * .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, + * .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + * .dma_buf_count = 8, + * .dma_buf_len = 64 + * }; + * + * i2s_pin_config_t pin_config = { + * .bck_io_num = 26, + * .ws_io_num = 25, + * .data_out_num = 22, + * .data_in_num = I2S_PIN_NO_CHANGE + * }; + * + * i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver + * + * i2s_set_pin(i2s_num, &pin_config); + * + * i2s_set_sample_rates(i2s_num, 22050); //set sample rates + * + * + * i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver + *@endcode + * + * ----------------EXAMPLE USING I2S WITH DAC --------------------- + * @code{c} + * + * #include "freertos/queue.h" + * #define I2S_INTR_NUM 17 //choose one interrupt number from soc.h + * int i2s_num = 0; //i2s port number + * i2s_config_t i2s_config = { + * .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN, + * .sample_rate = 44100, + * .bits_per_sample = 8, // Only 8-bit DAC support + * .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // + * .communication_format = I2S_COMM_FORMAT_I2S_MSB, + * .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + * .dma_buf_count = 8, + * .dma_buf_len = 64 + * }; + * + * + * i2s_driver_install(i2s_num, &i2s_config, 0, NULL); //install and start i2s driver + * + * i2s_set_pin(i2s_num, NULL); //for internal DAC + * + * i2s_set_sample_rates(i2s_num, 22050); //set sample rates + * + * i2s_driver_uninstall(i2s_num); //stop & destroy i2s driver + *@endcode + *-----------------------------------------------------------------------------* + ***************************END OF EXAMPLE**********************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _DRIVER_I2S_H_ */ diff --git a/examples/22_i2s/Makefile b/examples/22_i2s/Makefile new file mode 100644 index 0000000000..b15a137b88 --- /dev/null +++ b/examples/22_i2s/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# +VERBOSE = 1 +PROJECT_NAME := esp32-i2s-driver-example +include $(IDF_PATH)/make/project.mk + diff --git a/examples/22_i2s/main/app_main.c b/examples/22_i2s/main/app_main.c new file mode 100644 index 0000000000..9c8f80fd56 --- /dev/null +++ b/examples/22_i2s/main/app_main.c @@ -0,0 +1,72 @@ +/* I2S Example + + This example code will output 100Hz sine wave and triangle wave to 2-channel of I2S driver + + 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 "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "driver/i2s.h" +#include + + +#define SAMPLE_RATE (36000) +#define I2S_NUM (0) +#define WAVE_FREQ_HZ (100) +#define PI 3.14159265 + +#define SAMPLE_PER_CYCLE (SAMPLE_RATE/WAVE_FREQ_HZ) + +void app_main() +{ + unsigned int i, sample_val; + float sin_float, triangle_float, triangle_step = 65536.0 / SAMPLE_PER_CYCLE; + //for 36Khz sample rates, we create 100Hz sine wave, every cycle need 36000/100 = 360 samples (4-bytes each sample) + //using 6 buffers, we need 60-samples per buffer + //2-channels, 16-bit each channel, total buffer is 360*4 = 1440 bytes + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX + .sample_rate = SAMPLE_RATE, + .bits_per_sample = 16, //16-bit per channel + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB, + .dma_buf_count = 6, + .dma_buf_len = 60, // + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1 + }; + i2s_pin_config_t pin_config = { + .bck_io_num = 26, + .ws_io_num = 25, + .data_out_num = 22, + .data_in_num = -1 //Not used + }; + + nvs_flash_init(); + i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); + i2s_set_pin(I2S_NUM, &pin_config); + + triangle_float = -32767; + + for(i = 0; i < SAMPLE_PER_CYCLE; i++) { + sin_float = sin(i * PI / 180.0); + if(sin_float >= 0) + triangle_float += triangle_step; + else + triangle_float -= triangle_step; + sin_float *= 32767; + + sample_val = 0; + sample_val += (short)triangle_float; + sample_val = sample_val << 16; + sample_val += (short) sin_float; + + i2s_push_sample(I2S_NUM, (char *)&sample_val, portMAX_DELAY); + } +} diff --git a/examples/22_i2s/main/component.mk b/examples/22_i2s/main/component.mk new file mode 100644 index 0000000000..dbc5a7a9e7 --- /dev/null +++ b/examples/22_i2s/main/component.mk @@ -0,0 +1,10 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the SDK documents if you need to do this. +# + +COMPONENT_ADD_INCLUDEDIRS := . From d245f016ea013b6c12e1ca4755a671eb57a1cc61 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 20 Dec 2016 18:02:47 +1100 Subject: [PATCH 50/59] 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 51/59] 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 52/59] 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 53/59] 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 54/59] 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 55/59] 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 56/59] 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 57/59] 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 58/59] 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 59/59] 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(.);