diff --git a/code/nrf-connect/prstlib/CMakeLists.txt b/code/nrf-connect/prstlib/CMakeLists.txt index 518151a..ff3c82c 100644 --- a/code/nrf-connect/prstlib/CMakeLists.txt +++ b/code/nrf-connect/prstlib/CMakeLists.txt @@ -11,4 +11,4 @@ add_library(prstlib STATIC ) target_include_directories(prstlib PRIVATE include) -target_link_libraries(prstlib PUBLIC zephyr_interface) \ No newline at end of file +target_link_libraries(prstlib PUBLIC zephyr_interface kernel) diff --git a/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52833/bparasite_nrf52833.dts b/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52833/bparasite_nrf52833.dts index 549d2e3..7487715 100644 --- a/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52833/bparasite_nrf52833.dts +++ b/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52833/bparasite_nrf52833.dts @@ -28,9 +28,9 @@ buttons { compatible = "gpio-keys"; button0: button_0 { - // P0.12. - gpios = <&gpio0 12 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; - label = "Push button switch 0"; + // P0.30. + gpios = <&gpio0 30 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Push button SW1"; }; }; @@ -62,6 +62,9 @@ &gpio0 { status = "okay"; + // For low-power EDGE interrupts. + // See github.com/zephyrproject-rtos/zephyr/issues/28499. + sense-edge-mask = <0xffffffff>; }; &gpio1 { diff --git a/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52840/bparasite_nrf52840.dts b/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52840/bparasite_nrf52840.dts index 40cd44f..811d8a3 100644 --- a/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52840/bparasite_nrf52840.dts +++ b/code/nrf-connect/prstlib/boards/arm/bparasite_nrf52840/bparasite_nrf52840.dts @@ -28,9 +28,9 @@ buttons { compatible = "gpio-keys"; button0: button_0 { - // P0.12. - gpios = <&gpio0 12 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; - label = "Push button switch 0"; + // P0.30. + gpios = <&gpio0 30 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + label = "Push button SW1"; }; }; @@ -62,6 +62,9 @@ &gpio0 { status = "okay"; + // For low-power EDGE interrupts. + // github.com/zephyrproject-rtos/zephyr/issues/28499 + sense-edge-mask = <0xffffffff>; }; &gpio1 { diff --git a/code/nrf-connect/prstlib/include/prstlib/button.h b/code/nrf-connect/prstlib/include/prstlib/button.h index 374a914..a6a7c06 100644 --- a/code/nrf-connect/prstlib/include/prstlib/button.h +++ b/code/nrf-connect/prstlib/include/prstlib/button.h @@ -1,7 +1,24 @@ #ifndef _PRST_BUTTON_H_ #define _PRST_BUTTON_H_ -// Inits button driver and registers callback. +#include + +typedef enum { + PRST_BUTTON_SW1 = 0, +} prst_button_t; + +typedef void (*prst_button_callback_t)(prst_button_t button, bool is_active); + +// Inits button driver. int prst_button_init(); +// Configures ISR and calls callback on debounced button press/release. +int prst_button_register_callback(prst_button_callback_t callback); + +// Returns: +// 1 if button is active +// 0 if button is inactive +// -1 on error +int prst_button_poll(prst_button_t prst_button); + #endif // _PRST_BUTTON_H_ \ No newline at end of file diff --git a/code/nrf-connect/prstlib/src/button.c b/code/nrf-connect/prstlib/src/button.c index 54f2f2a..eca4510 100644 --- a/code/nrf-connect/prstlib/src/button.c +++ b/code/nrf-connect/prstlib/src/button.c @@ -1,6 +1,7 @@ #include "prstlib/button.h" #include +#include #include #include "prstlib/led.h" @@ -8,23 +9,60 @@ LOG_MODULE_REGISTER(button, CONFIG_PRSTLIB_LOG_LEVEL); +static struct k_work_delayable button_pressed_delayable; + static struct gpio_dt_spec button = GPIO_DT_SPEC_GET(DT_NODELABEL(button0), gpios); static struct gpio_callback cb_data; -static void button_pressed(const struct device *dev, struct gpio_callback *cb, - uint32_t pins) { - LOG_INF("Button pressed"); - prst_led_toggle(); +static prst_button_callback_t user_callback = NULL; + +static void maybe_call_user_callback(prst_button_t button, bool is_active) { + if (user_callback != NULL) { + user_callback(button, is_active); + } else { + LOG_WRN("No user callback registered for button %d", button); + } +} + +static void button_pressed_cb(struct k_work *work) { + int button_state = prst_button_poll(PRST_BUTTON_SW1); + if (button_state < 0) { + LOG_ERR("Failed to poll button"); + return; + } + return maybe_call_user_callback(PRST_BUTTON_SW1, button_state); +} + +static void button_pressed_isr(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) { + k_work_reschedule(&button_pressed_delayable, K_MSEC(10)); } int prst_button_init() { RET_IF_ERR(!device_is_ready(button.port)); RET_IF_ERR(gpio_pin_configure_dt(&button, GPIO_INPUT)); - // EDGE interrupts consume more power! Just use a LEVEL one. - RET_IF_ERR(gpio_pin_interrupt_configure_dt(&button, GPIO_INT_LEVEL_ACTIVE)); - gpio_init_callback(&cb_data, button_pressed, BIT(button.pin)); - RET_IF_ERR(gpio_add_callback(button.port, &cb_data)); return 0; +} + +int prst_button_register_callback(prst_button_callback_t callback) { + k_work_init_delayable(&button_pressed_delayable, button_pressed_cb); + // EDGE interrupts seem to consume more power than LEVEL ones. + // For GPIO_INT_EDGE_BOTH: 16 uA idle. + // For GPIO_INT_LEVEL_ACTIVE: 3 uA idle. + // Related issue: + // https://github.com/zephyrproject-rtos/zephyr/issues/28499 + // Apparently sense-edge-mask brings the power consumption down to + // 3 uA for EDGE interrupts too. + RET_IF_ERR(gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_BOTH)); + gpio_init_callback(&cb_data, button_pressed_isr, BIT(button.pin)); + RET_IF_ERR(gpio_add_callback(button.port, &cb_data)); + user_callback = callback; + return 0; +} + +int prst_button_poll(prst_button_t prst_button) { + RET_CHECK(prst_button == PRST_BUTTON_SW1, "Invalid button"); + return gpio_pin_get_dt(&button); } \ No newline at end of file diff --git a/code/nrf-connect/samples/ble/prj.conf b/code/nrf-connect/samples/ble/prj.conf index 1d6c19d..d6506f3 100644 --- a/code/nrf-connect/samples/ble/prj.conf +++ b/code/nrf-connect/samples/ble/prj.conf @@ -23,4 +23,5 @@ CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y CONFIG_PRST_BLE_ENCODING_BTHOME_V2=y # CONFIG_PRST_SLEEP_DURATION_SEC=1 -CONFIG_PRSTLIB_LOG_LEVEL_DBG=y \ No newline at end of file +# prstlib config - ser all options in prstlib/Kconfig. +# CONFIG_PRSTLIB_LOG_LEVEL_DBG=y diff --git a/code/nrf-connect/samples/input/.gitignore b/code/nrf-connect/samples/input/.gitignore new file mode 100644 index 0000000..2286ab6 --- /dev/null +++ b/code/nrf-connect/samples/input/.gitignore @@ -0,0 +1,2 @@ +build +build_* diff --git a/code/nrf-connect/samples/input/CMakeLists.txt b/code/nrf-connect/samples/input/CMakeLists.txt new file mode 100644 index 0000000..50b94e5 --- /dev/null +++ b/code/nrf-connect/samples/input/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.20.0) + +# Pull in the dts/ and boards/ from prstlib. +set(DTS_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../prstlib) +set(BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../prstlib) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(input) + +target_sources(app PRIVATE src/main.c) + +add_subdirectory(../../prstlib prstlib) +target_include_directories(app PRIVATE ../../prstlib/include) +target_link_libraries(app PUBLIC prstlib) \ No newline at end of file diff --git a/code/nrf-connect/samples/input/Kconfig b/code/nrf-connect/samples/input/Kconfig new file mode 100644 index 0000000..53b38b9 --- /dev/null +++ b/code/nrf-connect/samples/input/Kconfig @@ -0,0 +1,3 @@ + +source "Kconfig.zephyr" +rsource "../../prstlib/Kconfig" diff --git a/code/nrf-connect/samples/input/input.code-workspace b/code/nrf-connect/samples/input/input.code-workspace new file mode 100644 index 0000000..42d0580 --- /dev/null +++ b/code/nrf-connect/samples/input/input.code-workspace @@ -0,0 +1,16 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "../../prstlib" + } + ], + "settings": { + "C_Cpp.autoAddFileAssociations": false, + "nrf-connect.applications": [ + "${workspaceFolder}" + ] + } +} diff --git a/code/nrf-connect/samples/input/prj.conf b/code/nrf-connect/samples/input/prj.conf new file mode 100644 index 0000000..fef28f3 --- /dev/null +++ b/code/nrf-connect/samples/input/prj.conf @@ -0,0 +1,18 @@ +CONFIG_LOG=y +CONFIG_PWM=y +CONFIG_I2C=y +CONFIG_ADC=y +CONFIG_GPIO=y + +CONFIG_CBPRINTF_FP_SUPPORT=y + +CONFIG_SERIAL=n +CONFIG_PM=y +CONFIG_PM_DEVICE=y + +CONFIG_USE_SEGGER_RTT=y + +CONFIG_NEWLIB_LIBC=y +CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y + +CONFIG_PRSTLIB_LOG_LEVEL_DBG=y \ No newline at end of file diff --git a/code/nrf-connect/samples/input/src/main.c b/code/nrf-connect/samples/input/src/main.c new file mode 100644 index 0000000..e174229 --- /dev/null +++ b/code/nrf-connect/samples/input/src/main.c @@ -0,0 +1,57 @@ +// #include +// #include +// #include +// #include +// #include +// #include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG); + +void button_pressed_cb(prst_button_t button, bool is_active) { + if (is_active) { + LOG_INF("Button pressed (debounced)"); + prst_led_on(); + } else { + LOG_INF("Button released (debounced)"); + prst_led_off(); + } +} + +int main(void) { + RET_IF_ERR(prst_adc_init()); + RET_IF_ERR(prst_led_init()); + RET_IF_ERR(prst_button_init()); + + RET_IF_ERR(prst_led_flash(2)); + + prst_sensors_t sensors; + // Read the sensors just to ensure they'll be put to a low + // power mode afterward. + RET_IF_ERR(prst_sensors_read_all(&sensors)); + + int initial_button_state = prst_button_poll(PRST_BUTTON_SW1); + RET_CHECK(initial_button_state >= 0, "Failed to poll button"); + LOG_INF("Initial button state: %s", initial_button_state ? "active" : "inactive"); + + RET_IF_ERR(prst_button_register_callback(button_pressed_cb)); + + RET_IF_ERR(prst_led_flash(2)); + + while (true) { + LOG_INF("Main loop."); + k_sleep(K_FOREVER); + } + return 0; +}