diff --git a/esphome/components/pulse_counter/CMakeLists.txt b/esphome/components/pulse_counter/CMakeLists.txt index 28e2b97a1e..6e481ca4aa 100644 --- a/esphome/components/pulse_counter/CMakeLists.txt +++ b/esphome/components/pulse_counter/CMakeLists.txt @@ -1,3 +1,27 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) -idf_component_register(SRCS ${app_sources}) +idf_component_register(SRCS ${app_sources} +INCLUDE_DIRS "" +REQUIRES driver soc nvs_flash ulp) + +## +## ULP support additions to component CMakeLists.txt. +## +## 1. The ULP app name must be unique (if multiple components use ULP). +set(ulp_app_name ulp_main) +## +## 2. Specify all assembly source files. +## Files should be placed into a separate directory (in this case, ulp/), +## which should not be added to COMPONENT_SRCS. +##TODO +set(ulp_s_sources "../ulp/pulse_cnt.S" "../ulp/wake_up.S") +## +## 3. List all the component source files which include automatically +## generated ULP export file, ${ulp_app_name}.h: +set(ulp_exp_dep_srcs "components/pulse_counter/pulse_counter_sensor.cpp") +## +## 4. Call function to build ULP binary and embed in project using the argument +## values above. +ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}") +# +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/esphome/components/pulse_counter/sensor.py b/esphome/components/pulse_counter/sensor.py index 7772f57cdc..b5a720fa07 100644 --- a/esphome/components/pulse_counter/sensor.py +++ b/esphome/components/pulse_counter/sensor.py @@ -138,7 +138,16 @@ async def to_code(config): "src/CMakeLists.txt", os.path.join(os.path.dirname(__file__), "CMakeLists.txt"), ) - var = await sensor.new_sensor(config, config.get(CONF_USE_PCNT)) + esp32.add_extra_build_file( + "ulp/pulse_cnt.S", + os.path.join(os.path.dirname(__file__), "ulp/pulse_cnt.S"), + ) + esp32.add_extra_build_file( + "ulp/wake_up.S", os.path.join(os.path.dirname(__file__), "ulp/wake_up.S") + ) + esp32.add_idf_sdkconfig_option("CONFIG_ULP_COPROC_ENABLED", True) + esp32.add_idf_sdkconfig_option("CONFIG_ULP_COPROC_TYPE_FSM", True) + esp32.add_idf_sdkconfig_option("CONFIG_ULP_COPROC_RESERVE_MEM", 1024) await cg.register_component(var, config) pin = await cg.gpio_pin_expression(config[CONF_PIN]) diff --git a/esphome/components/pulse_counter/ulp/pulse_cnt.S b/esphome/components/pulse_counter/ulp/pulse_cnt.S new file mode 100644 index 0000000000..04a846feda --- /dev/null +++ b/esphome/components/pulse_counter/ulp/pulse_cnt.S @@ -0,0 +1,161 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/* ULP Example: pulse counting + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + + This file contains assembly code which runs on the ULP. + + ULP wakes up to run this code at a certain period, determined by the values + in SENS_ULP_CP_SLEEP_CYCx_REG registers. On each wake up, the program checks + the input on GPIO0. If the value is different from the previous one, the + program "debounces" the input: on the next debounce_max_count wake ups, + it expects to see the same value of input. + If this condition holds true, the program increments edge_count and starts + waiting for input signal polarity to change again. + When the edge counter reaches certain value (set by the main program), + this program running triggers a wake up from deep sleep. +*/ + +/* ULP assembly files are passed through C preprocessor first, so include directives + and C macros may be used in these files + */ +#include "sdkconfig.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/soc_ulp.h" +#include "soc/sens_reg.h" + + /* Define variables, which go into .bss section (zero-initialized data) */ + .bss + /* Next input signal edge expected: 0 (negative) or 1 (positive) */ + .global next_edge +next_edge: + .long 0 + + /* Counter started when signal value changes. + Edge is "debounced" when the counter reaches zero. */ + .global debounce_counter +debounce_counter: + .long 0 + + /* Value to which debounce_counter gets reset. + Set by the main program. */ + .global debounce_max_count +debounce_max_count: + .long 0 + + /* Total number of signal edges acquired */ + .global edge_count +edge_count: + .long 0 + + /* Number of edges to acquire before waking up the SoC. + Set by the main program. */ + .global edge_count_to_wake_up +edge_count_to_wake_up: + .long 0 + + /* RTC IO number used to sample the input signal. + Set by main program. */ + .global io_number +io_number: + .long 0 + + /* Code goes into .text section */ + .text + .global entry +entry: + /* Load io_number */ + move r3, io_number + ld r3, r3, 0 + +#if CONFIG_IDF_TARGET_ESP32S2 + /* ESP32S2 powers down RTC periph when entering deep sleep and thus by association SENS_SAR_IO_MUX_CONF_REG */ + WRITE_RTC_FIELD(SENS_SAR_IO_MUX_CONF_REG, SENS_IOMUX_CLK_GATE_EN, 1) +#elif CONFIG_IDF_TARGET_ESP32S3 + /* ESP32S3 powers down RTC periph when entering deep sleep and thus by association SENS_SAR_PERI_CLK_GATE_CONF_REG */ + WRITE_RTC_FIELD(SENS_SAR_PERI_CLK_GATE_CONF_REG, SENS_IOMUX_CLK_EN, 1); +#endif + + /* Lower 16 IOs and higher need to be handled separately, + * because r0-r3 registers are 16 bit wide. + * Check which IO this is. + */ + move r0, r3 + jumpr read_io_high, 16, ge + + /* Read the value of lower 16 RTC IOs into R0 */ + READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S, 16) + rsh r0, r0, r3 + jump read_done + + /* Read the value of RTC IOs 16-17, into R0 */ +read_io_high: + READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + 16, 2) + sub r3, r3, 16 + rsh r0, r0, r3 + +read_done: + and r0, r0, 1 + /* State of input changed? */ + move r3, next_edge + ld r3, r3, 0 + add r3, r0, r3 + and r3, r3, 1 + jump changed, eq + /* Not changed */ + /* Reset debounce_counter to debounce_max_count */ + move r3, debounce_max_count + move r2, debounce_counter + ld r3, r3, 0 + st r3, r2, 0 + /* End program */ + halt + + .global changed +changed: + /* Input state changed */ + /* Has debounce_counter reached zero? */ + move r3, debounce_counter + ld r2, r3, 0 + add r2, r2, 0 /* dummy ADD to use "jump if ALU result is zero" */ + jump edge_detected, eq + /* Not yet. Decrement debounce_counter */ + sub r2, r2, 1 + st r2, r3, 0 + /* End program */ + halt + + .global edge_detected +edge_detected: + /* Reset debounce_counter to debounce_max_count */ + move r3, debounce_max_count + move r2, debounce_counter + ld r3, r3, 0 + st r3, r2, 0 + /* Flip next_edge */ + move r3, next_edge + ld r2, r3, 0 + add r2, r2, 1 + and r2, r2, 1 + st r2, r3, 0 + /* Increment edge_count */ + move r3, edge_count + ld r2, r3, 0 + add r2, r2, 1 + st r2, r3, 0 + /* Compare edge_count to edge_count_to_wake_up */ + move r3, edge_count_to_wake_up + ld r3, r3, 0 + sub r3, r3, r2 + jump wake_up, eq + /* Not yet. End program */ + halt diff --git a/esphome/components/pulse_counter/ulp/wake_up.S b/esphome/components/pulse_counter/ulp/wake_up.S new file mode 100644 index 0000000000..472afae7f9 --- /dev/null +++ b/esphome/components/pulse_counter/ulp/wake_up.S @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* ULP assembly files are passed through C preprocessor first, so include directives + and C macros may be used in these files + */ +#include "soc/rtc_cntl_reg.h" +#include "soc/soc_ulp.h" +#include "sdkconfig.h" + + .global wake_up +wake_up: + /* Check if the system is in sleep mode */ +#if CONFIG_IDF_TARGET_ESP32 + READ_RTC_REG(RTC_CNTL_LOW_POWER_ST_REG, 27, 1) +#else + READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_MAIN_STATE_IN_IDLE) +#endif + move r1, r0 + /* Check if the system can be woken up */ + READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) + /* If the system is in normal mode or if the system is in sleep mode with ready for wakeup set, we can signal the main CPU to wakeup */ + or r0, r0, r1 + jump wake_up, eq + + /* Wake up the SoC, end program */ + wake + halt +