Contiki-NG
Loading...
Searching...
No Matches
button-hal.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2017, George Oikonomou - http://www.spd.gr
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31/*---------------------------------------------------------------------------*/
32/**
33 * \addtogroup button_hal
34 * @{
35 *
36 * \file
37 * Platform-independent button driver.
38 */
39/*---------------------------------------------------------------------------*/
40#include "contiki.h"
41#include "sys/process.h"
42#include "sys/ctimer.h"
43#include "sys/critical.h"
44#include "dev/gpio-hal.h"
45#include "dev/button-hal.h"
46
47#include <stdint.h>
48#include <stdbool.h>
49#include <string.h>
50/*---------------------------------------------------------------------------*/
51PROCESS(button_hal_process, "Button HAL process");
52/*---------------------------------------------------------------------------*/
53process_event_t button_hal_press_event;
56/*---------------------------------------------------------------------------*/
57/* A mask of all ports that have changed state since the last process poll */
58#if GPIO_HAL_PORT_PIN_NUMBERING
59static volatile uint8_t port_mask;
60#endif
61
62/* A mask of all pins that have changed state since the last process poll */
63static volatile gpio_hal_pin_mask_t pmask;
64/*---------------------------------------------------------------------------*/
65#define PORT_UNUSED 0xFF
66extern button_hal_button_t *button_hal_buttons[];
67/*---------------------------------------------------------------------------*/
68/* Button event handlers, one per port. Registered with the GPIO HAL */
69static gpio_hal_event_handler_t button_event_handlers[BUTTON_HAL_PORT_COUNT];
70/*---------------------------------------------------------------------------*/
71#if GPIO_HAL_PORT_PIN_NUMBERING
72#define BTN_PORT(b) (b)->port
73#else
74#define BTN_PORT(b) GPIO_HAL_NULL_PORT
75#endif
76/*---------------------------------------------------------------------------*/
77static void
78duration_exceeded_callback(void *btn)
79{
81
82 button->press_duration_seconds++;
83 ctimer_set(&button->duration_ctimer, CLOCK_SECOND,
84 duration_exceeded_callback, button);
85 process_post(PROCESS_BROADCAST, button_hal_periodic_event, button);
86}
87/*---------------------------------------------------------------------------*/
88static void
89debounce_handler(void *btn)
90{
91 button_hal_button_t *button;
92 int expired;
93 uint8_t button_state;
94
95 button = (button_hal_button_t *)btn;
96
97 /*
98 * A new debounce may have been triggered after expiration of the previous
99 * one but before we got called.
100 */
101 if(!ctimer_expired(&button->debounce_ctimer)) {
102 return;
103 }
104
105 expired = ctimer_expired(&button->duration_ctimer);
106
107 button_state = button_hal_get_state(button);
108
109 /*
110 * A debounce timer expired. Inspect the button's state. If the button's
111 * state is the same as it was before, then we ignore this as noise.
112 */
113 if(button_state == BUTTON_HAL_STATE_PRESSED && expired) {
114 /*
115 * Button is pressed and no tick counter running. Treat as new press.
116 * Include the debounce duration in the first periodic, so that the
117 * callback will happen 1 second after the button press, not 1 second
118 * after the end of the debounce. Notify process about the press event.
119 */
120 button->press_duration_seconds = 0;
121 ctimer_set(&button->duration_ctimer,
123 duration_exceeded_callback, button);
124 process_post(PROCESS_BROADCAST, button_hal_press_event, button);
125 } else if(button_state == BUTTON_HAL_STATE_RELEASED && expired == 0) {
126 /*
127 * Button is released and there is a duration_ctimer running. Treat this
128 * as a new release and notify processes.
129 */
130 ctimer_stop(&button->duration_ctimer);
131 process_post(PROCESS_BROADCAST, button_hal_release_event, button);
132 }
133}
134/*---------------------------------------------------------------------------*/
135static void
136press_release_handler(
138 gpio_hal_port_t port,
139#endif
140 gpio_hal_pin_mask_t pin_mask)
141{
142 pmask |= pin_mask;
143#if GPIO_HAL_PORT_PIN_NUMBERING
144 port_mask |= (1 << port);
145#endif
146 process_poll(&button_hal_process);
147}
148/*---------------------------------------------------------------------------*/
150get_handler_by_port(uint8_t port)
151{
152#if GPIO_HAL_PORT_PIN_NUMBERING
153 uint8_t i;
154
155 /* First, search in case a handler has already been dedicated to this port */
156 for(i = 0; i < BUTTON_HAL_PORT_COUNT; i++) {
157 if(button_event_handlers[i].port == port) {
158 return &button_event_handlers[i];
159 }
160 }
161
162 /* If not, allocate. The caller will set the port */
163 for(i = 0; i < BUTTON_HAL_PORT_COUNT; i++) {
164 if(button_event_handlers[i].port == PORT_UNUSED) {
165 return &button_event_handlers[i];
166 }
167 }
168
169 return NULL;
170#else
171 return &button_event_handlers[0];
172#endif
173}
174/*---------------------------------------------------------------------------*/
176button_hal_get_by_id(uint8_t unique_id)
177{
178 button_hal_button_t **button;
179
180 for(button = button_hal_buttons; *button != NULL; button++) {
181 if((*button)->unique_id == unique_id) {
182 return *button;
183 }
184 }
185
186 return NULL;
187}
188/*---------------------------------------------------------------------------*/
191{
192 if(index >= button_hal_button_count) {
193 return NULL;
194 }
195
196 return button_hal_buttons[index];
197}
198/*---------------------------------------------------------------------------*/
199uint8_t
201{
202 uint8_t pin_state = gpio_hal_arch_read_pin(BTN_PORT(button), button->pin);
203
204 if((pin_state == 0 && button->negative_logic == true) ||
205 (pin_state == 1 && button->negative_logic == false)) {
206 return BUTTON_HAL_STATE_PRESSED;
207 }
208
209 return BUTTON_HAL_STATE_RELEASED;
210}
211/*---------------------------------------------------------------------------*/
212void
214{
215 button_hal_button_t **button;
218 int i;
219
223
224 for(i = 0; i < BUTTON_HAL_PORT_COUNT; i++) {
225#if GPIO_HAL_PORT_PIN_NUMBERING
226 button_event_handlers[i].port = PORT_UNUSED;
227#endif
228 button_event_handlers[i].pin_mask = 0;
229 button_event_handlers[i].handler = press_release_handler;
230 }
231
232 for(button = button_hal_buttons; *button != NULL; button++) {
233 cfg = GPIO_HAL_PIN_CFG_EDGE_BOTH | GPIO_HAL_PIN_CFG_INT_ENABLE |
234 (*button)->pull;
235 gpio_hal_arch_pin_set_input(BTN_PORT(*button), (*button)->pin);
236 gpio_hal_arch_pin_cfg_set(BTN_PORT(*button), (*button)->pin, cfg);
237 gpio_hal_arch_interrupt_enable(BTN_PORT(*button), (*button)->pin);
238
239 handler = get_handler_by_port(BTN_PORT(*button));
240
241 if(handler) {
242#if GPIO_HAL_PORT_PIN_NUMBERING
243 handler->port = BTN_PORT(*button);
244#endif
245 handler->pin_mask |= gpio_hal_pin_to_mask((*button)->pin);
246 }
247 }
248
249 process_start(&button_hal_process, NULL);
250
251 for(i = 0; i < BUTTON_HAL_PORT_COUNT; i++) {
252 gpio_hal_register_handler(&button_event_handlers[i]);
253 }
254}
255/*---------------------------------------------------------------------------*/
256PROCESS_THREAD(button_hal_process, ev, data)
257{
258 int_master_status_t status;
260 button_hal_button_t **button;
261#if GPIO_HAL_PORT_PIN_NUMBERING
262 uint8_t ports;
263#endif
264
266
267 while(1) {
268 PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
269
270 status = critical_enter();
271 pins = pmask;
272 pmask = 0;
273#if GPIO_HAL_PORT_PIN_NUMBERING
274 ports = port_mask;
275 port_mask = 0;
276#endif
277 critical_exit(status);
278
279 for(button = button_hal_buttons; *button != NULL; button++) {
280 if((gpio_hal_pin_to_mask((*button)->pin) & pins)
282 && ((1 << (*button)->port) & ports)
283#endif
284 ) {
285 /* Ignore all button presses/releases during its debounce */
286 if(ctimer_expired(&(*button)->debounce_ctimer)) {
287 /*
288 * Here we merely set a debounce timer. At the end of the debounce we
289 * will inspect the button's state and we will take action only if it
290 * has changed.
291 *
292 * This is to prevent erroneous edge detections due to interference.
293 */
294 ctimer_set(&(*button)->debounce_ctimer, BUTTON_HAL_DEBOUNCE_DURATION,
295 debounce_handler, *button);
296 }
297 }
298 }
299 }
300
301 PROCESS_END();
302}
303/*---------------------------------------------------------------------------*/
304/** @} */
Header file for the button HAL.
Header file for the callback timer.
Header file for the GPIO HAL.
const uint8_t button_hal_button_count
The number of buttons on a device.
Definition buttons.c:39
process_event_t button_hal_press_event
A broadcast event generated when a button gets pressed.
Definition button-hal.c:53
uint8_t button_hal_get_state(button_hal_button_t *button)
Get the state of a button (pressed / released)
Definition button-hal.c:200
button_hal_button_t * button_hal_get_by_id(uint8_t unique_id)
Retrieve a button by ID.
Definition button-hal.c:176
process_event_t button_hal_release_event
A broadcast event generated when a button gets released.
Definition button-hal.c:54
#define BUTTON_HAL_DEBOUNCE_DURATION
Controls the software debounce timer duration.
Definition button-hal.h:100
void button_hal_init()
Initialise the button HAL.
Definition button-hal.c:213
#define BUTTON_HAL_PORT_COUNT
Number of different ports that buttons are connected to.
Definition button-hal.h:124
button_hal_button_t * button_hal_get_by_index(uint8_t index)
Retrieve a button by its index.
Definition button-hal.c:190
process_event_t button_hal_periodic_event
A broadcast event generated every second while a button is kept pressed.
Definition button-hal.c:55
struct button_hal_button_s button_hal_button_t
A logical representation of a user button.
Definition button-hal.h:145
#define CLOCK_SECOND
A second, measured in system clock time.
Definition clock.h:103
static void critical_exit(int_master_status_t status)
Exit a critical section and restore the master interrupt.
Definition critical.h:81
static int_master_status_t critical_enter()
Enter a critical section.
Definition critical.h:65
void ctimer_stop(struct ctimer *c)
Stop a pending callback timer.
Definition ctimer.c:138
bool ctimer_expired(struct ctimer *c)
Check if a callback timer has expired.
Definition ctimer.c:150
static void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition ctimer.h:137
void gpio_hal_register_handler(gpio_hal_event_handler_t *handler)
Register a function to be called whenever a pin triggers an event.
Definition gpio-hal.c:55
uint32_t gpio_hal_pin_mask_t
GPIO pin mask representation.
Definition gpio-hal.h:142
uint32_t gpio_hal_pin_cfg_t
GPIO pin configuration.
Definition gpio-hal.h:118
uint8_t gpio_hal_port_t
A data structure that represents ports.
Definition gpio-hal.h:110
#define gpio_hal_pin_to_mask(pin)
Convert a pin to a pin mask.
Definition gpio-hal.h:255
#define GPIO_HAL_PORT_PIN_NUMBERING
Specifies whether the HAL should support a port/pin convention.
Definition gpio-hal.h:73
uint32_t int_master_status_t
Master interrupt state representation data type.
Definition int-master.h:62
#define PROCESS(name, strname)
Declare a process.
Definition process.h:308
int process_post(struct process *p, process_event_t ev, process_data_t data)
Post an asynchronous event.
Definition process.c:325
process_event_t process_alloc_event(void)
Allocate a global event number.
Definition process.c:111
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition process.h:121
#define PROCESS_END()
Define the end of a process.
Definition process.h:132
void process_start(struct process *p, process_data_t data)
Start a process.
Definition process.c:121
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition process.h:274
#define PROCESS_YIELD_UNTIL(c)
Yield the currently running process until a condition occurs.
Definition process.h:179
void process_poll(struct process *p)
Request a process to be polled.
Definition process.c:366
Header file for the Contiki process interface.
Datatype for GPIO event handlers.
Definition gpio-hal.h:180