/******************************************************************************
* $Revision: 423 $
* $Date:: 2017-04-07 16:03:30 +0900#$
*****************************************************************************/
/* __DISCLAIMER_START__                                                      */
/******************************************************************************
* (c)2017, Cypress Semiconductor Corporation
* or a subsidiary of Cypress Semiconductor Corporation. All rights
* reserved.
*
* This software, including source code, documentation and related
* materials ( "Software" ), is owned by Cypress Semiconductor
* Corporation or one of its subsidiaries ( "Cypress" ) and is protected by
* and subject to worldwide patent protection (United States and foreign),
* United States copyright laws and international treaty provisions.
* Therefore, you may use this Software only as provided in the license
* agreement accompanying the software package from which you
* obtained this Software ( "EULA" ).
*
* If no EULA applies, Cypress hereby grants you a personal, nonexclusive,
* non-transferable license to copy, modify, and compile the
* Software source code solely for use in connection with Cypress' s
* integrated circuit products. Any reproduction, modification, translation,
* compilation, or representation of this Software except as specified
* above is prohibited without the express written permission of Cypress.
*
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING,
* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. Cypress reserves the right to make
* changes to the Software without notice. Cypress does not assume any
* liability arising out of the application or use of the Software or any
* product or circuit described in the Software. Cypress does not
* authorize its products for use in any products where a malfunction or
* failure of the Cypress product may reasonably be expected to result in
* significant property damage, injury or death ( "High Risk Product" ). By
* including Cypress' s product in a High Risk Product, the manufacturer
* of such system or application assumes all risk of such use and in doing
* so agrees to indemnify Cypress against all liability.
******************************************************************************/
/* __DISCLAIMER_END__                                                        */
/*****************************************************************************
** \file bt_pwm.c
**
** A detailed description is available at
** @link PwmGroup Pulse Width Modulation (PWM) description @endlink
**
** History:
** - 2014-05-30  0.01  ST  First version
*****************************************************************************/


/*****************************************************************************/
/* Include files                                                             */
/*****************************************************************************/
#include "common_include.h"

/**
*****************************************************************************
** \addtogroup PwmGroup
*****************************************************************************/
    
    /*****************************************************************************/
    /* Local pre-processor symbols/macros ('define')                             */
    /*****************************************************************************/
#define PWM_TMCR_CKS2_0       0x07   /* Bit mask for the CKS2-0 Bit */
#define PWM_TMCR2_CKS3        0x08   /* Bit mask for the CKS3 Bit */
    
#define PWM_TMCR_CKS2_0_SHIFT 0x00   /* Shift value for the CKS2-0 Bit */
#define PWM_TMCR2_CKS3_SHIFT  0x03   /* Shift value for the CKS3 Bit */
    
    
    /*****************************************************************************/
    /* Global variable definitions (declared in header file with 'extern')       */
    /*****************************************************************************/
    
    
    /*****************************************************************************/
    /* Local type definitions ('typedef')                                        */
    /*****************************************************************************/
    
    
    /*****************************************************************************/
    /* Local variable definitions ('static')                                     */
    /*****************************************************************************/
    
    
    /*****************************************************************************/
    /* Function implementation - global ('extern') and local ('static')          */
    /*****************************************************************************/
    
    //#include "bt_pwm_generated.c"
    
    /**
    *****************************************************************************
    ** \brief ISR callback for PWM
    **
    ** This callback is called by the global PWM ISR whenever a PWM triggers an
    ** interrupt. It in return calls the callback function that has been given
    ** during PWM configuration.
    **
    ** \param [in] pstcPwm           PWM instance that caused the interrupt
    ** \param [in] pstcPwmInternData Internal data associated with the PWM instance
    *****************************************************************************/
    void BtPwmIrqHandler( volatile stc_btn_pwm_t* pstcPwm,
                         stc_pwm_intern_data_t* pstcPwmInternData
                         )
    {
        /* Prepare the value used for clearing the Interrupt Request Flag */
        
        /* Check if a trigger interrupt is raised */
        if ( pstcPwm->unSTC.stcField.u1TGIR == 1 )
        {
            /* clear trigger flag of PWM */
            PDL_WRITE_REG_SYNC(pstcPwm->unSTCC.stcField.u1TGIRC, 1);
            
            /* Call PWM callback function */
            if ( pstcPwmInternData->pfnTriggerCallback != NULL )
            {
                pstcPwmInternData->pfnTriggerCallback() ;
            }
        } /* if ( pstcPwm->unSTC.stcField.u1TGIR == 1 ) */
        
        /* Check if a duty match interrupt is raised */
        if ( pstcPwm->unSTC.stcField.u1DTIR == 1 )
        {
            /* clear duty match flag of PWM */
            PDL_WRITE_REG_SYNC(pstcPwm->unSTCC.stcField.u1DTIRC, 1);
            
            /* Call PWM callback function */
            if ( pstcPwmInternData->pfnDutyMatchCallback != NULL )
            {
                pstcPwmInternData->pfnDutyMatchCallback() ;
            }
        } /* if ( pstcPwm->unSTC.stcField.u1DTIR == 1 ) */
        
        /* Check if a underflow interrupt is raised */
        if ( pstcPwm->unSTC.stcField.u1UDIR == 1 )
        {
            /* clear underflow flag of RLT */
            PDL_WRITE_REG_SYNC(pstcPwm->unSTCC.stcField.u1UDIRC, 1);
            
            /* Call RLT callback function */
            if ( pstcPwmInternData->pfnUnderflowCallback != NULL )
            {
                pstcPwmInternData->pfnUnderflowCallback() ;
            }
        } /* if ( pstcPwm->unSTC.stcField.u1UDIR == 1 ) */
        
        
        
    } /* BtPwmIrqHandler */
    
    
    /**
    *****************************************************************************
    ** \brief Initialize a Pulse Width Modulation.
    **
    ** This function initializes the given PWM instance with the provided
    ** parameters. Before configuration the PWM is disabled.
    ** The given callback is stored internally.
    **
    ** The PWM interrupt flags are cleared.
    **
    ** \param [in]  pstcPwm       PWM instance
    ** \param [in]  pstcConfig    PWM configuration
    **
    ** \retval Ok                    PWM configuration has been set
    ** \retval ErrorInvalidParameter If one of the following conditions is met:
    **          - pstcPwm == NULL
    **          - pstcConfig == NULL
    **          - pstcPwmInternData == NULL
    **
    *****************************************************************************/
    en_result_t Bt_Pwm_Init( volatile stc_btn_pwm_t* pstcPwm, stc_pwm_config_t* pstcConfig )
    {
        stc_pwm_intern_data_t* pstcInternData = NULL;
        
        /* Local buffer to prepare the settings */
        un_btn_pwm_tmcr_t     unTMCR  = { 0 } ;
        un_btn_pwm_stc_t      unSTC   = { 0 } ;
        un_btn_pwm_tmcr2_t    unTMCR2 = { 0 } ;
        
        /* Check for NULL pointers */
        if ( pstcPwm    == NULL ||
            pstcConfig == NULL
            )
        {
            /* One of the passed parameters is a NULL pointer */
            return ErrorInvalidParameter;
        }
        
        
        /* Get the pointer to the internal data structure for the given PWM instance */
        pstcInternData = BtPwmGetInternDataPtr( pstcPwm ) ;
        // ... and check for NULL pointer
        if ( pstcInternData == NULL )
        {
            return ErrorInvalidParameter ;
        }
        
        /* Write STC register to reset value which also disables the PWM */
        /* operation. u1TGIRC, u1DTIRC and u1UDIRC are set to 1 to clear the interrupt flag */
        /* First of all, disable possible interrupts */
        PDL_WRITE_REG_SYNC(pstcPwm->unSTCC.stcField.u1TGIRC, 1);
        PDL_WRITE_REG_SYNC(pstcPwm->unSTCC.stcField.u1DTIRC, 1);
        PDL_WRITE_REG_SYNC(pstcPwm->unSTCC.stcField.u1UDIRC, 1);
        
        /* Set the timer function */
        unTMCR.stcField.u3FMD = 1;     /* Select 16-bit PWM timer */
        
        /* Store the callback that shall be called within the interrupt routine in */
        /* the internal data structure. */
        pstcInternData->pfnTriggerCallback = pstcConfig->pfnTriggerCallback ;
        pstcInternData->pfnDutyMatchCallback = pstcConfig->pfnDutyMatchCallback ;
        pstcInternData->pfnUnderflowCallback = pstcConfig->pfnUnderflowCallback ;
        
        /* Setup clock settings */
        unTMCR.stcField.u3CKS2_0 = (pstcConfig->enPrescaler & PWM_TMCR_CKS2_0);
        unTMCR2.stcField.u1CKS3  = (pstcConfig->enPrescaler & PWM_TMCR2_CKS3) >> PWM_TMCR2_CKS3_SHIFT;
        
        /* Set the Trigger input edge selection */
        unTMCR.stcField.u2EGS = pstcConfig->enTriggerInputEdgeSelection;
        
        /* configure rest of PWM */
        unTMCR.stcField.u1RTGEN = (pstcConfig->bRestartEnable           == TRUE) ? 1 : 0 ;
        unTMCR.stcField.u1PMSK  = (pstcConfig->bPulseOutputMaskEnable   == TRUE) ? 1 : 0 ;
        unTMCR.stcField.u1OSEL  = (pstcConfig->bOutputPolarityInverse   == TRUE) ? 1 : 0 ;
        unTMCR.stcField.u1MDSE  = (pstcConfig->bOneShotOperation        == TRUE) ? 1 : 0 ;
        
        /* Write back shadow registers to real HW */
        pstcPwm->unTMCR   = unTMCR;
        pstcPwm->unSTC     = unSTC;
        pstcPwm->unTMCR2  = unTMCR2;
        
        return Ok;
    } /* Bt_Pwm_Init */
    
    
    /**
    *****************************************************************************
    ** \brief Start the PWM
    **
    ** Bt_Pwm_Init() must have run before
    **
    ** This function initialize PWM cycle, PWM duty, Start delay and ADC trigger value
    ** and enables operation.
    **
    ** If a callback was set by Bt_Pwm_Init(), the PWM interrupt is enabled and the
    ** internal ISR in turn calls the given callback.
    **
    ** If bSwTrigger is TRUE, the PWM is triggered immediately.
    **
    ** \param [in]  pstcPwm          PWM instance
    ** \param [in]  pstcCycle        PWM cycle configuration
    ** \param [in]  bSwTrigger       TRUE generates a software trigger to directly
    **                                start operation
    **
    ** \retval Ok                    PWM has been started
    ** \retval ErrorInvalidParameter If one of the following conditions are met:
    **          - one of the passed parameters is a NULL pointer
    **          - pstcRltInternData == NULL (pstcPwm is an invalid address)
    **            i.E. invalid or disabled PWM unit
    *****************************************************************************/
    en_result_t Bt_Pwm_Start( volatile stc_btn_pwm_t* pstcPwm,
                             stc_pwm_cycle_config_t* pstcCycle,
                             boolean_t bSwTrigger )
    {
        /* Pointer to the internal data structure */
        stc_pwm_intern_data_t* pstcInternData ;
        
        /* Check for NULL pointer */
        if ( pstcPwm == NULL || pstcCycle == NULL )
        {
            /* Passed parameter is a NULL pointer */
            return ErrorInvalidParameter ;
        }
        
        /* Get pointer to internal data ... */
        pstcInternData = BtPwmGetInternDataPtr( pstcPwm ) ;
        /* ... and check for NULL pointer */
        if ( pstcInternData == NULL )
        {
            /* The pstcPwm that was passed is not available or not enabled */
            return ErrorInvalidParameter ;
        }
        
        /* If necessary register ISR */
        if ( pstcInternData->pfnTriggerCallback != NULL )
        {
            /* Enable interrupt */
            pstcPwm->unSTCS.stcField.u1TGIES = 1;
        }
        if ( pstcInternData->pfnDutyMatchCallback != NULL )
        {
            /* Enable interrupt */
            pstcPwm->unSTCS.stcField.u1DTIES = 1;
        }
        if ( pstcInternData->pfnUnderflowCallback != NULL )
        {
            /* Enable interrupt */
            pstcPwm->unSTCS.stcField.u1UDIES = 1;
        }
        
        /* Set cycle */
        pstcPwm->unPCSR.u16Register = pstcCycle->u16Cycle;
        /* Set duty */
        pstcPwm->unPDUT.u16Register = pstcCycle->u16Duty;
        /* Set start delay */
        pstcPwm->unPSDR.u16Register = pstcCycle->u16StartDelay;
        /* Set ADC trigger */
        pstcPwm->unADTR.u16Register = pstcCycle->u16AdcTriggerValue;
        
        /* Enable timer and wait for trigger */
        pstcPwm->unTMCR.stcField.u1CTEN = 1;
        
        if ( bSwTrigger == TRUE )
        {
            pstcPwm->unTMCR.stcField.u1STRG = 1;
        } /* if ( bSwTrigger == TRUE ) */
        
        return Ok ;
    } /* Bt_Pwm_Start */
    
    
    /**
    *****************************************************************************
    ** \brief Stop the PWM
    **
    ** This function disables the PWM interrupt and stops the PWM by clearing
    **
    ** \param [in]  pstcPwm          PWM instance
    **
    ** \retval Ok                    PWM has been stopped
    ** \retval ErrorInvalidParameter If pstcPwm == NULL
    *****************************************************************************/
    en_result_t Bt_Pwm_Stop( volatile stc_btn_pwm_t* pstcPwm )
    {
        /* Check for NULL pointer */
        if ( pstcPwm == NULL )
        {
            /* Passed parameter is a NULL pointer */
            return ErrorInvalidParameter ;
        }
        
        /* disable timer before anything else is done */
        pstcPwm->unTMCR.stcField.u1CTEN = 0 ;
        
        /* disable interrupt */
        PDL_SAFELY_DISABLE_INTERRUPT(pstcPwm->unSTCC.stcField.u1TGIEC, 1)
        PDL_SAFELY_DISABLE_INTERRUPT(pstcPwm->unSTCC.stcField.u1DTIEC, 1)
        PDL_SAFELY_DISABLE_INTERRUPT(pstcPwm->unSTCC.stcField.u1UDIEC, 1)
        
        return Ok ;
    } /* Bt_Pwm_Stop */
/*****************************************************************************/
/* EOF (not truncated)                                                       */
/*****************************************************************************/
