/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2022. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file ac780x_gpio.c
 *
 * @brief This file provides gpio integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_gpio_reg.h"

/* ============================================  Define  ============================================ */
#define GPIO_BIT0       (1UL)
#define GPIO_INT_NUM    (5UL)

/* GPIO related info */
static const IRQn_Type s_gpioIRQ[GPIO_INT_NUM] =  \
{EXTI0_IRQn, EXTI1_IRQn, EXTI2_IRQn, EXTI3_8_IRQn, EXTI9_15_IRQn};

/*!< PIN MUX register define */
#define PMUX0                                     ((uint32_t *)(&(PMUX->PINMUX0)))
#define PMUX1                                     ((uint32_t *)(&(PMUX->PINMUX1)))
#define PMUX2                                     ((uint32_t *)(&(PMUX->PINMUX2)))

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
DeviceCallback_Type g_gpioCallbackArray[GPIO_INT_NUM] = {(DeviceCallback_Type)NULL};

/* ====================================  Functions declaration  ===================================== */
static void GPIO_SetPinExtIntSource(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin);
static GPIO_Type * GPIO_GetPendingExtIntSource(GPIO_PinType GPIO_Pin);

static void GPIO_SetPinFallingEdge(GPIO_PinType GPIO_Pin, ACTION_Type enable);
static void GPIO_SetPinRisingEdge(GPIO_PinType GPIO_Pin, ACTION_Type enable);

static void GPIO_SetPinExtIntMask(GPIO_PinType GPIO_Pin, ACTION_Type enable);

/* ======================================  Functions define  ======================================== */
/*!
* @brief Set several GPIO pins' Pmux and pin function
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] config: GPIO_Config type structure pointer
* @return none
*/
void GPIO_Init(GPIO_Type *GPIOx, const GPIO_ConfigType* config)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(config->GPIO_Pin));
    DEVICE_ASSERT(IS_GPIO_DIR(config->GPIO_Mode.GPIO_Dir));
    DEVICE_ASSERT(IS_GPIO_PINMUX(config->GPIO_Mode.GPIO_Fun));

    CKGEN_Enable(CLK_GPIO, ENABLE);
    CKGEN_SoftReset(SRST_GPIO, ENABLE);

    GPIO_SetGPIOxMODE(GPIOx, config->GPIO_Pin, config->GPIO_Mode.GPIO_Dir); /* GPIO mode config */

    switch (config->GPIO_Mode.GPIO_PuPd)             /* GPIO PuPd config */
    {
        case GPIO_FLOATING:
            GPIO_SetGPIOxPD(GPIOx, config->GPIO_Pin, DISABLE);
            GPIO_SetGPIOxPU(GPIOx, config->GPIO_Pin, DISABLE);
        break;

        case GPIO_PU:
            GPIO_SetGPIOxPD(GPIOx, config->GPIO_Pin, DISABLE);
            GPIO_SetGPIOxPU(GPIOx, config->GPIO_Pin, ENABLE);
        break;

        case GPIO_PD:
            GPIO_SetGPIOxPD(GPIOx, config->GPIO_Pin, ENABLE);
            GPIO_SetGPIOxPU(GPIOx, config->GPIO_Pin, DISABLE);
        break;

        default:
        break;
    }

    GPIO_SetFunc(GPIOx, config->GPIO_Pin, config->GPIO_Mode.GPIO_Fun);
    GPIO_SetGPIOxE4E2x(GPIOx, config->GPIO_Pin, config->GPIO_Driving);      /* GPIO config driving */
}

/*!
* @brief reset all GPIO: disable interrupt and disable clock
*
* @param[in] none
* @return none
*/
void GPIO_DeInit(void)
{
    uint8_t i = 0;

    for (i = 0; i < GPIO_INT_NUM; i++)
    {
        NVIC_DisableIRQ(s_gpioIRQ[i]);
        NVIC_ClearPendingIRQ(s_gpioIRQ[i]);
        g_gpioCallbackArray[i] = NULL;
    }

    CKGEN_SoftReset(SRST_GPIO, DISABLE);
    CKGEN_Enable(CLK_GPIO, DISABLE);
}

/*!
* @brief Set GPIO pin level by GPIO_ODR register
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] pinVal: Pin level
*                   -GPIO_LEVEL_LOW
*                   -GPIO_LEVEL_HIGH
* @return none
*/
void GPIO_SetPinLevel(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin, GPIO_LevelType pinLevel)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    GPIO_SetGPIOxODRx(GPIOx, GPIO_Pin, pinLevel);
}

/*!
* @brief Get GPIO pin value by GPIO_IDR register
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @return pinVal: Pin level
*                   -GPIO_LEVEL_LOW
*                   -GPIO_LEVEL_HIGH
*/
GPIO_LevelType GPIO_GetPinLevel(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin)
{
    uint32_t pinVal = 0;

    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    pinVal = ((GPIOx->IDR) >> GPIO_IDR_Pos(GPIO_Pin)) & 0x01;

    return ((GPIO_LevelType)pinVal);
}

/*!
* @brief Set Select GPIO pin by GPIO_BSRR register
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @return none
*/
void GPIO_SetPinBit(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    GPIO_SetGPIOxBSRRx(GPIOx, GPIO_Pin);
}

/*!
* @brief Reset Select GPIO pin
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @return none
*/
void GPIO_ResetPinBit(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    GPIO_SetGPIOxBRRx(GPIOx, GPIO_Pin);
}

/*!
* @brief Set GPIO port value by GPIO_ODR register
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] portLevel: GPIOx_ODR register value, it can be 0~0xFFFF
* @return none
*/
void GPIO_SetPortLevel(GPIO_Type *GPIOx, uint16_t portLevel)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));

    GPIOx->ODR = portLevel;
}

/*!
* @brief Get GPIO port level by GPIO_IDR register
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @return portLevel: GPIOx_IDR register value, it can be 0~0xFFFF
*/
uint16_t GPIO_GetPortLevel(GPIO_Type *GPIOx)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));

    return (uint16_t)(GPIOx->IDR & 0xFFFF);
}

/*!
* @brief Set a GPIO pin's Pmux and pin function
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] functionx: GPIO function
*                   -GPIO_FUN0
*                   -GPIO_FUN1
*                   -GPIO_FUN2
*                   -GPIO_FUN3
* @return none
*/
void GPIO_SetFunc(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin, GPIO_FunType functionx)
{
    uint8_t gpioNum = 0;

    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));
    DEVICE_ASSERT(IS_GPIO_PINMUX(functionx));

    switch ((uint32_t)GPIOx)
    {
        case GPIOA_BASE:
            gpioNum = 0;
        break;

        case GPIOB_BASE:
            gpioNum = 16;
        break;

        default:
        break;
    }

    /* Get GPIO number */
    gpioNum = gpioNum + (uint8_t)GPIO_Pin;

    /* Set PMUX */
    if (gpioNum < 10)          /* GPIO 0~9 */
    {
        PMUX_SetPMUXxPFSELx(PMUX0, gpioNum, functionx);
    }
    else if (gpioNum < 20)     /* GPIO 10~19 */
    {
        gpioNum = gpioNum - 10;
        PMUX_SetPMUXxPFSELx(PMUX1, gpioNum, functionx);
    }
    else if (gpioNum < 30)    /* GPIO 20~29 */
    {
        gpioNum = gpioNum - 20;
        PMUX_SetPMUXxPFSELx(PMUX2, gpioNum, functionx);
    }
    else
    {
        /* do nothing */
    }

    /* Enable input detection */
    GPIO_SetGPIOxIES(GPIOx, GPIO_Pin, ENABLE);

}

/*!
* @brief Get a GPIO pin's Pmux function
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @return GPIO pin Pmux function
*/
GPIO_FunType GPIO_GetPinFunc(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin)
{
    uint32_t pinFunc = 0;
    uint8_t gpioNum = 0;

    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    switch((uint32_t)GPIOx)
    {
        case GPIOA_BASE:
            gpioNum = 0;
        break;

        case GPIOB_BASE:
            gpioNum = 16;
        break;

        default:
        break;
    }

    /* Get GPIO number */
    gpioNum = gpioNum + (uint8_t)GPIO_Pin;

    /* Set PMUX */
    if (gpioNum < 10)          /* GPIO 0~9 */
    {
        pinFunc = (*PMUX0) >> (PMUX_PINMUX_Pos(gpioNum));
    }
    else if (gpioNum < 20)     /* GPIO 10~19 */
    {
        gpioNum = gpioNum - 10;
        pinFunc = (*PMUX1) >> (PMUX_PINMUX_Pos(gpioNum));
    }
    else if (gpioNum < 30)     /* GPIO 20~29 */
    {
        gpioNum = gpioNum - 20;
        pinFunc = (*PMUX2) >> (PMUX_PINMUX_Pos(gpioNum));
    }
    else
    {

    }

    return (GPIO_FunType)(pinFunc & 0x07UL);
}

/*!
* @brief Set several GPIO pins' Pmux( pin function )
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] pinMask: GPIO pin mask value indicate which GPIO need to be modifyed
* @param[in] functionx: GPIO function
*                   -GPIO_FUN0
*                   -GPIO_FUN1
*                   -GPIO_FUN2
*                   -GPIO_FUN3
* @return none
*/
void GPIO_SetFuncGroup(GPIO_Type *GPIOx, uint16_t pinMask, GPIO_FunType functionx)
{
    uint16_t pinPos = 0x00, pos = 0x00;

    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PINMUX(functionx));

    if (pinMask & 0xFFFF)
    {
        for (pinPos = 0x00; pinPos < 0x10; pinPos++)
        {
            pos = ((uint16_t)0x01) << pinPos;
            if (pinMask & pos)
            {
                /* Set pinmux */
                GPIO_SetFunc(GPIOx, (GPIO_PinType)pinPos, functionx);
                /* Enable input detection */
                GPIO_SetGPIOxIES(GPIOx, (GPIO_PinType)pinPos, ENABLE);
            }
            else
            {
                /* do nothing */
            }
        }
    }
    else
    {
        /* do nothing */
    }
}

/*!
* @brief Set a GPIO pin's mode, input or output
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] pinDir: GPIO direction, input or output
*                   -GPIO_IN
*                   -GPIO_OUT
* @return none
*/
void GPIO_SetDir(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin, GPIO_DirType pinDir)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));
    DEVICE_ASSERT(IS_GPIO_DIR(pinDir));

    GPIO_SetGPIOxMODE(GPIOx, GPIO_Pin, pinDir);
}

/*!
* @brief Get a GPIO pin's direction, input or output
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @return GPIO pin's direction
*                   -GPIO_IN
*                   -GPIO_OUT
*/
GPIO_DirType GPIO_GetDir(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin)
{
    uint32_t regVal = 0;

    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    regVal = GPIO_GetGPIOxCR(GPIOx);
    return (GPIO_DirType)((regVal >> GPIO_CR_Pos(GPIO_Pin)) & 0x01UL);
}


/*!
* @brief Set several GPIO pins' pin mode
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] pinMask: GPIO pin mask value indicate which gpios need to be modifyed
* @param[in] pinDir: GPIO direction, input or output
*                   -GPIO_IN
*                   -GPIO_OUT
* @return none
*/
void GPIO_SetDirGroup(GPIO_Type *GPIOx, uint16_t pinMask, GPIO_DirType pinDir)
{
    uint16_t currentMode = (uint16_t)pinDir, pinPos = 0x00, pos = 0x00;
    uint32_t tmpReg = 0x00;

    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_DIR(pinDir));

    if ((pinMask & 0xFFFF) > 0)
    {
        tmpReg = GPIOx->CR;
        for (pinPos = 0x00; pinPos < 0x10; pinPos++)
        {
            pos = ((uint16_t)0x01) << pinPos;
            if (pinMask & pos)
            {
                tmpReg &= ~pos;
                /* Write the mode configuration in the corresponding bits */
                tmpReg |= (currentMode << pinPos);
            }
        }
        GPIOx->CR = tmpReg;
    }
}

/*!
* @brief Set GPIO pin's pull-up resistor
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] enable: enable or disable pull-up resistor
*                   -ENABLE
*                   -DISABLE
* @return none
*/
void GPIO_SetPullup(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin, ACTION_Type enable)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    GPIO_SetGPIOxPU(GPIOx, GPIO_Pin, enable);
}

/*!
* @brief Set GPIO pin's pull-down resistor
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] enable: enable or disable pull-down resistor
*                   -ENABLE
*                   -DISABLE
* @return none
*/
void GPIO_SetPulldown(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin, ACTION_Type enable)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    GPIO_SetGPIOxPD(GPIOx, GPIO_Pin, enable);
}

/*!
* @brief Set a GPIO pin's driving ability
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] E4E2Value: Driving current select
*                   -GPIO_DRIVING_4MA
*                   -GPIO_DRIVING_8MA
*                   -GPIO_DRIVING_12MA
*                   -GPIO_DRIVING_16MA
* @return none
*/
void GPIO_SetDrivingAbility(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin, GPIO_DrivingType E4E2Value)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    GPIO_SetGPIOxE4E2x(GPIOx, GPIO_Pin, E4E2Value);
}

/*!
* @brief Set GPIO as high-Z state or floating input state.
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] enable: enable or disable high-z state. If disable high-z, IO will be set to floating input.
*                   -ENABLE
*                   -DISABLE
* @return none
*/
void GPIO_SetHighZ(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin, ACTION_Type enable)
{
    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    if ((GPIOx == GPIOB) && (GPIO_Pin > GPIO_PIN10))
    {
        return;
    }
    /* Set pinmux to GPIO */
    GPIO_SetFunc(GPIOx, GPIO_Pin, GPIO_FUN0);

    /* Disable pull-up and pull-down */
    GPIO_SetGPIOxPU(GPIOx, GPIO_Pin, DISABLE);
    GPIO_SetGPIOxPD(GPIOx, GPIO_Pin, DISABLE);

    /* Disable output */
    GPIO_SetGPIOxMODE(GPIOx, GPIO_Pin, GPIO_IN);

    /* Disable input or enable input */
    GPIO_SetGPIOxIES(GPIOx, GPIO_Pin, (!enable));
}

/*!
* @brief Enable a GPIO pin's external interrupt and trigger condition
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] risingFallingEdge: GPIO's external interrupt set,value can be
*                   -EXTI_TRIGGER_FALLING
*                   -EXTI_TRIGGER_RISING
*                   -EXTI_TRIGGER_RISING_FALLING
* @return none
*/
void GPIO_EnableExtInterrupt(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin, EXTI_TriggerType risingFallingEdge)
{
    uint8_t gpioPin = (uint8_t)GPIO_Pin;

    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));
    DEVICE_ASSERT(IS_EXTI_TRIGGER(risingFallingEdge));

    /* Disable pin interrupt mask */
    GPIO_SetPinExtIntMask(GPIO_Pin, DISABLE);

    /* Set pin as external interrupt source */
    GPIO_SetPinExtIntSource(GPIOx, GPIO_Pin);

    /* Set pin external interrupt trigger mode */
    if (risingFallingEdge == EXTI_TRIGGER_FALLING)
    {
        GPIO_SetPinRisingEdge(GPIO_Pin, DISABLE);
        GPIO_SetPinFallingEdge(GPIO_Pin, ENABLE);
    }
    else if (risingFallingEdge == EXTI_TRIGGER_RISING)
    {
        GPIO_SetPinRisingEdge(GPIO_Pin, ENABLE);
        GPIO_SetPinFallingEdge(GPIO_Pin, DISABLE);
    }
    else if (risingFallingEdge == EXTI_TRIGGER_RISING_FALLING)
    {
        GPIO_SetPinFallingEdge(GPIO_Pin, ENABLE);
        GPIO_SetPinRisingEdge(GPIO_Pin, ENABLE);
    }
    else
    {

    }

    /* Enable GPIO external interrupt */
    if (gpioPin > 8u)
    {
        NVIC_EnableIRQ(EXTI9_15_IRQn);
    }
    else if (gpioPin > 2u)
    {
        NVIC_EnableIRQ(EXTI3_8_IRQn);
    }
    else
    {
        switch (gpioPin)
        {
            case 0:
                NVIC_EnableIRQ(EXTI0_IRQn);
            break;

            case 1:
                NVIC_EnableIRQ(EXTI1_IRQn);
            break;

            case 2:
                NVIC_EnableIRQ(EXTI2_IRQn);
            break;

            default :
            break;
        }
    }

    GPIO_ClearPendingExtInterrupt(GPIO_Pin);
    /* Set pin interrupt mask */
    GPIO_SetPinExtIntMask(GPIO_Pin, ENABLE);
}

/*!
* @brief Disable a GPIO pin's external interrupt and trigger condition
*
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @return  none
*/
void GPIO_DisableExtInterrupt(GPIO_PinType GPIO_Pin)
{
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    /* Reset pin interrupt mask */
    GPIO_SetPinExtIntMask(GPIO_Pin, DISABLE);
}
/*!
* @brief Set a GPIO pin's external interrupt source
*
* @param[in] GPIOx: GPIO type structure pointer:
*                   -GPIOA
*                   -GPIOB
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @return none
*/
static void GPIO_SetPinExtIntSource(GPIO_Type *GPIOx, GPIO_PinType GPIO_Pin)
{
    uint8_t extiValue = 0;

    DEVICE_ASSERT(IS_GPIO_PERIPH(GPIOx));
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    switch((uint32_t)GPIOx)
    {
         case GPIOA_BASE:
             extiValue = 0;
         break;

         case GPIOB_BASE:
             extiValue = 1;
         break;

         default:
         break;
    }

    if (GPIO_Pin < GPIO_PIN4)                                      /* Pin0~3 */
    {
        MODIFY_REG32(EXTI->EXTICR0, EXTI_EXTICR_Msk(GPIO_Pin), EXTI_EXTICR_Pos(GPIO_Pin), extiValue);
    }
    else if ((GPIO_PIN4 <= GPIO_Pin) && (GPIO_Pin < GPIO_PIN8))    /* Pin4~7 */
    {
        MODIFY_REG32(EXTI->EXTICR1, EXTI_EXTICR_Msk(GPIO_Pin), EXTI_EXTICR_Pos(GPIO_Pin), extiValue);
    }
    else if ((GPIO_PIN8 <= GPIO_Pin) && (GPIO_Pin < GPIO_PIN12))   /* Pin8~11 */
    {
        MODIFY_REG32(EXTI->EXTICR2, EXTI_EXTICR_Msk(GPIO_Pin), EXTI_EXTICR_Pos(GPIO_Pin), extiValue);
    }
    else if ((GPIO_PIN12 <= GPIO_Pin) && (GPIO_Pin <= GPIO_PIN15))  /* Pin12~15 */
    {
        MODIFY_REG32(EXTI->EXTICR3, EXTI_EXTICR_Msk(GPIO_Pin), EXTI_EXTICR_Pos(GPIO_Pin), extiValue);
    }
    else
    {
        /* do nothing */
    }
}

/*!
* @brief Get GPIO_Type pointer, which is the source of pending external interrupt
*
* @param[in] GPIO_Pin: GPIO_Pin type num, which is the pin of pending inerrupt
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @return GPIO_Type pointer, which is the source of pending external interrupt
*                   -GPIOA
*                   -GPIOB
*/
static GPIO_Type * GPIO_GetPendingExtIntSource(GPIO_PinType GPIO_Pin)
{
    uint32_t gpioGroup = 0;
    GPIO_Type *GPIOx = GPIOA;

    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    /* Get interrupt source in EXTICR register */
    if(GPIO_Pin < GPIO_PIN4)      /* Pin0~3 */
    {
        gpioGroup = (EXTI->EXTICR0 >> EXTI_EXTICR_Pos(GPIO_Pin)) & 0x0FUL;
    }
    else if((GPIO_PIN4 <= GPIO_Pin) && (GPIO_Pin < GPIO_PIN8))     /* Pin4~7 */
    {
        gpioGroup = (EXTI->EXTICR1 >> EXTI_EXTICR_Pos(GPIO_Pin)) & 0x0FUL;
    }
    else if((GPIO_PIN8 <= GPIO_Pin) && (GPIO_Pin < GPIO_PIN12))     /* Pin8~11 */
    {
        gpioGroup = (EXTI->EXTICR2 >> EXTI_EXTICR_Pos(GPIO_Pin)) & 0x0FUL;
    }
    else if((GPIO_PIN12 <= GPIO_Pin) && (GPIO_Pin <= GPIO_PIN15))     /* Pin12~15 */
    {
        gpioGroup = (EXTI->EXTICR3 >> EXTI_EXTICR_Pos(GPIO_Pin)) & 0x0FUL;
    }
    else
    {
        /* do nothing */
    }

    /* Get GPIO_Type pointer by gpioGroup */
    switch(gpioGroup)
    {
        case 0:
            GPIOx = GPIOA;
        break;

        case 1:
            GPIOx = GPIOB;
        break;

        default:
        break;
    }

    return GPIOx;
}

/*!
* @brief Set a GPIO pin's external interrupt falling edge sensitive
*
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] enable: GPIO pin falling edge enable or disable
*                   -ENABLE
*                   -DISABLE
* @return none
*/
static void GPIO_SetPinFallingEdge(GPIO_PinType GPIO_Pin, ACTION_Type enable)
{
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    MODIFY_REG32(EXTI->FTSR, EXTI_FTSR_Msk(GPIO_Pin),  EXTI_FTSR_Pos(GPIO_Pin), enable);
}

/*!
* @brief Set a GPIO pin's external interrupt rising edge sensitive
*
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] enable: GPIO pin rising edge enable or disable
*                   -ENABLE
*                   -DISABLE
* @return none
*/
static void GPIO_SetPinRisingEdge(GPIO_PinType GPIO_Pin, ACTION_Type enable)
{
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    MODIFY_REG32(EXTI->RTSR, EXTI_RTSR_Msk(GPIO_Pin),  EXTI_RTSR_Pos(GPIO_Pin), enable);
}

/*!
* @brief Set a GPIO pin's external interrupt mask
*
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] enable: GPIO pin external interrupt mask
*                   -ENABLE : external interrupt active
*                   -DISABLE: external interrupt inactive
* @return none
*/
static void GPIO_SetPinExtIntMask(GPIO_PinType GPIO_Pin, ACTION_Type enable)
{
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    MODIFY_REG32(EXTI->IMR, EXTI_IMR_Msk(GPIO_Pin),  EXTI_IMR_Pos(GPIO_Pin), enable);
}

/*!
* @brief Get a GPIO pin's pending external interrupt IRQ number, a 16bit mask which represents the GPIO's group internal position
*
* @param[in] none
* @return GPIO's Pending external interrupt 16 bits mask
*/
uint16_t GPIO_GetPendingExtInterrupt(void)
{
    return (uint16_t)(EXTI->PR & 0xFFFF);
}

/*!
* @brief Clear a GPIO pin's external interrupt pending flag
*
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @return none
*/
void GPIO_ClearPendingExtInterrupt(GPIO_PinType GPIO_Pin)
{
    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    EXTI->PR = (0x01UL << EXTI_PR_Pos(GPIO_Pin));
}

/*!
* @brief Set GPIO interrupt callback function, Pin0~2 has the independent interrupt vector,
*         Pin3~8 share the same interrupt vector, and Pin9~15 share the same interrupt vector.
*
* @param[in] GPIO_Pin: GPIO_Pin type num:
*                   -GPIO_PIN0
*                   -GPIO_PIN1
*                   -GPIO_PIN2
*                   - ...
*                   -GPIO_PIN15
* @param[in] callback: GPIO interrupt callback function, which will be called in EXTIx_IRQHandler()
* @return none
*/
void GPIO_SetCallback(GPIO_PinType GPIO_Pin, const DeviceCallback_Type callback)
{
    uint8_t callbackIndex = 0;

    DEVICE_ASSERT(IS_GPIO_PIN(GPIO_Pin));

    /* set gpio callback function pointer */
    if (GPIO_Pin > GPIO_PIN8)
    {
        callbackIndex = 4;  /* 9~15 */
    }
    else if (GPIO_Pin > GPIO_PIN2)
    {
        callbackIndex = 3;  /* 3~8 */
    }
    else
    {
        callbackIndex = (uint8_t)GPIO_Pin;
    }

    /* Set GPIO interrupt callback */
    g_gpioCallbackArray[callbackIndex] = callback;
}

/*!
* @brief override the EXTI0_IRQHandler
*
* @param[in] none
* @return none
*/
void EXTI0_IRQHandler(void)
{
    GPIO_Type *GPIOx = GPIOA;

    GPIOx = GPIO_GetPendingExtIntSource(GPIO_PIN0);

    GPIO_ClearPendingExtInterrupt(GPIO_PIN0);

    if (g_gpioCallbackArray[0])
    {
        g_gpioCallbackArray[0](GPIOx, (uint32_t)GPIO_PIN0, 0);
    }
}

/*!
* @brief override the EXTI1_IRQHandler
*
* @param[in] none
* @return none
*/
void EXTI1_IRQHandler(void)
{
    GPIO_Type *GPIOx = GPIOA;

    GPIOx = GPIO_GetPendingExtIntSource(GPIO_PIN1);

    GPIO_ClearPendingExtInterrupt(GPIO_PIN1);

    if (g_gpioCallbackArray[1])
    {
        g_gpioCallbackArray[1](GPIOx, (uint32_t)GPIO_PIN1, 0);
    }
}

/*!
* @brief override the EXTI2_IRQHandler
*
* @param[in] none
* @return none
*/
void EXTI2_IRQHandler(void)
{
    GPIO_Type *GPIOx = GPIOA;

    GPIOx = GPIO_GetPendingExtIntSource(GPIO_PIN2);

    GPIO_ClearPendingExtInterrupt(GPIO_PIN2);

    if (g_gpioCallbackArray[2])
    {
        g_gpioCallbackArray[2](GPIOx, (uint32_t)GPIO_PIN2, 0);
    }
}

/*!
* @brief override the EXTI3_8_IRQHandler
*
* @param[in] none
* @return none
*/
void EXTI3_8_IRQHandler(void)
{
    GPIO_Type *GPIOx = GPIOA;
    uint16_t pendingIRQMask = 0, pinMask = 0, pin = 0;

    pendingIRQMask = GPIO_GetPendingExtInterrupt();

    for (pin = 3; pin < 9; pin++)
    {
        pinMask = GPIO_BIT0 << pin;
        if(pendingIRQMask & pinMask)
        {
            GPIOx = GPIO_GetPendingExtIntSource((GPIO_PinType)pin);
            GPIO_ClearPendingExtInterrupt((GPIO_PinType)pin);
            if (g_gpioCallbackArray[3])
            {
                g_gpioCallbackArray[3](GPIOx, (uint32_t)pin, 0);
            }
        }
    }
}

/*!
* @brief override the EXTI9_15_IRQHandler
*
* @param[in] none
* @return none
*/
void EXTI9_15_IRQHandler(void)
{
    GPIO_Type *GPIOx = GPIOA;
    uint16_t pendingIRQMask = 0, pinMask = 0, pin = 0;

    pendingIRQMask = GPIO_GetPendingExtInterrupt();

    for (pin = 9; pin < 16; pin++)
    {
        pinMask = GPIO_BIT0 << pin;
        if(pendingIRQMask & pinMask)
        {
            GPIOx = GPIO_GetPendingExtIntSource((GPIO_PinType)pin);
            GPIO_ClearPendingExtInterrupt((GPIO_PinType)pin);
            if (g_gpioCallbackArray[4])
            {
                g_gpioCallbackArray[4](GPIOx, (uint32_t)pin, 0);
            }
        }
    }
}

/* =============================================  EOF  ============================================== */
