Contiki-NG
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 /*---------------------------------------------------------------------------*/
51 PROCESS(button_hal_process, "Button HAL process");
52 /*---------------------------------------------------------------------------*/
53 process_event_t button_hal_press_event;
54 process_event_t button_hal_release_event;
55 process_event_t button_hal_periodic_event;
56 /*---------------------------------------------------------------------------*/
57 /* A mask of all ports that have changed state since the last process poll */
58 #if GPIO_HAL_PORT_PIN_NUMBERING
59 static volatile uint8_t port_mask;
60 #endif
61 
62 /* A mask of all pins that have changed state since the last process poll */
63 static volatile gpio_hal_pin_mask_t pmask;
64 /*---------------------------------------------------------------------------*/
65 #define PORT_UNUSED 0xFF
66 extern button_hal_button_t *button_hal_buttons[];
67 /*---------------------------------------------------------------------------*/
68 /* Button event handlers, one per port. Registered with the GPIO HAL */
69 static 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 /*---------------------------------------------------------------------------*/
77 static void
78 duration_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 /*---------------------------------------------------------------------------*/
88 static void
89 debounce_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 /*---------------------------------------------------------------------------*/
135 static void
136 press_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 /*---------------------------------------------------------------------------*/
150 get_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 /*---------------------------------------------------------------------------*/
176 button_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 /*---------------------------------------------------------------------------*/
199 uint8_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 /*---------------------------------------------------------------------------*/
212 void
214 {
215  button_hal_button_t **button;
216  gpio_hal_pin_cfg_t cfg;
217  gpio_hal_event_handler_t *handler;
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 /*---------------------------------------------------------------------------*/
256 PROCESS_THREAD(button_hal_process, ev, data)
257 {
258  int_master_status_t status;
259  gpio_hal_pin_mask_t pins;
260  button_hal_button_t **button;
261 #if GPIO_HAL_PORT_PIN_NUMBERING
262  uint8_t ports;
263 #endif
264 
265  PROCESS_BEGIN();
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 /** @} */
Datatype for GPIO event handlers.
Definition: gpio-hal.h:180
int ctimer_expired(struct ctimer *c)
Check if a callback timer has expired.
Definition: ctimer.c:161
process_event_t button_hal_periodic_event
A broadcast event generated every second while a button is kept pressed.
Definition: button-hal.c:55
button_hal_button_t * button_hal_get_by_id(uint8_t unique_id)
Retrieve a button by ID.
Definition: button-hal.c:176
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
process_event_t button_hal_release_event
A broadcast event generated when a button gets released.
Definition: button-hal.c:54
void ctimer_stop(struct ctimer *c)
Stop a pending callback timer.
Definition: ctimer.c:149
#define PROCESS_YIELD_UNTIL(c)
Yield the currently running process until a condition occurs.
Definition: process.h:178
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
static void critical_exit(int_master_status_t status)
Exit a critical section and restore the master interrupt.
Definition: critical.h:81
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
button_hal_button_t * button_hal_get_by_index(uint8_t index)
Retrieve a button by its index.
Definition: button-hal.c:190
#define BUTTON_HAL_DEBOUNCE_DURATION
Controls the software debounce timer duration.
Definition: button-hal.h:100
uint8_t gpio_hal_port_t
A data structure that represents ports.
Definition: gpio-hal.h:110
uint8_t button_hal_get_state(button_hal_button_t *button)
Get the state of a button (pressed / released)
Definition: button-hal.c:200
static int_master_status_t critical_enter()
Enter a critical section.
Definition: critical.h:65
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
const uint8_t button_hal_button_count
The number of buttons on a device.
Definition: buttons.c:39
INT_MASTER_STATUS_DATATYPE int_master_status_t
Master interrupt state representation data type.
Definition: int-master.h:62
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
Header file for the callback timer
void process_poll(struct process *p)
Request a process to be polled.
Definition: process.c:371
process_event_t button_hal_press_event
A broadcast event generated when a button gets pressed.
Definition: button-hal.c:53
void ctimer_set(struct ctimer *c, clock_time_t t, void(*f)(void *), void *ptr)
Set a callback timer.
Definition: ctimer.c:99
Header file for the Contiki process interface.
#define BUTTON_HAL_PORT_COUNT
Number of different ports that buttons are connected to.
Definition: button-hal.h:124
struct button_hal_button_s button_hal_button_t
A logical representation of a user button.
Definition: button-hal.h:145
process_event_t process_alloc_event(void)
Allocate a global event number.
Definition: process.c:93
#define GPIO_HAL_PORT_PIN_NUMBERING
Specifies whether the HAL should support a port/pin convention.
Definition: gpio-hal.h:73
uint32_t gpio_hal_pin_mask_t
GPIO pin mask representation.
Definition: gpio-hal.h:142
int process_post(struct process *p, process_event_t ev, process_data_t data)
Post an asynchronous event.
Definition: process.c:322
PROCESS_THREAD(cc2538_rf_process, ev, data)
Implementation of the cc2538 RF driver process.
Definition: cc2538-rf.c:1110
#define gpio_hal_pin_to_mask(pin)
Convert a pin to a pin mask.
Definition: gpio-hal.h:255
Header file for the GPIO HAL.
void button_hal_init()
Initialise the button HAL.
Definition: button-hal.c:213
Header file for the button HAL.
uint32_t gpio_hal_pin_cfg_t
GPIO pin configuration.
Definition: gpio-hal.h:118
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99