Esempio n. 1
0
/**************************************************************************************************
 * @fn          halDriverBegPM
 *
 * @brief       This function is called before entering PM so that drivers can be put into their
 *              lowest power states.
 *
 * input parameters
 *
 * None.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
void halDriverBegPM(void)
{
#if ((defined HAL_LED) && (HAL_LED == TRUE))
    HalLedEnterSleep();
#endif
#if ((defined HAL_KEY) && (HAL_KEY == TRUE))
    HalKeyEnterSleep();
#endif
#if ((defined HAL_I2C) && (HAL_I2C == TRUE))
    HalI2CEnterSleep();
#endif
}
Esempio n. 2
0
/**************************************************************************************************
 * @fn          halSleep
 *
 * @brief       This function is called from the OSAL task loop using and existing OSAL
 *              interface.  It sets the low power mode of the MAC and MCU.
 *
 * input parameters
 *
 * @param       osal_timeout - Next OSAL timer timeout.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
void halSleep( uint32 osal_timeout )
{
  uint32        timeout;
  uint32        macTimeout;

  /* Avoid using critical section macros because low power mode macros.
   * Assert if we enter sleep without interrupt enabled.
   */
  HAL_ASSERT( HAL_INTERRUPTS_ARE_ENABLED() );

  /* Don't sleep if time too short or if a key is pressed */
  if ( osal_timeout && (osal_timeout < MIN_SLEEP_TIME) )
    return;

  /* initialize the accumulated sleep time */
  halAccumulatedSleepTime = 0;

  /* get next OSAL timer expiration converted to 320 usec units */
  timeout = HAL_SLEEP_MS_TO_320US(osal_timeout);
  if (timeout == 0)
  {
    timeout = MAC_PwrNextTimeout();
  }
  else
  {
    /* get next MAC timer expiration */
    macTimeout = MAC_PwrNextTimeout();

    /* get lesser of two timeouts */
    if ((macTimeout != 0) && (macTimeout < timeout))
    {
      timeout = macTimeout;
    }
  }

  /* Adjust the wakup time so that the radio is awake in time
   * for beacon transmission.
   */
  if ( timeout > HAL_BEACON_ENABLE_EARLY_WAKEUP() )
  {
    timeout -= HAL_BEACON_ENABLE_EARLY_WAKEUP();
  }

  /* HAL_SLEEP_PM2 is entered only if the timeout is zero */
  halPwrMgtMode = (timeout == 0)? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;

  if ( (timeout > HAL_SLEEP_MS_TO_320US(MIN_SLEEP_TIME)) || (timeout == 0) )
  {

    HAL_SLEEP_DISABLE_INTERRUPTS();

    /* always use "deep sleep" to turn off radio VREG on MSP430 */
    if (MAC_PwrOffReq(MAC_PWR_SLEEP_DEEP) == MAC_SUCCESS)
    {
      /* Shut down LED */
      HalLedEnterSleep();

      while ( (HAL_SLEEP_MS_TO_320US(halAccumulatedSleepTime) < timeout) || (timeout == 0) )
      {
        if (timeout != 0)
        {
          /* set sleep timer, timeout is adjusted for the next time */
          halSleepSetTimer( timeout );

          /* set up sleep timer interrupt */
          HAL_SLEEP_TIMER_ENABLE_INT();
        }
        else
        {
          /* Halt the timer */
          HAL_MAC_SLEEP_TIMER_HALT();
        }

        HalKeyEnterSleep();

        /* set MSP430 power mode, global interrupt will be enabled in the macro */
        HAL_SLEEP_SET_POWER_MODE(halPwrMgtMode);
        /* wake up from sleep in ISR */

        HAL_SLEEP_DISABLE_INTERRUPTS();

        /* disable sleep timer interrupt */
        HAL_SLEEP_TIMER_DISABLE_INT();

        if (timeout != 0)
        {
          /* Calculate timer elapsed only if timer sleep */
          halAccumulatedSleepTime += halMacTimerElapsed( &timeout );
        }
        else
        {
          /* Restart timer */
          HAL_MAC_SLEEP_TIMER_RESTART();
        }

        /* Process keyboard "wake-up" interrupt, exit while loop if key interrupt */
        if ( HalKeyExitSleep() || timeout == 0 )
        {
          break;
        }
      }

      /* Restart the LED */
      HalLedExitSleep();

      /* power on the MAC; blocks until completion.
       * the MAC timer will be turned on if off.
       */
      MAC_PwrOnReq();

      /* Update MAC timer count for OSAL */
      macMcuPrecisionCountSleepUpdate( halAccumulatedSleepTime );
      
      HAL_ENABLE_INTERRUPTS();
    }
    else
    {
      HAL_ENABLE_INTERRUPTS();
    }
  }
}
Esempio n. 3
0
/**************************************************************************************************
 * @fn          halSleep
 *
 * @brief       This function is called from the OSAL task loop using and existing OSAL
 *              interface.  It sets the low power mode of the MAC and the CC2530.
 *
 * input parameters
 *
 * @param       osal_timeout - Next OSAL timer timeout.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
void halSleep( uint16 osal_timeout )
{
  uint32        timeout;
  uint32        macTimeout = 0;

  /* get next OSAL timer expiration converted to 320 usec units */
  timeout = HAL_SLEEP_MS_TO_320US(osal_timeout);
  if (timeout == 0)
  {
    timeout = MAC_PwrNextTimeout();
  }
  else
  {
    /* get next MAC timer expiration */
    macTimeout = MAC_PwrNextTimeout();

    /* get lesser of two timeouts */
    if ((macTimeout != 0) && (macTimeout < timeout))
    {
      timeout = macTimeout;
    }
  }

  /* HAL_SLEEP_PM2 is entered only if the timeout is zero and
   * the device is a stimulated device.
   */
  halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;

  /* DEEP sleep can only be entered when zgPollRate == 0.
   * This is to eliminate any possibility of entering PM3 between
   * two network timers.
   */
#if ZG_BUILD_ENDDEVICE_TYPE && defined (NWK_AUTO_POLL)
  if ((timeout > HAL_SLEEP_MS_TO_320US(PM_MIN_SLEEP_TIME)) ||
      (timeout == 0 && zgPollRate == 0))
#else
  if ((timeout > HAL_SLEEP_MS_TO_320US(PM_MIN_SLEEP_TIME)) ||
      (timeout == 0))
#endif
  {
    halIntState_t ien0, ien1, ien2;

    HAL_ASSERT(HAL_INTERRUPTS_ARE_ENABLED());
    HAL_DISABLE_INTERRUPTS();

    /* always use "deep sleep" to turn off radio VREG on CC2530 */
    if (halSleepPconValue != 0 && MAC_PwrOffReq(MAC_PWR_SLEEP_DEEP) == MAC_SUCCESS)
    {
      /* The PCON value is not zero. There is no interrupt overriding the 
       * sleep decision. Also, the radio granted the sleep request.
       */

#if ((defined HAL_KEY) && (HAL_KEY == TRUE))
      /* get peripherals ready for sleep */
      HalKeyEnterSleep();
#endif

#ifdef HAL_SLEEP_DEBUG_LED
      HAL_TURN_OFF_LED3();
#else
      /* use this to turn LEDs off during sleep */
      HalLedEnterSleep();
#endif

      /* enable sleep timer interrupt */
      if (timeout != 0)
      {
        if (timeout > HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME ))
        {
          timeout -= HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME );
          halSleepSetTimer(HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME ));
        }
        else
        {
          /* set sleep timer */
          halSleepSetTimer(timeout);
        }

        /* set up sleep timer interrupt */
        HAL_SLEEP_TIMER_CLEAR_INT();
        HAL_SLEEP_TIMER_ENABLE_INT();
      }

#ifdef HAL_SLEEP_DEBUG_LED
      if (halPwrMgtMode == CC2530_PM1)
      {
        HAL_TURN_ON_LED1();
      }
      else
      {
        HAL_TURN_OFF_LED1();
      }
#endif

      if (ZNP_CFG1_UART == znpCfg1)
      {
        HalUARTSuspend();
      }

      /* Prep CC2530 power mode */
      HAL_SLEEP_PREP_POWER_MODE(halPwrMgtMode);

      /* save interrupt enable registers and disable all interrupts */
      HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2);
      HAL_ENABLE_INTERRUPTS();

      /* set CC2530 power mode, interrupt is disabled after this function
       * Note that an ISR (that could wake up from power mode) which runs
       * between the previous instruction enabling interrupts and before
       * power mode is set would switch the halSleepPconValue so that
       * power mode shall not be entered in such a case. 
       */
      HAL_SLEEP_SET_POWER_MODE();

      /* Disable interrupt immediately */
      HAL_DISABLE_INTERRUPTS();

      /* restore interrupt enable registers */
      HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2);

      /* disable sleep timer interrupt */
      HAL_SLEEP_TIMER_DISABLE_INT();

      if (ZNP_CFG1_UART == znpCfg1)
      {
        HalUARTResume();
      }

#ifdef HAL_SLEEP_DEBUG_LED
      HAL_TURN_ON_LED3();
#else
      /* use this to turn LEDs back on after sleep */
      HalLedExitSleep();
#endif

#if ((defined HAL_KEY) && (HAL_KEY == TRUE))
      /* handle peripherals */
      (void)HalKeyExitSleep();
#endif

      /* power on the MAC; blocks until completion */
      MAC_PwrOnReq();

      HAL_ENABLE_INTERRUPTS();

      /* For CC2530, T2 interrupt won’t be generated when the current count is greater than
       * the comparator. The interrupt is only generated when the current count is equal to
       * the comparator. When the CC2530 is waking up from sleep, there is a small window
       * that the count may be grater than the comparator, therefore, missing the interrupt.
       * This workaround will call the T2 ISR when the current T2 count is greater than the
       * comparator. The problem only occurs when POWER_SAVING is turned on, i.e. the 32KHz
       * drives the chip in sleep and SYNC start is used.
       */
      macMcuTimer2OverflowWorkaround();
    }
    else
    {
      /* Sleep request is not granted. Check PCON value to see why the sleep is not granted. */
      if (halSleepPconValue == 0)
      {
        /* An interrupt may have changed the sleep decision. Do not sleep at all. Turn on 
         * the interrupt, exit normally, and the next sleep will be allowed.
         */
        HAL_ENABLE_INTERRUPTS();
      }
      else
      {
        /* PCON value is okay but Radio cannot enter power mode hence just put CPU to idle mode.
         * Interrupt will be enabled in halSleepEnterIdleMode().
         */
        halSleepEnterIdleMode(timeout);
      }
    }
  }
  else if (timeout > PM_MIN_IDLE_TIME)
  {
    /* Timeout is too close to enter power mode. Try idle mode. */
    HAL_DISABLE_INTERRUPTS();

    /* Interrupt will be enabled in halSleepEnterIdleMode(). */
    halSleepEnterIdleMode(timeout);
  }
}
Esempio n. 4
0
/*******************************************************************************
 * @fn          halSleep
 *
 * @brief       This function is called from the OSAL task loop using and
 *              existing OSAL interface.  It sets the low power mode of the LL
 *              and the CC2540.
 *
 * input parameters
 *
 * @param       osal_timeout - Next OSAL timer timeout, in msec.
 *
 * output parameters
 *
 * @param       None.
 *
 * @return      None.
 */
void halSleep( uint32 osal_timeout )
{
  uint32 timeout;
  uint32 llTimeout;
  uint32 sleepTimer;

  halDriverBegPM();

#ifdef DEBUG_GPIO
  // TEMP
  P1_0 = 1;
#endif // DEBUG_GPIO

  if (osal_timeout > MAX_16BIT_TIMEOUT)
  {
    osal_timeout = MAX_16BIT_TIMEOUT;
  }

  // get LL timeout value already converted to 32kHz ticks
  LL_TimeToNextRfEvent( &sleepTimer, &llTimeout );

  // check if no OSAL timeout
  // Note: If the next wake event is due to an OSAL timeout, then wakeForRF
  //       will already be FALSE, and the call to LL_TimeToNExtRfEvent will
  //       already have taken a snapshot of the Sleep Timer.
  if (osal_timeout == 0)
  {
    // use common variable
    timeout = llTimeout;

    // check if there's time before the next radio event
    // Note: Since the OSAL timeout is zero, then if the radio timeout is
    //       not zero, the next wake (if one) will be due to the radio event.
    wakeForRF = (timeout != 0) ? TRUE : FALSE;
  }
  else // OSAL timeout is non-zero
  {
    // convet OSAL timeout to sleep time
    // Note: Could be early by one 32kHz timer tick due to rounding.
    timeout = HAL_SLEEP_MS_TO_32KHZ( osal_timeout );

    // so check time to radio event is non-zero, and if so, use shorter value
    if ((llTimeout != 0) && (llTimeout < timeout))
    {
      // use common variable
      timeout = llTimeout;

      // the next ST wake time is due to radio
      wakeForRF = TRUE;
    }
    else // OSAL timeout will be used to wake
    {
      // so take a snapshot of the sleep timer for sleep based on OSAL timeout
      sleepTimer = halSleepReadTimer();

      // the next ST wake time is not due to radio
      wakeForRF = FALSE;
    }
  }

  // HAL_SLEEP_PM3 is entered only if the timeout is zero
  halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;

#ifdef DEBUG_GPIO
  // TEMP
  P1_0 = 0;
#endif // DEBUG_GPIO

  // check if sleep should be entered
  if ( (timeout > PM_MIN_SLEEP_TIME) || (timeout == 0) )
  {
    halIntState_t ien0, ien1, ien2;

#ifdef DEBUG_GPIO
    // TEMP
    P1_0 = 1;
#endif // DEBUG_GPIO

    HAL_ASSERT( HAL_INTERRUPTS_ARE_ENABLED() );
    HAL_DISABLE_INTERRUPTS();

    // check if radio allows sleep, and if so, preps system for shutdown
    if ( LL_PowerOffReq(halPwrMgtMode) == LL_SLEEP_REQUEST_ALLOWED )
    {
#if ((defined HAL_KEY) && (HAL_KEY == TRUE))
      // get peripherals ready for sleep
      HalKeyEnterSleep();
#endif // ((defined HAL_KEY) && (HAL_KEY == TRUE))

#ifdef HAL_SLEEP_DEBUG_LED
      HAL_TURN_OFF_LED3();
#else
      // use this to turn LEDs off during sleep
      HalLedEnterSleep();
#endif // HAL_SLEEP_DEBUG_LED

      // enable sleep timer interrupt
      if (timeout != 0)
      {
        // check if the time to next wake event is greater than max sleep time
        if (timeout > MAX_SLEEP_TIME )
        {
          // it is, so limit to max allowed sleep time (~510s)
          halSleepSetTimer( sleepTimer, MAX_SLEEP_TIME );
        }
        else // not more than allowed sleep time
        {
          // so set sleep time to actual amount
          halSleepSetTimer( sleepTimer, timeout );
        }
      }

      // prep CC254x power mode
      HAL_SLEEP_PREP_POWER_MODE(halPwrMgtMode);

      // save interrupt enable registers and disable all interrupts
      HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2);
      HAL_ENABLE_INTERRUPTS();

#ifdef DEBUG_GPIO
      // TEMP
      P1_0 = 0;
#endif // DEBUG_GPIO

      // set CC254x power mode; interrupts are disabled after this function
      // Note: Any ISR that could wake the device from sleep needs to use
      //       CLEAR_SLEEP_MODE(), which will clear the halSleepPconValue flag
      //       used to enter sleep mode, thereby preventing the device from
      //       missing this interrupt.
      HAL_SLEEP_SET_POWER_MODE();

#ifdef DEBUG_GPIO
      // TEMP
      P1_0 = 1;
#endif // DEBUG_GPIO

      // check if ST interrupt pending, and if not, clear wakeForRF flag
      // Note: This is needed in case we are not woken by the sleep timer but
      //       by for example a key press. In this case, the flag has to be
      //       cleared as we are not just before a radio event.
      // Note: There is the possiblity that we may wake from an interrupt just
      //       before the sleep timer would have woken us just before a radio
      //       event, in which case power will be wasted as we will probably
      //       enter this routine one or more times before the radio event.
      //       However, this is presumably unusual, and isn't expected to have
      //       much impact on average power consumption.
      if ( (wakeForRF == TRUE) && !(IRCON & 0x80) )
      {
        wakeForRF = FALSE;
      }

      // restore interrupt enable registers
      HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2);

      // power on the LL; blocks until completion
      // Note: This is done here to ensure the 32MHz XOSC has stablized, in
      //       case it is needed (e.g. the ADC is used by the joystick).
      LL_PowerOnReq( (halPwrMgtMode == CC2540_PM3), wakeForRF );

#ifdef HAL_SLEEP_DEBUG_LED
      HAL_TURN_ON_LED3();
#else //!HAL_SLEEP_DEBUG_LED
      // use this to turn LEDs back on after sleep
      HalLedExitSleep();
#endif // HAL_SLEEP_DEBUG_LED

#if ((defined HAL_KEY) && (HAL_KEY == TRUE))
      // handle peripherals
      (void)HalKeyExitSleep();
#endif // ((defined HAL_KEY) && (HAL_KEY == TRUE))
    }

    HAL_ENABLE_INTERRUPTS();
  }

  halDriverEndPM();

#ifdef DEBUG_GPIO
      // TEMP
      P1_0 = 0;
#endif // DEBUG_GPIO

  return;
}
Esempio n. 5
0
/**************************************************************************************************
 * @fn          halSleep
 *
 * @brief       This function is called from the OSAL task loop using and existing OSAL
 *              interface.  It sets the low power mode of the MAC and the CC2530.
 *
 * input parameters
 *
 * @param       osal_timeout - Next OSAL timer timeout.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
void halSleep( uint16 osal_timeout )
{
  uint32        timeout;
  uint32        macTimeout = 0;

  halAccumulatedSleepTime = 0;

  /* get next OSAL timer expiration converted to 320 usec units */
  timeout = HAL_SLEEP_MS_TO_320US(osal_timeout);
  if (timeout == 0)
  {
    timeout = MAC_PwrNextTimeout();
  }
  else
  {
    /* get next MAC timer expiration */
    macTimeout = MAC_PwrNextTimeout();

    /* get lesser of two timeouts */
    if ((macTimeout != 0) && (macTimeout < timeout))
    {
      timeout = macTimeout;
    }
  }

  /* HAL_SLEEP_PM2 is entered only if the timeout is zero and
   * the device is a stimulated device.
   */
  halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;

  /* DEEP sleep can only be entered when zgPollRate == 0.
   * This is to eliminate any possibility of entering PM3 between
   * two network timers.
   */
#if ZG_BUILD_ENDDEVICE_TYPE && defined (NWK_AUTO_POLL)
  if ((timeout > HAL_SLEEP_MS_TO_320US(PM_MIN_SLEEP_TIME)) ||
      (timeout == 0 && zgPollRate == 0))
#else
  if ((timeout > HAL_SLEEP_MS_TO_320US(PM_MIN_SLEEP_TIME)) ||
      (timeout == 0))
#endif
  {
    halIntState_t ien0, ien1, ien2;

    HAL_ASSERT(HAL_INTERRUPTS_ARE_ENABLED());
    HAL_DISABLE_INTERRUPTS();

    /* always use "deep sleep" to turn off radio VREG on CC2530 */
    if (MAC_PwrOffReq(MAC_PWR_SLEEP_DEEP) == MAC_SUCCESS)
    {
#if ((defined HAL_KEY) && (HAL_KEY == TRUE))
      /* get peripherals ready for sleep */
      HalKeyEnterSleep();
#endif

#ifdef HAL_SLEEP_DEBUG_LED
      HAL_TURN_OFF_LED3();
#else
      /* use this to turn LEDs off during sleep */
      HalLedEnterSleep();
#endif

      /* enable sleep timer interrupt */
      if (timeout != 0)
      {
        if (timeout > HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME ))
        {
          timeout -= HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME );
          halSleepSetTimer(HAL_SLEEP_MS_TO_320US( MAX_SLEEP_TIME ));
        }
        else
        {
          /* set sleep timer */
          halSleepSetTimer(timeout);
        }

        /* set up sleep timer interrupt */
        HAL_SLEEP_TIMER_CLEAR_INT();
        HAL_SLEEP_TIMER_ENABLE_INT();
      }

#ifdef HAL_SLEEP_DEBUG_LED
      if (halPwrMgtMode == CC2530_PM1)
      {
        HAL_TURN_ON_LED1();
      }
      else
      {
        HAL_TURN_OFF_LED1();
      }
#endif

      /* save interrupt enable registers and disable all interrupts */
      HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2);
      HAL_ENABLE_INTERRUPTS();

      /* set CC2530 power mode, interrupt is disabled after this function */
      HAL_SLEEP_SET_POWER_MODE(halPwrMgtMode);

      /* the interrupt is disabled - see halSetSleepMode() */

      /* restore interrupt enable registers */
      HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2);

      /* disable sleep timer interrupt */
      HAL_SLEEP_TIMER_DISABLE_INT();

      /* Calculate timer elasped */
      halAccumulatedSleepTime += (HalTimerElapsed() / TICK_COUNT);


#ifdef HAL_SLEEP_DEBUG_LED
      HAL_TURN_ON_LED3();
#else
      /* use this to turn LEDs back on after sleep */
      HalLedExitSleep();
#endif

#if ((defined HAL_KEY) && (HAL_KEY == TRUE))
      /* handle peripherals */
      (void)HalKeyExitSleep();
#endif

      /* power on the MAC; blocks until completion */
      MAC_PwrOnReq();

      HAL_ENABLE_INTERRUPTS();

      /* For CC2530, T2 interrupt won’t be generated when the current count is greater than
       * the comparator. The interrupt is only generated when the current count is equal to
       * the comparator. When the CC2530 is waking up from sleep, there is a small window
       * that the count may be grater than the comparator, therefore, missing the interrupt.
       * This workaround will call the T2 ISR when the current T2 count is greater than the
       * comparator. The problem only occurs when POWER_SAVING is turned on, i.e. the 32KHz
       * drives the chip in sleep and SYNC start is used.
       */
      macMcuTimer2OverflowWorkaround();
    }
    else
    {
      HAL_ENABLE_INTERRUPTS();
    }
  }
}