/*!
 * @file        main.c
 *
 * @brief       Main program body
 *
 * @version     V1.0.0
 *
 * @date        2023-03-30
 *
 * @attention
 *
 *  Copyright (C) 2023 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

/* Includes */
#include "main.h"
#include "Board.h"
#include <stdio.h>
#include "apm32a0xx_comp.h"
#include "apm32a0xx_tmr.h"
#include "apm32a0xx_gpio.h"
#include "apm32a0xx_eint.h"
#include "apm32a0xx_misc.h"
#include "apm32a0xx_fmc.h"
#include "apm32a0xx_pmu.h"

/** @addtogroup Examples
  @{
  */

/** @addtogroup COMP_AnalogWatchdog
  @{
  */

/** @defgroup COMP_AnalogWatchdog_Macros Macros
  @{
*/

/* printf using USART1  */
#define DEBUG_USART  USART1

/* Exported types */
#define State_Over_ThreShold   0x00000001
#define State_Within_ThreShold 0x00000002
#define State_Under_ThreShold  0x00000003

/**@} end of group COMP_AnalogWatchdog_Macros */

/** @defgroup COMP_AnalogWatchdog_Variables Variables
  @{
*/

__IO uint32_t state = 0;

/**@} end of group COMP_AnalogWatchdog_Variables */


/** @defgroup COMP_AnalogWatchdog_Functions Functions
  @{
  */
/* Restore config */
void ConfigurationRestore(void);
/* Stop Mode */
void EntryStop(void);
/* Configure COMP with Interrupt */
void COMP_Init(void);
/* Compare the input voltage level */
void StateCheck(void);


/*!
 * @brief       Main program
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
int main(void)
{
    /* Configures LED GPIO    */
    APM_MINI_LEDInit(LED2);
    APM_MINI_LEDInit(LED3);
    
    /* Enable PMU clock */
    RCM_EnableAHBPeriphClock(RCM_APB1_PERIPH_PMU);
    
    /* Configure COMP1 and COMP2 with interrupt enable*/
    COMP_Init();
    
    /* Compare the input voltage level: above the upper threshold,
       within the threshold or under the threshold.*/
    StateCheck();
    
    while (1)
    {
        if(state == State_Over_ThreShold)
        {
            /* Restore config */
            ConfigurationRestore();
            
            /* Turns selected LED On */
            APM_MINI_LEDOn(LED2);
            APM_MINI_LEDOn(LED3);
        }
        else if(state == State_Within_ThreShold)
        {
            /* Enter STOP mode with regulator is lower power */
            EntryStop();
            /* Turns selected LED On */
            APM_MINI_LEDOn(LED2);
        }
        else
        {
            /* Restore config */
            ConfigurationRestore();
            /* Turns selected LED On */
            APM_MINI_LEDOn(LED3);    
        }
    }
}


/*!
 * @brief       Configure COMP1 and COMP2 with interrupt.
 *
 * @param       None
 *
 * @retval      None
 *
 */
void COMP_Init(void)
{
    GPIO_Config_T gpioConfig;
    COMP_Config_T compConfig;
    EINT_Config_T eintConfig;

    /* Enable GPIOA clock */
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);

    /* Configure PA1: PA1 is used as COMP1 and COMP2 non inveting input*/
    GPIO_ConfigStructInit(&gpioConfig);
    gpioConfig.mode = GPIO_MODE_AN;
    gpioConfig.pin = GPIO_PIN_1;
    gpioConfig.pupd = GPIO_PUPD_NO;
    GPIO_Config(GPIOA,&gpioConfig);

    /* Enable COMP Peripheral clock */
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);

    /* COMP1 Configure: the higher threshold is set to VREFINT*3/4 ~ 1.23*3 / 4 ~ 0.92V */
    COMP_ConfigStructInit(&compConfig);
    compConfig.invertingInput = COMP_INVERTING_INPUT_3_4VREFINT;
    compConfig.output = COMP_OUTPUT_NONE;
    compConfig.mode = COMP_MODE_LOWPOWER;
    compConfig.hysterrsis = COMP_HYSTERRSIS_LOW;
    COMP_Config(COMP_SELECT_COMP1,&compConfig);

    /* COMP2 Configure: the lower threshold is set to VREFINT/4 ~ 1.23 / 4 ~ 0.307V */
    COMP_ConfigStructInit(&compConfig);
    compConfig.invertingInput = COMP_INVERTING_INPUT_1_4VREFINT;
    compConfig.output = COMP_OUTPUT_NONE;
    compConfig.mode = COMP_MODE_LOWPOWER;
    compConfig.hysterrsis = COMP_HYSTERRSIS_LOW;
    COMP_Config(COMP_SELECT_COMP2,&compConfig);

    /* Enables the window mode */
    COMP_EnableWindow();
    
    /* Enable and Select COMP1 */
    COMP_Enable(COMP_SELECT_COMP1);
    
    /* Enable and Select COMP2 */
    COMP_Enable(COMP_SELECT_COMP2);

    /* Configure EINT LINE 21 in interrupt mode */
    EINT_ConfigStructInit(&eintConfig);
    eintConfig.line = EINT_LINE21;
    eintConfig.mode = EINT_MODE_INTERRUPT;
    eintConfig.trigger = EINT_TRIGGER_FALLING;
    eintConfig.lineCmd = ENABLE;
    EINT_Config(&eintConfig);

    /* Configure EINT LINE 22 in interrupt mode */
    EINT_ConfigStructInit(&eintConfig);
    eintConfig.line = EINT_LINE22;
    eintConfig.mode = EINT_MODE_INTERRUPT;
    eintConfig.trigger = EINT_TRIGGER_FALLING;
    eintConfig.lineCmd = ENABLE;
    EINT_Config(&eintConfig);

    /* Clear EINT21 line */
    EINT_ClearIntFlag(EINT_LINE21);

    /* Clear EINT22 line */
    EINT_ClearIntFlag(EINT_LINE22);

    /* Configure COMP IRQ */
    NVIC_EnableIRQRequest(ADC1_COMP_IRQn, 0);
    
}

/*!
 * @brief       Compare the input voltage level: above the upper threshold,
 *              within the threshold or under the threshold.
 *
 * @param       None
 *
 * @retval      None
 *
 */
void StateCheck(void)
{
    /* Check output level is high */
    if((COMP_ReadOutPutLevel (COMP_SELECT_COMP1)== COMP_OUTPUTLEVEL_HIGH) 
      && (COMP_ReadOutPutLevel (COMP_SELECT_COMP2) == COMP_OUTPUTLEVEL_HIGH))
    {
        /* When it detects rising edge,the input voltage is higher than VREFINT */
        state = State_Over_ThreShold;
    }
    else if((COMP_ReadOutPutLevel (COMP_SELECT_COMP1) == COMP_OUTPUTLEVEL_LOW)
      && (COMP_ReadOutPutLevel (COMP_SELECT_COMP2) == COMP_OUTPUTLEVEL_HIGH))
    {
        /* When it detects falling edge,the input voltage is lower than VREFINT */
        state = State_Within_ThreShold;
    }
    else if((COMP_ReadOutPutLevel (COMP_SELECT_COMP1) == COMP_OUTPUTLEVEL_LOW)
      && (COMP_ReadOutPutLevel (COMP_SELECT_COMP2) == COMP_OUTPUTLEVEL_LOW))
    {
        state = State_Under_ThreShold;
    }
}

/*!
 * @brief       Restore peripheral config before entering STOP mode.
 *
 * @param       None
 *
 * @retval      None
 *
 */
void ConfigurationRestore(void)
{
    __IO uint32_t STARTUpCount = 0, HSEStatus = 0;
        
    /* Enable HSE */
    RCM_ConfigHSE(RCM_HSE_OPEN);
        
    /* Waits for HSE be ready */
    HSEStatus = RCM_WaitHSEReady();
    
    if(HSEStatus == (uint32_t)0x01)
    {
        /* Enable Prefetch Buffer */
        FMC_SetLatency(FMC_LATENCY_1);

        /* HCLK = SYSCLK */
        RCM_ConfigAHB(RCM_SYSCLK_DIV_1);

        /* PCLK = HCLK */
        RCM_ConfigAPB(RCM_HCLK_DIV_1);

        /* PLL configure = HSE * 6 = 48MHz */
        RCM_ConfigCLKDIV(RCM_CLK_DIV_1);
        RCM_ConfigPLL(RCM_PLL_SEL_HSE,RCM_PLLMF_6);

        /* Enable PLL */
        RCM_EnablePLL();

        /* PLL selected as system clock source */
        RCM_ConfigSYSCLK(RCM_SYSCLK_SEL_PLL);    
    }
}

/*!
 * @brief       Prepare the system to STOP mode.
 *
 * @param       None
 *
 * @retval      None
 *
 */
void EntryStop(void)
{
    GPIO_Config_T gpioConfig;
    
    /* Enable GPIOx clock */
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOB);
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOC);
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOD);
    RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOF);
    
    /* Configure all GPIO port pins in Analog mode */
    gpioConfig.pin = GPIO_PIN_ALL;
    gpioConfig.mode = GPIO_MODE_AN;
    gpioConfig.pupd = GPIO_PUPD_NO;
    GPIO_Config(GPIOA,&gpioConfig);
    GPIO_Config(GPIOB,&gpioConfig);
    GPIO_Config(GPIOC,&gpioConfig);
    GPIO_Config(GPIOD,&gpioConfig);
    GPIO_Config(GPIOF,&gpioConfig);
    
    /* Request to enter STOP mode with regular in low power */
    PMU_EnterSTOPMode(PMU_REGULATOR_LowPower,PMU_STOPENTRY_WFI);
}

/*!
 * @brief       Control COMP interrupt request.
 *
 * @param       None
 *
 * @retval      None
 *
 */
void ADC_COMP_Isr(void)
{
    if((EINT_ReadStatusFlag(EINT_LINE21) != RESET) || (EINT_ReadStatusFlag(EINT_LINE22) != RESET))
    {
        /* Compare the input voltage level: above the upper threshold,
        within the threshold or under the threshold */
        StateCheck();
            
        /* Clear EINT21 pending bit */
        EINT_ClearIntFlag(EINT_LINE21);
            
        /* Clear EINT22 pending bit */
        EINT_ClearIntFlag(EINT_LINE22);
     }
}



#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @param       *f:  pointer to a FILE that can recording all information
*              needed to control a stream
*
* @retval      The characters that need to be send.
*
* @note
*/
int fputc(int ch, FILE* f)
{
    /* send a byte of data to the serial port */
    USART_TxData(DEBUG_USART, (uint8_t)ch);

    /* wait for the data to be send  */
    while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);

    return (ch);
}

#elif defined (__GNUC__)

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @retval      The characters that need to be send.
*
* @note
*/
int __io_putchar(int ch)
{
    /* send a byte of data to the serial port */
    USART_TxData(DEBUG_USART, ch);

    /* wait for the data to be send  */
    while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);

    return ch;
}

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       file:  Meaningless in this function.
*
* @param       *ptr:  Buffer pointer for data to be sent.
*
* @param       len:  Length of data to be sent.
*
* @retval      The characters that need to be send.
*
* @note
*/
int _write(int file, char* ptr, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        __io_putchar(*ptr++);
    }

    return len;
}

#else
#warning Not supported compiler type
#endif

/**@} end of group COMP_AnalogWatchdog_Functions */
/**@} end of group COMP_AnalogWatchdog */
/**@} end of group Examples */
