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