MCUXpresso_LPC845/components/button/fsl_component_button.c

570 lines
17 KiB
C

/*
* Copyright 2018-2019 NXP
* All rights reserved.
*
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_adapter_gpio.h"
#include "fsl_component_timer_manager.h"
#include "fsl_component_button.h"
/*
* The OSA_USED macro can only be defined when the OSA component is used.
* If the source code of the OSA component does not exist, the OSA_USED cannot be defined.
* OR, If OSA component is not added into project event the OSA source code exists, the OSA_USED
* also cannot be defined.
* The source code path of the OSA component is <MCUXpresso_SDK>/components/osa.
*
*/
#if defined(OSA_USED)
#include "fsl_os_abstraction.h"
#if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
#include "fsl_component_common_task.h"
#endif
#endif
/*******************************************************************************
* Definitions
******************************************************************************/
#if defined(OSA_USED)
#include "fsl_os_abstraction.h"
#if (defined(USE_RTOS) && (USE_RTOS > 0U))
#define BUTTON_SR_ALLOC() OSA_SR_ALLOC()
#define BUTTON_ENTER_CRITICAL() OSA_ENTER_CRITICAL();
#define BUTTON_EXIT_CRITICAL() OSA_EXIT_CRITICAL()
#else
#define BUTTON_SR_ALLOC()
#define BUTTON_ENTER_CRITICAL()
#define BUTTON_EXIT_CRITICAL()
#endif
#else
#define BUTTON_SR_ALLOC() uint32_t buttonPrimask;
#define BUTTON_ENTER_CRITICAL() buttonPrimask = DisableGlobalIRQ();
#define BUTTON_EXIT_CRITICAL() EnableGlobalIRQ(buttonPrimask);
#endif
typedef enum _button_press_status
{
kStatus_BUTTON_PressIdle = 0U, /*!< Idle */
kStatus_BUTTON_Pressed = 1U, /*!< Pressed */
kStatus_BUTTON_PressDoubleStart = 2U, /*!< Start double click */
kStatus_BUTTON_PressDoublePressed = 3U, /*!< Second press for double click */
} button_press_status_t;
typedef struct _button_state
{
struct _button_state *next;
button_callback_t callback;
void *callbackParam;
GPIO_HANDLE_DEFINE(gpioHandle);
volatile uint32_t pushPeriodCount;
volatile uint32_t pushPeriodCountLast;
uint8_t pinStateDefault;
uint8_t port;
uint8_t pin;
struct
{
volatile uint8_t pressed : 3U;
volatile uint8_t msg : 5U;
} state;
} button_state_t;
typedef struct _button_list
{
volatile uint32_t periodCount;
TIMER_MANAGER_HANDLE_DEFINE(timerHandle);
#if defined(OSA_USED)
#if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
common_task_message_t commonTaskMsg;
#else
OSA_EVENT_HANDLE_DEFINE(eventHandle);
OSA_TASK_HANDLE_DEFINE(taskHandle);
#endif
#endif
button_state_t *button;
volatile uint8_t timerOpenedNesting;
volatile uint8_t activeButtonCount;
} button_list_t;
/*******************************************************************************
* Prototypes
******************************************************************************/
static void BUTTON_Task(void *param);
/*******************************************************************************
* Variables
******************************************************************************/
static button_list_t s_buttonList;
#if defined(OSA_USED)
#if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
#else
/*
* \brief Defines the button task's stack
*/
static OSA_TASK_DEFINE(BUTTON_Task, BUTTON_TASK_PRIORITY, 1, BUTTON_TASK_STACK_SIZE, false);
#endif
#endif
/*******************************************************************************
* Code
******************************************************************************/
static void BUTTON_NotificationUpdate(button_state_t *buttonState, button_event_t event)
{
buttonState->state.pressed = (uint8_t)kStatus_BUTTON_PressIdle;
buttonState->state.msg = (uint8_t)event;
#if defined(OSA_USED)
#if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
s_buttonList.commonTaskMsg.callback = BUTTON_Task;
s_buttonList.commonTaskMsg.callbackParam = buttonState;
(void)COMMON_TASK_post_message(&s_buttonList.commonTaskMsg);
#else
(void)OSA_EventSet((osa_event_handle_t)s_buttonList.eventHandle, BUTTON_EVENT_BUTTON);
#endif
#else
BUTTON_Task(&s_buttonList);
#endif
}
static void BUTTON_Event(void *param)
{
button_state_t *buttonState = (button_state_t *)param;
uint8_t pinState = 0U;
assert(param);
(void)HAL_GpioGetInput(buttonState->gpioHandle, &pinState);
pinState = (0U != pinState) ? 1U : 0U;
if (((uint8_t)kStatus_BUTTON_PressIdle == buttonState->state.pressed) ||
((uint8_t)kStatus_BUTTON_PressDoubleStart == buttonState->state.pressed))
{
if (buttonState->pinStateDefault != pinState)
{
buttonState->state.pressed++;
buttonState->pushPeriodCount = s_buttonList.periodCount;
/* Start timer for interval scan button state. */
if (0U == s_buttonList.activeButtonCount)
{
(void)TM_Start(s_buttonList.timerHandle, (uint8_t)kTimerModeIntervalTimer, BUTTON_TIMER_INTERVAL);
}
s_buttonList.activeButtonCount++;
}
}
else
{
if (buttonState->pinStateDefault == pinState)
{
if ((BUTTON_DOUBLE_CLICK_THRESHOLD + buttonState->pushPeriodCountLast) >= buttonState->pushPeriodCount)
{
if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_SHORT_PRESS_THRESHOLD)
{
#if (defined(BUTTON_EVENT_DOUBLECLICK_ENABLE) && BUTTON_EVENT_DOUBLECLICK_ENABLE > 0U)
BUTTON_NotificationUpdate(buttonState, kBUTTON_EventDoubleClick);
#endif /* BUTTON_EVENT_DOUBLECLICK_ENABLE */
}
else
{
BUTTON_NotificationUpdate(buttonState, kBUTTON_EventError);
}
}
else
{
if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_SHORT_PRESS_THRESHOLD)
{
buttonState->pushPeriodCountLast = s_buttonList.periodCount;
buttonState->state.pressed = (uint8_t)kStatus_BUTTON_PressDoubleStart;
}
#if (defined(BUTTON_EVENT_SHORTPRESS_ENABLE) && BUTTON_EVENT_SHORTPRESS_ENABLE > 0U)
else if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_LONG_PRESS_THRESHOLD)
{
BUTTON_NotificationUpdate(buttonState, kBUTTON_EventShortPress);
}
#endif /* BUTTON_EVENT_SHORTPRESS_ENABLE */
#if (defined(BUTTON_EVENT_LONGPRESS_ENABLE) && BUTTON_EVENT_LONGPRESS_ENABLE > 0U)
else
{
BUTTON_NotificationUpdate(buttonState, kBUTTON_EventLongPress);
}
#else
else
{
BUTTON_NotificationUpdate(buttonState, kBUTTON_EventError);
}
#endif /* BUTTON_EVENT_LONGPRESS_ENABLE */
}
}
}
}
static void BUTTON_Task(void *param)
{
#if defined(OSA_USED)
#if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
#else
osa_event_flags_t ev = 0;
do
{
if (KOSA_StatusSuccess ==
OSA_EventWait((osa_event_handle_t)s_buttonList.eventHandle, osaEventFlagsAll_c, 0U, osaWaitForever_c, &ev))
{
#endif
#endif
button_state_t *buttonState = s_buttonList.button;
BUTTON_SR_ALLOC();
BUTTON_ENTER_CRITICAL();
while (NULL != buttonState)
{
if (0U != buttonState->state.msg)
{
button_callback_message_t msg;
BUTTON_EXIT_CRITICAL();
msg.event = (button_event_t)buttonState->state.msg;
(void)buttonState->callback(buttonState, &msg, buttonState->callbackParam);
buttonState->state.msg = 0U;
BUTTON_ENTER_CRITICAL();
/* Stop timer for efficiency */
s_buttonList.activeButtonCount--;
if (0U == s_buttonList.activeButtonCount)
{
(void)TM_Stop(s_buttonList.timerHandle);
}
}
buttonState = buttonState->next;
}
BUTTON_EXIT_CRITICAL();
#if defined(OSA_USED)
#if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
#else
}
} while (0U != gUseRtos_c);
#endif
#endif
}
static void BUTTON_TimerEvent(void *param)
{
#if (defined(BUTTON_EVENT_ONECLICK_ENABLE) && BUTTON_EVENT_ONECLICK_ENABLE > 0U)
button_state_t *buttonState;
BUTTON_SR_ALLOC();
#endif /* BUTTON_EVENT_ONECLICK_ENABLE */
s_buttonList.periodCount += BUTTON_TIMER_INTERVAL;
#if (defined(BUTTON_EVENT_ONECLICK_ENABLE) && BUTTON_EVENT_ONECLICK_ENABLE > 0U)
BUTTON_ENTER_CRITICAL();
buttonState = s_buttonList.button;
while (NULL != buttonState)
{
/*
* The code block is used to indentify the button event is one click or double click.
* If the flag pending is set and the button is not pressed, check the user activity is timeout or not.
* If is times out, notify the upper layer it is kBUTTON_EventOneClick.
* Otherwise, check the status next time.
*/
if ((uint8_t)kStatus_BUTTON_PressDoubleStart == buttonState->state.pressed)
{
if ((BUTTON_DOUBLE_CLICK_THRESHOLD + buttonState->pushPeriodCountLast) < s_buttonList.periodCount)
{
BUTTON_NotificationUpdate(buttonState, kBUTTON_EventOneClick);
buttonState->pushPeriodCountLast = 0U;
}
}
buttonState = buttonState->next;
}
BUTTON_EXIT_CRITICAL();
#endif /* BUTTON_EVENT_ONECLICK_ENABLE */
}
static void BUTTON_OpenTimer(void)
{
BUTTON_SR_ALLOC();
uint8_t initTimer = 0U;
BUTTON_ENTER_CRITICAL();
initTimer = (uint8_t)(!(bool)s_buttonList.timerOpenedNesting);
s_buttonList.timerOpenedNesting++;
BUTTON_EXIT_CRITICAL();
if (0U != initTimer)
{
timer_status_t timerStatus;
timerStatus = TM_Open((timer_handle_t)s_buttonList.timerHandle);
assert(kStatus_TimerSuccess == timerStatus);
timerStatus = TM_InstallCallback(s_buttonList.timerHandle, BUTTON_TimerEvent, &s_buttonList);
assert(kStatus_TimerSuccess == timerStatus);
(void)timerStatus;
}
}
static void BUTTON_CloseTimer(void)
{
BUTTON_SR_ALLOC();
uint8_t deinitTimer = 0U;
BUTTON_ENTER_CRITICAL();
if (s_buttonList.timerOpenedNesting > 0U)
{
s_buttonList.timerOpenedNesting--;
deinitTimer = (uint8_t)(!(bool)s_buttonList.timerOpenedNesting);
}
BUTTON_EXIT_CRITICAL();
if (0U != deinitTimer)
{
timer_status_t timerStatus;
timerStatus = TM_Close((timer_handle_t)s_buttonList.timerHandle);
assert(kStatus_TimerSuccess == timerStatus);
(void)timerStatus;
}
}
button_status_t BUTTON_Init(button_handle_t buttonHandle, button_config_t *buttonConfig)
{
button_state_t *buttonState;
hal_gpio_status_t gpioStatus;
BUTTON_SR_ALLOC();
assert((NULL != buttonHandle) && (NULL != buttonConfig));
assert(BUTTON_HANDLE_SIZE >= sizeof(button_state_t));
buttonState = (button_state_t *)buttonHandle;
(void)memset(buttonHandle, 0, sizeof(button_state_t));
BUTTON_ENTER_CRITICAL();
BUTTON_OpenTimer();
if (NULL == s_buttonList.button)
{
#if defined(OSA_USED)
#if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
(void)COMMON_TASK_init();
#else
osa_status_t osaStatus;
osaStatus = OSA_EventCreate((osa_event_handle_t)s_buttonList.eventHandle, 1U);
assert(KOSA_StatusSuccess == osaStatus);
osaStatus = OSA_TaskCreate((osa_task_handle_t)s_buttonList.taskHandle, OSA_TASK(BUTTON_Task), &s_buttonList);
assert(KOSA_StatusSuccess == osaStatus);
(void)osaStatus;
#endif
#endif
}
else
{
buttonState->next = s_buttonList.button;
}
s_buttonList.button = buttonState;
/* Timer only works when button have activities, so s_buttonList.periodCount would be 0 for the first press and
double click check case will be triggered. So we need set a start time for prevent this situation, a start time
bigger than BUTTON_DOUBLE_CLICK_THRESHOLD is works. */
s_buttonList.periodCount = BUTTON_DOUBLE_CLICK_THRESHOLD + BUTTON_TIMER_INTERVAL;
BUTTON_EXIT_CRITICAL();
(void)memcpy(&buttonState->pinStateDefault, &buttonConfig->gpio.pinStateDefault, 3U);
gpioStatus = HAL_GpioInit(buttonState->gpioHandle, (hal_gpio_pin_config_t *)((void *)buttonConfig));
assert(kStatus_HAL_GpioSuccess == gpioStatus);
gpioStatus = HAL_GpioSetTriggerMode(buttonState->gpioHandle, kHAL_GpioInterruptEitherEdge);
assert(kStatus_HAL_GpioSuccess == gpioStatus);
(void)gpioStatus;
return kStatus_BUTTON_Success;
}
button_status_t BUTTON_InstallCallback(button_handle_t buttonHandle, button_callback_t callback, void *callbackParam)
{
button_state_t *buttonState;
assert(buttonHandle);
buttonState = (button_state_t *)buttonHandle;
buttonState->callback = callback;
buttonState->callbackParam = callbackParam;
(void)HAL_GpioInstallCallback(buttonState->gpioHandle, BUTTON_Event, buttonState);
return kStatus_BUTTON_Success;
}
button_status_t BUTTON_Deinit(button_handle_t buttonHandle)
{
button_state_t *buttonState;
button_state_t *buttonStatePre;
BUTTON_SR_ALLOC();
assert(buttonHandle);
buttonState = (button_state_t *)buttonHandle;
BUTTON_ENTER_CRITICAL();
buttonStatePre = s_buttonList.button;
if (buttonStatePre != buttonState)
{
while ((NULL != buttonStatePre) && (buttonStatePre->next != buttonState))
{
buttonStatePre = buttonStatePre->next;
}
if (NULL != buttonStatePre)
{
buttonStatePre->next = buttonState->next;
}
}
else
{
s_buttonList.button = buttonState->next;
}
if (NULL == s_buttonList.button)
{
#if defined(OSA_USED)
#if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
#else
(void)OSA_TaskDestroy((osa_task_handle_t)s_buttonList.taskHandle);
(void)OSA_EventDestroy((osa_event_handle_t)s_buttonList.eventHandle);
#endif
#endif
}
BUTTON_CloseTimer();
BUTTON_EXIT_CRITICAL();
(void)HAL_GpioDeinit(buttonState->gpioHandle);
return kStatus_BUTTON_Success;
}
button_status_t BUTTON_GetInput(button_handle_t buttonHandle, uint8_t *pinState)
{
button_state_t *buttonState;
assert(buttonHandle);
buttonState = (button_state_t *)buttonHandle;
(void)HAL_GpioGetInput(buttonState->gpioHandle, pinState);
return kStatus_BUTTON_Success;
}
button_status_t BUTTON_WakeUpSetting(button_handle_t buttonHandle, uint8_t enable)
{
button_state_t *buttonState;
hal_gpio_status_t status;
assert(buttonHandle);
buttonState = (button_state_t *)buttonHandle;
status = HAL_GpioWakeUpSetting(buttonState->gpioHandle, enable);
if (kStatus_HAL_GpioSuccess == status)
{
return kStatus_BUTTON_Success;
}
return kStatus_BUTTON_Error;
}
button_status_t BUTTON_EnterLowpower(button_handle_t buttonHandle)
{
button_state_t *buttonState;
hal_gpio_status_t status;
/* MISRA C-2012 Rule 11.6 */
uint32_t *pLowpowerHandle = BUTTON_ALL_ENTER_EXIT_LOWPOWER_HANDLE;
assert(buttonHandle);
if ((button_handle_t)pLowpowerHandle != buttonHandle)
{
buttonState = (button_state_t *)buttonHandle;
}
else
{
buttonState = s_buttonList.button;
}
while (NULL != buttonState)
{
status = HAL_GpioEnterLowpower(buttonState->gpioHandle);
assert(kStatus_HAL_GpioSuccess == status);
(void)status;
BUTTON_CloseTimer();
if ((button_handle_t)pLowpowerHandle != buttonHandle)
{
break;
}
buttonState = buttonState->next;
}
return kStatus_BUTTON_Success;
}
button_status_t BUTTON_ExitLowpower(button_handle_t buttonHandle)
{
button_state_t *buttonState;
hal_gpio_status_t status;
/* MISRA C-2012 Rule 11.6 */
uint32_t *pLowpowerHandle = BUTTON_ALL_ENTER_EXIT_LOWPOWER_HANDLE;
assert(buttonHandle);
if ((button_handle_t)pLowpowerHandle != buttonHandle)
{
buttonState = (button_state_t *)buttonHandle;
}
else
{
buttonState = s_buttonList.button;
}
while (NULL != buttonState)
{
status = HAL_GpioExitLowpower(buttonState->gpioHandle);
assert(kStatus_HAL_GpioSuccess == status);
(void)status;
BUTTON_OpenTimer();
BUTTON_Event(buttonState);
if ((button_handle_t)pLowpowerHandle != buttonHandle)
{
break;
}
buttonState = buttonState->next;
}
return kStatus_BUTTON_Success;
}