1 /*
2 * Copyright 2018-2019 NXP
3 * All rights reserved.
4 *
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include "fsl_adapter_gpio.h"
10 #include "fsl_component_timer_manager.h"
11
12 #include "fsl_component_button.h"
13 /*
14 * The OSA_USED macro can only be defined when the OSA component is used.
15 * If the source code of the OSA component does not exist, the OSA_USED cannot be defined.
16 * OR, If OSA component is not added into project event the OSA source code exists, the OSA_USED
17 * also cannot be defined.
18 * The source code path of the OSA component is <MCUXpresso_SDK>/components/osa.
19 *
20 */
21 #if defined(OSA_USED)
22 #include "fsl_os_abstraction.h"
23 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
24 #include "fsl_component_common_task.h"
25 #endif
26 #endif
27
28 /*******************************************************************************
29 * Definitions
30 ******************************************************************************/
31
32 #if defined(OSA_USED)
33 #include "fsl_os_abstraction.h"
34 #define BUTTON_SR_ALLOC() OSA_SR_ALLOC()
35 #define BUTTON_ENTER_CRITICAL() OSA_ENTER_CRITICAL();
36 #define BUTTON_EXIT_CRITICAL() OSA_EXIT_CRITICAL()
37 #else
38 #define BUTTON_SR_ALLOC() uint32_t buttonPrimask;
39 #define BUTTON_ENTER_CRITICAL() buttonPrimask = DisableGlobalIRQ();
40 #define BUTTON_EXIT_CRITICAL() EnableGlobalIRQ(buttonPrimask);
41 #endif
42
43 typedef enum _button_press_status
44 {
45 kStatus_BUTTON_PressIdle = 0U, /*!< Idle */
46 kStatus_BUTTON_Pressed = 1U, /*!< Pressed */
47 kStatus_BUTTON_PressDoubleStart = 2U, /*!< Start double click */
48 kStatus_BUTTON_PressDoublePressed = 3U, /*!< Second press for double click */
49 } button_press_status_t;
50
51 typedef struct _button_state
52 {
53 struct _button_state *next;
54 button_callback_t callback;
55 void *callbackParam;
56 GPIO_HANDLE_DEFINE(gpioHandle);
57 volatile uint32_t pushPeriodCount;
58 volatile uint32_t pushPeriodCountLast;
59 uint8_t pinStateDefault;
60 uint8_t port;
61 uint8_t pin;
62 struct
63 {
64 volatile uint8_t pressed : 3U;
65 volatile uint8_t msg : 5U;
66 } state;
67 } button_state_t;
68
69 typedef struct _button_list
70 {
71 volatile uint32_t periodCount;
72 TIMER_MANAGER_HANDLE_DEFINE(timerHandle);
73 #if defined(OSA_USED)
74
75 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
76 common_task_message_t commonTaskMsg;
77 #else
78 OSA_EVENT_HANDLE_DEFINE(eventHandle);
79 OSA_TASK_HANDLE_DEFINE(taskHandle);
80 #endif
81
82 #endif
83 button_state_t *button;
84 volatile uint8_t timerOpenedNesting;
85 volatile uint8_t activeButtonCount;
86 } button_list_t;
87
88 /*******************************************************************************
89 * Prototypes
90 ******************************************************************************/
91
92 static void BUTTON_Task(void *param);
93
94 /*******************************************************************************
95 * Variables
96 ******************************************************************************/
97
98 static button_list_t s_buttonList;
99
100 #if defined(OSA_USED)
101 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
102 #else
103 /*
104 * \brief Defines the button task's stack
105 */
106 static OSA_TASK_DEFINE(BUTTON_Task, BUTTON_TASK_PRIORITY, 1, BUTTON_TASK_STACK_SIZE, false);
107 #endif
108 #endif
109
110 /*******************************************************************************
111 * Code
112 ******************************************************************************/
113
BUTTON_NotificationUpdate(button_state_t * buttonState,button_event_t event)114 static void BUTTON_NotificationUpdate(button_state_t *buttonState, button_event_t event)
115 {
116 buttonState->state.pressed = (uint8_t)kStatus_BUTTON_PressIdle;
117 buttonState->state.msg = (uint8_t)event;
118 #if defined(OSA_USED)
119
120 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
121 s_buttonList.commonTaskMsg.callback = BUTTON_Task;
122 s_buttonList.commonTaskMsg.callbackParam = buttonState;
123 (void)COMMON_TASK_post_message(&s_buttonList.commonTaskMsg);
124 #else
125 (void)OSA_EventSet((osa_event_handle_t)s_buttonList.eventHandle, BUTTON_EVENT_BUTTON);
126 #endif
127
128 #else
129 BUTTON_Task(&s_buttonList);
130 #endif
131 }
132
BUTTON_Event(void * param)133 static void BUTTON_Event(void *param)
134 {
135 button_state_t *buttonState = (button_state_t *)param;
136 uint8_t pinState = 0U;
137
138 assert(param);
139
140 (void)HAL_GpioGetInput(buttonState->gpioHandle, &pinState);
141 pinState = (0U != pinState) ? 1U : 0U;
142 if (((uint8_t)kStatus_BUTTON_PressIdle == buttonState->state.pressed) ||
143 ((uint8_t)kStatus_BUTTON_PressDoubleStart == buttonState->state.pressed))
144 {
145 if (buttonState->pinStateDefault != pinState)
146 {
147 buttonState->state.pressed++;
148 buttonState->pushPeriodCount = s_buttonList.periodCount;
149
150 /* Start timer for interval scan button state. */
151 if (0U == s_buttonList.activeButtonCount)
152 {
153 (void)TM_Start(s_buttonList.timerHandle, (uint8_t)kTimerModeIntervalTimer, BUTTON_TIMER_INTERVAL);
154 }
155
156 if (((uint8_t)kStatus_BUTTON_Pressed) == (buttonState->state.pressed))
157 {
158 s_buttonList.activeButtonCount++;
159 }
160 }
161 }
162 else
163 {
164 if (buttonState->pinStateDefault == pinState)
165 {
166 if ((BUTTON_DOUBLE_CLICK_THRESHOLD + buttonState->pushPeriodCountLast) >= buttonState->pushPeriodCount)
167 {
168 if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_SHORT_PRESS_THRESHOLD)
169 {
170 #if (defined(BUTTON_EVENT_DOUBLECLICK_ENABLE) && BUTTON_EVENT_DOUBLECLICK_ENABLE > 0U)
171 BUTTON_NotificationUpdate(buttonState, kBUTTON_EventDoubleClick);
172 #endif /* BUTTON_EVENT_DOUBLECLICK_ENABLE */
173 }
174 else
175 {
176 BUTTON_NotificationUpdate(buttonState, kBUTTON_EventError);
177 }
178 }
179 else
180 {
181 if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_SHORT_PRESS_THRESHOLD)
182 {
183 buttonState->pushPeriodCountLast = s_buttonList.periodCount;
184 buttonState->state.pressed = (uint8_t)kStatus_BUTTON_PressDoubleStart;
185 }
186 #if (defined(BUTTON_EVENT_SHORTPRESS_ENABLE) && BUTTON_EVENT_SHORTPRESS_ENABLE > 0U)
187 else if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_LONG_PRESS_THRESHOLD)
188 {
189 BUTTON_NotificationUpdate(buttonState, kBUTTON_EventShortPress);
190 }
191 #endif /* BUTTON_EVENT_SHORTPRESS_ENABLE */
192 #if (defined(BUTTON_EVENT_LONGPRESS_ENABLE) && BUTTON_EVENT_LONGPRESS_ENABLE > 0U)
193 else
194 {
195 BUTTON_NotificationUpdate(buttonState, kBUTTON_EventLongPress);
196 }
197 #else
198 else
199 {
200 BUTTON_NotificationUpdate(buttonState, kBUTTON_EventError);
201 }
202 #endif /* BUTTON_EVENT_LONGPRESS_ENABLE */
203 }
204 }
205 }
206 }
207
BUTTON_Task(void * param)208 static void BUTTON_Task(void *param)
209 {
210 #if defined(OSA_USED)
211
212 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
213 #else
214 osa_event_flags_t ev = 0;
215
216 do
217 {
218 if (KOSA_StatusSuccess ==
219 OSA_EventWait((osa_event_handle_t)s_buttonList.eventHandle, osaEventFlagsAll_c, 0U, osaWaitForever_c, &ev))
220 {
221 #endif
222
223 #endif
224 button_state_t *buttonState = s_buttonList.button;
225 BUTTON_SR_ALLOC();
226
227 BUTTON_ENTER_CRITICAL();
228 while (NULL != buttonState)
229 {
230 if (0U != buttonState->state.msg)
231 {
232 button_callback_message_t msg;
233 BUTTON_EXIT_CRITICAL();
234 msg.event = (button_event_t)buttonState->state.msg;
235 (void)buttonState->callback(buttonState, &msg, buttonState->callbackParam);
236 buttonState->state.msg = 0U;
237 BUTTON_ENTER_CRITICAL();
238
239 /* Stop timer for efficiency */
240 s_buttonList.activeButtonCount--;
241 if (0U == s_buttonList.activeButtonCount)
242 {
243 (void)TM_Stop(s_buttonList.timerHandle);
244 }
245 }
246 buttonState = buttonState->next;
247 }
248 BUTTON_EXIT_CRITICAL();
249 #if defined(OSA_USED)
250
251 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
252 #else
253 }
254 } while (0U != gUseRtos_c);
255 #endif
256
257 #endif
258 }
259
260 static void BUTTON_TimerEvent(void *param)
261 {
262 #if (defined(BUTTON_EVENT_ONECLICK_ENABLE) && BUTTON_EVENT_ONECLICK_ENABLE > 0U)
263 button_state_t *buttonState;
264 BUTTON_SR_ALLOC();
265 #endif /* BUTTON_EVENT_ONECLICK_ENABLE */
266
267 s_buttonList.periodCount += BUTTON_TIMER_INTERVAL;
268
269 #if (defined(BUTTON_EVENT_ONECLICK_ENABLE) && BUTTON_EVENT_ONECLICK_ENABLE > 0U)
270 BUTTON_ENTER_CRITICAL();
271 buttonState = s_buttonList.button;
272 while (NULL != buttonState)
273 {
274 /*
275 * The code block is used to indentify the button event is one click or double click.
276 * If the flag pending is set and the button is not pressed, check the user activity is timeout or not.
277 * If is times out, notify the upper layer it is kBUTTON_EventOneClick.
278 * Otherwise, check the status next time.
279 */
280 if ((uint8_t)kStatus_BUTTON_PressDoubleStart == buttonState->state.pressed)
281 {
282 if ((BUTTON_DOUBLE_CLICK_THRESHOLD + buttonState->pushPeriodCountLast) < s_buttonList.periodCount)
283 {
284 BUTTON_NotificationUpdate(buttonState, kBUTTON_EventOneClick);
285 buttonState->pushPeriodCountLast = 0U;
286 }
287 }
288 buttonState = buttonState->next;
289 }
290 BUTTON_EXIT_CRITICAL();
291 #endif /* BUTTON_EVENT_ONECLICK_ENABLE */
292 }
293
294 static void BUTTON_OpenTimer(void)
295 {
296 BUTTON_SR_ALLOC();
297 uint8_t initTimer = 0U;
298
299 BUTTON_ENTER_CRITICAL();
300 initTimer = (uint8_t)(!(bool)s_buttonList.timerOpenedNesting);
301 s_buttonList.timerOpenedNesting++;
302 BUTTON_EXIT_CRITICAL();
303
304 if (0U != initTimer)
305 {
306 timer_status_t timerStatus;
307 timerStatus = TM_Open((timer_handle_t)s_buttonList.timerHandle);
308 assert(kStatus_TimerSuccess == timerStatus);
309
310 timerStatus = TM_InstallCallback(s_buttonList.timerHandle, BUTTON_TimerEvent, &s_buttonList);
311 assert(kStatus_TimerSuccess == timerStatus);
312
313 (void)timerStatus;
314 }
315 }
316
317 static void BUTTON_CloseTimer(void)
318 {
319 BUTTON_SR_ALLOC();
320 uint8_t deinitTimer = 0U;
321
322 BUTTON_ENTER_CRITICAL();
323 if (s_buttonList.timerOpenedNesting > 0U)
324 {
325 s_buttonList.timerOpenedNesting--;
326 deinitTimer = (uint8_t)(!(bool)s_buttonList.timerOpenedNesting);
327 }
328 BUTTON_EXIT_CRITICAL();
329
330 if (0U != deinitTimer)
331 {
332 timer_status_t timerStatus;
333 timerStatus = TM_Close((timer_handle_t)s_buttonList.timerHandle);
334 assert(kStatus_TimerSuccess == timerStatus);
335 (void)timerStatus;
336 }
337 }
338
339 button_status_t BUTTON_Init(button_handle_t buttonHandle, button_config_t *buttonConfig)
340 {
341 button_state_t *buttonState;
342 hal_gpio_status_t gpioStatus;
343 BUTTON_SR_ALLOC();
344
345 assert((NULL != buttonHandle) && (NULL != buttonConfig));
346 assert(BUTTON_HANDLE_SIZE >= sizeof(button_state_t));
347
348 buttonState = (button_state_t *)buttonHandle;
349
350 (void)memset(buttonHandle, 0, sizeof(button_state_t));
351
352 BUTTON_ENTER_CRITICAL();
353 BUTTON_OpenTimer();
354 if (NULL == s_buttonList.button)
355 {
356 #if defined(OSA_USED)
357
358 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
359 (void)COMMON_TASK_init();
360 #else
361 osa_status_t osaStatus;
362
363 osaStatus = OSA_EventCreate((osa_event_handle_t)s_buttonList.eventHandle, 1U);
364 assert(KOSA_StatusSuccess == osaStatus);
365
366 osaStatus = OSA_TaskCreate((osa_task_handle_t)s_buttonList.taskHandle, OSA_TASK(BUTTON_Task), &s_buttonList);
367 assert(KOSA_StatusSuccess == osaStatus);
368 (void)osaStatus;
369 #endif
370
371 #endif
372 }
373 else
374 {
375 buttonState->next = s_buttonList.button;
376 }
377 s_buttonList.button = buttonState;
378 /* Timer only works when button have activities, so s_buttonList.periodCount would be 0 for the first press and
379 double click check case will be triggered. So we need set a start time for prevent this situation, a start time
380 bigger than BUTTON_DOUBLE_CLICK_THRESHOLD is works. */
381 s_buttonList.periodCount = BUTTON_DOUBLE_CLICK_THRESHOLD + BUTTON_TIMER_INTERVAL;
382 BUTTON_EXIT_CRITICAL();
383
384 (void)memcpy(&buttonState->pinStateDefault, &buttonConfig->gpio.pinStateDefault, 3U);
385 gpioStatus = HAL_GpioInit(buttonState->gpioHandle, (hal_gpio_pin_config_t *)((void *)buttonConfig));
386
387 assert(kStatus_HAL_GpioSuccess == gpioStatus);
388
389 gpioStatus = HAL_GpioSetTriggerMode(buttonState->gpioHandle, kHAL_GpioInterruptEitherEdge);
390 assert(kStatus_HAL_GpioSuccess == gpioStatus);
391 (void)gpioStatus;
392
393 return kStatus_BUTTON_Success;
394 }
395
396 button_status_t BUTTON_InstallCallback(button_handle_t buttonHandle, button_callback_t callback, void *callbackParam)
397 {
398 button_state_t *buttonState;
399 assert(buttonHandle);
400
401 buttonState = (button_state_t *)buttonHandle;
402
403 buttonState->callback = callback;
404 buttonState->callbackParam = callbackParam;
405
406 (void)HAL_GpioInstallCallback(buttonState->gpioHandle, BUTTON_Event, buttonState);
407
408 return kStatus_BUTTON_Success;
409 }
410
411 button_status_t BUTTON_Deinit(button_handle_t buttonHandle)
412 {
413 button_state_t *buttonState;
414 button_state_t *buttonStatePre;
415 BUTTON_SR_ALLOC();
416
417 assert(buttonHandle);
418
419 buttonState = (button_state_t *)buttonHandle;
420
421 BUTTON_ENTER_CRITICAL();
422 buttonStatePre = s_buttonList.button;
423 if (buttonStatePre != buttonState)
424 {
425 while ((NULL != buttonStatePre) && (buttonStatePre->next != buttonState))
426 {
427 buttonStatePre = buttonStatePre->next;
428 }
429 if (NULL != buttonStatePre)
430 {
431 buttonStatePre->next = buttonState->next;
432 }
433 }
434 else
435 {
436 s_buttonList.button = buttonState->next;
437 }
438
439 if (NULL == s_buttonList.button)
440 {
441 #if defined(OSA_USED)
442
443 #if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
444
445 #else
446 (void)OSA_TaskDestroy((osa_task_handle_t)s_buttonList.taskHandle);
447 (void)OSA_EventDestroy((osa_event_handle_t)s_buttonList.eventHandle);
448 #endif
449
450 #endif
451 }
452 BUTTON_CloseTimer();
453 BUTTON_EXIT_CRITICAL();
454
455 (void)HAL_GpioDeinit(buttonState->gpioHandle);
456
457 return kStatus_BUTTON_Success;
458 }
459
460 button_status_t BUTTON_GetInput(button_handle_t buttonHandle, uint8_t *pinState)
461 {
462 button_state_t *buttonState;
463
464 assert(buttonHandle);
465
466 buttonState = (button_state_t *)buttonHandle;
467
468 (void)HAL_GpioGetInput(buttonState->gpioHandle, pinState);
469
470 return kStatus_BUTTON_Success;
471 }
472
473 button_status_t BUTTON_WakeUpSetting(button_handle_t buttonHandle, uint8_t enable)
474 {
475 button_state_t *buttonState;
476 hal_gpio_status_t status;
477
478 assert(buttonHandle);
479
480 buttonState = (button_state_t *)buttonHandle;
481
482 status = HAL_GpioWakeUpSetting(buttonState->gpioHandle, enable);
483
484 if (kStatus_HAL_GpioSuccess == status)
485 {
486 return kStatus_BUTTON_Success;
487 }
488 return kStatus_BUTTON_Error;
489 }
490
491 button_status_t BUTTON_EnterLowpower(button_handle_t buttonHandle)
492 {
493 button_state_t *buttonState;
494 hal_gpio_status_t status;
495 /* MISRA C-2012 Rule 11.6 */
496 uint32_t *pLowpowerHandle = BUTTON_ALL_ENTER_EXIT_LOWPOWER_HANDLE;
497
498 assert(buttonHandle);
499
500 if ((button_handle_t)pLowpowerHandle != buttonHandle)
501 {
502 buttonState = (button_state_t *)buttonHandle;
503 }
504 else
505 {
506 buttonState = s_buttonList.button;
507 }
508
509 while (NULL != buttonState)
510 {
511 status = HAL_GpioEnterLowpower(buttonState->gpioHandle);
512
513 assert(kStatus_HAL_GpioSuccess == status);
514 (void)status;
515
516 if ((button_handle_t)pLowpowerHandle != buttonHandle)
517 {
518 break;
519 }
520
521 buttonState = buttonState->next;
522 }
523
524 return kStatus_BUTTON_Success;
525 }
526
527 button_status_t BUTTON_ExitLowpower(button_handle_t buttonHandle)
528 {
529 button_state_t *buttonState;
530 hal_gpio_status_t status;
531 /* MISRA C-2012 Rule 11.6 */
532 uint32_t *pLowpowerHandle = BUTTON_ALL_ENTER_EXIT_LOWPOWER_HANDLE;
533
534 assert(buttonHandle);
535
536 if ((button_handle_t)pLowpowerHandle != buttonHandle)
537 {
538 buttonState = (button_state_t *)buttonHandle;
539 }
540 else
541 {
542 buttonState = s_buttonList.button;
543 }
544
545 while (NULL != buttonState)
546 {
547 status = HAL_GpioExitLowpower(buttonState->gpioHandle);
548
549 assert(kStatus_HAL_GpioSuccess == status);
550 (void)status;
551
552 BUTTON_Event(buttonState);
553
554 if ((button_handle_t)pLowpowerHandle != buttonHandle)
555 {
556 break;
557 }
558
559 buttonState = buttonState->next;
560 }
561
562 return kStatus_BUTTON_Success;
563 }
564