/**************************************************************************//**
 * @brief BURTC_IRQHandler
 * Interrupt Service Routine for RTC which is used as system tick counter in EM3
 *****************************************************************************/
void BURTC_IRQHandler(void)
{
  /* If using preemption, also force a context switch. */
#if (configUSE_PREEMPTION == 1)
  port_NVIC_INT_CTRL_REG = port_NVIC_PENDSVSET_BIT;
#endif /* (configUSE_PREEMPTION == 1) */
  /* Set BURTC interrupt to one system tick period*/
  BURTC_Enable(false);
  BURTC_CompareSet(0, ulTimerReloadValueForOneTick);
  /* Restart the counter */
  BURTC_CounterReset();
#if (configUSE_TICKLESS_IDLE == 1)
  /* Set flag that interrupt was made*/
  intTickFlag = true;
#endif /* (configUSE_TICKLESS_IDLE == 1) */
  /* Critical section which protect incrementing the tick*/
  ( void ) portSET_INTERRUPT_MASK_FROM_ISR();
  {
    xTaskIncrementTick();
  }
  portCLEAR_INTERRUPT_MASK_FROM_ISR(0);
  /* Clear interrupt */
  BURTC_IntClear(_RTC_IFC_MASK);
  BURTC_CounterReset();
}
예제 #2
0
/***************************************************************************//**
 * @brief Initialize BURTC
 *
 * @details
 *    Configures the BURTC peripheral.
 *
 * @note
 *   Before initialization, BURTC module must first be enabled by clearing the
 *   reset bit in the RMU, i.e.
 * @verbatim
 *   RMU_ResetControl(rmuResetBU, rmuResetModeClear);
 * @endverbatim
 *   Compare channel 0 must be configured outside this function, before
 *   initialization if enable is set to true. The counter will always be reset.
 *
 * @param[in] burtcInit
 *   Pointer to BURTC initialization structure
 ******************************************************************************/
void BURTC_Init(const BURTC_Init_TypeDef *burtcInit)
{
  uint32_t ctrl;
  uint32_t presc;

  /* Check initializer structure integrity */
  EFM_ASSERT(burtcInit != (BURTC_Init_TypeDef *) 0);
  /* Clock divider must be between 1 and 128, really on the form 2^n */
  EFM_ASSERT((burtcInit->clkDiv >= 1) && (burtcInit->clkDiv <= 128));
  /* Ignored compare bits during low power operation must be less than 7 */
  /* Note! Giant Gecko revision C errata, do NOT use LPCOMP=7 */
  EFM_ASSERT(burtcInit->lowPowerComp <= 6);
  /* You cannot enable the BURTC if mode is set to disabled */
  EFM_ASSERT((burtcInit->enable == false) ||
             ((burtcInit->enable == true)
              && (burtcInit->mode != burtcModeDisable)));
  /* Low power mode is only available with LFRCO or LFXO as clock source */
  EFM_ASSERT((burtcInit->clkSel != burtcClkSelULFRCO)
             || ((burtcInit->clkSel == burtcClkSelULFRCO)
                  && (burtcInit->lowPowerMode == burtcLPDisable)));

  /* Calculate prescaler value from clock divider input */
  /* Note! If clock select (clkSel) is ULFRCO, a clock divisor (clkDiv) of
     value 1 will select a 2kHz ULFRCO clock, while any other value will
     select a 1kHz ULFRCO clock source. */
  presc = divToLog2(burtcInit->clkDiv);

  /* Make sure all registers are updated simultaneously */
  if (burtcInit->enable)
  {
    BURTC_FreezeEnable(true);
  }

  /* Modification of LPMODE register requires sync with potential ongoing
   * register updates in LF domain. */
  regSync(BURTC_SYNCBUSY_LPMODE);

  /* Configure low power mode */
  BURTC->LPMODE = (uint32_t) (burtcInit->lowPowerMode);

  /* New configuration */
  ctrl = (BURTC_CTRL_RSTEN
          | (burtcInit->mode)
          | (burtcInit->debugRun << _BURTC_CTRL_DEBUGRUN_SHIFT)
          | (burtcInit->compare0Top << _BURTC_CTRL_COMP0TOP_SHIFT)
          | (burtcInit->lowPowerComp << _BURTC_CTRL_LPCOMP_SHIFT)
          | (presc << _BURTC_CTRL_PRESC_SHIFT)
          | (burtcInit->clkSel)
          | (burtcInit->timeStamp << _BURTC_CTRL_BUMODETSEN_SHIFT));

  /* Clear interrupts */
  BURTC_IntClear(0xFFFFFFFF);

  /* Set new configuration */
  BURTC->CTRL = ctrl;

  /* Enable BURTC and counter */
  if (burtcInit->enable)
  {
    /* To enable BURTC counter, we need to disable reset */
    BURTC_Enable(true);

    /* Clear freeze */
    BURTC_FreezeEnable(false);
  }
}
/**************************************************************************//**
 * @brief vPortSetupTimerInterrupt
 * Override the default definition of vPortSuppressTicksAndSleep() that is weakly
 * defined in the FreeRTOS Cortex-M3 port layer layer
 *****************************************************************************/
void vPortSuppressTicksAndSleep(portTickType xExpectedIdleTime)
{
  unsigned long ulReloadValue, ulCompleteTickPeriods;
  portTickType  xModifiableIdleTime;
  /* Make sure the SysTick reload value does not overflow the counter. */
  if (xExpectedIdleTime > xMaximumPossibleSuppressedTicks)
  {
    xExpectedIdleTime = xMaximumPossibleSuppressedTicks;
  }

  /* Calculate the reload value required to wait xExpectedIdleTime
   * tick periods.  -1 is used because this code will execute part way
   * through one of the tick periods, and the fraction of a tick period is
   * accounted for later. */
  ulReloadValue = (ulTimerReloadValueForOneTick * (xExpectedIdleTime ));
  if (ulReloadValue > ulStoppedTimerCompensation)
  {
    ulReloadValue -= ulStoppedTimerCompensation;
  }

  /* Stop the SysTick momentarily.  The time the SysTick is stopped for
   * is accounted for as best it can be, but using the tickless mode will
   * inevitably result in some tiny drift of the time maintained by the
   * kernel with respect to calendar time. */
  /* Stop the RTC clock*/
  BURTC_Enable(false);
  /* Enter a critical section but don't use the taskENTER_CRITICAL()
   * method as that will mask interrupts that should exit sleep mode. */
  INT_Disable();
  /* The tick flag is set to false before sleeping.  If it is true when sleep
   * mode is exited then sleep mode was probably exited because the tick was
   * suppressed for the entire xExpectedIdleTime period. */
  intTickFlag = false;
  /* If a context switch is pending or a task is waiting for the scheduler
   * to be unsuspended then abandon the low power entry. */
  if (eTaskConfirmSleepModeStatus() == eAbortSleep)
  {
    BURTC_Enable(true);
    /* Re-enable interrupts */
    INT_Enable();
  }
  else
  {
    /* Set the new reload value. */
    ulReloadValue -= BURTC_CounterGet();
    BURTC_CompareSet(0, ulReloadValue);
    /* Restart the counter*/
    BURTC_CounterReset();
    /* Sleep until something happens.  configPRE_SLEEP_PROCESSING() can
     * set its parameter to 0 to indicate that its implementation contains
     * its own wait for interrupt or wait for event instruction, and so wfi
     * should not be executed again.  However, the original expected idle
     * time variable must remain unmodified, so a copy is taken. */
    xModifiableIdleTime = xExpectedIdleTime;
    configPRE_SLEEP_PROCESSING(xModifiableIdleTime);
    if (xModifiableIdleTime > 0)
    {
      SLEEP_Sleep();
      __DSB();
      __ISB();
    }
    configPOST_SLEEP_PROCESSING(xExpectedIdleTime);
    /* Stop SysTick.  Again, the time the SysTick is stopped for is
     * accounted for as best it can be, but using the tickless mode will
     * inevitably result in some tiny drift of the time maintained by the
     * kernel with respect to calendar time. */
    BURTC_Enable(false);
    /* Re-enable interrupts - see comments above __disable_interrupt()
     * call above. */
    INT_Enable();
    if (intTickFlag != false)
    {
      /* The tick interrupt has already executed,
       * Reset the alarm value with whatever remains of this tick period. */
      BURTC_CompareSet(0, TIMER_CAPACITY & (ulTimerReloadValueForOneTick - BURTC_CounterGet()));
      /* The tick interrupt handler will already have pended the tick
       * processing in the kernel.  As the pending tick will be
       * processed as soon as this function exits, the tick value
       * maintained by the tick is stepped forward by one less than the
       * time spent waiting. */
      ulCompleteTickPeriods = xExpectedIdleTime - 1UL;
    }
    else
    {
      /* Some other interrupt than system tick ended the sleep.
       * Calculate how many tick periods passed while the processor
       * was waiting */
      ulCompleteTickPeriods = BURTC_CounterGet() / ulTimerReloadValueForOneTick;

      /* The reload value is set to whatever fraction of a single tick
       * period remains. */
      if (ulCompleteTickPeriods == 0)
      {
        ulReloadValue = ulTimerReloadValueForOneTick - BURTC_CounterGet();
      }
      else
      {
        ulReloadValue = BURTC_CounterGet() - (ulCompleteTickPeriods * ulTimerReloadValueForOneTick);
      }
      BURTC_CompareSet(0, ulReloadValue);
    }
    /* Restart the RTCounter*/
    BURTC_CounterReset();
    /* The tick forward by the number of tick periods that
     * remained in a low power state. */
    vTaskStepTick(ulCompleteTickPeriods);
  }
}
예제 #4
0
파일: main.c 프로젝트: leblebitozu/bspacm
void main ()
{
  uint32_t lfa_Hz;
  uint16_t reset_cause;
  int sleep_mode = 0;
  uint32_t burtc_ctrl;

  CHIP_Init();
  reset_cause = RMU->RSTCAUSE;
  RMU_ResetCauseClear();
#if ! defined(_EFM32_ZERO_FAMILY)
  BSPACM_CORE_BITBAND_PERIPH(RMU->CTRL, _RMU_CTRL_BURSTEN_SHIFT) = 0;
#endif
  SystemCoreClockUpdate();
  vBSPACMledConfigure();

  setvbuf(stdout, NULL, _IONBF, 0);

  BSPACM_CORE_ENABLE_INTERRUPT();

  printf("\n" __DATE__ " " __TIME__ "\n");
  printf("System clock %lu Hz\n", SystemCoreClock);
  {
    int i = sizeof(xResetCause)/sizeof(*xResetCause);

    printf("Reset cause [%04x]:", reset_cause);
    while (0 <= --i) {
      const sResetCause * const rcp = xResetCause + i;
      if (rcp->value == (reset_cause & rcp->mask)) {
        printf(" %s", rcp->name);
      }
    }
    printf("\nRMU CTRL %lx\n", RMU->CTRL);
  }

  /* Enable low-energy support. */
  CMU_ClockEnable(cmuClock_CORELE, true);
  BURTC_Enable(true);

  if (MAGIC != retained_state->magic) {
    memset(retained_state, 0, sizeof(*retained_state));
    retained_state->magic = MAGIC;
    printf("Resetting retained state\n");
  }
  ++retained_state->boots;
  printf("Boot count %lu\n", retained_state->boots);

  printf("BURTC clock source: ");
#if (WITH_ULFRCO - 0)
  /* Use ULFRCO, which enables EM4 wakeup but is pretty inaccurate. */
  printf("ULFRCO\n");
  /* NB: DIV2 means 1 kHz instead of 2 kHz */
  burtc_ctrl = BURTC_CTRL_CLKSEL_ULFRCO | BURTC_CTRL_PRESC_DIV2;
  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_ULFRCO);
  lfa_Hz = CMU_ClockFreqGet(cmuClock_LFA);
  {
    EMU_EM4Init_TypeDef cfg = {
      .lockConfig = 1,
      .osc = EMU_EM4CONF_OSC_ULFRCO,
      .buRtcWakeup = 1,
      .vreg = 1,
    };
    EMU_EM4Init(&cfg);
  }
#elif (WITH_LFRCO - 0)
  printf("LFRCO/32\n");
  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
  burtc_ctrl = BURTC_CTRL_CLKSEL_LFRCO | BURTC_CTRL_PRESC_DIV32;
  lfa_Hz = CMU_ClockFreqGet(cmuClock_LFA) / 32;
#else
  printf("LFXO/32\n");
  CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
  burtc_ctrl = BURTC_CTRL_CLKSEL_LFXO | BURTC_CTRL_PRESC_DIV32;
  lfa_Hz = CMU_ClockFreqGet(cmuClock_LFA) / 32;
#endif /* LFA source */
  printf("LFA clock at %lu Hz ; wake every %u seconds\n", lfa_Hz, WAKE_INTERVAL_S);

  /* Initialize BURTC. */
  if (! (RMU_RSTCAUSE_EM4WURST & reset_cause)) {
    printf("Initializing BURTC\n");
    BURTC->FREEZE = BURTC_FREEZE_REGFREEZE;
    BURTC->LPMODE = BURTC_LPMODE_LPMODE_DISABLE;
    BURTC->CTRL = burtc_ctrl /* CLKSEL + PRESC */
      | BURTC_CTRL_RSTEN
      | BURTC_CTRL_MODE_EM4EN
      ;
    BURTC->COMP0 = WAKE_INTERVAL_S * lfa_Hz;
    BURTC->IEN = BURTC_IF_COMP0;
    BURTC->CTRL &= ~BURTC_CTRL_RSTEN;
    BURTC->FREEZE = 0;
  } else {
    while (BURTC_SYNCBUSY_COMP0 & BURTC->SYNCBUSY) {
    }
    BURTC->COMP0 += WAKE_INTERVAL_S * lfa_Hz;
  }
  BURTC->IFC = BURTC_IFC_COMP0;
  NVIC_EnableIRQ(BURTC_IRQn);

  printf("BURTC CTRL %lx IEN %lx\n", BURTC->CTRL, BURTC->IEN);
  (void)iBSPACMperiphUARTflush(hBSPACMdefaultUART, eBSPACMperiphUARTfifoState_TX);
  while (1) {
    static const sBSPACMperiphUARTconfiguration cfg = { .speed_baud = 0 };
    printf("Sleeping in mode %u, %lu to %lu rtc_if %x or %lx\n", sleep_mode, BURTC->CNT, BURTC->COMP0, burtc_if, BURTC->IF);
    BSPACM_CORE_DISABLE_INTERRUPT();
    do {
      (void)iBSPACMperiphUARTflush(hBSPACMdefaultUART, eBSPACMperiphUARTfifoState_TX);
      hBSPACMperiphUARTconfigure(hBSPACMdefaultUART, 0);
      switch (sleep_mode) {
        case 0:
          while (! (BURTC_IF_COMP0 & BURTC->IF)) {
          }
          ++sleep_mode;
          break;
        case 1:
          EMU_EnterEM1();
          ++sleep_mode;
          break;
        case 2:
          EMU_EnterEM2(true);

          SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
          if (cmuSelect_ULFRCO == CMU_ClockSelectGet(cmuClock_LFA)) {
            ++sleep_mode;
          } else {
            sleep_mode = 0;
          }
          break;
        case 3:
          EMU_EnterEM3(true);
          SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
          if (cmuSelect_ULFRCO == CMU_ClockSelectGet(cmuClock_LFA)) {
            ++sleep_mode;
          } else {
            sleep_mode = 0;
          }
          break;
        case 4:
          EMU_EnterEM4();
          sleep_mode = 0;
          break;
      }
    } while (0);
    BSPACM_CORE_ENABLE_INTERRUPT();
    hBSPACMperiphUARTconfigure(hBSPACMdefaultUART, &cfg);
    while (BURTC_SYNCBUSY_COMP0 & BURTC->SYNCBUSY) {
    }
    BURTC->COMP0 += WAKE_INTERVAL_S * lfa_Hz;
    /* Giant Gecko
     *  Source    EM0    EM1    EM2     EM3    EM4
     * ULFRCO    2.5m   1.1m   622n    622n   450n
     */
  }

}