/**************************************************************************
 * @file     pan_pwm.h
 * @version  V1.00
 * $Revision: 3 $
 * $Date: 2023/11/08  $  
 * @brief    Panchip series PWM driver header file
 *
 * @note
 * Copyright (C) 2025 Panchip Technology Corp. All rights reserved.
 *****************************************************************************/

#ifndef __PAN_PWM_H__
#define __PAN_PWM_H__

/**
 * @brief Pwm Interface
 * @defgroup pwm_interface Pwm Interface
 * @{
 */

#ifdef __cplusplus
extern "C"
{
#endif

#include "pan271x.h"

#define PWM_CHANNEL_NUM                     (6)   /*!< PWM channel number */

/**@defgroup PWM_CHANNEL_SEL_FLAG Pwm channel number 
 * @brief       Pwm channel number constants definitions
 * @{ */ 
#define PWM_CH0                 0x0         /*!< PWM channel 0 */
#define PWM_CH1                 0x1         /*!< PWM channel 1 */
#define PWM_CH2                 0x2         /*!< PWM channel 2 */
#define PWM_CH3                 0x3         /*!< PWM channel 3 */
#define PWM_CH4                 0x4         /*!< PWM channel 4 */
#define PWM_CH5                 0x5         /*!< PWM channel 5 */
/**@} */

#if 0
#define PWM_TRIGGER_ADC_CNTR_IS_0           PWM_ADCTCTL0_ZPTRGEN0_Msk   /*!< PWM trigger ADC while counter matches 0 */
#define PWM_TRIGGER_ADC_CNTR_IS_CMR_D       PWM_ADCTCTL0_CDTRGEN0_Msk   /*!< PWM trigger ADC while counter matches CMR during down count */
#define PWM_TRIGGER_ADC_CNTR_IS_CNR         PWM_ADCTCTL0_CPTRGEN0_Msk   /*!< PWM trigger ADC while counter matches CNR */
#define PWM_TRIGGER_ADC_CNTR_IS_CMR_U       PWM_ADCTCTL0_CUTRGEN0_Msk   /*!< PWM trigger ADC while counter matches CMR during up count  */
#endif

typedef enum _PWM_ClkSrcSelDef
{
    PWM_CLK_SRC_APB       = 0, /*!< PWM clock src sel apb  */
    PWM_CLK_SRC_RCL_XTL   = 1, /*!< PWM clock src sel 32k rcl or 32k xtl*/
} PWM_ClkSrcSelDef;

/**@defgroup PWM_CLK_DIV_FLAG Pwm clk divider 
 * @brief       Pwm clk divider constants definitions
 * @{ */ 
typedef enum _PWM_ClkDivDef
{
    PWM_CLK_DIV_1   = 4, /*!< PWM clock divide by 1 */
    PWM_CLK_DIV_2   = 0, /*!< PWM clock divide by 2 */
    PWM_CLK_DIV_4   = 1, /*!< PWM clock divide by 4 */
    PWM_CLK_DIV_8   = 2, /*!< PWM clock divide by 8 */
    PWM_CLK_DIV_16  = 3, /*!< PWM clock divide by 16 */
} PWM_ClkDivDef;
/**@} */

/**
 * @brief This function enables PWM output generation of selected channels
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelMask Combination of enabled channels. Each bit corresponds to a channel.
 *                           Set bit 0 to 1 enables channel 0 output, set bit 1 to 1 enables channel 1 output...
 * @return None
 */
__STATIC_INLINE void PWM_EnableOutput(PWM_T *pwm, uint32_t u32ChannelMask)
{
    pwm->PWM_POEN |= u32ChannelMask;
}

/**
 * @brief This function disables PWM output generation of selected channels
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelMask Combination of enabled channels. Each bit corresponds to a channel
 *                           Set bit 0 to 1 disables channel 0 output, set bit 1 to 1 disables channel 1 output...
 * @return None
 */
__STATIC_INLINE void PWM_DisableOutput(PWM_T *pwm, uint32_t u32ChannelMask)
{
    pwm->PWM_POEN &= ~u32ChannelMask;
}

/**
 * @brief This function starts specified channel of PWM module
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return None
 */
__STATIC_INLINE void PWM_StartChannel(PWM_T *pwm, uint32_t u32ChannelNum)
{
    pwm->PWM_CTL |= (PWM_CTL_CNTEN0_Msk << (u32ChannelNum * 4));
}

/**
 * @brief This function forcedly stops specified channel of PWM module
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return None
 */
__STATIC_INLINE void PWM_StopChannel(PWM_T *pwm, uint32_t u32ChannelNum)
{
    pwm->PWM_CTL &= ~(PWM_CTL_CNTEN0_Msk << (u32ChannelNum * 4));
}

/**
 * @brief This function enable Dead zone of selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @param[in] u32Duration Dead Zone length in PWM clock count, valid values are between 0~0xFF, but 0 means there is no
 *                        dead zone.
 * @return None
 */
__STATIC_INLINE void PWM_EnableDeadZone(PWM_T *pwm, uint32_t u32ChannelNum, uint32_t u32Duration)
{
    // every two channels shares the same setting
    u32ChannelNum >>= 1;
    // set duration
    pwm->PWM_DTCTL = (pwm->PWM_DTCTL & ~(PWM_DTCTL_DTI01_Msk << (8 * u32ChannelNum))) | (u32Duration << (8 * u32ChannelNum));
    // enable dead zone
    pwm->PWM_CTL2 |= (PWM_CTL2_DTCNT01_Msk << u32ChannelNum);
}

/**
 * @brief This function disable Dead zone of selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return None
 */
__STATIC_INLINE void PWM_DisableDeadZone(PWM_T *pwm, uint32_t u32ChannelNum)
{
    // every two channels shares the same setting
    u32ChannelNum >>= 1;
    // enable dead zone
    pwm->PWM_CTL2 &= ~(PWM_CTL2_DTCNT01_Msk << u32ChannelNum);
}

/**
 * @brief This function enable Compare_Down interrupt of selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return None
 */
__STATIC_INLINE void PWM_EnableCMPDInt(PWM_T *pwm, uint32_t u32ChannelNum)
{
    (pwm)->PWM_INTEN |= ((1 << PWM_INTEN_CMPDIENn_Pos) << u32ChannelNum);
}

/**
 * @brief This function disable Compare_Down interrupt of selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return None
 */
__STATIC_INLINE void PWM_DisableCMPDInt(PWM_T *pwm, uint32_t u32ChannelNum)
{
    (pwm)->PWM_INTEN &= ~((1 << PWM_INTEN_CMPDIENn_Pos) << u32ChannelNum);
}

/**
 * @brief This function clears Compare_Down interrupt flag of selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return None
 */
__STATIC_INLINE void PWM_ClearCMPDIntFlag(PWM_T *pwm, uint32_t u32ChannelNum)
{
    pwm->PWM_INTSTS = ((1 << PWM_INTSTS_CMPDIFn_Pos) << u32ChannelNum);
}

/**
 * @brief This function get Compare_Down interrupt flag of selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return Compare_Down interrupt flag of specified channel
 * @retval 0 Compare_Down interrupt did not occurred
 * @retval 1 Compare_Down interrupt occurred
 */
__STATIC_INLINE uint32_t PWM_GetCMPDIntFlag(PWM_T *pwm, uint32_t u32ChannelNum)
{
    return ((pwm->PWM_INTSTS & ((1 << PWM_INTSTS_CMPDIFn_Pos) << u32ChannelNum)) ? (1) : (0));
}

/**
 * @brief This function enable Period interrupt of selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return None
 */
__STATIC_INLINE void PWM_EnablePeriodInt(PWM_T *pwm, uint32_t u32ChannelNum)
{
    (pwm)->PWM_INTEN |= ((1 << PWM_INTEN_PIENn_Pos) << u32ChannelNum);
}

/**
 * @brief This function disable Period interrupt of selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return None
 */
__STATIC_INLINE void PWM_DisablePeriodInt(PWM_T *pwm, uint32_t u32ChannelNum)
{
    (pwm)->PWM_INTEN &= ~((1 << PWM_INTEN_PIENn_Pos) << u32ChannelNum);
}

/**
 * @brief This function clears Period interrupt flag of selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return None
 */
__STATIC_INLINE void PWM_ClearPeriodIntFlag(PWM_T *pwm, uint32_t u32ChannelNum)
{
    pwm->PWM_INTSTS = ((1 << PWM_INTSTS_PIFn_Pos) << u32ChannelNum);
}

/**
 * @brief This function get Period interrupt flag of selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return Period interrupt flag of specified channel
 * @retval 0 Period interrupt did not occurred
 * @retval 1 Period interrupt occurred
 */
__STATIC_INLINE uint32_t PWM_GetPeriodIntFlag(PWM_T *pwm, uint32_t u32ChannelNum)
{
    return ((pwm->PWM_INTSTS & ((1 << PWM_INTSTS_PIFn_Pos) << u32ChannelNum)) ? (1) : (0));
}


/**
 * @brief This function enable independent mode
 * @param[in] pwm The base address of PWM module
 * @return None
 */
__STATIC_INLINE void PWM_EnableIndependentMode(PWM_T *pwm)
{
    pwm->PWM_CTL2 &= ~PWM_CTL2_MODE_Msk;
}
/**
 * @brief This function enable complementary mode
 * @param[in] pwm The base address of PWM module
 * @return None
 */
__STATIC_INLINE void PWM_EnableComplementaryMode(PWM_T *pwm)
{
    pwm->PWM_CTL2 = (pwm->PWM_CTL2 & ~PWM_CTL2_MODE_Msk) | (1UL << PWM_CTL2_MODE_Pos);
}

/**
 * @brief This function disable complementary mode, and enable independent mode.
 * @param[in] pwm The base address of PWM module
 * @return None
 */
__STATIC_INLINE void PWM_DisableComplementaryMode(PWM_T *pwm)
{
    pwm->PWM_CTL2 &= ~(1UL << PWM_CTL2_MODE_Pos);
}

/**
 * @brief This function enable group mode
 * @param[in] pwm The base address of PWM module
 * @return None
 */
__STATIC_INLINE void PWM_EnableGroupMode(PWM_T *pwm)
{
    pwm->PWM_CTL2 |= PWM_CTL2_GROUPEN_Msk;
}

/**
 * @brief This function disable group mode
 * @param[in] pwm The base address of PWM module
 * @return None
 */
__STATIC_INLINE void PWM_DisableGroupMode(PWM_T *pwm)
{
    pwm->PWM_CTL2 &= ~PWM_CTL2_GROUPEN_Msk;
}

/**
 * @brief This function enable synchronous mode
 * @param[in] pwm The base address of PWM module
 * @return None
 */
__STATIC_INLINE void PWM_EnableSyncMode(PWM_T *pwm)
{
    pwm->PWM_CTL2 = (pwm->PWM_CTL2 & ~PWM_CTL2_MODE_Msk) | (2UL << PWM_CTL2_MODE_Pos);
}

/**
 * @brief This function disable synchronous mode, and enable independent mode.
 * @param[in] pwm The base address of PWM module
 * @return None
 */
__STATIC_INLINE void PWM_DisableSyncMode(PWM_T *pwm)
{
    pwm->PWM_CTL2 &= ~(2UL << PWM_CTL2_MODE_Pos);
}


/**
 * @brief This function set the divider of the selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @param[in] Divider Clock divider of specified channel. Valid values are
 *              - \ref PWM_CLK_DIV_1
 *              - \ref PWM_CLK_DIV_2
 *              - \ref PWM_CLK_DIV_4
 *              - \ref PWM_CLK_DIV_8
 *              - \ref PWM_CLK_DIV_16
 * @return None
 */
__STATIC_INLINE void PWM_SetDivider(PWM_T *pwm, uint32_t u32ChannelNum, PWM_ClkDivDef Divider)
{
    pwm->PWM_CLKDIV = (pwm->PWM_CLKDIV & ~(PWM_CLKDIV_CLKDIV0_Msk << ((u32ChannelNum) * 4))) | (Divider << (u32ChannelNum * 4));
}

/**
 * @brief This function set the period of the selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5 
 * @param[in] u32Period Period of specified channel. Valid values are between 0~0xFFFF
 * @return None
 * @note This new setting will take effect on next PWM period
 * @note PWM counter will stop if period length set to 0
 */
__STATIC_INLINE void PWM_SetPeriod(PWM_T *pwm, uint32_t u32ChannelNum, uint32_t u32Period)
{
    (*((__IO uint32_t *) ((((uint32_t)&((pwm)->PWM_PERIOD0)) + (u32ChannelNum) * 4))) = (u32Period & 0xffff));
}

/**
 * @brief This function set the duty of the selected channel for PWM asymmetric Mode
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5 
 * @param[in] u32CmpData compare data. Valid values are between 0~0xFFFF
 * @return None
 * @note This new setting will take effect on next PWM period
 */
__STATIC_INLINE void PWM_SetCompareData(PWM_T *pwm, uint32_t u32ChannelNum, uint32_t u32CmpData)
{
    (*((__IO uint32_t *)((((uint32_t) & ((pwm)->PWM_CMPDAT0)) + (u32ChannelNum) * 4)))= (u32CmpData & 0xffff));
}


/**
 * @brief This function config PWM generator and get the nearest frequency in edge aligned auto-reload mode
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @param[in] u32Frequency Target generator frequency
 * @param[in] u32DutyCycle Target generator duty cycle percentage. Valid range are between 0 ~ 100. 10 means 10%, 20 means 20%...
 * @param[in] OperateType  Target operation type.Valid value Edge-aligned,Center-Aligned,Precise Center-Aligned 
 * @return Nearest frequency clock in nano second
 * @note Since every two channels, (0 & 1), (2 & 3), (4 & 5),  (6 & 7) shares a prescaler. Call this API to configure PWM frequency may affect
 *       existing frequency of other channel.
 */
uint32_t PWM_ConfigOutputChannel(PWM_T *pwm,
                                  uint32_t u32ChannelNum, 
                                  uint32_t u32Frequency, 
                                  uint32_t u32DutyCycle);
void PWM_SetPeriodAndDuty(PWM_T *pwm, uint32_t u32ChannelNum, uint16_t u16PeriodCycle, uint16_t u16PulseCycle);
void PWM_Start(PWM_T *pwm, uint32_t u32ChannelMask);

/**
 * @brief This function stop PWM module
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelMask Combination of enabled channels. Each bit corresponds to a channel.
 *                           Bit 0 is channel 0, bit 1 is channel 1...
 * @return None
 */
void PWM_Stop(PWM_T *pwm, uint32_t u32ChannelMask);

/**
 * @brief This function stop PWM generation immediately by clear channel enable bit
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelMask Combination of enabled channels. Each bit corresponds to a channel.
 *                           Bit 0 is channel 0, bit 1 is channel 1...
 * @return None
 */
void PWM_ForceStop(PWM_T *pwm, uint32_t u32ChannelMask);

/**
 * @brief This function enable output inverter of specified channel(s)
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelMask Combination of enabled channels. Each bit corresponds to a channel
 *                           Bit 0 represents channel 0, bit 1 represents channel 1...
 * @return None
 */
void PWM_EnableOutputInverter(PWM_T *pwm, uint32_t u32ChannelMask);

/**
 * @brief This function disable output inverter of specified channel(s)
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelMask Combination of channels to disable. Each bit corresponds to a channel
 *                           Bit 0 represents channel 0, bit 1 represents channel 1...
 * @return None
 */
void PWM_DisableOutputInverter(PWM_T *pwm, uint32_t u32ChannelMask);

/**
 * @brief This function set the prescaler of the selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @param[in] u32Prescaler Clock prescaler of specified channel. Valid values are between 1 ~ 0xFF
 * @return None
 * @note Every even channel N, and channel (N + 1) share a prescaler. So if channel 0 prescaler changed, 
 *       channel 1 will also be affected.
 */
void PWM_SetPrescaler(PWM_T *pwm, uint32_t u32ChannelNum, uint32_t u32Prescaler);

/**
 * @brief This function reset the prescaler of the selected channel
 * @param[in] pwm The base address of PWM module
 * @param[in] u32ChannelNum PWM channel number. Valid values are between 0~5
 * @return None
 * @note Every even channel N, and channel (N + 1) share a prescaler. So if channel 0 prescaler reset, 
 *       channel 1 will also be affected.
 */
void PWM_ResetPrescaler(PWM_T *pwm, uint32_t u32ChannelNum);

/**@} */

#ifdef __cplusplus
}
#endif

#endif //__PAN_PWM_H__

/*** (C) COPYRIGHT 2023 Panchip Technology Corp. ***/
