/* 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) 2023. 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.
 */
/******************************************************************************
 * @version: V1.0.0 demo
 ******************************************************************************/

/*!
* @file pwm_input.c
*
* @brief This file contains the user pwm input function.
*/

/* ===========================================  Includes  =========================================== */

#include "pwm_input.h"
#include "ac780x_gpio.h"
#include <string.h>

/* ============================================  Define  ============================================ */

#define PWM2CH0_GPIO    GPIOA
#define PWM2CH0_PIN     GPIO_PIN3
#define PWM2CH0_FUN     GPIO_FUN1

#define PWM2CH1_GPIO    GPIOB
#define PWM2CH1_PIN     GPIO_PIN11
#define PWM2CH1_FUN     GPIO_FUN4

#define PWM2CH2_GPIO    GPIOB
#define PWM2CH2_PIN     GPIO_PIN3
#define PWM2CH2_FUN     GPIO_FUN1

#define PWM2CH3_GPIO    GPIOB
#define PWM2CH3_PIN     GPIO_PIN13
#define PWM2CH3_FUN     GPIO_FUN1

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

/* ==========================================  Variables  =========================================== */

pwm_capture_t g_pwmCapture[4];

/* ====================================  Functions declaration  ===================================== */

static void PulseWidthProcess(pwm_capture_t *pwmCapture, PWM_Type *PWMx, PWM_ChannelType channel, GPIO_Type *channelGpio, GPIO_PinType channelPin);

/* ======================================  Functions define  ======================================== */

/*!
 * @brief PrintResults
 *        Calculate the frequency and duty cycle from the pulse width and print it out
 *
 * @param[in] pwmCapture: Save the captured pulse width information structure
 *
 * @param[in] str: Print usage, indicating which channel is used
 *
 * @return none
 */
void PrintResults(pwm_capture_t *pwmCapture, char *str)
{
    pwmCapture->mutex = 1;  /* Mutex lock, prevent the use of variables when the calculation, is interrupted to modify */
    if ((pwmCapture->positivePulseWidth + pwmCapture->negativePulseWidth) != 0)
    {
        /* The frequency and duty cycle results are amplified processed to avoid accuracy loss */
        pwmCapture->freq = g_periphAPBClock * 100UL / PWM2_INPUTCAPTURE_CLKPSC / (pwmCapture->negativePulseWidth + pwmCapture->positivePulseWidth);
        pwmCapture->dutyCycle = pwmCapture->positivePulseWidth * 10000UL / (pwmCapture->negativePulseWidth + pwmCapture->positivePulseWidth);
    }
    else
    {
        /* No effective pulse width information was captured and the variable was emptied */
        pwmCapture->freq = 0;
        pwmCapture->dutyCycle = 0;
    }
    printf("%s:  capture freq value:%d/100 Hz, duty cycle value:%d/10000\r\n", str, pwmCapture->freq, pwmCapture->dutyCycle);
    pwmCapture->positivePulseWidth = 0;
    pwmCapture->negativePulseWidth = 0;
    pwmCapture->mutex = 0;
}

/*!
 * @brief PulseWidthProcess
 *        The pulse width is calculated based on the captured value
 *
 * @param[in] pwmCapture: Save the captured pulse width information structure
 *
 * @param[in] PWMx: pwm module
 *
 * @param[in] channel: The channel that generates the interrupt
 *
 * @param[in] channelGpio: GPIO for the channel
 *
 * @param[in] channelPin: The PIN corresponding to the channel
 *
 * @return none
 */
static void PulseWidthProcess(pwm_capture_t *pwmCapture, PWM_Type *PWMx, PWM_ChannelType channel, GPIO_Type *channelGpio, GPIO_PinType channelPin)
{
    pwmCapture->captureValue = PWM_GetChannelCountValue(PWMx, channel);    /* Get the count of the capture */

    /* After the capture interruption, if the external level is low, the captured data is the high level count value.
       If the external level is high, the captured data is a low level count value */
    if (GPIO_GetPinLevel(channelGpio, channelPin) != GPIO_LEVEL_LOW)
    {
        if (pwmCapture->captureValue > pwmCapture->captureValueLast)    /* Overflow determination */
        {
            pwmCapture->negativePulseWidth = pwmCapture->captureValue - pwmCapture->captureValueLast;   /* Negative pulse width count value */
        }
        else
        {
            /* After overflow, the overflow value needs to be added */
            pwmCapture->negativePulseWidth = PWM_MAX_COUNTER_VALUE + pwmCapture->captureValue - pwmCapture->captureValueLast + 1;
        }
    }
    else
    {
        if (pwmCapture->captureValue > pwmCapture->captureValueLast)
        {
            pwmCapture->positivePulseWidth = pwmCapture->captureValue - pwmCapture->captureValueLast;
        }
        else
        {
            pwmCapture->positivePulseWidth = PWM_MAX_COUNTER_VALUE + pwmCapture->captureValue - pwmCapture->captureValueLast + 1;
        }
    }
    pwmCapture->captureValueLast = pwmCapture->captureValue;
}

/*!
 * @brief PWM2_Callback
 *        PWM2 interrupts user callback function
 *
 * @param[in] wpara: Overflow interrupt flag parameter
 *
 * @param[in] lpara: Channel matching interrupt flag parameter
 *
 * @return none
 */
void PWM2_Callback(void *device, uint32_t wpara, uint32_t lpara)
{
    if ((g_pwmCapture[0].mutex != 1) && (lpara & PWM_STR_CH0SF_Msk))  /* Channel 0 matches interrupt determination */
    {
        PulseWidthProcess(&g_pwmCapture[0], (PWM_Type *)device, PWM_CH_0, PWM2CH0_GPIO, PWM2CH0_PIN);
    }

    if ((g_pwmCapture[1].mutex != 1) && (lpara & PWM_STR_CH1SF_Msk))
    {
        PulseWidthProcess(&g_pwmCapture[1], (PWM_Type *)device, PWM_CH_1, PWM2CH1_GPIO, PWM2CH1_PIN);
    }

    if ((g_pwmCapture[2].mutex != 1) && (lpara & PWM_STR_CH2SF_Msk))
    {
        PulseWidthProcess(&g_pwmCapture[2], (PWM_Type *)device, PWM_CH_2, PWM2CH2_GPIO, PWM2CH2_PIN);
    }

    if ((g_pwmCapture[3].mutex != 1) && (lpara & PWM_STR_CH3SF_Msk))
    {
        PulseWidthProcess(&g_pwmCapture[3], (PWM_Type *)device, PWM_CH_3, PWM2CH3_GPIO, PWM2CH3_PIN);
    }
}

/*!
 * @brief PWM2_InputCapture_Single_Init
 *        The PWM2 module is initialized, PWM2_CH2 is configured as the input capture channel,
 *        and the PWM_INPUTCAP_SINGLE_EDGE mode is used for capture, and both rising and falling edges trigger capture
 *
 * @param none
 *
 * @return none
 */
void PWM2_InputCapture_Single_Init(void)
{
    PWM_ConfigType config;
    PWM_inputCaptureConfigType inputConfig;
    PWM_inputChConfigType inputChConfig[4];

    memset(&config, 0U, sizeof(config));
    memset(&inputConfig, 0U, sizeof(inputConfig));
    memset(&inputChConfig, 0U, sizeof(inputChConfig));

    GPIO_SetPullup(PWM2CH0_GPIO, PWM2CH0_PIN, ENABLE);
    GPIO_SetPullup(PWM2CH1_GPIO, PWM2CH1_PIN, ENABLE);
    GPIO_SetPullup(PWM2CH2_GPIO, PWM2CH2_PIN, ENABLE);
    GPIO_SetPullup(PWM2CH3_GPIO, PWM2CH3_PIN, ENABLE);

    GPIO_SetFunc(PWM2CH0_GPIO, PWM2CH0_PIN, PWM2CH0_FUN);  /* PA3  PWM2_CH0 */
    GPIO_SetFunc(PWM2CH1_GPIO, PWM2CH1_PIN, PWM2CH1_FUN);  /* PB11 PWM2_CH1 */
    GPIO_SetFunc(PWM2CH2_GPIO, PWM2CH2_PIN, PWM2CH2_FUN);  /* PB3  PWM2_CH2 */
    GPIO_SetFunc(PWM2CH3_GPIO, PWM2CH3_PIN, PWM2CH3_FUN);  /* PB13 PWM2_CH3 */

    /*input channel config*/
    inputChConfig[0].channel = PWM_CH_0;                                /* Input channel selection */
    inputChConfig[0].mode = PWM_INPUTCAP_SINGLE_EDGE;                   /* Input capture mode selection: PWM_INPUTCAP_SINGLE_EDGE is normal input capture, PWM_INPUTCAP_DUAL_EDGE is dual edge capture */
    inputChConfig[0].filterEn = ENABLE;                                 /* Input filter enable bit */
    inputChConfig[0].filterValue = 16;                                  /* Input the filter value */
    inputChConfig[0].interruptEn = ENABLE;                              /* Channel interrupt enable bit */

    /* Parameters to be configured in PWM_INPUTCAP_SINGLE_EDGE mode, which can be ignored in dual edge capture mode */
    inputChConfig[0].detectType = PWM_BOTH_EDGE_DETECT;                 /* The input captures the edge selection */
    inputChConfig[0].eventPsc = PWM_EVENT_PSC_1;                        /* Capture ratio, PWM_EVENT_PSC_1, occurrence of one valid edge produces one interrupt, PWM_EVENT_PSC_2, occurrence of two valid edges produces one interrupt */

    /* Parameters that need to be configured in PWM_INPUTCAP_DUAL_EDGE mode can be ignored in single edge capture mode */
    inputChConfig[0].measureType = (PWM_InputDualEdgeMeasureType)NULL;  /* The channel measurement pattern of the bilateral along the capture pattern */
    inputChConfig[0].onceMode = (PMW_DualOnceModeType)NULL;             /* Continuous mode configuration */


    inputChConfig[1].channel = PWM_CH_1;
    inputChConfig[1].mode = PWM_INPUTCAP_SINGLE_EDGE;
    inputChConfig[1].filterEn = ENABLE;
    inputChConfig[1].filterValue = 16;
    inputChConfig[1].interruptEn = ENABLE;

    inputChConfig[1].detectType = PWM_BOTH_EDGE_DETECT;
    inputChConfig[1].eventPsc = PWM_EVENT_PSC_1;

    inputChConfig[1].measureType = (PWM_InputDualEdgeMeasureType)NULL;
    inputChConfig[1].onceMode = (PMW_DualOnceModeType)NULL;


    inputChConfig[2].channel = PWM_CH_2;
    inputChConfig[2].mode = PWM_INPUTCAP_SINGLE_EDGE;
    inputChConfig[2].filterEn = ENABLE;
    inputChConfig[2].filterValue = 16;
    inputChConfig[2].interruptEn = ENABLE;

    inputChConfig[2].detectType = PWM_BOTH_EDGE_DETECT;
    inputChConfig[2].eventPsc = PWM_EVENT_PSC_1;

    inputChConfig[2].measureType = (PWM_InputDualEdgeMeasureType)NULL;
    inputChConfig[2].onceMode = (PMW_DualOnceModeType)NULL;


    inputChConfig[3].channel = PWM_CH_3;
    inputChConfig[3].mode = PWM_INPUTCAP_SINGLE_EDGE;
    inputChConfig[3].filterEn = ENABLE;
    inputChConfig[3].filterValue = 16;
    inputChConfig[3].interruptEn = ENABLE;

    inputChConfig[3].detectType = PWM_BOTH_EDGE_DETECT;
    inputChConfig[3].eventPsc = PWM_EVENT_PSC_1;

    inputChConfig[3].measureType = (PWM_InputDualEdgeMeasureType)NULL;
    inputChConfig[3].onceMode = (PMW_DualOnceModeType)NULL;

    /*input capture config*/
    inputConfig.channelNum = 4;                   /* Input the number of capture channels */
    inputConfig.channelConfig = inputChConfig;    /* The input captures the channel configuration assignment */

    /* PWM clock frequency division should be set reasonably.
    The minimum frequency that can be captured(provided the clock source is PWM_CLK_SOURCE_APB): miniFreq = APB_CLK/(clkPsc+1)/(maxValue+1) = 32000000/5/65536 ≈ 98HZ
    The maximum frequency that can be captured with 1% duty cycle accuracy (provided that the clock source is PWM_CLK_SOURCE_APB): maxFreq = APB_CLK/(clkPsc+1)/100 = 32000000/5/100 = 64KHZ
    Note that high-frequency waveform capture will frequently enter interrupts, which may cause the main program to fail to run.
    especially if there are multiple high-frequency waveforms, for example, only one high-frequency waveform can be captured at 64KHz */

    config.mode = PWM_MODE_INPUT_CAPTURE;         /* PWM mode configuration */
    config.initModeStruct = &inputConfig;         /* Different modes initialize configuration struct assignments accordingly */
    config.clkSource = PWM_CLK_SOURCE_APB;        /* PWM clock source selection */
    config.clkPsc = PWM2_INPUTCAPTURE_CLKPSC - 1; /* Clock division factor */
    config.initValue = 0;                         /* Initial value of the counter */
    config.maxValue = PWM_MAX_COUNTER_VALUE;      /* Maximum count value */
    config.overflowInterrupEn = DISABLE;          /* Overflow interrupts the enable bit */
    config.cntOverflowFreq = 0;                   /* The frequency at which overflow interrupts are generated, 0 means that each overflow produces an interrupt */
    config.interruptEn = ENABLE;                  /* PWM total interrupt enable bit */
    config.callback = PWM2_Callback;              /* PWM interrupts user callback function */

    PWM_Init(PWM2, &config);
}

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