/**
  ******************************************************************************
  * @file    main.c
  * @author  MCU Application Team
  * @brief   Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 Puya Semiconductor Co.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by Puya under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "ms32c0xx.h"

/* Private define ------------------------------------------------------------*/
#define HSI_TIMEOUT_VALUE          ((uint32_t)100)  /* 100 ms */
#define LSI_TIMEOUT_VALUE          ((uint32_t)100)  /* 100 ms */
#define CLOCKSWITCH_TIMEOUT_VALUE  ((uint32_t)5000) /* 5 s    */

#define ERROR_HSI_TIMEOUT          0x01
#define ERROR_CLKSWITCH_TIMEOUT    0x02
#define ERROR_LSI_TIMEOUT          0x04

/* Private variables ---------------------------------------------------------*/
static __IO uint32_t Tick;
volatile uint16_t error = 0; 

/* Private user code ---------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
static __INLINE void APP_SystemClockConfig(void);
static __INLINE void APP_GPIOConfig(void);
static __INLINE void APP_LPTIMConfig(void);
static __INLINE void APP_EnterStop(void);
/**
  * @brief  Main program.
  * @param  None
  * @retval int
  */
int main(void)
{
  SysTick_Config(8000); /* 1ms config */
  APP_SystemClockConfig();

  RCC->APBENR1 |= (RCC_APBENR1_PWREN);
  RCC->APBENR2 |= (RCC_APBENR2_SYSCFGEN);

  APP_GPIOConfig();

  APP_LPTIMConfig();
  if (error != 0)
  {
    while(1) /* endless loop */
    {
    }
  }
  SysTick->CTRL &= ~(SysTick_CTRL_TICKINT_Msk); /* Disable Systick Interrupt */
  
  GPIOA->ODR &= ~(1 << 1); /* Turn on led */
  /* Wait for Button pressed PA0 */ 
  while((GPIOA->IDR & 0x1) == 0)
  {
  }
  GPIOA->ODR |= (1 << 1); /* Turn off led */

  while (1) /* Infinite loop */
  {   
    /* (1) Enable LPTimer */
    /* (2) Set ARR value :50 */
    /* (3) Start LPTIM in once mode */
    /* (4) Config and enter low power moder */
    /* (5) LPTIM must be disabled to restore internal state before next time enter stop mode */
    /* (6) Toggle GPIOA PIN3 */
    LPTIM->CR |= LPTIM_CR_ENABLE; /* (1) */
    LPTIM->ARR = 51 - 1; /* (2) */
    LPTIM->CR |= LPTIM_CR_SNGSTRT; /* (3) */
    APP_EnterStop(); /* (4) */
    LPTIM->CR &= ~(LPTIM_CR_ENABLE); /* (5) */
    GPIOA->ODR ^= (1 << 3); /* (6) */
  }
}

/**
  * @brief  Enter Stop mode
  * @param  None
  * @retval None
  */
static __INLINE void APP_EnterStop(void)
{
  /* (1) Enable the peripheral clock of PWR */
  /* (2) STOP mode powered by LPR */
  /* (3) Set SLEEPDEEP bit of Cortex System Control Register */
  /* (4) Request Wait For interrupt */
  /* (5) Clear SLEEPDEEP bit of Cortex System Control Register */
  RCC->APBENR1 |= (RCC_APBENR1_PWREN); /* (1) */
  PWR->CR1 = ((PWR->CR1 &~(PWR_CR1_VR_MODE)) | (PWR_CR1_VR_MODE_0)); /* (2) */
  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; /* (3) */
  
  __WFI(); /* (4) */
  
  SCB->SCR &= ~(SCB_SCR_SLEEPDEEP_Msk); /* (5) */
}

/**
  * @brief   This function configures the LPTIMx in Counter mode.
  *         To use another timer, channel or GPIO, the RCC and GPIO configuration 
  *         must be adapted according to the datasheet. 
  * @param   None
  * @retval  None
  */
static __INLINE void APP_LPTIMConfig(void)
{
  uint32_t tickstart = 0;
  /* (1) Enable LSI */
  /* (2) Set LSI 32768Hz and Triming Data */
  /* (3) Wait for LSI ready flag */
  /* (4) Select LSI as LPTIM kernel clock */
  RCC->CSR |= RCC_CSR_LSION; /* (1) */
  RCC->ICSCR = (RCC->ICSCR & 0xF007FFFF) | (((*(uint32_t *)(0x1FFF0090)) & 0x1FF) << RCC_ICSCR_LSI_TRIM_Pos); /* (2) */
  tickstart = Tick;
  while ((RCC->CSR & (RCC_CSR_LSIRDY)) != (RCC_CSR_LSIRDY )) /* (3) */
  {
    if ((Tick - tickstart ) > LSI_TIMEOUT_VALUE)
    {
      error = ERROR_LSI_TIMEOUT; /* Report an error */
      return;
    }      
  }
  RCC->CCIPR = (RCC->CCIPR & ~(RCC_CCIPR_LPTIMSEL)) | RCC_CCIPR_LPTIMSEL_0; /* (4) */
  
  /* (1) Enable the peripheral clock of LPTimer x */
  /* (2) Config LPTIM prescaler(LSI/128),Immediate update mode */
  /* (3) Enable LPTIM ARR match interrupt,disable ARR update ok interrupt */
  RCC->APBENR1 |= RCC_APBENR1_LPTIMEN; /* (1) */
  LPTIM->CFGR = LPTIM_CFGR_PRESC; /* (2) */
  LPTIM->IER = LPTIM_IER_ARRMIE; /* (3) */
  
  /* Configure EXTI and NVIC for LPTIM */
  /* (1) Configure extended interrupt for LPTIM1 */
  /* (2) Enable Interrupt on LPTIM1 */
  /* (3) Set priority for LPTIM1 */  
  EXTI->IMR |= EXTI_IMR_IM29; /* (1) */
  NVIC_EnableIRQ(LPTIM1_IRQn); /* (2) */
  NVIC_SetPriority(LPTIM1_IRQn,3); /* (3) */  
}

/**
  * @brief  This function configures the system clock @8MHz 
  *         assuming the registers have their reset value before the call.
  *         SYSTEM CLOCK  = HSI DIV1
  * @param  None
  * @retval None
  */
static __INLINE void APP_SystemClockConfig(void)
{
  uint32_t tickstart = 0;
  /* (1) HSI DIV1 and Enable HSI */
  /* (2) Set HSI 8M and Triming Data */
  /* (3) Wait for HSI ready flag */
  /* (4) Set AHB divider:HCLK = SYSCLK */
  /* (5) Select HSISYS as system clock */
  /* (6) Wait for clock switched on HSI */
  /* (7) Set APB divider:PCLK = HCLK */

  RCC->CR = (RCC->CR &~(RCC_CR_HSIDIV)) | RCC_CR_HSION;                            /* (1) */

  RCC->ICSCR = (RCC->ICSCR & 0xFFFE0000) | (0 << RCC_ICSCR_HSI_FS_CR_Pos) | \
               ((*(uint32_t *)(0x1FFF0080)) & 0xFFFF);                             /* (2) */

  tickstart = Tick;
  while ((RCC->CR & (RCC_CR_HSIRDY)) != (RCC_CR_HSIRDY ))                          /* (3) */
  {
    if ((Tick - tickstart ) > HSI_TIMEOUT_VALUE)
    {
      error = ERROR_HSI_TIMEOUT; /* Report an error */
      return;
    }
  }

  RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_HPRE));                                      /* (4) */

  RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_SW)) | RCC_CFGR_SW_HSISYS;                   /* (5) */
  tickstart = Tick;
  while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSISYS)                        /* (6) */
  {
    if ((Tick - tickstart ) > CLOCKSWITCH_TIMEOUT_VALUE)
    {
      error = ERROR_CLKSWITCH_TIMEOUT; /* Report an error */
      return;
    }
  }

  RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_PPRE));                                      /* (7) */
}

/**
  * @brief   This function enables the peripheral clocks on GPIO port A,
  *         configures GPIO PA1 in output mode for the Green LED pin,
  *         GPIO PA0 in input mode for the Button
  * @param   None
  * @retval  None
  */
static __INLINE void APP_GPIOConfig(void)
{  
  /* (1) Enable the peripheral clock of GPIOA */
  /* (2) Select output mode (01) on GPIOA pin 1/3 */
  /* (3) Select input mode (00) on GPIOA pin 0 */
  RCC->IOPENR |= RCC_IOPENR_GPIOAEN ; /* (1) */  
  GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODE1 | GPIO_MODER_MODE3)) 
               | (GPIO_MODER_MODE1_0 | GPIO_MODER_MODE3_0); /* (2) */  
  GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODE0)); /* (3) */ 
}

/******************************************************************************/
/*            Cortex-M0 Plus Processor Exceptions Handlers                    */
/******************************************************************************/

/**
  * @brief  This function handles NMI exception.
  * @param  None
  * @retval None
  */
void NMI_Handler(void)
{
}

/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
  */
void SVC_Handler(void)
{
}

/**
  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval None
  */
void PendSV_Handler(void)
{
}

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
  Tick++;
}

/******************************************************************************/
/*                 MS32C001B Peripherals Interrupt Handlers                   */
/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
/*  available peripheral interrupt handler's name please refer to the startup */
/*  file (startup_ms32c001bxx.s).                                             */
/******************************************************************************/
/**
  * @brief   This function handles LPTIM interrupt request.
  * @param   None
  * @retval  None
  */
void LPTIM1_IRQHandler(void)
{
  if ((LPTIM1->ISR & LPTIM_ISR_ARRM) != 0) /* Check ARR match */ 
  {
    LPTIM1->ICR |= LPTIM_ICR_ARRMCF; /* Clear ARR match flag */
    GPIOA->ODR ^= (1 << 1); /* Toggle green led */
  }
}

/************************ (C) COPYRIGHT Puya *****END OF FILE******************/
