/**
 * @file        main.c
 *
 * @brief       Main program body
 *
 * @version     V1.0.0
 *
 * @date        2023-12-01
 *
 * @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"

/* Private includes *******************************************************/
#include "apm32f4xx_device_cfg.h"
#include "log.h"

/* Private macro **********************************************************/

/* Sets the macro of following low power mode to 1 to turn on
   the testing of the corresponding mode. Only one mode can set as 1
   at the same time.
*/
#define SYS_SLEEP_MODE                  1U
#define SYS_STOP_MODE                   0U
#define SYS_STANDBY_MODE                0U

/* Private typedef ********************************************************/

/* Private variables ******************************************************/
static const char* tag = "main";

/* External variables *****************************************************/
extern RTC_HandleTypeDef hrtc;
extern UART_HandleTypeDef huart1;

/* Private function prototypes ********************************************/
#if (SYS_STANDBY_MODE)
    static void PMU_Consumption_StandbyMode(void);
#elif (SYS_STOP_MODE)
    static void PMU_Consumption_StopMode(void);
#elif (SYS_SLEEP_MODE)
    static void PMU_Consumption_SleepMode(void);
#else
#endif /* SYS_STANDBY_RTC_BKPSRAM_MODE | SYS_STANDBY_RTC_MODE | SYS_STANDBY_MODE | SYS_STOP_MODE | SYS_SLEEP_MODE */

/* External functions *****************************************************/

/**
 * @brief   Main program
 *
 * @param   None
 *
 * @retval  None
 */
int main(void)
{
    /* Device configuration */
    DAL_DeviceConfig();

    /* Enable PMU clock */
    __DAL_RCM_PMU_CLK_ENABLE();

    /* If system was resumed from Standby mode*/
    if(__DAL_PMU_GET_FLAG(PMU_FLAG_SB) != RESET)
    {
        __DAL_PMU_CLEAR_FLAG(PMU_FLAG_SB);
        LOGI(tag, "System was resumed from standby mode\r\n");
        
        while (1)
        {
            BOARD_LED_Toggle(LED2);
            DAL_Delay(500U);
        }
    }

    while (BOARD_BUTTON_GetState(BUTTON_KEY1))
    {
    }

    while (!BOARD_BUTTON_GetState(BUTTON_KEY1))
    {
    }

#if (SYS_STANDBY_MODE)
    PMU_Consumption_StandbyMode();
#elif (SYS_STOP_MODE)
    PMU_Consumption_StopMode();
#elif (SYS_SLEEP_MODE)
    PMU_Consumption_SleepMode();
#else
    LOGI(tag, "Running normal mode\r\n");
#endif /* SYS_STANDBY_MODE | SYS_STOP_MODE | SYS_SLEEP_MODE */
    
    /* Infinite loop */
    while (1)
    {
        BOARD_LED_Toggle(LED2);
        DAL_Delay(500U);
    }
}

#if (SYS_SLEEP_MODE)

/* Private functions ******************************************************/

/**
 * @brief   Consumption measure in sleep mode
 *
 * @param   None
 *
 * @retval  None
 *
 * @note    Wakeup using EINT line 1
 */
static void PMU_Consumption_SleepMode(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    LOGI(tag, "Enter sleep mode\r\n");

    /* Configure all GPIO as analog to reduce current consumption */
    /* Enable GPIOs clock */
    __DAL_RCM_GPIOA_CLK_ENABLE();
    __DAL_RCM_GPIOB_CLK_ENABLE();
    __DAL_RCM_GPIOC_CLK_ENABLE();
    __DAL_RCM_GPIOD_CLK_ENABLE();
    __DAL_RCM_GPIOE_CLK_ENABLE();
    __DAL_RCM_GPIOH_CLK_ENABLE();

    GPIO_InitStruct.Pin     = GPIO_PIN_All;
    GPIO_InitStruct.Mode    = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Speed   = GPIO_SPEED_LOW;
    GPIO_InitStruct.Pull    = GPIO_NOPULL;

    DAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    DAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    DAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    DAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    DAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    DAL_GPIO_Init(GPIOH, &GPIO_InitStruct);

    /* Disable GPIOs clock */
    __DAL_RCM_GPIOA_CLK_DISABLE();
    __DAL_RCM_GPIOB_CLK_DISABLE();
    __DAL_RCM_GPIOC_CLK_DISABLE();
    __DAL_RCM_GPIOD_CLK_DISABLE();
    __DAL_RCM_GPIOE_CLK_DISABLE();
    __DAL_RCM_GPIOH_CLK_DISABLE();

    /* Reset UART state for configure again and deinit UART */
    DAL_UART_DeInit(&huart1);
    BOARD_BUTTON_Config(BUTTON_KEY1, BUTTON_MODE_EINT);

    /* Suspend system tick */
    DAL_SuspendTick();

    /* Wait for interrupt */
    DAL_PMU_EnterSLEEPMode(PMU_MAINREGULATOR_ON, PMU_SLEEPENTRY_WFI);

    /* Resume system tick */
    DAL_ResumeTick();

    DAL_USART1_Config();
    BOARD_LED_Config(LED2);
    LOGI(tag, "Exit sleep mode\r\n");
}

#endif /* SYS_SLEEP_MODE */

#if (SYS_STOP_MODE)

/**
 * @brief   Consumption measure in stop mode
 *  
 * @param   None
 *  
 * @retval  None
 */
static void PMU_Consumption_StopMode(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    RCM_ClkInitTypeDef RCM_ClkInitStruct = {0};
    RCM_OscInitTypeDef RCM_OscInitStruct = {0};
    uint32_t flashLatency = 0U;

    LOGI(tag, "Enter stop mode\r\n");

    /* Configure all GPIO as analog to reduce current consumption */
    /* Enable GPIOs clock */
    __DAL_RCM_GPIOA_CLK_ENABLE();
    __DAL_RCM_GPIOB_CLK_ENABLE();
    __DAL_RCM_GPIOC_CLK_ENABLE();
    __DAL_RCM_GPIOD_CLK_ENABLE();
    __DAL_RCM_GPIOE_CLK_ENABLE();
    __DAL_RCM_GPIOH_CLK_ENABLE();

    GPIO_InitStruct.Pin     = GPIO_PIN_All;
    GPIO_InitStruct.Mode    = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Speed   = GPIO_SPEED_LOW;
    GPIO_InitStruct.Pull    = GPIO_NOPULL;

    DAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    DAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    DAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    DAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    DAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    DAL_GPIO_Init(GPIOH, &GPIO_InitStruct);

    /* Disable GPIOs clock */
    __DAL_RCM_GPIOA_CLK_DISABLE();
    __DAL_RCM_GPIOB_CLK_DISABLE();
    __DAL_RCM_GPIOC_CLK_DISABLE();
    __DAL_RCM_GPIOD_CLK_DISABLE();
    __DAL_RCM_GPIOE_CLK_DISABLE();
    __DAL_RCM_GPIOH_CLK_DISABLE();

    /* Reset UART state for configure again and deinit UART */
    DAL_UART_DeInit(&huart1);

    /* RTC Config */
    DAL_RTC_Config();

    /* Sets wake-up time = 10s */
    DAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x500BU, RTC_WAKEUPCLOCK_RTCCLK_DIV16);

    /* FLASH deep power down mode enabled */
    DAL_PMUEx_EnableFlashPowerDown();

    /* Enter stop */
    DAL_PMU_EnterSTOPMode(PMU_LOWPOWERREGULATOR_ON, PMU_STOPENTRY_WFI);
    
    /* Configures system clock after wake-up from STOP mode */
    /* Get the oscillators configuration according to the internal RCM registers */
    DAL_RCM_GetOscConfig(&RCM_OscInitStruct);

    /* Configures oscillators */
    RCM_OscInitStruct.OscillatorType    = RCM_OSCILLATORTYPE_HSE;
    RCM_OscInitStruct.HSEState          = RCM_HSE_ON;
    RCM_OscInitStruct.PLL.PLLState      = RCM_PLL_ON;
    if (DAL_RCM_OscConfig(&RCM_OscInitStruct) != DAL_OK)
    {
        Error_Handler();
    }

    /* Get the clocks configuration according to the internal RCM registers */
    DAL_RCM_GetClockConfig(&RCM_ClkInitStruct, &flashLatency);
    
    /* Configure the HCLK, PCLK1 and PCLK2 clocks dividers */
    RCM_ClkInitStruct.ClockType     = RCM_CLOCKTYPE_SYSCLK;
    RCM_ClkInitStruct.SYSCLKSource  = RCM_SYSCLKSOURCE_PLLCLK;
    if (DAL_RCM_ClockConfig(&RCM_ClkInitStruct, flashLatency) != DAL_OK)
    {
        Error_Handler();
    }

    /* Disable Wake-up timer */
    if(DAL_RTCEx_DeactivateWakeUpTimer(&hrtc) != DAL_OK)
    {
        Error_Handler();
    }

    DAL_USART1_Config();
    BOARD_LED_Config(LED2);
    LOGI(tag, "Exit stop mode\r\n");
}

#endif /* SYS_STOP_MODE */

#if (SYS_STANDBY_MODE)
/**
 * @brief   Consumption measure in standby mode
 *  
 * @param   None
 *  
 * @retval  None
 */
static void PMU_Consumption_StandbyMode(void)
{
    LOGI(tag, "Enter standby mode\r\n");

    /* Enable PMU Clock*/
    __DAL_RCM_PMU_CLK_ENABLE();

    /* Allow access to backup */
    DAL_PMU_EnableBkUpAccess();

    /* Reset RTC domain */
    __DAL_RCM_BACKUPRESET_FORCE();
    __DAL_RCM_BACKUPRESET_RELEASE();

    /* Disable all used wake-up sources: PA0 */
    DAL_PMU_DisableWakeUpPin(PMU_WAKEUP_PIN1);

    /* Clear all related wake-up flags */
    __DAL_PMU_CLEAR_FLAG(PMU_FLAG_WU);

    /* Re-enable all used wake-up sources: PA0 */
    DAL_PMU_EnableWakeUpPin(PMU_WAKEUP_PIN1);

    /* Enter STANDBY mode  */
    DAL_PMU_EnterSTANDBYMode();

    while (1)
    {
        
    }
}
#endif /* SYS_STANDBY_MODE */
