/**************************************************************************//**
 * @file     NuEdu-Basic01_PWM_Capture.c
 * @version  V1.00
 * $Revision: 3 $
 * $Date: 14/09/12 11:15p $
 * @brief    NuEdu-Basic01_PWM_Capture driver source file for NuEdu-SDK-Nano130
 *
 * @note
 * SPDX-License-Identifier: Apache-2.0
 * Copyright (C) 2014 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/
#include <stdio.h>
#include "NUC200Series.h"
#include "NuEdu-Basic01_PWM_Capture.h"

uint32_t PWM23_Clock            = 1000000;  //1 MHz
uint32_t PWM67_Clock            = 1000000;  //1 MHz
uint32_t Open_PWM6_OUT_Fail     = FALSE;
uint32_t Open_PWM3_Capture_Fail = FALSE;
uint32_t Open_PWM7_Capture_Fail = FALSE;

PWM_Capture_T PWM3 = {0,0,0,0,0,0,0};
PWM_Capture_T PWM7 = {0,0,0,0,0,0,0};

void Open_PWM6_OUT(uint32_t PWM_Frequency, uint32_t PWM_Duty)
{
    uint32_t TempPrescale;
    uint32_t TempCounter;
    uint32_t TempComparator;
    uint32_t PWM_Freq_Max;
    uint32_t PWM_Freq_Min;

    Open_PWM6_OUT_Fail = FALSE;

    //Initial PWM6 Function Pin
    SYS->GPE_MFP = (SYS->GPE_MFP & ~SYS_GPE_MFP_PE0_Msk) | SYS_GPE_MFP_PE0_PWM6;

    //Initial PWM67 Clock Source from XTAL's 12 MHz
    SYS_UnlockReg();
    if(!(SYSCLK->PWRCON&SYSCLK_PWRCON_XTL12M_EN_Msk))
    {
        SYSCLK->PWRCON = SYSCLK_PWRCON_XTL12M_EN_Msk;                   //Enable XTAL's 12 MHz
        while(!(SYSCLK->CLKSTATUS&SYSCLK_CLKSTATUS_XTL12M_STB_Msk));
        SystemCoreClockUpdate();
    }
//  SYS->IPRSTC2 |= SYS_IPRSTC2_PWM47_RST_Msk;                          //Donot Reset PWM, Safe for other unknown PWM device
//  SYS->IPRSTC2 &= ~SYS_IPRSTC2_PWM47_RST_Msk;
    SYSCLK->CLKSEL2 = (SYSCLK->CLKSEL2 & ~SYSCLK_CLKSEL2_PWM67_S_EXT_Msk) | SYSCLK_CLKSEL2_PWM67_EXT_XTAL;
    SYSCLK->CLKSEL2 = (SYSCLK->CLKSEL2 & ~SYSCLK_CLKSEL2_PWM67_S_Msk) | SYSCLK_CLKSEL2_PWM67_XTAL;
    SYSCLK->APBCLK |= SYSCLK_APBCLK_PWM67_EN_Msk;
    SYS_LockReg();

    //Initial PWM67 Peripheral Clock Prescale
    TempPrescale = _PWM_Source_Clock / PWM67_Clock;                     //Prescale = XTAL / PWM67_Clock
    if(TempPrescale>_PWM_Prescale_Max)
    {
        Open_PWM6_OUT_Fail = TRUE;
        TempPrescale = _PWM_Prescale_Max;
        PWM67_Clock = _PWM_Source_Clock / _PWM_Prescale_Max;            //PWM67_Clock = _PWM_Source_Clock / Prescale
        printf("\nPWM67 Clock Prescale Initial Fail!\n");
    }
    _PWM_SET_TIMER_PRESCALE(PWMB, PWM_CH2, (TempPrescale-1));           //Setting PWM67 Clock Prescale
    while(PWMB->SYNCBUSY2);

    //Check Local PWM6 Channel Frequency and Duty-Scale
    PWM_Freq_Max = PWM67_Clock / _PWM_Duty_Scale;
    PWM_Freq_Min = PWM67_Clock / _PWM_Resolution;
    if(PWM_Frequency>PWM_Freq_Max)
    {
        Open_PWM6_OUT_Fail = TRUE;
        PWM_Frequency = PWM_Freq_Max;
        printf("\nPWM6 Frequency too High! Max are %d Hz\n", PWM_Freq_Max);
    }

    if(PWM_Frequency<PWM_Freq_Min)
    {
        Open_PWM6_OUT_Fail = TRUE;
        PWM_Frequency = PWM_Freq_Min;
        printf("\nPWM6 Frequency too Low! Min are %d Hz\n", PWM_Freq_Min);
    }

    if(PWM_Duty>_PWM_Duty_Scale)
    {
        Open_PWM6_OUT_Fail = TRUE;
        PWM_Duty = _PWM_Duty_Scale;
        printf("\nPWM6 Duty too High! Max are %d %\n", _PWM_Duty_Scale);
    }

    //Initial Local PWM6 Channel
    _PWM_SET_TIMER_AUTO_RELOAD_MODE(PWMB, PWM_CH2);
    while(PWMB->SYNCBUSY2);
    PWMB->CSR = (PWMB->CSR & ~PWM_CSR_CSR2_Msk) | PWM_CSR_CSR2(PWM_CSR_DIV1);   //Set PWM6 Clock Source Divider = 1
    while(PWMB->SYNCBUSY2);

    TempCounter = PWM67_Clock / PWM_Frequency;
    TempComparator = (PWM_Duty * TempCounter) / _PWM_Duty_Scale;
    PWMB->CNR2 = TempCounter - 1;
    while(PWMB->SYNCBUSY2);
    if(TempComparator==0)   PWMB->CMR2 = TempComparator;
    else                    PWMB->CMR2 = TempComparator - 1;
    while(PWMB->SYNCBUSY2);

    //Start PWM6_OUT
    _PWM_ENABLE_PWM_OUT(PWMB, PWM_CH2);
    while(PWMB->SYNCBUSY2);
    _PWM_ENABLE_TIMER(PWMB, PWM_CH2);
    while(PWMB->SYNCBUSY2);

//  if(Open_PWM6_OUT_Fail==FALSE)
//      printf("\nOpen PWM_OUT Success!\n");
}

void Close_PWM6_OUT(void)
{
    //Close PWM6 Function Pin
    SYS->GPE_MFP &= ~SYS_GPE_MFP_PE0_Msk;

    //Disable PWM6 Function
    _PWM_DISABLE_TIMER(PWMB, PWM_CH2);
    _PWM_DISABLE_PWM_OUT(PWMB, PWM_CH2);

    //Donot Cloce PWM67, Safe for other unknown PWM device
    /*  //Close PWM Clock Source
        SYS_UnlockReg();
    //  SYS->IPRSTC2 |= SYS_IPRSTC2_PWM47_RST_Msk;
    //  SYS->IPRSTC2 &= ~SYS_IPRSTC2_PWM47_RST_Msk;
        SYSCLK->APBCLK &= ~SYSCLK_APBCLK_PWM67_EN_Msk;
        SYS_LockReg();
    */
}

void Open_PWM7_Capture(void)
{
    uint32_t TempPrescale;

    Open_PWM7_Capture_Fail = FALSE;

    //Initial PWM7 Function Pin
    SYS->GPE_MFP = (SYS->GPE_MFP & ~SYS_GPE_MFP_PE1_Msk) | SYS_GPE_MFP_PE1_PWM7;

    //Initial PWM67 Clock Source from XTAL's 12 MHz
    SYS_UnlockReg();
    if(!(SYSCLK->PWRCON&SYSCLK_PWRCON_XTL12M_EN_Msk))
    {
        SYSCLK->PWRCON = SYSCLK_PWRCON_XTL12M_EN_Msk;                           //Enable XTAL's 12 MHz
        while(!(SYSCLK->CLKSTATUS&SYSCLK_CLKSTATUS_XTL12M_STB_Msk));
        SystemCoreClockUpdate();
    }
//  SYS->IPRSTC2 |= SYS_IPRSTC2_PWM47_RST_Msk;                                  //Donot Reset PWM, Safe for other unknown PWM device
//  SYS->IPRSTC2 &= ~SYS_IPRSTC2_PWM47_RST_Msk;
    SYSCLK->CLKSEL2 = (SYSCLK->CLKSEL2 & ~SYSCLK_CLKSEL2_PWM67_S_EXT_Msk) | SYSCLK_CLKSEL2_PWM67_EXT_XTAL;
    SYSCLK->CLKSEL2 = (SYSCLK->CLKSEL2 & ~SYSCLK_CLKSEL2_PWM67_S_Msk) | SYSCLK_CLKSEL2_PWM67_XTAL;
    SYSCLK->APBCLK |= SYSCLK_APBCLK_PWM67_EN_Msk;
    SYS_LockReg();

    //Initial PWM67 Peripheral Clock Prescale
    TempPrescale = _PWM_Source_Clock / PWM67_Clock;                             //Prescale = XTAL / PWM67_Clock
    if(TempPrescale>_PWM_Prescale_Max)
    {
        Open_PWM7_Capture_Fail = TRUE;
        TempPrescale = _PWM_Prescale_Max;
        PWM67_Clock = _PWM_Source_Clock / _PWM_Prescale_Max;                    //PWM67_Clock = _PWM_Source_Clock / Prescale
        printf("\nPWM67 Clock Prescale Initial Fail!\n");
    }
    _PWM_SET_TIMER_PRESCALE(PWMB, PWM_CH3, (TempPrescale-1));
    while(PWMB->SYNCBUSY3);   //Setting PWM67 Clock Prescale

    //Initial Local PWM7 Channel
    _PWM_SET_TIMER_AUTO_RELOAD_MODE(PWMB, PWM_CH3);
    while(PWMB->SYNCBUSY3);
    _PWM_SET_TIMER_CLOCK_DIV(PWMB, PWM_CH3, PWM_CSR_DIV1);
    while(PWMB->SYNCBUSY3);     //Set PWM7 Clock Source Divider = 1
    PWMB->CNR3 = _PWM_Resolution - 1;
    while(PWMB->SYNCBUSY3);
    PWMB->PBCR |= PWM_PBCR_BCn_Msk;

    //Start Capture
    _PWM_ENABLE_CAP_IN(PWMB, PWM_CH3);
    while(PWMB->SYNCBUSY3);
    _PWM_ENABLE_CAP_FUNC(PWMB, PWM_CH3);
    while(PWMB->SYNCBUSY3);
    _PWM_ENABLE_TIMER(PWMB, PWM_CH3);
    while(PWMB->SYNCBUSY3);

//  if(Open_PWM7_OUT_Fail==FALSE)
//      printf("\nOpen PWM7_Capture Success!\n");
}

void Close_PWM7_Capture(void)
{
    //Close PWM7 Function Pin
    SYS->GPE_MFP &= ~SYS_GPE_MFP_PE1_Msk;

    //Disable PWM7 Function
    _PWM_DISABLE_TIMER(PWMB, PWM_CH3);
    while(PWMB->SYNCBUSY3);
    _PWM_DISABLE_CAP_FUNC(PWMB, PWM_CH3);
    while(PWMB->SYNCBUSY3);
    _PWM_DISABLE_CAP_IN(PWMB, PWM_CH3);
    while(PWMB->SYNCBUSY3);

    //Donot Cloce PWM67, Safe for other unknown PWM device
    /*  //Close PWM Clock Source
        SYS_UnlockReg();
    //  SYS->IPRSTC2 |= SYS_IPRSTC2_PWM47_RST_Msk;
    //  SYS->IPRSTC2 &= ~SYS_IPRSTC2_PWM47_RST_Msk;
        SYSCLK->APBCLK &= ~SYSCLK_APBCLK_PWM67_EN_Msk;
        SYS_LockReg();
    */
}

void Get_PWM7_Capture_Data(void)
{
    uint32_t i;

    _PWM_CLR_CAP_RISING_INDICATOR(PWMB, PWM_CH3);
    _PWM_CLR_CAP_FALLING_INDICATOR(PWMB, PWM_CH3);

    for(i=0; i<4; )
    {
        if(_PWM_GET_CAP_RISING_INDICATOR(PWMB, PWM_CH3))
        {
            PWM7.Capture_Rising[i>>1] = PWMB->CRLR3;
            PWM7.Last_Edge = Rising;
            _PWM_CLR_CAP_RISING_INDICATOR(PWMB, PWM_CH3);
            i++;
        }
        else if(_PWM_GET_CAP_FALLING_INDICATOR(PWMB, PWM_CH3))
        {
            PWM7.Capture_Falling[i>>1] = PWMB->CFLR3;
            PWM7.Last_Edge = Falling;
            _PWM_CLR_CAP_FALLING_INDICATOR(PWMB, PWM_CH3);
            i++;
        }
    }

    if(PWM7.Last_Edge == Falling)
    {
        //Calculate Case 1:
        if(PWM7.Capture_Rising[0]>PWM7.Capture_Rising[1])
        {
            PWM7.Signal_Period = PWM7.Capture_Rising[0] - PWM7.Capture_Rising[1];                       //(us)
            PWM7.High_Period = PWM7.Capture_Rising[0] - PWM7.Capture_Falling[0];                        //(us)
        }
        //Calculate Case 2:
        else if(PWM7.Capture_Rising[0]<PWM7.Capture_Rising[1])
        {
            PWM7.Signal_Period = _PWM_Resolution + PWM7.Capture_Rising[0] - PWM7.Capture_Rising[1];     //(us)
            PWM7.High_Period = PWM7.Capture_Rising[1] - PWM7.Capture_Falling[1];                        //(us)
        }
        PWM7.Low_Period = PWM7.Signal_Period - PWM7.High_Period;                                        //(us)
        PWM7.Signal_Frequency = (float) PWM67_Clock / (float) PWM7.Signal_Period;                       //(Hz)
    }
    else if(PWM7.Last_Edge == Rising)
    {
        //Calculate Case 3:
        if(PWM7.Capture_Falling[0]>PWM7.Capture_Falling[1])
        {
            PWM7.Signal_Period = PWM7.Capture_Falling[0] - PWM7.Capture_Falling[1];                     //(us)
            PWM7.Low_Period = PWM7.Capture_Falling[0] - PWM7.Capture_Rising[0];                         //(us)
        }
        //Calculate Case 4:
        else if(PWM7.Capture_Rising[0]<PWM7.Capture_Rising[1])
        {
            PWM7.Signal_Period = _PWM_Resolution + PWM7.Capture_Falling[0] - PWM7.Capture_Falling[1];   //(us)
            PWM7.Low_Period = PWM7.Capture_Falling[1] - PWM7.Capture_Rising[1];                         //(us)
        }
        PWM7.High_Period = PWM7.Signal_Period - PWM7.Low_Period;                                        //(us)
        PWM7.Signal_Frequency = (float) PWM67_Clock / (float) PWM7.Signal_Period;                       //(Hz)
    }
}

void Open_PWM3_Capture(void)
{
    uint32_t TempPrescale;

    Open_PWM3_Capture_Fail = FALSE;

    //Initial PWM3 Function Pin
    SYS->GPA_MFP = (SYS->GPA_MFP & ~SYS_GPA_MFP_PA15_Msk) | SYS_GPA_MFP_PA15_PWM3;
    SYS->ALT_MFP = (SYS->ALT_MFP & ~SYS_ALT_MFP_PA15_Msk) | SYS_ALT_MFP_PA15_PWM3;
    SYS->ALT_MFP1 = (SYS->ALT_MFP1 & ~SYS_ALT_MFP1_PA15_Msk) | SYS_ALT_MFP1_PA15_PWM3;

    //Initial PWM23 Clock Source from XTAL's 12 MHz
    SYS_UnlockReg();
    if(!(SYSCLK->PWRCON&SYSCLK_PWRCON_XTL12M_EN_Msk))
    {
        SYSCLK->PWRCON = SYSCLK_PWRCON_XTL12M_EN_Msk;                           //Enable XTAL's 12 MHz
        while(!(SYSCLK->CLKSTATUS&SYSCLK_CLKSTATUS_XTL12M_STB_Msk));
        SystemCoreClockUpdate();
    }
//  SYS->IPRSTC2 |= SYS_IPRSTC2_PWM03_RST_Msk;                                  //Donot Reset PWM, Safe for other unknown PWM device
//  SYS->IPRSTC2 &= ~SYS_IPRSTC2_PWM03_RST_Msk;
    SYSCLK->CLKSEL1 = (SYSCLK->CLKSEL1 & ~SYSCLK_CLKSEL1_PWM23_S_Msk) | SYSCLK_CLKSEL1_PWM23_XTAL;
    SYSCLK->CLKSEL2 = (SYSCLK->CLKSEL2 & ~SYSCLK_CLKSEL2_PWM23_S_EXT_Msk) | SYSCLK_CLKSEL2_PWM23_EXT_XTAL;
    SYSCLK->APBCLK |= SYSCLK_APBCLK_PWM23_EN_Msk;
    SYS_LockReg();

    //Initial PWM23 Peripheral Clock Prescale
    TempPrescale = _PWM_Source_Clock / PWM23_Clock;                             //Prescale = XTAL / PWM23_Clock
    if(TempPrescale>_PWM_Prescale_Max)
    {
        Open_PWM3_Capture_Fail = TRUE;
        TempPrescale = _PWM_Prescale_Max;
        PWM23_Clock = _PWM_Source_Clock / _PWM_Prescale_Max;                    //PWM23_Clock = _PWM_Source_Clock / Prescale
        printf("\nPWM23 Clock Prescale Initial Fail!\n");
    }
    _PWM_SET_TIMER_PRESCALE(PWMA, PWM_CH3, (TempPrescale-1));
    while(PWMA->SYNCBUSY3);   //Setting PWM23 Clock Prescale

    //Initial Local PWM3 Channel
    _PWM_SET_TIMER_AUTO_RELOAD_MODE(PWMA, PWM_CH3);
    while(PWMA->SYNCBUSY3);
    _PWM_SET_TIMER_CLOCK_DIV(PWMA, PWM_CH3, PWM_CSR_DIV1);
    while(PWMA->SYNCBUSY3);     //Set PWM3 Clock Source Divider = 1
    PWMA->CNR3 = _PWM_Resolution - 1;
    while(PWMA->SYNCBUSY3);
    PWMA->PBCR |= PWM_PBCR_BCn_Msk;

    //Start Capture
    _PWM_ENABLE_CAP_IN(PWMA, PWM_CH3);
    while(PWMA->SYNCBUSY3);
    _PWM_ENABLE_CAP_FUNC(PWMA, PWM_CH3);
    while(PWMA->SYNCBUSY3);
    _PWM_ENABLE_TIMER(PWMA, PWM_CH3);
    while(PWMA->SYNCBUSY3);

//  if(Open_PWM3_OUT_Fail==FALSE)
//      printf("\nOpen PWM3_Capture Success!\n");
}

void Close_PWM3_Capture(void)
{
    //Close PWM3 Function Pin
    SYS->GPA_MFP &= ~SYS_GPA_MFP_PA15_Msk;
    SYS->ALT_MFP &= ~SYS_ALT_MFP_PA15_Msk;
    SYS->ALT_MFP1 &= ~SYS_ALT_MFP1_PA15_Msk;

    //Disable PWM3 Function
    _PWM_DISABLE_TIMER(PWMA, PWM_CH3);
    while(PWMA->SYNCBUSY3);
    _PWM_DISABLE_CAP_FUNC(PWMA, PWM_CH3);
    while(PWMA->SYNCBUSY3);
    _PWM_DISABLE_CAP_IN(PWMA, PWM_CH3);
    while(PWMA->SYNCBUSY3);

    //Donot Cloce PWM23, Safe for other unknown PWM device
    /*  //Close PWM Clock Source
        SYS_UnlockReg();
    //  SYS->IPRSTC2 |= SYS_IPRSTC2_PWM03_RST_Msk;
    //  SYS->IPRSTC2 &= ~SYS_IPRSTC2_PWM03_RST_Msk;
        SYSCLK->APBCLK &= ~SYSCLK_APBCLK_PWM23_EN_Msk;
        SYS_LockReg();
    */
}

void Get_PWM3_Capture_Data(void)
{
    uint32_t i;

    _PWM_CLR_CAP_RISING_INDICATOR(PWMA, PWM_CH3);
    _PWM_CLR_CAP_FALLING_INDICATOR(PWMA, PWM_CH3);

    for(i=0; i<4; )
    {
        if(_PWM_GET_CAP_RISING_INDICATOR(PWMA, PWM_CH3))
        {
            PWM3.Capture_Rising[i>>1] = PWMA->CRLR3;
            PWM3.Last_Edge = Rising;
            _PWM_CLR_CAP_RISING_INDICATOR(PWMA, PWM_CH3);
            i++;
        }
        else if(_PWM_GET_CAP_FALLING_INDICATOR(PWMA, PWM_CH3))
        {
            PWM3.Capture_Falling[i>>1] = PWMA->CFLR3;
            PWM3.Last_Edge = Falling;
            _PWM_CLR_CAP_FALLING_INDICATOR(PWMA, PWM_CH3);
            i++;
        }
    }

    if(PWM3.Last_Edge == Falling)
    {
        //Calculate Case 1:
        if(PWM3.Capture_Rising[0]>PWM3.Capture_Rising[1])
        {
            PWM3.Signal_Period = PWM3.Capture_Rising[0] - PWM3.Capture_Rising[1];                       //(us)
            PWM3.High_Period = PWM3.Capture_Rising[0] - PWM3.Capture_Falling[0];                        //(us)
        }
        //Calculate Case 2:
        else if(PWM3.Capture_Rising[0]<PWM3.Capture_Rising[1])
        {
            PWM3.Signal_Period = _PWM_Resolution + PWM3.Capture_Rising[0] - PWM3.Capture_Rising[1];     //(us)
            PWM3.High_Period = PWM3.Capture_Rising[1] - PWM3.Capture_Falling[1];                        //(us)
        }
        PWM3.Low_Period = PWM3.Signal_Period - PWM3.High_Period;                                        //(us)
        PWM3.Signal_Frequency = (float) PWM23_Clock / (float) PWM3.Signal_Period;                       //(Hz)
    }
    else if(PWM3.Last_Edge == Rising)
    {
        //Calculate Case 3:
        if(PWM3.Capture_Falling[0]>PWM3.Capture_Falling[1])
        {
            PWM3.Signal_Period = PWM3.Capture_Falling[0] - PWM3.Capture_Falling[1];                     //(us)
            PWM3.Low_Period = PWM3.Capture_Falling[0] - PWM3.Capture_Rising[0];                         //(us)
        }
        //Calculate Case 4:
        else if(PWM3.Capture_Rising[0]<PWM3.Capture_Rising[1])
        {
            PWM3.Signal_Period = _PWM_Resolution + PWM3.Capture_Falling[0] - PWM3.Capture_Falling[1];   //(us)
            PWM3.Low_Period = PWM3.Capture_Falling[1] - PWM3.Capture_Rising[1];                         //(us)
        }
        PWM3.High_Period = PWM3.Signal_Period - PWM3.Low_Period;                                        //(us)
        PWM3.Signal_Frequency = (float) PWM23_Clock / (float) PWM3.Signal_Period;                       //(Hz)
    }
}
