1 /*
2 * Copyright (c) 2016-2019, Texas Instruments Incorporated
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 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 /*
34 * ======== Button.c ========
35 */
36
37 #include <stdint.h>
38 #include <stdbool.h>
39
40 #include <ti/drivers/GPIO.h>
41 #include <ti/drivers/dpl/ClockP.h>
42 #include <ti/drivers/apps/Button.h>
43
44 /* Default Button_Params parameters structure */
45 const Button_Params Button_defaultParams =
46 {
47 5, /* 5 ms is the debounce timer value */
48 2000, /* 2 seconds long press duration */
49 200, /* 200 ms double key press detection timeout */
50 0xFF /* button subscribed for all callbacks */
51 };
52
53 extern Button_Config Button_config[];
54
55 /* Local functions */
56
57 /*
58 * ======== timediff ========
59 * Calculates time difference between input system tick and current system
60 * tick value. If more than 32 bits of ticks have passed, this function is
61 * unreliable.
62 */
timediff(uint32_t startTick)63 static uint32_t timediff(uint32_t startTick)
64 {
65 uint32_t currentTick = ClockP_getSystemTicks();
66
67 if(currentTick > startTick)
68 {
69 return(currentTick - startTick);
70 }
71 else {
72 /* System tick value overflowed */
73 return(currentTick + ((uint32_t)(~0) - startTick));
74 }
75 }
76
77 /*
78 * ======== Button_close ========
79 * Closes an instance of a Button
80 */
Button_close(Button_Handle handle)81 bool Button_close(Button_Handle handle)
82 {
83 Button_Object *obj = (Button_Object *)(handle->object);
84 Button_HWAttrs *hw = (Button_HWAttrs *)handle->hwAttrs;
85 GPIO_disableInt(hw->gpioIndex);
86 ClockP_stop(obj->clockHandle);
87 ClockP_delete(obj->clockHandle);
88 obj->clockHandle = NULL;
89 return(true);
90 }
91
92 /*
93 * ======== Button_gpioCallbackFxn ========
94 * Callback function for the button interrupts
95 */
Button_gpioCallbackFxn(uint_least8_t index)96 void Button_gpioCallbackFxn(uint_least8_t index)
97 {
98 /* Handle GPIO interrupt */
99 uint_least8_t i;
100
101 for(i = 0; i < Button_count; i++)
102 {
103 Button_Object *obj = (Button_Object*) Button_config[i].object;
104 Button_HWAttrs *hw = (Button_HWAttrs*) Button_config[i].hwAttrs;
105 if(hw->gpioIndex == index)
106 {
107 ClockP_setTimeout(obj->clockHandle, obj->debounceDuration);
108 ClockP_start(obj->clockHandle);
109
110 switch(obj->buttonStateVariables.state)
111 {
112 case Button_PRESSING:
113 obj->buttonStateVariables.state = Button_RELEASING;
114 break;
115 case Button_PRESSED:
116 case Button_LONGPRESSING:
117 obj->buttonStateVariables.state = Button_RELEASING;
118 break;
119 case Button_LONGPRESSED:
120 obj->buttonStateVariables.state = Button_RELEASING_LONG;
121 break;
122 case Button_RELEASED:
123 obj->buttonStateVariables.state = Button_PRESSING;
124 break;
125 case Button_DBLPRESS_DETECTION:
126 obj->buttonStateVariables.state = Button_DBLPRESSING;
127 break;
128 case Button_DBLPRESSED:
129 obj->buttonStateVariables.state = Button_RELEASING_DBLPRESSED;
130 break;
131 /*
132 * Any other case, mark the button as pressed.
133 * Typically we should not come here.
134 */
135 default:
136 obj->buttonStateVariables.state = Button_PRESSING;
137 break;
138 }
139 /* Disable the interrupt until the debounce timer expires */
140 GPIO_disableInt(index);
141 break;
142 }
143 }
144 }
145
146 /*
147 * ======== Button_init ========
148 */
Button_init()149 void Button_init()
150 {
151 /* Initialize GPIO driver if it wasn't already */
152 GPIO_init();
153 }
154
155 /*
156 * ======== Button_clockTimeoutHandler ========
157 * Timeout handler for the clock timeouts
158 */
Button_clockTimeoutHandler(uintptr_t arg)159 static void Button_clockTimeoutHandler(uintptr_t arg)
160 {
161 Button_Object *obj;
162 Button_HWAttrs *hw;
163 Button_Handle buttonHandle;
164 GPIO_PinConfig pinConfig;
165 Button_EventMask buttonEvents = 0;
166
167 buttonHandle = (Button_Handle)arg;
168 obj = (Button_Object *)buttonHandle->object;
169 hw = (Button_HWAttrs*)buttonHandle->hwAttrs;
170 GPIO_getConfig(hw->gpioIndex,&pinConfig);
171
172 if(GPIO_read(hw->gpioIndex) == obj->buttonPull)
173 {
174 /*
175 * Getting a debounce duration timeout callback. The button is
176 * currently in unpressed (pull) state.
177 */
178 switch(obj->buttonStateVariables.state)
179 {
180 case Button_RELEASING:
181 if(obj->buttonEventMask & Button_EV_DOUBLECLICKED)
182 {
183 /* Set clock to detect a double press */
184 obj->buttonStateVariables.state = Button_DBLPRESS_DETECTION;
185 ClockP_setTimeout(obj->clockHandle,
186 obj->doublePressDetectiontimeout -
187 obj->debounceDuration);
188 ClockP_start(obj->clockHandle);
189 }
190 else
191 {
192 obj->buttonStateVariables.lastPressedDuration =
193 timediff(obj->buttonStateVariables.pressedStartTime);
194 obj->buttonStateVariables.state = Button_RELEASED;
195 if(obj->buttonEventMask & Button_EV_RELEASED)
196 {
197 buttonEvents |= Button_EV_RELEASED;
198 }
199 if(obj->buttonEventMask & Button_EV_CLICKED)
200 {
201 buttonEvents |= Button_EV_CLICKED;
202 }
203 }
204 break;
205
206 case Button_DBLPRESS_DETECTION:
207 obj->buttonStateVariables.lastPressedDuration =
208 timediff(obj->buttonStateVariables.pressedStartTime);
209 if(obj->buttonEventMask & Button_EV_RELEASED)
210 {
211 buttonEvents |= Button_EV_RELEASED;
212 }
213 if(obj->buttonEventMask & Button_EV_CLICKED)
214 {
215 buttonEvents |= Button_EV_CLICKED;
216 }
217 obj->buttonStateVariables.state = Button_RELEASED;
218 break;
219
220 case Button_RELEASING_LONG:
221 obj->buttonStateVariables.lastPressedDuration =
222 timediff(obj->buttonStateVariables.pressedStartTime);
223 if(obj->buttonEventMask & Button_EV_LONGCLICKED)
224 {
225 buttonEvents |= Button_EV_LONGCLICKED;
226 buttonEvents |= Button_EV_RELEASED;
227 }
228 obj->buttonStateVariables.state = Button_RELEASED;
229 break;
230
231 case Button_RELEASING_DBLPRESSED:
232 obj->buttonStateVariables.state = Button_RELEASED;
233 if(obj->buttonEventMask & Button_EV_RELEASED)
234 {
235 buttonEvents |= Button_EV_RELEASED;
236 }
237 break;
238
239 case Button_PRESSING:
240 case Button_DBLPRESSING:
241 case Button_LONGPRESSING:
242 /*
243 * Button was pressed and released within debounce time
244 * Does not count.
245 */
246 obj->buttonStateVariables.state = Button_RELEASED;
247 break;
248
249 /*
250 * Any other case, mark the button as pressed.
251 * Typically we should not come here.
252 */
253 default:
254 obj->buttonStateVariables.state = Button_RELEASED;
255 break;
256 }
257 if(obj->buttonPull == Button_PULL_DOWN)
258 {
259 GPIO_setConfig(hw->gpioIndex,
260 ((pinConfig & (~GPIO_CFG_INT_MASK))|
261 GPIO_CFG_IN_INT_RISING));
262 }
263 else if(obj->buttonPull == Button_PULL_UP)
264 {
265 GPIO_setConfig(hw->gpioIndex,
266 ((pinConfig & (~GPIO_CFG_INT_MASK))|
267 GPIO_CFG_IN_INT_FALLING));
268 }
269 }
270 /*
271 * Getting a debounce duration timeout callback. The button is currently
272 * pressed.
273 */
274 else
275 {
276 switch(obj->buttonStateVariables.state)
277 {
278 case Button_PRESSING:
279 /* This is a debounced press */
280 obj->buttonStateVariables.pressedStartTime = ClockP_getSystemTicks();
281 if(obj->buttonEventMask & Button_EV_PRESSED)
282 {
283 buttonEvents |= Button_EV_PRESSED;
284 }
285 /* Start countdown if interest in long-press */
286 if(obj->buttonEventMask &
287 (Button_EV_LONGPRESSED | Button_EV_LONGCLICKED))
288 {
289 obj->buttonStateVariables.state = Button_LONGPRESSING;
290 ClockP_setTimeout(obj->clockHandle, obj->longPressDuration - obj->debounceDuration);
291 ClockP_start(obj->clockHandle);
292 }
293 else
294 {
295 obj->buttonStateVariables.state = Button_PRESSED;
296 }
297 break;
298
299 case Button_DBLPRESSING:
300 /* This is a debounced press (this is considered as double click) */
301 if(obj->buttonEventMask & Button_EV_DOUBLECLICKED)
302 {
303 buttonEvents |= Button_EV_DOUBLECLICKED;
304 }
305 obj->buttonStateVariables.state = Button_DBLPRESSED;
306 break;
307
308 case Button_LONGPRESSING:
309 obj->buttonStateVariables.state = Button_LONGPRESSED;
310 if(obj->buttonEventMask & Button_EV_LONGPRESSED)
311 {
312 buttonEvents |= Button_EV_LONGPRESSED;
313 }
314 break;
315
316 case Button_RELEASING:
317 case Button_RELEASING_LONG:
318 case Button_RELEASING_DBLPRESSED:
319 /*
320 * We're releasing, but isn't released after debounce.
321 * Start count down again if interest in long-press
322 */
323 if(obj->buttonEventMask &
324 (Button_EV_LONGPRESSED | Button_EV_LONGCLICKED))
325 {
326 obj->buttonStateVariables.state = Button_LONGPRESSING;
327 ClockP_setTimeout(obj->clockHandle, obj->longPressDuration - obj->debounceDuration);
328 ClockP_start(obj->clockHandle);
329 obj->buttonStateVariables.state = Button_LONGPRESSING;
330 }
331 else
332 {
333 obj->buttonStateVariables.state = Button_PRESSED;
334 }
335
336 /*
337 * Any other case, mark the button as pressed.
338 * Typically we should not come here
339 */
340 default:
341 obj->buttonStateVariables.state = Button_PRESSED;
342 break;
343 }
344 if(obj->buttonPull == Button_PULL_DOWN)
345 {
346 GPIO_setConfig(hw->gpioIndex,
347 ((pinConfig & (~GPIO_CFG_INT_MASK))
348 |GPIO_CFG_IN_INT_FALLING));
349 }
350 else if(obj->buttonPull == Button_PULL_UP)
351 {
352 GPIO_setConfig(hw->gpioIndex,
353 ((pinConfig & (~GPIO_CFG_INT_MASK))|
354 GPIO_CFG_IN_INT_RISING));
355 }
356 }
357 if((buttonEvents != 0) && (obj->buttonCallback != NULL))
358 {
359 obj->buttonCallback(buttonHandle,buttonEvents);
360 }
361 GPIO_enableInt(hw->gpioIndex);
362 }
363
364 /*
365 * ======== Button_open ========
366 * Open a Button instance
367 */
Button_open(uint_least8_t buttonIndex,Button_Callback buttonCallback,Button_Params * params)368 Button_Handle Button_open(uint_least8_t buttonIndex,
369 Button_Callback buttonCallback,
370 Button_Params *params)
371 {
372 Button_Params localParams;
373 Button_Handle handle;
374 Button_Object *obj;
375 Button_HWAttrs *hw;
376 GPIO_PinConfig pinConfig;
377 ClockP_Params clockParams;
378
379 /*
380 * This sets the init state of the button
381 * buttonIndex cannot be greater than total ButtonCount
382 */
383 if(buttonIndex >= Button_count)
384 {
385 return(NULL);
386 }
387
388 /* If params is null then use the default params */
389 if(params == NULL)
390 {
391 /*
392 * Make a local copy of default params to pass, to avoid casting away
393 * const on Button_defaultParams
394 */
395 localParams = Button_defaultParams;
396 params = &localParams;
397 }
398
399 /* Call init in case user forgot */
400 Button_init();
401
402 /* Get instance state structure */
403 handle = (Button_Handle)&Button_config[buttonIndex];
404 obj = (Button_Object*)(Button_config[buttonIndex].object);
405 hw = (Button_HWAttrs*)(Button_config[buttonIndex].hwAttrs);
406
407 /* If instance already has a clock then it is already open */
408 if(obj->clockHandle != NULL)
409 {
410 return(NULL);
411 }
412
413 /* Set internal variables */
414 obj->debounceDuration = ClockP_convertMsToSystemTicksRound(params->debounceDuration);
415 obj->longPressDuration = ClockP_convertMsToSystemTicksRound(params->longPressDuration);
416 obj->doublePressDetectiontimeout =
417 ClockP_convertMsToSystemTicksRound(params->doublePressDetectiontimeout);
418 obj->buttonCallback = buttonCallback;
419 obj->buttonEventMask = params->buttonEventMask;
420
421 /* Get button configuration from GPIO pin config */
422 obj->buttonPull = Button_PULL_NOTSET;
423 GPIO_getConfig(hw->gpioIndex, &pinConfig);
424 if(pinConfig & GPIO_CFG_IN_NOPULL)
425 {
426 /*
427 * Must infer pull from trigger edge. There is likely an external pull
428 * up/down attached to the button
429 */
430 if(pinConfig & GPIO_CFG_IN_INT_FALLING)
431 {
432 obj->buttonPull = Button_PULL_UP;
433 }
434 else if(pinConfig & GPIO_CFG_IN_INT_RISING)
435 {
436 obj->buttonPull = Button_PULL_DOWN;
437 }
438 }
439 else
440 {
441 /* Using an internal pull up/down */
442 if(pinConfig & GPIO_CFG_IN_PU)
443 {
444 obj->buttonPull = Button_PULL_UP;
445 }
446 else if(pinConfig & GPIO_CFG_IN_PD)
447 {
448 obj->buttonPull = Button_PULL_DOWN;
449 }
450 }
451
452 /* If read incorrect gpio settings, fail to open */
453 if(obj->buttonPull == Button_PULL_NOTSET)
454 {
455 return(NULL);
456 }
457
458 /* Create one shot clock for handling debounce */
459 ClockP_Params_init(&clockParams);
460 clockParams.period = 0; /* Indicates a one shot clock */
461 clockParams.startFlag = false;
462 clockParams.arg = (uintptr_t) handle;
463 obj->clockHandle = ClockP_create(Button_clockTimeoutHandler, 0, &clockParams);
464 if(NULL == obj->clockHandle)
465 {
466 return(NULL);
467 }
468
469 /* Enable gpio interrupt */
470 GPIO_setCallback(hw->gpioIndex, &Button_gpioCallbackFxn);
471 GPIO_enableInt(hw->gpioIndex);
472 return(handle);
473 }
474
475 /*
476 * ======== Button_Params_init ========
477 * Initialize a Button_Params struct to default settings.
478 */
Button_Params_init(Button_Params * params)479 void Button_Params_init(Button_Params *params)
480 {
481 *params = Button_defaultParams;
482 }
483
484 /*
485 * ======== Button_setCallback ========
486 * Set the callback for the buttons.
487 */
Button_setCallback(Button_Handle handle,Button_Callback buttonCallback)488 void Button_setCallback(Button_Handle handle,Button_Callback buttonCallback)
489 {
490 Button_Object *obj = (Button_Object *)handle->object;
491
492 obj->buttonCallback = buttonCallback;
493 }
494
495 /*
496 * ======== Button_getLastPressedDuration ========
497 * Return the get last pressed duration
498 */
Button_getLastPressedDuration(Button_Handle handle)499 extern uint32_t Button_getLastPressedDuration(Button_Handle handle)
500 {
501 uint32_t ticks = ((Button_Object *)(handle->object))->
502 buttonStateVariables.lastPressedDuration;
503 return(ClockP_convertSystemTicksToMsRound(ticks));
504 }
505