/*
  ******************************************************************************
  * @file    GPIO.c
  * @version V1.0.0
  * @date    2024  
  * @brief   GPIO module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the General Purpose Input/Output (GPIO) peripheral:
  *           @ Initialization  functions
  *           @ IO operation functions
  ******************************************************************************
*/

#include "fxx_std.h"

/*********************************************************************************
* Function    : HAL_GPIO_IRQHandler
* Description : GPIO interrupt Handler
* Input       : 
* Outpu       : 
* Author      : Chris_Kyle                         Date : 2020年
**********************************************************************************/
void GPIO_IRQHandler(enum_GPIOx_t fe_GPIO, uint32_t fu32_GPIO_Pin)
{
    GPIO_TypeDef *GPIOx;
    
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;

        default: break;
    }

    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD )
    {
        fu32_GPIO_Pin <<= 16;
    }

    if (GPIOx->RIS & fu32_GPIO_Pin) 
    {   
        GPIOx->IC = fu32_GPIO_Pin;
        
        /* user can call your application process function here */ 
        /* ...... */
    }
}

/*********************************************************************************
* Function    : GPIO_Init
* Description : Initializes the GPIOx peripheral according to the specified
*                parameters in the GPIO_InitStruct
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
*               GPIO_InitStruct:pointer to a GPIO_InitTypeDef structure that contains 
*                               the configuration information for the specified GPIO peripheral.
* Output      : None
**********************************************************************************/
void GPIO_Init(enum_GPIOx_t fe_GPIO, GPIO_InitTypeDef* GPIO_InitStruct)
{
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    assert_param (IS_GPIO_PIN(GPIO_InitStruct->Pin));
    assert_param (IS_GPIO_MODE(GPIO_InitStruct->Mode)); 

    
    uint32_t lu32_Position = 0;
    uint32_t lu32_Current_Pin;
    uint32_t lu32_Position_Mask;
    
    volatile uint32_t *lu32_SEL1 = NULL;        // 指向 -> 管脚复用寄存器1
    volatile uint32_t *lu32_SEL2 = NULL;        // 指向 -> 管脚复用寄存器2
    volatile uint32_t *lu32_PollUP   = NULL;    // 指向 -> 上拉选择寄存器
    volatile uint32_t *lu32_PollDown = NULL;    // 指向 -> 下拉选择寄存器
    volatile uint32_t *lu32_ODEnable = NULL;    // 指向 -> 开漏使能寄存器
    volatile uint32_t *lu32_ADS      = NULL;    // 指向 -> 数字、模拟选择寄存器
    
    GPIO_TypeDef *GPIOx=NULL;
    
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
            
            lu32_PollUP   = &(SCU->PABPUR);
            lu32_PollDown = &(SCU->PABPDR);
            lu32_ODEnable = &(SCU->PABODR);
            lu32_ADS      = &(SCU->PABADS);
            
            if (fe_GPIO == GPIOB) 
            {
                GPIO_InitStruct->Pin <<= 16;
                
                lu32_SEL1 = &(SCU->PBSEL1);
                lu32_SEL2 = &(SCU->PBSEL2);
            }
            else 
            {
                lu32_SEL1 = &(SCU->PASEL1);
                lu32_SEL2 = &(SCU->PASEL2);
            }
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
            
            lu32_PollUP   = &(SCU->PCDPUR);
            lu32_PollDown = &(SCU->PCDPDR);
            lu32_ODEnable = &(SCU->PCDODR);
            lu32_ADS      = &(SCU->PCDADS);
            
            if (fe_GPIO == GPIOD) 
            {
                GPIO_InitStruct->Pin <<= 16;
                
                lu32_SEL1 = &(SCU->PDSEL1);
            }
            else 
            {
                lu32_SEL1 = &(SCU->PCSEL1);
                lu32_SEL2 = &(SCU->PCSEL2);
            }
        }break;

        default: break; 
    }

    /* Configure Select pins */
    while ((GPIO_InitStruct->Pin) >> lu32_Position != 0) 
    {
        /* Get current pin position */
        lu32_Current_Pin = (GPIO_InitStruct->Pin) & (1uL << lu32_Position);
        
        if (lu32_Current_Pin) 
        {
            // < For PC13/PC14/PC15, Special cases 
            if ( ( (GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15) & lu32_Current_Pin) && (GPIOC == fe_GPIO) )
            {
                RTC_Domain_PCLK_ENABLE();  
                
                switch(lu32_Current_Pin)
                {
                    case GPIO_PIN_13:
                    if (GPIO_PIN_13 == lu32_Current_Pin)
                    {
                        if (GPIO_InitStruct->Mode == GPIO_MODE_ANALOG) 
                        {
                            PMU->IOCR |= BIT6;  
                        }
                        else
                        {
                            PMU->IOCR &= ~BIT6;  
                        }
                        
                        if (GPIO_InitStruct->Pull == GPIO_PULLUP) 
                        {
                            PMU->IOCR |= BIT0; 
                            PMU->IOCR &= ~BIT1; 
                        }
                        else if(GPIO_InitStruct->Pull == GPIO_PULLDOWN)  
                        {
                            PMU->IOCR &= ~BIT0; 
                            PMU->IOCR |= BIT1; 
                        }
                        else
                        {
                            PMU->IOCR &= ~(BIT0 | BIT1);   
                        }
                    }
                    break;
                    
                    case GPIO_PIN_14:
                    if (GPIO_PIN_14 == lu32_Current_Pin)
                    {
                        if (GPIO_InitStruct->Mode == GPIO_MODE_ANALOG) 
                        {
                            PMU->IOCR |= BIT14; 
                        }
                        else
                        {
                            PMU->IOCR &= ~BIT14;  
                        }
                        
                        if (GPIO_InitStruct->Pull == GPIO_PULLUP) 
                        {
                            PMU->IOCR |= BIT8; 
                            PMU->IOCR &= ~BIT9; 
                        }
                        else if(GPIO_InitStruct->Pull == GPIO_PULLDOWN)  
                        {
                            PMU->IOCR &= ~BIT8; 
                            PMU->IOCR |= BIT9; 
                        }
                        else
                        {
                            PMU->IOCR &= ~(BIT8 | BIT9);   
                        }
                    }
                    break;
                    
                    case GPIO_PIN_15: 
                    if (GPIO_PIN_15 == lu32_Current_Pin)
                    {
                        if (GPIO_InitStruct->Mode == GPIO_MODE_ANALOG) 
                        {
                            PMU->IOCR |= BIT22; 
                        }
                        else
                        {
                            PMU->IOCR &= ~BIT22; 
                        }
                        
                        if (GPIO_InitStruct->Pull == GPIO_PULLUP) 
                        {
                            PMU->IOCR |= BIT16; 
                            PMU->IOCR &= ~BIT17; 
                        }
                        else if(GPIO_InitStruct->Pull == GPIO_PULLDOWN)  
                        {
                            PMU->IOCR &= ~BIT16; 
                            PMU->IOCR |= BIT17; 
                        }
                        else
                        {
                            PMU->IOCR &= ~(BIT16 | BIT17);     
                        }
                    }
                    break;   
                    
                    default:
                    break;   
                }
                
                RTC_Domain_PCLK_DISABLE();
            }
            // > For PC13/PC14/PC15, Special cases 
            
            switch (GPIO_InitStruct->Mode)
            {
                /* GPIO IN Function */
                case GPIO_MODE_INPUT:
                {
                    GPIOx->DIR &= ~lu32_Current_Pin;
                }break;
                
                /* GPIO OUT Function */
                case GPIO_MODE_OUTPUT_PP: 
                case GPIO_MODE_OUTPUT_OD: 
                {
                    GPIOx->DIR |= lu32_Current_Pin;
                }break;

                /* Alternate Function */
                case GPIO_MODE_AF_PP:
                case GPIO_MODE_AF_OD:
                {
                    /* Get Position Mask */
                    if (lu32_Position < 16) 
                    {   /* GOIOA、GPIOC */
                        lu32_Position_Mask = lu32_Position;
                    }
                    else 
                    {   /* GPIOB、GPIOD */
                        lu32_Position_Mask = lu32_Position - 16;
                    }
                    
                    /* SET GPIO Function */
                    if (lu32_Position_Mask < 8) 
                    {
                        *lu32_SEL1 = (*lu32_SEL1 & ~(0xF << (lu32_Position_Mask * 4))) | (GPIO_InitStruct->Alternate << (lu32_Position_Mask * 4));
                    }
                    else 
                    {
                        *lu32_SEL2 = (*lu32_SEL2 & ~(0xF << ((lu32_Position_Mask - 8) * 4))) | (GPIO_InitStruct->Alternate << ((lu32_Position_Mask - 8) * 4));
                    }
                }break;

                /* GPIO INT Function */
                case GPIO_MODE_IT_RISING:
                case GPIO_MODE_IT_FALLING:
                case GPIO_MODE_IT_RISING_FALLING:
                case GPIO_MODE_IT_HIGH_LEVEL:
                case GPIO_MODE_IT_LOW_LEVEL:
                {
                    /* Set direction Input、Enable INT */
                    GPIOx->DIR &= ~lu32_Current_Pin;
                    GPIOx->IEN |=  lu32_Current_Pin;

                    /* Single edge */
                    if (GPIO_InitStruct->Mode == GPIO_MODE_IT_RISING || GPIO_InitStruct->Mode == GPIO_MODE_IT_FALLING) 
                    {
                        /* edge trigger */
                        GPIOx->IS  &= ~lu32_Current_Pin;
                        /* Single trigger */
                        GPIOx->IBE &= ~lu32_Current_Pin;
                        
                        if (GPIO_InitStruct->Mode == GPIO_MODE_IT_RISING) 
                        {
                            GPIOx->IEV |= lu32_Current_Pin;
                        }
                        else 
                        {
                            GPIOx->IEV &= ~lu32_Current_Pin;
                        }
                    }

                    /* Double edge */
                    if (GPIO_InitStruct->Mode == GPIO_MODE_IT_RISING_FALLING) 
                    {
                        /* edge trigger */
                        GPIOx->IS  &= ~lu32_Current_Pin;
                        /* Double trigger */
                        GPIOx->IBE |=  lu32_Current_Pin;
                    }
                    
                    /* LEVEL trigger */
                    if (GPIO_InitStruct->Mode == GPIO_MODE_IT_HIGH_LEVEL || GPIO_InitStruct->Mode == GPIO_MODE_IT_LOW_LEVEL) 
                    {
                        /* LEVEL trigger */
                        GPIOx->IS |= lu32_Current_Pin;

                        if (GPIO_InitStruct->Mode == GPIO_MODE_IT_HIGH_LEVEL) 
                        {
                            GPIOx->IEV |= lu32_Current_Pin;
                        }
                        else 
                        {
                            GPIOx->IEV &= ~lu32_Current_Pin;
                        }
                    }
                }break;

                default: break; 
            }

            /* Set Pull UP or DOWN or NO */
            if (GPIO_InitStruct->Pull == GPIO_NOPULL) 
            {
                *lu32_PollUP   &= ~lu32_Current_Pin;
                *lu32_PollDown &= ~lu32_Current_Pin;
            }
            else if (GPIO_InitStruct->Pull == GPIO_PULLUP) 
            {
                *lu32_PollUP   |=  lu32_Current_Pin;
                *lu32_PollDown &= ~lu32_Current_Pin;
            }
            else if (GPIO_InitStruct->Pull == GPIO_PULLDOWN) 
            {
                *lu32_PollUP   &= ~lu32_Current_Pin;
                *lu32_PollDown |=  lu32_Current_Pin;
            }
            
            /* Set Open Drain Mode */
            if (GPIO_InitStruct->Mode & GPIO_MODE_OD_MASK) 
            {
                *lu32_ODEnable |= lu32_Current_Pin;
            }
            else 
            {
                *lu32_ODEnable &= ~lu32_Current_Pin;
            }
            
            /* GPIO Function */
            if (GPIO_InitStruct->Mode & GPIO_MODE_IO_MASK) 
            {
                /* Get Position Mask */
                if (lu32_Position < 16) 
                {   /* GOIOA、GPIOC */
                    lu32_Position_Mask = lu32_Position;
                }
                else 
                {   /* GPIOB、GPIOD */
                    lu32_Position_Mask = lu32_Position - 16;
                }
                
                /* SET GPIO Function */
                if (lu32_Position_Mask < 8) 
                {
                    *lu32_SEL1 = (*lu32_SEL1 & ~(0xF << (lu32_Position_Mask * 4))) | (GPIO_FUNCTION_0 << (lu32_Position_Mask * 4));
                }
                else 
                {
                    *lu32_SEL2 = (*lu32_SEL2 & ~(0xF << ((lu32_Position_Mask - 8) * 4))) | (GPIO_FUNCTION_0 << ((lu32_Position_Mask - 8) * 4));
                }
            }

            /* SET Digital or Analog */
            if (GPIO_InitStruct->Mode == GPIO_MODE_ANALOG) 
            {
                *lu32_ADS |= lu32_Current_Pin;
            }
            else 
            {
                *lu32_ADS &= ~lu32_Current_Pin;
            }
        }

        lu32_Position++;
    }
}

/*********************************************************************************
* Function    : GPIO_DeInit
* Description : Deinitializes the GPIOx peripheral registers to their default reset values.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
* Output      : None
**********************************************************************************/
void GPIO_DeInit(enum_GPIOx_t fe_GPIO)
{
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    
//    GPIO_TypeDef *GPIOx=NULL;
    
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            System_Module_Disable(EN_GPIOAB);
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            System_Module_Disable(EN_GPIOCD);
        }break;

        default: break; 
    } 
}

/*********************************************************************************
* Function    : GPIO_StructInit
* Description : Fills each GPIO_InitStruct member with its default value.
* Input       : GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure which will
*                                be initialized.
* Output      : None
**********************************************************************************/
void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct)
{
    /* Reset GPIO init structure parameters values */
    GPIO_InitStruct->Pin  = GPIO_Pin_All;
    GPIO_InitStruct->Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct->Pull = GPIO_NOPULL; 
    GPIO_InitStruct->Alternate = GPIO_FUNCTION_0;   
}


/*********************************************************************************
* Function    : GPIO_DriveConfig
* Description : Quickly Configure to drive capability
* Input       : fe_GPIO：to select the GPIO peripheral.
* Input       : fu32_Pin：specifies the port bit to be written.
                This parameter can be one of GPIO_PIN_x where x can be (0..15).
* Outpu       : 
* Author      : Chris_Kyle                         Data : 2020年
**********************************************************************************/
void GPIO_DriveConfig(enum_GPIOx_t fe_GPIO, uint32_t fu32_Pin, uint32_t fu32_Drive)
{
    uint32_t lu32_Position;  
    
    volatile uint32_t *lu32_Drive    = NULL;    // 指向 -> 驱动选择寄存器
    
#if (USE_FULL_ASSERT == 1)
    /* Check the parameters */
    if (!IS_GPIO_ALL_INSTANCE(fe_GPIO))     return;
    if (!IS_GPIO_PIN(fu32_Pin))             return;
    if (!IS_GPIO_DRIVE(fu32_Drive))         return;
#endif

    switch (fe_GPIO)
    {
        case GPIOA: 
        {
            System_Module_Enable(EN_GPIOAB);
            lu32_Drive = &(SCU->PASTR);
        }break;
        
        case GPIOB: 
        {
            System_Module_Enable(EN_GPIOAB);
            lu32_Drive = &(SCU->PBSTR);
        }break;

        case GPIOC: 
        {
            System_Module_Enable(EN_GPIOCD);
            lu32_Drive = &(SCU->PCSTR);
        }break;
        
        case GPIOD: 
        {
            System_Module_Enable(EN_GPIOCD);
            lu32_Drive = &(SCU->PDSTR);
        }break;


        default: break; 
    }

    /* Configure Select pins */
    lu32_Position = 0;
    while ((fu32_Pin) >> lu32_Position != 0) 
    {
        if ((fu32_Pin) & (1uL << lu32_Position)) 
        {
            /* Set drive capability */
            *lu32_Drive = (*lu32_Drive & ~(3uL << (lu32_Position << 1))) | ((fu32_Drive & 0x03) << (lu32_Position << 1));
        }

        lu32_Position++;
    }
}

/*********************************************************************************
* Function    : GPIO_AnalogEnable
* Description : Quickly Configure to analog function
* Input       : fe_GPIO：to select the GPIO peripheral.
* Input       : fu32_Pin：specifies the port bit to be written.
                This parameter can be one of GPIO_PIN_x where x can be (0..15).
* Outpu       : 
* Author      : Chris_Kyle                         Data : 2020年
**********************************************************************************/
void GPIO_AnalogEnable(enum_GPIOx_t fe_GPIO, uint32_t fu32_Pin)
{
    uint32_t lu32_Position;
    uint32_t lu32_Current_Pin;

    volatile uint32_t *lp32_ADS = NULL;    // 指向 -> 数字、模拟选择寄存器

#if (USE_FULL_ASSERT == 1)
    /* Check the parameters */
    if (!IS_GPIO_ALL_INSTANCE(fe_GPIO))      return;
    if (!IS_GPIO_PIN(fu32_Pin))              return;
#endif

    lu32_Position = 0;
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            System_Module_Enable(EN_GPIOAB);
            
            lp32_ADS = &(SCU->PABADS);
            
            if (fe_GPIO == GPIOB) 
            {
                fu32_Pin <<= 16;
                lu32_Position = 16;
            }
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            System_Module_Enable(EN_GPIOCD);

            lp32_ADS = &(SCU->PCDADS);
            
            if (fe_GPIO == GPIOD) 
            {
                fu32_Pin <<= 16;
                lu32_Position = 16;
            }
        }break;


        default: break; 
    }
    
    /* Configure Select pins */
    while ((fu32_Pin) >> lu32_Position != 0) 
    {
        /* Get current pin position */
        lu32_Current_Pin = (fu32_Pin) & (1uL << lu32_Position);
        
        if (lu32_Current_Pin) 
        {
            *lp32_ADS |= lu32_Current_Pin;
        }

        lu32_Position++;
    }
}

/*********************************************************************************
* Function    : GPIO_AnalogDisable
* Description : Quickly Configure to analog function
* Input       : fe_GPIO：to select the GPIO peripheral.
* Input       : fu32_Pin：specifies the port bit to be written.
                This parameter can be one of GPIO_PIN_x where x can be (0..15).
* Outpu       : 
* Author      : Chris_Kyle                         Data : 2020年
**********************************************************************************/
void GPIO_AnalogDisable(enum_GPIOx_t fe_GPIO, uint32_t fu32_Pin)
{
    uint32_t lu32_Position;
    uint32_t lu32_Current_Pin;

    volatile uint32_t *lp32_ADS = NULL;    // 指向 -> 数字、模拟选择寄存器

#if (USE_FULL_ASSERT == 1)
    /* Check the parameters */
    if (!IS_GPIO_ALL_INSTANCE(fe_GPIO))      return;
    if (!IS_GPIO_PIN(fu32_Pin))              return;
#endif

    lu32_Position = 0;
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            System_Module_Enable(EN_GPIOAB);
            
            lp32_ADS = &(SCU->PABADS);
            
            if (fe_GPIO == GPIOB) 
            {
                fu32_Pin <<= 16;
                lu32_Position = 16;
            }
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            System_Module_Enable(EN_GPIOCD);

            lp32_ADS = &(SCU->PCDADS);
            
            if (fe_GPIO == GPIOD) 
            {
                fu32_Pin <<= 16;
                lu32_Position = 16;
            }
        }break;


        default: break; 
    }
    
    /* Configure Select pins */
    while ((fu32_Pin) >> lu32_Position != 0) 
    {
        /* Get current pin position */
        lu32_Current_Pin = (fu32_Pin) & (1uL << lu32_Position);
        
        if (lu32_Current_Pin) 
        {
            *lp32_ADS &= ~lu32_Current_Pin;
        }

        lu32_Position++;
    }
}

/*********************************************************************************
* Function    : GPIO_SchmidtEnable
* Description : Quickly Configure to schmidt function
* Input       : fe_GPIO：to select the GPIO peripheral.
* Input       : fu32_Pin：specifies the port bit to be written.
                This parameter can be one of GPIO_PIN_x where x can be (0..15).
* Outpu       : 
* Author      : Chris_Kyle                         Data : 2020年
**********************************************************************************/
void GPIO_SchmidtEnable(enum_GPIOx_t fe_GPIO, uint32_t fu32_Pin)
{
    uint32_t lu32_Position;
    uint32_t lu32_Current_Pin;

    volatile uint32_t *lp32_SMTR = NULL;    // 指向 -> 施密特使能寄存器

#if (USE_FULL_ASSERT == 1)
    /* Check the parameters */
    if (!IS_GPIO_ALL_INSTANCE(fe_GPIO))      return;
    if (!IS_GPIO_PIN(fu32_Pin))              return;
#endif

    lu32_Position = 0;
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            System_Module_Enable(EN_GPIOAB);
            
            lp32_SMTR = &(SCU->PABSMTR);
            
            if (fe_GPIO == GPIOB) 
            {
                fu32_Pin <<= 16;
                lu32_Position = 16;
            }
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            System_Module_Enable(EN_GPIOCD);

            lp32_SMTR = &(SCU->PCDSMTR);
            
            if (fe_GPIO == GPIOD) 
            {
                fu32_Pin <<= 16;
                lu32_Position = 16;
            }
        }break;


        default: break; 
    }
    
    /* Configure Select pins */
    while ((fu32_Pin) >> lu32_Position != 0) 
    {
        /* Get current pin position */
        lu32_Current_Pin = (fu32_Pin) & (1uL << lu32_Position);
        
        if (lu32_Current_Pin) 
        {
            *lp32_SMTR |= lu32_Current_Pin;
        }

        lu32_Position++;
    }
}

/*********************************************************************************
* Function    : GPIO_SchmidtDisable
* Description : Quickly Configure to schmidt function
* Input       : fe_GPIO：to select the GPIO peripheral.
* Input       : fu32_Pin：specifies the port bit to be written.
                This parameter can be one of GPIO_PIN_x where x can be (0..15).
* Outpu       : 
* Author      : Chris_Kyle                         Data : 2020年
**********************************************************************************/
void GPIO_SchmidtDisable(enum_GPIOx_t fe_GPIO, uint32_t fu32_Pin)
{
    uint32_t lu32_Position;
    uint32_t lu32_Current_Pin;

    volatile uint32_t *lp32_SMTR = NULL;    // 指向 -> 施密特使能寄存器

#if (USE_FULL_ASSERT == 1)
    /* Check the parameters */
    if (!IS_GPIO_ALL_INSTANCE(fe_GPIO))      return;
    if (!IS_GPIO_PIN(fu32_Pin))              return;
#endif

    lu32_Position = 0;
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            System_Module_Enable(EN_GPIOAB);
            
            lp32_SMTR = &(SCU->PABSMTR);
            
            if (fe_GPIO == GPIOB) 
            {
                fu32_Pin <<= 16;
                lu32_Position = 16;
            }
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            System_Module_Enable(EN_GPIOCD);

            lp32_SMTR = &(SCU->PCDSMTR);
            
            if (fe_GPIO == GPIOD) 
            {
                fu32_Pin <<= 16;
                lu32_Position = 16;
            }
        }break;


        default: break; 
    }
    
    /* Configure Select pins */
    while ((fu32_Pin) >> lu32_Position != 0) 
    {
        /* Get current pin position */
        lu32_Current_Pin = (fu32_Pin) & (1uL << lu32_Position);
        
        if (lu32_Current_Pin) 
        {
            *lp32_SMTR &= ~lu32_Current_Pin;
        }

        lu32_Position++;
    }
}

/*********************************************************************************
* Function    : GPIO_ReadInputDataBit
* Description : Reads the specified input port pin.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
*               fu32_GPIO_Pin:specifies the port bit to read.
*                             This parameter can be GPIO_PIN_x where x can be (0..15).
* Output      : The input port pin value.
**********************************************************************************/
uint8_t GPIO_ReadInputDataBit(enum_GPIOx_t fe_GPIO, uint32_t fu32_GPIO_Pin)
{
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    assert_param (IS_GPIO_PIN(fu32_GPIO_Pin));
    
    GPIO_TypeDef *GPIOx;

    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;


        default: break;
    }
    
    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD)
    {
        fu32_GPIO_Pin <<= 16;
    }
    
    if (GPIOx->IDATA & fu32_GPIO_Pin) 
    {
        return Bit_SET;
    }
    else 
    {
        return Bit_RESET;
    }
}

/*********************************************************************************
* Function    : GPIO_ReadInputData
* Description : Reads the specified GPIO input data port.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
* Output      : GPIO input data port value.
**********************************************************************************/
uint16_t GPIO_ReadInputData(enum_GPIOx_t fe_GPIO)
{
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    
    GPIO_TypeDef *GPIOx;
    uint32_t GPIO_MASK = 0;
    uint16_t InputData= 0;
    
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;


        default: break;
    }
    
    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD )
    {
        GPIO_MASK = 0xFFFF0000;
        InputData=(GPIOx->IDATA & GPIO_MASK)>>16;
    }
    else
    {
        GPIO_MASK = 0x0000FFFF;
        InputData=(GPIOx->IDATA & GPIO_MASK);
    }
    
    return InputData;
}

/*********************************************************************************
* Function    : GPIO_ReadOutputDataBit
* Description : Reads the specified output data port bit.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
*               fu32_GPIO_Pin:specifies the port bit to read.
*                             This parameter can be GPIO_PIN_x where x can be (0..15).
* Output      : The output port pin value.
**********************************************************************************/
uint8_t GPIO_ReadOutputDataBit(enum_GPIOx_t fe_GPIO, uint32_t fu32_GPIO_Pin)
{
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    assert_param (IS_GPIO_PIN(fu32_GPIO_Pin));
    
    GPIO_TypeDef *GPIOx;
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;


        default: break;
    }
    
    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD )
    {
        fu32_GPIO_Pin <<= 16;
    }
    
    if (GPIOx->ODATA & fu32_GPIO_Pin) 
    {
        return Bit_SET;
    }
    else 
    {
        return Bit_RESET;
    }
}

/*********************************************************************************
* Function    : GPIO_ReadOutputData
* Description : Reads the specified GPIO output data port.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
* Output      : GPIO output data port value.
**********************************************************************************/
uint16_t GPIO_ReadOutputData(enum_GPIOx_t fe_GPIO)
{
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));

    
    GPIO_TypeDef *GPIOx;
    uint32_t GPIO_MASK = 0;
    uint16_t OutputData= 0;
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;


        default: break;
    }
    
    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD)
    {
        GPIO_MASK = 0xFFFF0000;
        OutputData=(GPIOx->ODATA & GPIO_MASK)>>16;
    }else
    {
        GPIO_MASK = 0x0000FFFF;
        OutputData=(GPIOx->ODATA & GPIO_MASK);
    }
    
    return OutputData;
}


/*********************************************************************************
* Function    : GPIO_SetBits
* Description : Sets the selected data port bits.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
*               fu32_GPIO_Pin:specifies the port bit to read.
*                             This parameter can be GPIO_PIN_x where x can be (0..15).
* Output      : None
**********************************************************************************/
void GPIO_SetBits(enum_GPIOx_t fe_GPIO, uint32_t fu32_GPIO_Pin)
{
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    assert_param (IS_GPIO_PIN(fu32_GPIO_Pin));
    
    GPIO_TypeDef *GPIOx;

    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;



        default: break;
    }

    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD )
    {
        fu32_GPIO_Pin <<= 16;
    }
    GPIOx->ODATA |= fu32_GPIO_Pin;
}


/*********************************************************************************
* Function    : GPIO_ResetBits
* Description : Resets the selected data port bits.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
*               fu32_GPIO_Pin:specifies the port bit to read.
*                             This parameter can be GPIO_PIN_x where x can be (0..15).
* Output      : None
**********************************************************************************/
void GPIO_ResetBits(enum_GPIOx_t fe_GPIO, uint32_t fu32_GPIO_Pin)
{
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    assert_param (IS_GPIO_PIN(fu32_GPIO_Pin));
    
    GPIO_TypeDef *GPIOx;

    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;


        default: break;
    }

    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD )
    {
        fu32_GPIO_Pin <<= 16;
    }
    
    GPIOx->ODATA &= ~fu32_GPIO_Pin;
}


/*********************************************************************************
* Function    : GPIO_WriteBit
* Description : Sets or clears the selected data port bit.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
*               fu32_GPIO_Pin:specifies the port bit to read.
*                             This parameter can be GPIO_PIN_x where x can be (0..15).
*               BitVal: specifies the value to be written to the selected bit.
*                       This parameter can be one of the BitAction enum values:
*                       Bit_RESET: to clear the port pin
*                       Bit_SET: to set the port pin
* Output      : None
**********************************************************************************/
void GPIO_WriteBit(enum_GPIOx_t fe_GPIO, uint32_t fu32_GPIO_Pin, BitAction BitVal)
{
    GPIO_TypeDef *GPIOx;
    
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    assert_param (IS_GPIO_PIN(fu32_GPIO_Pin));
    assert_param (IS_GPIO_PIN_ACTION(BitVal));

    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;


        default: break;
    }

    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD )
    {
        fu32_GPIO_Pin <<= 16;
    }

    if (Bit_SET == BitVal) 
    {
        GPIOx->ODATA |= fu32_GPIO_Pin;
    }
    else 
    {
        GPIOx->ODATA &= ~fu32_GPIO_Pin;
    }
}


/*********************************************************************************
* Function    : GPIO_Write
* Description : Writes data to the specified GPIO data port.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
*               PortVal: specifies the value to be written to the port output data register.
* Output      : None
**********************************************************************************/
void GPIO_Write(enum_GPIOx_t fe_GPIO, uint16_t PortVal)
{    
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    
    GPIO_TypeDef *GPIOx;

    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;


        default: break;
    }

    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD )
    {
        GPIOx->ODATA = (PortVal<<16);
    }else
    {
        GPIOx->ODATA = PortVal;
    }
}


/*********************************************************************************
* Function    : GPIO_ITConfig
* Description : GPIO IT Config.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
*               fu32_GPIO_Pin:specifies the port bit to read.
*                             This parameter can be GPIO_PIN_x where x can be (0..15).
*               NewState:  new it state of the port pin remapping.
*                          This parameter can be: ENABLE or DISABLE.
* Output      : None
**********************************************************************************/
void GPIO_ITConfig(enum_GPIOx_t fe_GPIO, uint32_t fu32_GPIO_Pin, FunctionalState NewState)
{
    
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    assert_param (IS_GPIO_PIN(fu32_GPIO_Pin));

    GPIO_TypeDef *GPIOx;

    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;


        default: break;
    }

    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD )
    {
         fu32_GPIO_Pin <<= 16;
    }
    
    if (ENABLE == NewState) 
    {
        GPIOx->IEN |= fu32_GPIO_Pin;
    }
    else 
    {
        GPIOx->IEN &= ~fu32_GPIO_Pin;
    }  
}


/*********************************************************************************
* Function    : GPIO_ITClear
* Description : GPIO IT Clear.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
*               fu32_GPIO_Pin:specifies the port bit to read.
*                             This parameter can be GPIO_PIN_x where x can be (0..15).
* Output      : None
**********************************************************************************/
void GPIO_ITClear(enum_GPIOx_t fe_GPIO, uint32_t fu32_GPIO_Pin)
{
        
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    assert_param (IS_GPIO_PIN(fu32_GPIO_Pin));

    GPIO_TypeDef *GPIOx;

    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;


        default: break;
    }

    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD)
    {
         fu32_GPIO_Pin <<= 16;
    }
    GPIOx->IC |= fu32_GPIO_Pin;
}



/*********************************************************************************
* Function    : GPIO_GetITStatus
* Description : Get GPIO Pin IT Status.
* Input       : fe_GPIO:This parameter can be GPIOx where x can be (A..F).
*               fu32_GPIO_Pin:specifies the port bit to read.
*                             This parameter can be GPIO_PIN_x where x can be (0..15).
* Output      : ITStatus
**********************************************************************************/
ITStatus GPIO_GetITStatus(enum_GPIOx_t fe_GPIO, uint32_t fu32_GPIO_Pin)
{
    /* Check the parameters */
    assert_param (IS_GPIO_ALL_INSTANCE(fe_GPIO));
    assert_param (IS_GPIO_PIN(fu32_GPIO_Pin));
    
    GPIO_TypeDef *GPIOx;
    ITStatus bitstatus = RESET;
    
    switch (fe_GPIO)
    {
        case GPIOA: 
        case GPIOB: 
        {
            GPIOx = GPIOAB;
        }break;

        case GPIOC: 
        case GPIOD: 
        {
            GPIOx = GPIOCD;
        }break;



        default: break;
    }

    if (fe_GPIO == GPIOB || fe_GPIO == GPIOD)
    {
        fu32_GPIO_Pin <<= 16;
    }
    
    if (GPIOx->RIS & fu32_GPIO_Pin) 
    {
        bitstatus = SET;
    }
    else 
    {
        bitstatus = RESET;
    } 
    return bitstatus;
}
