blob: 5dad7f51ed12fc9f1f4082168fdc301b5d97e965 [file] [log] [blame]
/*
* Copyright 2018-2019 NXP
* All rights reserved.
*
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "fsl_common.h"
#include "gpio.h"
#include "timer_manager.h"
#include "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 "common_task.h"
#endif
#endif
/*******************************************************************************
* Definitions
******************************************************************************/
#define BUTTON_SR_ALLOC() uint32_t buttonPrimask;
#define BUTTON_ENTER_CRITICAL() buttonPrimask = DisableGlobalIRQ();
#define BUTTON_EXIT_CRITICAL() EnableGlobalIRQ(buttonPrimask);
typedef enum _button_press_status
{
kStatus_BUTTON_PressIdle = 0, /*!< Idle */
kStatus_BUTTON_Pressed = 1, /*!< Pressed */
kStatus_BUTTON_PressDoubleStart = 2, /*!< Start double click */
kStatus_BUTTON_PressDoublePressed = 3, /*!< 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;
struct
{
uint16_t port : 3U;
uint16_t reserved : 1U;
uint16_t pin : 5U;
uint16_t pinStateDefault : 1U;
uint16_t reserved2 : 6U;
} config;
struct
{
volatile uint8_t pressed;
volatile uint8_t msg;
} 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;
} button_list_t;
/*******************************************************************************
* Prototypes
******************************************************************************/
static void BUTTON_Task(void *param);
/*******************************************************************************
* Variables
******************************************************************************/
static button_list_t s_buttonList;
#if defined(OSA_USED)
extern const uint8_t gUseRtos_c;
#endif
#if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
#else
/*
* \brief Defines the button task's stack
*/
OSA_TASK_DEFINE(BUTTON_Task, BUTTON_TASK_PRIORITY, 1, BUTTON_TASK_STACK_SIZE, false);
#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;
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->config.pinStateDefault != pinState)
{
buttonState->state.pressed++;
buttonState->pushPeriodCount = s_buttonList.periodCount;
}
}
else
{
if (buttonState->config.pinStateDefault == pinState)
{
if ((BUTTON_DOUBLE_CLICK_THRESHOLD + buttonState->pushPeriodCountLast) >= buttonState->pushPeriodCount)
{
if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_SHORT_PRESS_THRESHOLD)
{
BUTTON_NotificationUpdate(buttonState, kBUTTON_EventDoubleClick);
}
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;
}
else if ((s_buttonList.periodCount - buttonState->pushPeriodCount) < BUTTON_LONG_PRESS_THRESHOLD)
{
BUTTON_NotificationUpdate(buttonState, kBUTTON_EventShortPress);
}
else
{
BUTTON_NotificationUpdate(buttonState, kBUTTON_EventLongPress);
}
}
}
}
}
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, false,
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();
}
buttonState = buttonState->next;
}
BUTTON_EXIT_CRITICAL();
#if defined(OSA_USED)
#if (defined(BUTTON_USE_COMMON_TASK) && (BUTTON_USE_COMMON_TASK > 0U))
#else
}
} while (gUseRtos_c);
#endif
#endif
}
static void BUTTON_TimerEvent(void *param)
{
button_state_t *buttonState;
BUTTON_SR_ALLOC();
s_buttonList.periodCount += BUTTON_TIMER_INTERVAL;
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();
}
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);
timerStatus = TM_Start(s_buttonList.timerHandle, (uint8_t)kTimerModeLowPowerTimer, BUTTON_TIMER_INTERVAL);
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)
{
hal_gpio_pin_config_t controlPin;
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))
COMMON_TASK_init();
#else
osa_status_t osaStatus;
osaStatus = OSA_EventCreate((osa_event_handle_t)s_buttonList.eventHandle, true);
assert(KOSA_StatusSuccess == osaStatus);
osaStatus = OSA_TaskCreate((osa_task_handle_t)s_buttonList.taskHandle, OSA_TASK(BUTTON_Task), &s_buttonList);
assert(KOSA_StatusSuccess == osaStatus);
#endif
#endif
}
else
{
buttonState->next = s_buttonList.button;
}
s_buttonList.button = buttonState;
BUTTON_EXIT_CRITICAL();
controlPin.port = buttonConfig->gpio.port;
controlPin.pin = buttonConfig->gpio.pin;
controlPin.direction = kHAL_GpioDirectionIn;
controlPin.level = buttonConfig->gpio.pinStateDefault;
gpioStatus = HAL_GpioInit(buttonState->gpioHandle, &controlPin);
assert(kStatus_HAL_GpioSuccess == gpioStatus);
(void)gpioStatus;
buttonState->config.port = buttonConfig->gpio.port;
buttonState->config.pin = buttonConfig->gpio.pin;
buttonState->config.pinStateDefault = (0U != buttonConfig->gpio.pinStateDefault) ? 1U : 0U;
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_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;
assert(buttonHandle);
buttonState = (button_state_t *)buttonHandle;
status = HAL_GpioEnterLowpower(buttonState->gpioHandle);
if (kStatus_HAL_GpioSuccess != status)
{
return kStatus_BUTTON_Error;
}
BUTTON_CloseTimer();
return kStatus_BUTTON_Success;
}
button_status_t BUTTON_ExitLowpower(button_handle_t buttonHandle)
{
button_state_t *buttonState;
hal_gpio_status_t status;
assert(buttonHandle);
buttonState = (button_state_t *)buttonHandle;
BUTTON_OpenTimer();
status = HAL_GpioExitLowpower(buttonState->gpioHandle);
if (kStatus_HAL_GpioSuccess != status)
{
return kStatus_BUTTON_Error;
}
BUTTON_Event(buttonState);
return kStatus_BUTTON_Success;
}