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