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