Exemplo n.º 1
0
/*---------------------------------------------------------------------------*/
void
lpm_exit()
{
  if((REG(SYS_CTRL_PMCTL) & SYS_CTRL_PMCTL_PM3) == SYS_CTRL_PMCTL_PM0) {
    /* We either just exited PM0 or we were not sleeping in the first place.
     * We don't need to do anything clever */
    return;
  }

  LPM_STATS_ADD(REG(SYS_CTRL_PMCTL) & SYS_CTRL_PMCTL_PM3,
                RTIMER_NOW() - sleep_enter_time);

  /* Adjust the system clock, since it was not counting while we were sleeping
   * We need to convert sleep duration from rtimer ticks to clock ticks and
   * this will cost us some accuracy */
  clock_adjust((clock_time_t)
               ((RTIMER_NOW() - sleep_enter_time) / RTIMER_CLOCK_TICK_RATIO));

  /* Restore system clock to the 32 MHz XOSC */
  select_32_mhz_xosc();

  /* Restore PMCTL to PM0 for next pass */
  REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM0;

  /* Remember IRQ energest for next pass */
  ENERGEST_IRQ_SAVE(irq_energest);

  ENERGEST_ON(ENERGEST_TYPE_CPU);
  ENERGEST_OFF(ENERGEST_TYPE_LPM);
}
Exemplo n.º 2
0
Arquivo: lpm.c Projeto: 1uk3/contiki
/*---------------------------------------------------------------------------*/
void
lpm_exit()
{
  if((REG(SYS_CTRL_PMCTL) & SYS_CTRL_PMCTL_PM3) == SYS_CTRL_PMCTL_PM0) {
    /* We either just exited PM0 or we were not sleeping in the first place.
     * We don't need to do anything clever */
    return;
  }

  /*
   * When returning from PM1/2, the sleep timer value (used by RTIMER_NOW()) is
   * not up-to-date until a positive edge on the 32-kHz clock has been detected
   * after the system clock restarted. To ensure an updated value is read, wait
   * for a positive transition on the 32-kHz clock by polling the
   * SYS_CTRL_CLOCK_STA.SYNC_32K bit, before reading the sleep timer value.
   */
  while(REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SYNC_32K);
  while(!(REG(SYS_CTRL_CLOCK_STA) & SYS_CTRL_CLOCK_STA_SYNC_32K));

  LPM_STATS_ADD(REG(SYS_CTRL_PMCTL) & SYS_CTRL_PMCTL_PM3,
                RTIMER_NOW() - sleep_enter_time);

  /* Adjust the system clock, since it was not counting while we were sleeping
   * We need to convert sleep duration from rtimer ticks to clock ticks and
   * this will cost us some accuracy */
  clock_adjust((clock_time_t)
               ((RTIMER_NOW() - sleep_enter_time) / RTIMER_CLOCK_TICK_RATIO));

  /* Restore system clock to the 32 MHz XOSC */
  select_32_mhz_xosc();

  /* Restore PMCTL to PM0 for next pass */
  REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM0;

  /* Remember IRQ energest for next pass */
  ENERGEST_IRQ_SAVE(irq_energest);

  ENERGEST_ON(ENERGEST_TYPE_CPU);
  ENERGEST_OFF(ENERGEST_TYPE_LPM);
}
Exemplo n.º 3
0
/*---------------------------------------------------------------------------*/
void
lpm_enter()
{
  rtimer_clock_t lpm_exit_time;
  rtimer_clock_t duration;

  /*
   * If either the RF or the registered peripherals are on, dropping to PM1/2
   * would equal pulling the rug (32MHz XOSC) from under their feet. Thus, we
   * only drop to PM0. PM0 is also used if max_pm==0.
   */
  if((REG(RFCORE_XREG_FSMSTAT0) & RFCORE_XREG_FSMSTAT0_FSM_FFCTRL_STATE) != 0
     || !periph_permit_pm1() || max_pm == 0) {
    enter_pm0();

    /* We reach here when the interrupt context that woke us up has returned */
    return;
  }

  /*
   * Registered peripherals were off. Radio was off: Some Duty Cycling in place.
   * rtimers run on the Sleep Timer. Thus, if we have a scheduled rtimer
   * task, a Sleep Timer interrupt will fire and will wake us up.
   * Choose the most suitable PM based on anticipated deep sleep duration
   */
  lpm_exit_time = rtimer_arch_next_trigger();
  duration = lpm_exit_time - RTIMER_NOW();

  if(duration < DEEP_SLEEP_PM1_THRESHOLD || lpm_exit_time == 0) {
    /* Anticipated duration too short or no scheduled rtimer task. Use PM0 */
    enter_pm0();

    /* We reach here when the interrupt context that woke us up has returned */
    return;
  }

  /* If we reach here, we -may- (but may as well not) be dropping to PM1+. We
   * know the registered peripherals and RF are off so we can switch to the
   * 16MHz RCOSC. */
  select_16_mhz_rcosc();

  /*
   * Switching the System Clock from the 32MHz XOSC to the 16MHz RC OSC may
   * have taken a while. Re-estimate sleep duration.
   */
  duration = lpm_exit_time - RTIMER_NOW();

  if(duration < DEEP_SLEEP_PM1_THRESHOLD) {
    /*
     * oops... The clock switch took some time and now the remaining sleep
     * duration is too short. Restore the clock source to the 32MHz XOSC and
     * abort the LPM attempt altogether. We can't drop to PM0,
     * we need to yield to main() since we may have events to service now.
     */
    select_32_mhz_xosc();

    return;
  } else if(duration >= DEEP_SLEEP_PM2_THRESHOLD && max_pm == 2) {
    /* Long sleep duration and PM2 is allowed. Use it */
    REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM2;
  } else {
    /*
     * Anticipated duration too short for PM2 but long enough for PM1 and we
     * are allowed to use PM1
     */
    REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM1;
  }

  /* We are only interested in IRQ energest while idle or in LPM */
  ENERGEST_IRQ_RESTORE(irq_energest);
  ENERGEST_OFF(ENERGEST_TYPE_CPU);
  ENERGEST_ON(ENERGEST_TYPE_LPM);

  /* Remember the current time so we can adjust the clock when we wake up */
  sleep_enter_time = RTIMER_NOW();

  /*
   * Last chance to abort entering Deep Sleep.
   *
   * - There is the slight off-chance that a SysTick interrupt fired while we
   *   were trying to make up our mind. This may have raised an event.
   * - The Sleep Timer may have fired
   *
   * Check if there is still a scheduled rtimer task and check for pending
   * events before going to Deep Sleep
   */
  if(process_nevents() || rtimer_arch_next_trigger() == 0) {
    /* Event flag raised or rtimer inactive.
     * Turn on the 32MHz XOSC, restore PMCTL and abort */
    select_32_mhz_xosc();

    REG(SYS_CTRL_PMCTL) = SYS_CTRL_PMCTL_PM0;
  } else {
    /* All clear. Assert WFI and drop to PM1/2. This is now un-interruptible */
    assert_wfi();
  }

  /*
   * We reach here after coming back from PM1/2. The interrupt context that
   * woke us up has returned. lpm_exit() has run, it has switched the system
   * clock source back to the 32MHz XOSC, it has adjusted the system clock,
   * it has restored PMCTL and it has done energest housekeeping
   */
  return;
}