Skip to content

Commit 0c92154

Browse files
authored
Merge pull request #144 from tmobile/cfspdk-1075-button-press-cb
Cfspdk 1075 button press cb
2 parents 9bd7a66 + fd48bd5 commit 0c92154

File tree

7 files changed

+457
-0
lines changed

7 files changed

+457
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) 2023 T-Mobile USA, Inc.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
cmake_minimum_required(VERSION 3.20.0)
6+
7+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
8+
project(button_press_cb)
9+
10+
target_sources(app PRIVATE src/main.c)
11+
target_sources(app PRIVATE src/button.c)

samples/button_press_cb/Kconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright (c) 2022 T-Mobile USA, Inc.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
mainmenu "Button Sample DevEdge board V04 Production"
6+
7+
config BUTTON_DEBOUNCE_TIMEOUT_MS
8+
int "Debounce filter timeout for button press in millisec"
9+
range 0 100
10+
default 50
11+
help
12+
Set this to configure timer value in order to debounce button press
13+
and unpress.
14+
15+
source "Kconfig.zephyr"

samples/button_press_cb/README.txt

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
Title: button_press_cb
2+
3+
Description:
4+
5+
A sample to demonstrate a callback feature of the user push button on the DevEdge kit
6+
The user will implement the Button callback function in the application code.
7+
It gives the timestamp of the button pressed (from system uptime in millisecs), the duration of
8+
how long the button was pressed (also in millisecs), and the type of button press, whether a normal or a timeout.
9+
10+
The duration parameter will be the max timeout value when a timeout occurs, this won't represent
11+
the real physical button press duration though. The callback will be invoked as soon as the
12+
timeout reaches even if the user has not release the button.
13+
14+
--------------------------------------------------------------------------------
15+
16+
Requirements
17+
************
18+
19+
-A T-Mobile DevEdge dev kit (https://devedge.t-mobile.com/)
20+
-A Zephyr build environment (https://docs.zephyrproject.org/latest/develop/getting_started/index.html)
21+
22+
Building and Running Project:
23+
24+
How this project can be built:
25+
26+
-Checkout the T-Mobile downstream zephyr repo:
27+
cd ~/zephyrproject
28+
git clone https://github.com/tmobile/DevEdge-IoTDevKit-ZephyrRTOS zephyr
29+
30+
-Checkout the T-Mobile zephyr-tmo-sdk repo:
31+
cd ~/zephyrproject
32+
git clone https://github.com/tmobile/DevEdge-IoTDevKit-ZephyrSDK tmo-zephyr-sdk
33+
34+
-Run 'west update'
35+
cd ~/zephyrproject
36+
west update
37+
38+
-Build button_press_cb:
39+
cd ~/zephyrproject
40+
west build ~/zephyrproject/tmo-zephyr-sdk/samples/button_press_cb -p -b tmo_dev_edge -- -DBOARD_ROOT=<home folder>/zephyrproject/tmo-zephyr-sdk
41+
(substitute your home folder for '<home folder>' in the command above)
42+
43+
-Connect DevEdge dev kit:
44+
For DevEdge dev kits running tmo_shell built with DevEdge SDK v1.9.1 or earlier:
45+
connect BOTH USB-C ports to your computer or wall power
46+
For DevEdge dev kits running tmo_shell built with DevEdge SDK v1.10.0 or later:
47+
connect only the USB-C port furthest from the button to your computer
48+
(both USB-C ports can be connected, but only one needs to be connected)
49+
50+
-Flash button_press_cb:
51+
cd ~/zephyrproject
52+
west flash
53+
54+
Sample output:
55+
```
56+
*** Booting Zephyr OS build ccdf94636486 ***
57+
58+
59+
Welcome to T-Mobile DevEdge!
60+
61+
This application aims to demonstrate the button press callback sample module which
62+
uses SW0 interrupt pin, which is connected to the user pushbutton switch of the
63+
DevEdge module.
64+
65+
Set up button at gpio@4000a030 pin 13
66+
67+
factory_reset_callback: Button pressed: timestamp: 2562; duration: 10000, button press type: BUTTON_PRESS_TIMEOUT
68+
Set up button at gpio@4000a030 pin 13
69+
70+
factory_reset_callback: Button pressed: timestamp: 14983; duration: 159, button press type: BUTTON_PRESS_NORMAL
71+
factory_reset_callback: Button pressed: timestamp: 15920; duration: 137, button press type: BUTTON_PRESS_NORMAL
72+
factory_reset_callback: Button pressed: timestamp: 16690; duration: 5000, button press type: BUTTON_PRESS_TIMEOUT
73+
factory_reset_callback: Button pressed: timestamp: 22771; duration: 139, button press type: BUTTON_PRESS_NORMAL
74+
factory_reset_callback: Button pressed: timestamp: 23757; duration: 179, button press type: BUTTON_PRESS_NORMAL
75+
```
76+

samples/button_press_cb/prj.conf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2022 T-Mobile USA, Inc.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
# Required to disable default behavior of deep sleep on timeout
6+
CONFIG_GPIO=y
7+
8+
# Stack sizes
9+
CONFIG_MAIN_STACK_SIZE=4096
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
* Copyright (c) 2023 T-Mobile USA, Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/device.h>
8+
#include <zephyr/drivers/gpio.h>
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/sys/printk.h>
11+
12+
#include "button.h"
13+
14+
#define SW0_NODE DT_ALIAS(sw0)
15+
16+
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
17+
static struct gpio_callback button_cb_data;
18+
static button_press_callback_t registered_callback;
19+
static struct gpio_callback button_cb_data;
20+
button_press_state button_state = BUTTON_STATE_DEINIT;
21+
struct k_timer debounce_timer;
22+
struct k_timer timeout_timer;
23+
static int64_t last_button_press_time;
24+
static int64_t button_press_duration;
25+
static int64_t max_button_press_timeout;
26+
27+
/* button GPIO interrupt handler */
28+
static void button_press_handler(const struct device *port, struct gpio_callback *cb,
29+
gpio_port_pins_t pins)
30+
{
31+
k_timer_stop(&debounce_timer);
32+
k_timer_start(&debounce_timer, K_MSEC(50), K_NO_WAIT);
33+
button_state = BUTTON_STATE_DEBOUNCING;
34+
}
35+
36+
static void button_timeout_handler(struct k_timer *p_timeout_timer)
37+
{
38+
button_state = BUTTON_STATE_TIMEOUT;
39+
if (NULL != registered_callback) {
40+
/* saving the last button press time, since k_uptime_delta updates it with current
41+
* system uptime, which is undesired behavior in the context of measuring the real
42+
* button press duration even if timeout has occurred */
43+
int64_t save_last_button_press = last_button_press_time;
44+
45+
button_press_duration = k_uptime_delta(&last_button_press_time);
46+
last_button_press_time = save_last_button_press;
47+
/* Invoke the registered user callback handler */
48+
registered_callback(last_button_press_time, button_press_duration,
49+
BUTTON_PRESS_TIMEOUT);
50+
}
51+
k_timer_stop(p_timeout_timer);
52+
}
53+
54+
/* debounce timer handler */
55+
static void button_debounce_timer_handler(struct k_timer *p_debounce_timer)
56+
{
57+
/* As soon as a button press action (hold or release) has occurred, we don't need the
58+
* timeout timer enabled anymore, until the next button event */
59+
k_timer_stop(&timeout_timer);
60+
61+
if (BUTTON_STATE_DEINIT == button_state) {
62+
return;
63+
}
64+
button_state = BUTTON_STATE_DEBOUNCED;
65+
/* Each the button is pressed, last_button_press_time will be 0, indicating a new action on
66+
* user button */
67+
if (last_button_press_time == 0) {
68+
/* button press detected */
69+
last_button_press_time = k_uptime_get();
70+
k_timer_start(&timeout_timer, K_MSEC(max_button_press_timeout), K_NO_WAIT);
71+
} else if (registered_callback != NULL) {
72+
/* button release detected */
73+
74+
/* This basically compares the timestamps of each time the timer handler is called
75+
* (one on button press, another on button release )*/
76+
button_press_duration = k_uptime_delta(&last_button_press_time);
77+
78+
/* Depending on whether the time delta exceeds the configured max timeout value set
79+
* during init function, the button press type is evaluated */
80+
if (button_state != BUTTON_STATE_TIMEOUT &&
81+
button_press_duration < max_button_press_timeout) {
82+
/* Invoke the registered user callback handler */
83+
registered_callback(last_button_press_time, button_press_duration,
84+
BUTTON_PRESS_NORMAL);
85+
}
86+
87+
/* Reset last_button_press_time to capture next button press event */
88+
last_button_press_time = 0;
89+
90+
button_state = BUTTON_STATE_INIT;
91+
}
92+
}
93+
94+
int button_press_module_init(uint32_t max_timeout, button_press_callback_t callback)
95+
{
96+
/* Cannot initialize new paramters of button press without calling deinit first */
97+
if (BUTTON_STATE_DEINIT < button_state) {
98+
return -EBUSY;
99+
}
100+
/* Invalid params */
101+
if (0 == max_timeout || NULL == callback) {
102+
return -EINVAL;
103+
}
104+
105+
int ret = 0;
106+
107+
max_button_press_timeout = max_timeout;
108+
registered_callback = callback;
109+
110+
ret = gpio_pin_configure_dt(&button, GPIO_INPUT | GPIO_PULL_UP);
111+
if (ret != 0) {
112+
printk("Error %d: failed to configure %s pin %d\n", ret, button.port->name,
113+
button.pin);
114+
button_state = BUTTON_STATE_ERROR;
115+
return ret;
116+
}
117+
118+
/* Configrue interrputs on both edges so we can sample timestamp to caluclate duration of
119+
* button press */
120+
ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_BOTH);
121+
if (ret != 0) {
122+
printk("Error %d: failed to configure interrupt on %s pin %d\n", ret,
123+
button.port->name, button.pin);
124+
button_state = BUTTON_STATE_ERROR;
125+
return ret;
126+
}
127+
gpio_init_callback(&button_cb_data, button_press_handler, BIT(button.pin));
128+
gpio_add_callback_dt(&button, &button_cb_data);
129+
printk("Set up button at %s pin %d\n\n", button.port->name, button.pin);
130+
131+
k_timer_init(&debounce_timer, button_debounce_timer_handler, NULL);
132+
k_timer_init(&timeout_timer, button_timeout_handler, NULL);
133+
134+
/* Set state of button as configured. Only one configruation can exist at a time */
135+
button_state = BUTTON_STATE_INIT;
136+
137+
return ret;
138+
}
139+
140+
int button_press_module_deinit(void)
141+
{
142+
if (BUTTON_STATE_DEINIT == button_state) {
143+
return 0;
144+
}
145+
146+
int ret = 0;
147+
148+
max_button_press_timeout = 0;
149+
registered_callback = NULL;
150+
151+
/* Disconnect the GPIO from the MCU */
152+
ret = gpio_pin_configure_dt(&button, GPIO_DISCONNECTED);
153+
if (ret != 0) {
154+
printk("Error %d: failed to disconnect %s pin %d\n", ret, button.port->name,
155+
button.pin);
156+
button_state = BUTTON_STATE_ERROR;
157+
return ret;
158+
}
159+
160+
/* Disable all interrupts on it */
161+
ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_DISABLE);
162+
if (ret != 0) {
163+
printk("Error %d: failed to disable interrupt on %s pin %d\n", ret,
164+
button.port->name, button.pin);
165+
button_state = BUTTON_STATE_ERROR;
166+
return ret;
167+
}
168+
169+
gpio_remove_callback_dt(&button, &button_cb_data);
170+
if (ret != 0) {
171+
printk("Error %d: failed to remove intr callback on %s pin %d\n", ret,
172+
button.port->name, button.pin);
173+
button_state = BUTTON_STATE_ERROR;
174+
return ret;
175+
}
176+
177+
k_timer_stop(&debounce_timer);
178+
k_timer_stop(&timeout_timer);
179+
180+
/* Set the button state to undefined so a new configruation can be set by calling the init
181+
* function */
182+
button_state = BUTTON_STATE_DEINIT;
183+
184+
return ret;
185+
}
186+
187+
button_press_state button_press_get_state(void)
188+
{
189+
/* Return current state of button */
190+
return button_state;
191+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) 2023 T-Mobile USA, Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#ifndef BUTTON_H
7+
#define BUTTON_H
8+
9+
#include <zephyr/kernel.h>
10+
11+
typedef enum {
12+
BUTTON_PRESS_NORMAL = 0,
13+
BUTTON_PRESS_TIMEOUT = 1
14+
} button_press_type;
15+
16+
typedef enum {
17+
BUTTON_STATE_ERROR = -1,
18+
BUTTON_STATE_DEINIT = 0,
19+
BUTTON_STATE_INIT = 1,
20+
BUTTON_STATE_DEBOUNCING = 2,
21+
BUTTON_STATE_DEBOUNCED = 3,
22+
BUTTON_STATE_TIMEOUT = 4
23+
} button_press_state;
24+
25+
/**
26+
* @brief Button callback definition, the user will implement the callback in the application code.
27+
* It gives the timestamp of the button pressed (from system uptime in millisecs), the duration of
28+
* how long the button was pressed, and the type of button press, whether a normal or a timeout.
29+
*
30+
* The duration parameter will be the max timeout value when a timeout occurs, this won't represent
31+
* the real physical button press duration though. The callback will be invoked as soon as the
32+
* timeout reaches even if the user has not release the button.
33+
*/
34+
typedef void (*button_press_callback_t)(uint32_t timestamp, uint32_t duration,
35+
button_press_type type);
36+
37+
/**
38+
* @brief Button press initialization. Called by user application code to configure and initialize
39+
* user button to generate callback on button presses.
40+
*
41+
* @param max_timeout [in] Max timeout value for which the user button can be pressed, after
42+
* which it is considered to be not normal operation.
43+
* @param callback [in] Function pointer to the callback that will be called when the button
44+
* has been pressed for a certain duration
45+
* @return int status code indicating success/failure.
46+
*/
47+
int button_press_module_init(uint32_t max_timeout, button_press_callback_t callback);
48+
49+
/**
50+
* @brief De-initialize the button interrupt and callbacks if it was previously done so. Usually
51+
* performed if we want to set a new configuration on the button.
52+
*
53+
* @return int status code indicating success/failure
54+
*/
55+
int button_press_module_deinit(void);
56+
57+
/**
58+
* @brief Get the button configured state. Handy getter function that user application code can use
59+
* to determine if existing callback is set before configuring it with new parameters.
60+
*
61+
* @return button_press_state
62+
*/
63+
button_press_state button_press_get_state(void);
64+
65+
#endif

0 commit comments

Comments
 (0)