Implements button handling
- DTS bindings - Debouncing logic - New `input` for testing and interrupt power profiling
This commit is contained in:
parent
02b3970ffc
commit
e82c1b6bc1
12 changed files with 190 additions and 17 deletions
|
|
@ -11,4 +11,4 @@ add_library(prstlib STATIC
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(prstlib PRIVATE include)
|
target_include_directories(prstlib PRIVATE include)
|
||||||
target_link_libraries(prstlib PUBLIC zephyr_interface)
|
target_link_libraries(prstlib PUBLIC zephyr_interface kernel)
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@
|
||||||
buttons {
|
buttons {
|
||||||
compatible = "gpio-keys";
|
compatible = "gpio-keys";
|
||||||
button0: button_0 {
|
button0: button_0 {
|
||||||
// P0.12.
|
// P0.30.
|
||||||
gpios = <&gpio0 12 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
gpios = <&gpio0 30 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||||
label = "Push button switch 0";
|
label = "Push button SW1";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -62,6 +62,9 @@
|
||||||
|
|
||||||
&gpio0 {
|
&gpio0 {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
// For low-power EDGE interrupts.
|
||||||
|
// See github.com/zephyrproject-rtos/zephyr/issues/28499.
|
||||||
|
sense-edge-mask = <0xffffffff>;
|
||||||
};
|
};
|
||||||
|
|
||||||
&gpio1 {
|
&gpio1 {
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@
|
||||||
buttons {
|
buttons {
|
||||||
compatible = "gpio-keys";
|
compatible = "gpio-keys";
|
||||||
button0: button_0 {
|
button0: button_0 {
|
||||||
// P0.12.
|
// P0.30.
|
||||||
gpios = <&gpio0 12 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
gpios = <&gpio0 30 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||||
label = "Push button switch 0";
|
label = "Push button SW1";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -62,6 +62,9 @@
|
||||||
|
|
||||||
&gpio0 {
|
&gpio0 {
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
// For low-power EDGE interrupts.
|
||||||
|
// github.com/zephyrproject-rtos/zephyr/issues/28499
|
||||||
|
sense-edge-mask = <0xffffffff>;
|
||||||
};
|
};
|
||||||
|
|
||||||
&gpio1 {
|
&gpio1 {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,24 @@
|
||||||
#ifndef _PRST_BUTTON_H_
|
#ifndef _PRST_BUTTON_H_
|
||||||
#define _PRST_BUTTON_H_
|
#define _PRST_BUTTON_H_
|
||||||
|
|
||||||
// Inits button driver and registers callback.
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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();
|
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_
|
#endif // _PRST_BUTTON_H_
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "prstlib/button.h"
|
#include "prstlib/button.h"
|
||||||
|
|
||||||
#include <zephyr/drivers/gpio.h>
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/logging/log.h>
|
#include <zephyr/logging/log.h>
|
||||||
|
|
||||||
#include "prstlib/led.h"
|
#include "prstlib/led.h"
|
||||||
|
|
@ -8,23 +9,60 @@
|
||||||
|
|
||||||
LOG_MODULE_REGISTER(button, CONFIG_PRSTLIB_LOG_LEVEL);
|
LOG_MODULE_REGISTER(button, CONFIG_PRSTLIB_LOG_LEVEL);
|
||||||
|
|
||||||
|
static struct k_work_delayable button_pressed_delayable;
|
||||||
|
|
||||||
static struct gpio_dt_spec button =
|
static struct gpio_dt_spec button =
|
||||||
GPIO_DT_SPEC_GET(DT_NODELABEL(button0), gpios);
|
GPIO_DT_SPEC_GET(DT_NODELABEL(button0), gpios);
|
||||||
|
|
||||||
static struct gpio_callback cb_data;
|
static struct gpio_callback cb_data;
|
||||||
|
|
||||||
static void button_pressed(const struct device *dev, struct gpio_callback *cb,
|
static prst_button_callback_t user_callback = NULL;
|
||||||
uint32_t pins) {
|
|
||||||
LOG_INF("Button pressed");
|
static void maybe_call_user_callback(prst_button_t button, bool is_active) {
|
||||||
prst_led_toggle();
|
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() {
|
int prst_button_init() {
|
||||||
RET_IF_ERR(!device_is_ready(button.port));
|
RET_IF_ERR(!device_is_ready(button.port));
|
||||||
RET_IF_ERR(gpio_pin_configure_dt(&button, GPIO_INPUT));
|
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;
|
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);
|
||||||
}
|
}
|
||||||
|
|
@ -23,4 +23,5 @@ CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y
|
||||||
CONFIG_PRST_BLE_ENCODING_BTHOME_V2=y
|
CONFIG_PRST_BLE_ENCODING_BTHOME_V2=y
|
||||||
# CONFIG_PRST_SLEEP_DURATION_SEC=1
|
# CONFIG_PRST_SLEEP_DURATION_SEC=1
|
||||||
|
|
||||||
CONFIG_PRSTLIB_LOG_LEVEL_DBG=y
|
# prstlib config - ser all options in prstlib/Kconfig.
|
||||||
|
# CONFIG_PRSTLIB_LOG_LEVEL_DBG=y
|
||||||
|
|
|
||||||
2
code/nrf-connect/samples/input/.gitignore
vendored
Normal file
2
code/nrf-connect/samples/input/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
build
|
||||||
|
build_*
|
||||||
15
code/nrf-connect/samples/input/CMakeLists.txt
Normal file
15
code/nrf-connect/samples/input/CMakeLists.txt
Normal file
|
|
@ -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)
|
||||||
3
code/nrf-connect/samples/input/Kconfig
Normal file
3
code/nrf-connect/samples/input/Kconfig
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
source "Kconfig.zephyr"
|
||||||
|
rsource "../../prstlib/Kconfig"
|
||||||
16
code/nrf-connect/samples/input/input.code-workspace
Normal file
16
code/nrf-connect/samples/input/input.code-workspace
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../../prstlib"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"C_Cpp.autoAddFileAssociations": false,
|
||||||
|
"nrf-connect.applications": [
|
||||||
|
"${workspaceFolder}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
18
code/nrf-connect/samples/input/prj.conf
Normal file
18
code/nrf-connect/samples/input/prj.conf
Normal file
|
|
@ -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
|
||||||
57
code/nrf-connect/samples/input/src/main.c
Normal file
57
code/nrf-connect/samples/input/src/main.c
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
// #include <prstlib/button.h>
|
||||||
|
// #include <prstlib/led.h>
|
||||||
|
// #include <prstlib/sensors.h>
|
||||||
|
// #include <zephyr/logging/log.h>
|
||||||
|
// #include <zephyr/pm/device.h>
|
||||||
|
// #include <zephyr/pm/pm.h>
|
||||||
|
// #include <zephyr/pm/policy.h>
|
||||||
|
#include <prstlib/adc.h>
|
||||||
|
#include <prstlib/button.h>
|
||||||
|
#include <prstlib/led.h>
|
||||||
|
#include <prstlib/macros.h>
|
||||||
|
#include <prstlib/sensors.h>
|
||||||
|
#include <prstlib/shtc3.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/pm/device.h>
|
||||||
|
#include <zephyr/pm/pm.h>
|
||||||
|
#include <zephyr/pm/policy.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue