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 pins that have changed state since the last process poll */
58 static volatile gpio_hal_pin_mask_t pmask;
59 /*---------------------------------------------------------------------------*/
60 extern button_hal_button_t *button_hal_buttons[];
61 /*---------------------------------------------------------------------------*/
62 /* Common handler for all handler events, and register it with the GPIO HAL */
63 static gpio_hal_event_handler_t button_event_handler;
64 /*---------------------------------------------------------------------------*/
65 #if GPIO_HAL_PORT_PIN_NUMBERING
66 #define BTN_PORT(b) (b)->port
67 #else
68 #define BTN_PORT(b) GPIO_HAL_NULL_PORT
69 #endif
70 /*---------------------------------------------------------------------------*/
71 static void
72 duration_exceeded_callback(void *btn)
73 {
75 
76  button->press_duration_seconds++;
77  ctimer_set(&button->duration_ctimer, CLOCK_SECOND,
78  duration_exceeded_callback, button);
79  process_post(PROCESS_BROADCAST, button_hal_periodic_event, button);
80 }
81 /*---------------------------------------------------------------------------*/
82 static void
83 debounce_handler(void *btn)
84 {
85  button_hal_button_t *button;
86  int expired;
87  uint8_t button_state;
88 
89  button = (button_hal_button_t *)btn;
90 
91  /*
92  * A new debounce may have been triggered after expiration of the previous
93  * one but before we got called.
94  */
95  if(!ctimer_expired(&button->debounce_ctimer)) {
96  return;
97  }
98 
99  expired = ctimer_expired(&button->duration_ctimer);
100 
101  button_state = button_hal_get_state(button);
102 
103  /*
104  * A debounce timer expired. Inspect the button's state. If the button's
105  * state is the same as it was before, then we ignore this as noise.
106  */
107  if(button_state == BUTTON_HAL_STATE_PRESSED && expired) {
108  /*
109  * Button is pressed and no tick counter running. Treat as new press.
110  * Include the debounce duration in the first periodic, so that the
111  * callback will happen 1 second after the button press, not 1 second
112  * after the end of the debounce. Notify process about the press event.
113  */
114  button->press_duration_seconds = 0;
115  ctimer_set(&button->duration_ctimer,
117  duration_exceeded_callback, button);
118  process_post(PROCESS_BROADCAST, button_hal_press_event, button);
119  } else if(button_state == BUTTON_HAL_STATE_RELEASED && expired == 0) {
120  /*
121  * Button is released and there is a duration_ctimer running. Treat this
122  * as a new release and notify processes.
123  */
124  ctimer_stop(&button->duration_ctimer);
125  process_post(PROCESS_BROADCAST, button_hal_release_event, button);
126  }
127 }
128 /*---------------------------------------------------------------------------*/
129 static void
130 press_release_handler(
132  gpio_hal_port_t port,
133 #endif
134  gpio_hal_pin_mask_t pin_mask)
135 {
136  pmask |= pin_mask;
137  process_poll(&button_hal_process);
138 }
139 /*---------------------------------------------------------------------------*/
141 button_hal_get_by_id(uint8_t unique_id)
142 {
143  button_hal_button_t **button;
144 
145  for(button = button_hal_buttons; *button != NULL; button++) {
146  if((*button)->unique_id == unique_id) {
147  return *button;
148  }
149  }
150 
151  return NULL;
152 }
153 /*---------------------------------------------------------------------------*/
156 {
157  if(index >= button_hal_button_count) {
158  return NULL;
159  }
160 
161  return button_hal_buttons[index];
162 }
163 /*---------------------------------------------------------------------------*/
164 uint8_t
166 {
167  uint8_t pin_state = gpio_hal_arch_read_pin(BTN_PORT(button), button->pin);
168 
169  if((pin_state == 0 && button->negative_logic == true) ||
170  (pin_state == 1 && button->negative_logic == false)) {
171  return BUTTON_HAL_STATE_PRESSED;
172  }
173 
174  return BUTTON_HAL_STATE_RELEASED;
175 }
176 /*---------------------------------------------------------------------------*/
177 void
179 {
180  button_hal_button_t **button;
181  gpio_hal_pin_cfg_t cfg;
182 
186 
187  button_event_handler.pin_mask = 0;
188  button_event_handler.handler = press_release_handler;
189 
190  for(button = button_hal_buttons; *button != NULL; button++) {
191  cfg = GPIO_HAL_PIN_CFG_EDGE_BOTH | GPIO_HAL_PIN_CFG_INT_ENABLE |
192  (*button)->pull;
193  gpio_hal_arch_pin_set_input(BTN_PORT(*button), (*button)->pin);
194  gpio_hal_arch_pin_cfg_set(BTN_PORT(*button), (*button)->pin, cfg);
195  gpio_hal_arch_interrupt_enable(BTN_PORT(*button), (*button)->pin);
196  button_event_handler.pin_mask |= gpio_hal_pin_to_mask((*button)->pin);
197  }
198 
199  process_start(&button_hal_process, NULL);
200 
201  gpio_hal_register_handler(&button_event_handler);
202 }
203 /*---------------------------------------------------------------------------*/
204 PROCESS_THREAD(button_hal_process, ev, data)
205 {
206  int_master_status_t status;
207  gpio_hal_pin_mask_t pins;
208  button_hal_button_t **button;
209 
210  PROCESS_BEGIN();
211 
212  while(1) {
213  PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
214 
215  status = critical_enter();
216  pins = pmask;
217  pmask = 0;
218  critical_exit(status);
219 
220  for(button = button_hal_buttons; *button != NULL; button++) {
221  if(gpio_hal_pin_to_mask((*button)->pin) & pins) {
222  /* Ignore all button presses/releases during its debounce */
223  if(ctimer_expired(&(*button)->debounce_ctimer)) {
224  /*
225  * Here we merely set a debounce timer. At the end of the debounce we
226  * will inspect the button's state and we will take action only if it
227  * has changed.
228  *
229  * This is to prevent erroneous edge detections due to interference.
230  */
231  ctimer_set(&(*button)->debounce_ctimer, BUTTON_HAL_DEBOUNCE_DURATION,
232  debounce_handler, *button);
233  }
234  }
235  }
236  }
237 
238  PROCESS_END();
239 }
240 /*---------------------------------------------------------------------------*/
241 /** @} */
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:141
#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:155
#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:165
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.
struct button_hal_button_s button_hal_button_t
A logical representation of a user button.
Definition: button-hal.h:132
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:1107
#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:178
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