/*******************************************************************************
MODULE      :   main.c
  Simple scheduler. Wakes up about every 1ms by RTCC (0.976ms). When RTCC is
  running by internal LFRCO some jitter and inaccuracy is to be expected. From
  main loop tasks can be called every about 5ms, 10ms, 25ms.... Additionally
  iTask1s is set every 1s (RTC accuracy) for time keeping.

  After all tasks are completed EFM32 is switched to EM2 to minimize energy
  consumption. LED lits whenever core is out of EM2.

  When some pheriperal needs HFCLK switching to EM2 can be prohbited by
  signaling via f.i. TraceIsRunning(). In this case core is switched to EM1
  instead. Core is woken up by RTCC interrupt or any other interrupt.

201xxxxx AB     Initial
20150407 AB     Adapted for USV_BatteryMonitor Slave, remove LEDs and Testpins.
20191212 AB     JADE EFM32JG1B test, TRACE now needs HFCLK. RTCC instead RTC.
*******************************************************************************/

#define LOGGING_TOKEN    MAI_LOGGING
#include "TraceLeUart.h"

#include <stdio.h>
#include "em_chip.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_adc.h"
#include "em_device.h"
#include "em_gpio.h"
#include "em_ldma.h"
#include "em_prs.h"
#include "em_rtcc.h"

#include "command.h"
#include "types.h"
#include "adc.h"
#include "leds.h"

/* Defines for RTCC */
#define RTCC_WAKEUP_MS          1
#define RTCC_WAKEUP_COUNT       (((32768 * RTCC_WAKEUP_MS) / 1000) - 1)

#define TESTPIN_INIT();         { GPIO_PinModeSet(gpioPortD, 15, gpioModePushPull, 0);}
#define TESTPIN( x )            { if ( x ) GPIO->P[gpioPortD].DOUT |= 1 << 15; else GPIO->P[gpioPortD].DOUT &= ~(1 << 15); }

// --- PUBLIC Variables -------------------------------------------------------
extern int _STACK_START;
extern int _STACK_END;

// --- Variables --------------------------------------------------------------
uint32_t rtccFlag;

static int iMsTicks = 0;      // for counting real seconds
static int iTaskTicks = 0;    // for counting various tasks

static int iTask5ms = 0;
static int iTask10ms = 0;
static int iTask25ms = 0;
static int iTask50ms = 0;
static int iTask100ms = 0;
static int iTask200ms = 0;
static int iTask500ms = 0;
static int iTask1s = 0;

static int iTaskClock1s = 0;  // real seconds task

/*static int iSec = 0;
static int iMin = 0;
static int iHours = 0;
static int iDays = 0;
static uint32_t Seconds = 0;  // counts seconds from boot, wraps around in >130 years
*/
// --- Macros -----------------------------------------------------------------

// --- Functionprototypes -----------------------------------------------------
void RtcInit(void);

/**************************************************************************//**
 * @brief Main function
 *****************************************************************************/
int main(void)
  {
  int Seconds = 0;

  /* Chip errata */
  CHIP_Init();

  TESTPIN_INIT();
  TESTPIN(ON);

  LedInit();

/*  // LFRCO Clock Output on Pin PD9
  // only series 2 CMU_ClkOutPinConfig(0, cmuSelect_LFRCO, 1, gpioPortD, 9);
  CMU->CTRL = (CMU->CTRL | CMU_CTRL_CLKOUTSEL0_LFRCO);
  CMU->ROUTEPEN =   (CMU->ROUTEPEN | 1 );   // CLKOUT0PEN
  CMU->ROUTELOC0 =  (CMU->ROUTELOC0 | 4 );  // LOC4

  CMU_ClockEnable(cmuClock_GPIO, true);       // Enable GPIO clock
  GPIO_PinModeSet(gpioPortD, 9, gpioModePushPull, 1);
*/

  RtcInit();

  TraceInit();
  TraceInit2();

  CommandInit();

  AdcInit();

  while (1)
    {
    TESTPIN(OFF);

    // switch to EM2 except some module needs HFCLK
    __disable_irq();    // __asm("CPSID i");
    if ( TraceIsRunning() /* | AdcIsRunning() */ )
      { // if some module needs HFCLK switch to EM1 instead EM2
      EMU_EnterEM1();
      __enable_irq();   // __asm("CPSIE i");
      }
    else
      {// go to EM2
      LedOff(2);
      EMU_EnterEM2(true);
      __enable_irq();   // __asm("CPSIE i");
      LedOn(2);         // superflous here cause should be done in every IRQ handler
      }

    if ( iTask5ms )
      {
      iTask5ms = FALSE;
      // tasks to be done every 5 milliseconds

      }

    if ( iTask10ms )
      {
      iTask10ms = FALSE;
      // tasks to be done every 10 milliseconds

      LedOff(0);   // switch of LED (was switched on in Task1s)
      }

    if ( iTask25ms )
      {
      iTask25ms = FALSE;
      // tasks to be done every 25 milliseconds

      }

    if ( iTask50ms )
      {
      iTask50ms = FALSE;
      // tasks to be done every 50 milliseconds

      AdcTicker50ms();
      }

    if ( iTask100ms )
      {
      iTask100ms = FALSE;
      // tasks to be done every 100 milliseconds

      AdcTicker100ms();
      }

    if ( iTask200ms )
      {
      iTask200ms = FALSE;
      // tasks to be done every 200 milliseconds

      AdcTicker200ms();
      CommandProcessing();
      }


    if ( iTask500ms )
      {
      iTask500ms = FALSE;
      // tasks to be done every 500 milliseconds

      }

    if ( iTask1s )
      {
      iTask1s = FALSE;
      // tasks to be be done every second (about)

      LedOn(0);   // switch on LED once a second (alive)
      AdcTicker1s();
      }

    if ( iTaskClock1s )
      {
      iTaskClock1s = FALSE;
      // tasks to be done every real second

//      MaiUptimeInc();
      Seconds++;      // overflow in 136 years

      TraceTicker1s();
      }
    }
  }

/**************************************************************************//**
 * @brief  Setup RTC
 *****************************************************************************/
void RtcInit(void)
  {
  RTCC_Init_TypeDef rtcc = RTCC_INIT_DEFAULT;
  // Configure the compare settings
  RTCC_CCChConf_TypeDef compare = RTCC_CH_INIT_COMPARE_DEFAULT;

  /* Enabling clock to the interface of the low energy modules */
  CMU_ClockEnable(cmuClock_CORELE, true);

  /* Routing the LFRCO clock to the RTC */
  CMU_ClockSelectSet(cmuClock_LFE, cmuSelect_LFRCO);
  CMU_ClockEnable(cmuClock_RTCC, true);

  // Configure the RTCC settings
  rtcc.enable   = false;
  rtcc.presc = rtccCntPresc_1;
  rtcc.cntMode = _RTCC_CTRL_CNTMODE_NORMAL;
  rtcc.cntWrapOnCCV1 = true;

  /* Initialize the RTCC */
  RTCC_Init(&rtcc);

  // Initialise RTCC compare with a date, the date when interrupt will occur
  RTCC_ChannelInit(1, &compare);
  RTCC_ChannelCompareValueSet(1, (((32768 * RTCC_WAKEUP_MS) / 1000) - 1) );

  // Set channel 1 to cause an interrupt
  RTCC_IntEnable(RTCC_IEN_CC1);
  NVIC_SetPriority(RTCC_IRQn, 7);
  NVIC_ClearPendingIRQ(RTCC_IRQn);
  NVIC_EnableIRQ(RTCC_IRQn);

  // Start counter after all initialisations are complete
  RTCC_Enable(true);


  /* Setting up RTC for about 1ms */
  // - 1 is needed for 32 count cycles => 0.9765625ms
  // let it run for 33 counts (without - 1) gives 1.007080078ms
  // ToDo:  RTC_CompareSet(0, (RTC_FREQ / 1000) - 1);

  // wait for sync
  // while ( RTC->SYNCBUSY ) ;
  }

/******************************************************************************
void RTC_IRQHandler(void)
  Called every ~976us. Set flags for various periodically tasks.
  Times are multiples of 976us so iTask50ms is set every ~48.828ms and iTask1s
  every 0.97656s.

  In addition iTaskClock1s is set every real seconds.

  ToDo: Check if still true with JADE RTCC
******************************************************************************/
//void RTC_IRQHandler(void)
void RTCC_IRQHandler(void)
  {
  LedOn(2);
  // Read the interrupt source
  rtccFlag = RTCC_IntGet();

  // Clear interrupt flag
  RTCC_IntClear(rtccFlag);

  iTaskTicks++;

  // prevent iTaskTicks overflow as this will produce glitches
  if ( iTaskTicks > 2147482999 )  // wraps around every 2199028s = 610.84h = 25 days
    {                             // then there's a little error for iTaskClock1s
    iTaskTicks = 1000;
    }

  if ( (iTaskTicks % 5)  == 0 )    iTask5ms = TRUE;
  if ( (iTaskTicks % 10)  == 0 )   iTask10ms = TRUE;
  if ( (iTaskTicks % 25)  == 0 )   iTask25ms = TRUE;
  if ( (iTaskTicks % 50)  == 0 )   iTask50ms = TRUE;
  if ( (iTaskTicks % 100) == 0 )   iTask100ms = TRUE;
  if ( (iTaskTicks % 200) == 0 )   iTask200ms = TRUE;
  if ( (iTaskTicks % 500) == 0 )   iTask500ms = TRUE;
  if ( (iTaskTicks % 1000) == 0 )  iTask1s = TRUE;

  // for counting real seconds
  iMsTicks++;
  if ( (iMsTicks % 1024) == 0 )  iTaskClock1s = TRUE;
  }

/******************************************************************************
int StackCheck (void)
  Checks max usage of stack, searches from STACK_END upwards till other than
  initial value is found

  INITIALIZE_STACK must be defined for thumb_crt0.s in order to enable stack
  checking. ..\..\System\thumb_crt0.s is a modified version of Rowleys

Returns unused bytes on stack left or -1 in case of error
******************************************************************************/
int StackCheck (void)
  {
  int i;
  BYTE *pB;

  // _STACK_START and _STACK_END is reversed in startup code
  pB = (BYTE *) _STACK_START;

  TRACE_L3("MAI: stack from 0x%0X to 0x%0X (%d bytes)", _STACK_END, _STACK_START, _STACK_END - _STACK_START);
  if (_STACK_START == _STACK_END)
    {
    TRACE_L2("MAI: stack not initialized, unable to perform stack check");
    return -1;
    }

  for (i = 0; i < _STACK_END - _STACK_START; i++, pB++)
    {
    if (*pB != 0xCC)
      {
      TRACE_L3("MAI: stack - %d bytes free", i );
      return i;
      }
    }

  return -1;
  }




