This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2530: CC2530 hal_sleep with two osal timers

Part Number: CC2530
Other Parts Discussed in Thread: CC2430, CC2591

Dear all,

My name is Joaquim Martinez and I am working on a low power project with CC2530 and CC2591 chips. This project is an update of CC2430 with new features, as for example the use of two osal timers.

Due to my needs I am using Basic RF library (from CC2530 basic examples: PER_TEST, LIGHT_SWITCH, etc). In addition, I need to use the sleep capability of these chips, specially the CC2530. For this feature I am using the hal_sleep.c of the Zstack for the CC2430 with some modifications in order to adapt the file to Basic RF library and CC2530. 

My updated/modified firmware to CC2530+CC2591 is working well and the consumption in SLEEP_DEEP mode (CC2530_PM3 according to hal_sleep.h) is the expected. This the same performance when I used CC2430.

Now I need to use two osal timers: timeout #1 1000ms and timeout #2 5000ms. Now the sleep mode is SLEEP_TIMER (CC2530_PM2 according to hal_sleep.h). If I use both timers without going to sleep, both timers work fine (I have two leds toggling in each ISR), even if I use one of them and going to sleep it works fine also, and the consumption matches with the datasheet. The problem occurs when I use both timers and going to sleep. The first time that timer 1 and 2 experire, leds are toggled OK, but for the second and next timeouts both leds are toggling every 1000ms, so timeout 2 (5000ms) is not working properly.

I am sure that the problem is the way how I manage the timeout in the hal_sleep function or the way how osal timer layer manage the ISR, but after several modifications I can't find the solution.

Please, can anybody help me with this?

My main() function is described below and the hal_sleep file is attached.

Thanks.

Joaquim.

// MAIN

void main (void)
{
WaitMs(100);

Inicialitzacions();

WaitMs(100);

for (uint8 i = 0; i < 5; i++)
{
HalIOExpsLedSet(3,VERMELL,ON);
WaitMs(500);
HalIOExpsLedSet(3,VERMELL,OFF);
WaitMs(500);
}

osal_start_timerEx(&UnSegonISR,1000);
osal_start_timerEx(&CincSegonsISR,5000);

static uint16 timeout;
istate_t intStateLock;

while (1)
{
intStateLock = halIntLock();

timeout = osal_next_timeout();

halIntUnlock( intStateLock );

halSleep(timeout);

if (InterrupcioUnSegon) {HandleTimerUnSegonEvent();}
if (InterrupcioCincSegons) {HandleTimerCincSegonsEvent();}
}

}

// ISR 1000ms

void UnSegonISR()
{
     InterrupcioUnSegon = TRUE;
}

// ISR 5000ms

void CincSegonsISR()
{
      InterrupcioCincSegons = TRUE;
}

// TOGGLE LED EVERY 5000ms

void HandleTimerCincSegonsEvent()
{
if(InterrupcioCincSegons)
{
    InterrupcioCincSegons=FALSE;

   osal_start_timerEx(&CincSegonsISR,5000);
   toogle_led3 = !toogle_led3;
   HalIOExpsLedSet(3,VERD,toogle_led3);
}
}

// TOGGLE LED EVERY 5000ms

void HandleTimerUnSegonEvent()
{
if (InterrupcioUnSegon)
{

InterrupcioUnSegon = FALSE;
osal_start_timerEx(&UnSegonISR,1000);

toogle_led2 = !toogle_led2;
HalIOExpsLedSet(3,BLAU,toogle_led2);

}
}

/**************************************************************************************
  Filename:       hal_sleep.c
  Revised:        $Date: 2014-12-19 13:07:30 -0800 (Fri, 19 Dec 2014) $
  Revision:       $Revision: 41556 $

  Description:    This module contains the HAL power management procedures for the CC2530.


  Copyright 2006-2014 Texas Instruments Incorporated. All rights reserved.

  IMPORTANT: Your use of this Software is limited to those specific rights
  granted under the terms of a software license agreement between the user
  who downloaded the software, his/her employer (which must be your employer)
  and Texas Instruments Incorporated (the "License").  You may not use this
  Software unless you agree to abide by the terms of the License. The License
  limits your use, and you acknowledge, that the Software may not be modified,
  copied or distributed unless embedded on a Texas Instruments microcontroller
  or used solely and exclusively in conjunction with a Texas Instruments radio
  frequency transceiver, which is integrated into your product.  Other than for
  the foregoing purpose, you may not use, reproduce, copy, prepare derivative
  works of, modify, distribute, perform, display or sell this Software and/or
  its documentation for any purpose.

  YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
  PROVIDED �AS IS� WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
  INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
  NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
  TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
  NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
  LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
  INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
  OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
  OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
  (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.

  Should you have any questions regarding your right to use this Software,
  contact Texas Instruments Incorporated at www.TI.com.
**************************************************************************************************/

/* ------------------------------------------------------------------------------------------------
 *                                          Includes
 * ------------------------------------------------------------------------------------------------
 */
#include "hal_types.h"
#include "hal_mcu.h"
#include "hal_board.h"
#include "hal_sleep.h"
#include "hal_led.h"
#include "OSAL_Timers.h"
#include "hal_assert.h"
#include "basic_rf.h"
#include "hal_rf.h"
#include "hal_ioexp.h"
#include "clock.h"
#include "hal_int.h"
#include "hal_LF.h"
#include "hal_button.h"
#include "hal_SPI.h"

#ifndef ZG_BUILD_ENDDEVICE_TYPE
# define ZG_BUILD_ENDDEVICE_TYPE FALSE
#endif

#if ZG_BUILD_ENDDEVICE_TYPE && defined (NWK_AUTO_POLL)
#include "nwk_globals.h"
#include "ZGlobals.h"
#endif

/* ------------------------------------------------------------------------------------------------
 *                                           Macros
 * ------------------------------------------------------------------------------------------------
 */

/* POWER CONSERVATION DEFINITIONS
 * Sleep mode H/W definitions (enabled with POWER_SAVING compile option)
 */
#define CC2530_PM0            0  /* PM0, Clock oscillators on, voltage regulator on */
#define CC2530_PM1            1  /* PM1, 32.768 kHz oscillators on, voltage regulator on */
#define CC2530_PM2            2  /* PM2, 32.768 kHz oscillators on, voltage regulator off */
#define CC2530_PM3            3  /* PM3, All clock oscillators off, voltage regulator off */

/* HAL power management mode is set according to the power management state. The default
 * setting is HAL_SLEEP_OFF. The actual value is tailored to different HW platform. Both
 * HAL_SLEEP_TIMER and HAL_SLEEP_DEEP selections will:
 *   1. turn off the system clock, and
 *   2. halt the MCU.
 * HAL_SLEEP_TIMER can be woken up by sleep timer interrupt, I/O interrupt and reset.
 * HAL_SLEEP_DEEP can be woken up by I/O interrupt and reset.
 */
#define HAL_SLEEP_OFF         CC2530_PM0
#define HAL_SLEEP_TIMER       CC2530_PM2
#define HAL_SLEEP_DEEP        CC2530_PM2//CC2530_PM3

/* MAX_SLEEP_TIME calculation:
 *   Sleep timer maximum duration = 0xFFFF7F / 32768 Hz = 511.996 seconds
 *   Round it to 510 seconds or 510000 ms
 */
#define MAX_SLEEP_TIME                   510000             /* maximum time to sleep allowed by ST */

/* 
 * Choosing value to be lower than MAC_BACKOFF_TIMER_DEFAULT_NONBEACON_ROLLOVER
 *  The unit is in ms. The back off timer rollover should be greater 
 * than the value below
 */ 
#define MAX_SLEEP_LOOP_TIME              0x510000           /* ~84 minutes */ 
#define TICKS_SUBTRACTED                 2

/* minimum time to sleep, this macro is to:
 * 1. avoid thrashing in-and-out of sleep with short OSAL timer (~2ms)
 * 2. define minimum safe sleep period
 */
#if !defined (PM_MIN_SLEEP_TIME)
#define PM_MIN_SLEEP_TIME                2                  /* default to minimum safe sleep time minimum CAP */
#endif

/* The PCON instruction must be 4-byte aligned. The following code may cause excessive power
 * consumption if not aligned. See linker file ".xcl" for actual placement.
 */
#warning T10R_08. Aquesta funci� est� definit a hal_sleep.h
//#pragma location = "SLEEP_CODE"
//void halSetSleepMode(void);

/* This value is used to adjust the sleep timer compare value such that the sleep timer
 * compare takes into account the amount of processing time spent in function halSleep().
 * The first value is determined by measuring the number of sleep timer ticks it from
 * the beginning of the function to entering sleep mode or more precisely, when
 * MAC_PwrNextTimeout() is called.  The second value is determined by measuring the number
 * of sleep timer ticks from exit of sleep mode to the call to MAC_PwrOnReq() where the
 * MAC timer is restarted.
 */
#define HAL_SLEEP_ADJ_TICKS   (11 + 12)

#ifndef HAL_SLEEP_DEBUG_POWER_MODE
/* set CC2530 power mode; always use PM2 */
#define HAL_SLEEP_PREP_POWER_MODE(mode)     st( SLEEPCMD &= ~PMODE; /* clear mode bits */    \
                                                SLEEPCMD |= mode;   /* set mode bits   */    \
                                                while (!(STLOAD & LDRDY));                   \
                                                halSleepPconValue = PCON_IDLE;               \
                                              )
#define HAL_SLEEP_SET_POWER_MODE()          halSetSleepMode()
#else
/* Debug: don't set power mode, just block until sleep timer interrupt */
#define HAL_SLEEP_PREP_POWER_MODE(mode)     /* nothing */
#define HAL_SLEEP_SET_POWER_MODE()          st( while(halSleepInt == FALSE); \
                                                halSleepInt = FALSE;         \
                                                HAL_DISABLE_INTERRUPTS();    \
                                              )
#endif

/* sleep and external interrupt port masks */
#define STIE_BV                             BV(5)
#define P0IE_BV                             BV(5)
#define P1IE_BV                             BV(4)
#define P2IE_BV                             BV(1)

/* sleep timer interrupt control */
#define HAL_SLEEP_TIMER_ENABLE_INT()        st(IEN0 |= STIE_BV;)     /* enable sleep timer interrupt */
#define HAL_SLEEP_TIMER_DISABLE_INT()       st(IEN0 &= ~STIE_BV;)    /* disable sleep timer interrupt */
#define HAL_SLEEP_TIMER_CLEAR_INT()         st(STIF = 0;)            /* clear sleep interrupt flag */

/* backup interrupt enable registers before sleep */
#define HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2) st(ien0  = IEN0;    /* backup IEN0 register */ \
                                                             ien1  = IEN1;    /* backup IEN1 register */ \
                                                             ien2  = IEN2;    /* backup IEN2 register */ \
                                                             IEN0 &= STIE_BV; /* disable IEN0 except STIE */ \
                                                             IEN1 &= P0IE_BV; /* disable IEN1 except P0IE */ \
                                                             IEN2 &= (P1IE_BV|P2IE_BV);) /* disable IEN2 except P1IE, P2IE */

/* restore interrupt enable registers before sleep */
#define HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2) st(IEN0 = ien0;   /* restore IEN0 register */ \
                                                  IEN1 = ien1;   /* restore IEN1 register */ \
                                                  IEN2 = ien2;)  /* restore IEN2 register */

/* convert msec to 320 usec units with round */
#define HAL_SLEEP_MS_TO_320US(ms)           (((((uint32) (ms)) * 100) + 31) / 32)

/* for optimized indexing of uint32's */
#if HAL_MCU_LITTLE_ENDIAN()
#define UINT32_NDX0   0
#define UINT32_NDX1   1
#define UINT32_NDX2   2
#define UINT32_NDX3   3
#else
#define UINT32_NDX0   3
#define UINT32_NDX1   2
#define UINT32_NDX2   1
#define UINT32_NDX3   0
#endif

/* ------------------------------------------------------------------------------------------------
 *                                        Global Variables
 * ------------------------------------------------------------------------------------------------
 */
/* PCON register value to program when setting power mode */
volatile __data uint8 halSleepPconValue = PCON_IDLE;

static uint32 maxSleepLoopTime =  HAL_SLEEP_MS_TO_320US(MAX_SLEEP_LOOP_TIME);

/* ------------------------------------------------------------------------------------------------
 *                                        Local Variables
 * ------------------------------------------------------------------------------------------------
 */

/* HAL power management mode is set according to the power management state.
 */
uint8 halPwrMgtMode = HAL_SLEEP_OFF;

#ifdef HAL_SLEEP_DEBUG_POWER_MODE
static bool halSleepInt = FALSE;
#endif

/* stores the sleep timer count upon entering sleep */
static uint32 halSleepTimerStart;

/* stores the accumulated sleep time */
static uint32 halAccumulatedSleepTime;

bool toogle_led4 = 0;

/* ------------------------------------------------------------------------------------------------
 *                                      Function Prototypes
 * ------------------------------------------------------------------------------------------------
 */

void halSleepSetTimer(uint32 timeout);
uint32 HalTimerElapsed(void);

/**************************************************************************************************
 * @fn          halSetSleepMode
 *
 * @brief       This function put the CC2530 to sleep. The PCON instruction must be 4-byte aligned.
 *              The following code may cause excessive power consumption if not aligned. See linker
 *              file ".xcl" for actual placement.
 *
 * input parameters
 *
 * @param       None.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
void halSetSleepMode(void)
{
  PCON = halSleepPconValue;
  HAL_DISABLE_INTERRUPTS();
}

/**************************************************************************************************
 * @fn          halSetMaxSleepLoopTime
 *
 * @brief       This function is to used to setup the maximum sleep loop time. This sleep loop time
 *              should be lesser than T2 rollover so that a maximum of only one rollover occurs
 *              when cc2530 is in sleep. This function should be called whenever rolloverTime is
 *              changed using the function macBackoffTimerSetRollover(macTimerRollover);
 *
 * input parameters
 *
 * @param       rolloverTime.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
void halSetMaxSleepLoopTime(uint32 rolloverTime)
{
  if( rolloverTime > HAL_SLEEP_MS_TO_320US(MAX_SLEEP_TIME) )
  {
    rolloverTime = HAL_SLEEP_MS_TO_320US(MAX_SLEEP_TIME);
  }
  maxSleepLoopTime = (rolloverTime - TICKS_SUBTRACTED);
}

/**************************************************************************************************
 * @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( uint32 osal_timeout )
{
  uint32 timeout;
  uint8 ien0, ien1, ien2;
  uint16 intState;  

  halAccumulatedSleepTime = 0;
  
  // Es posa senyal SPI com a GPIO output i a zero per baixa consum.
  //HalSPI_Init();
  
  // Apaguem la radio
  basicRfReceiveOff();
 
  // Get next OSAL timer expiration converted to 320 usec units
  timeout = HAL_SLEEP_MS_TO_320US(osal_timeout);
  halPwrMgtMode = (timeout == 0) ? HAL_SLEEP_DEEP : HAL_SLEEP_TIMER;  

  while ((HAL_SLEEP_MS_TO_320US(halAccumulatedSleepTime) < timeout) || (timeout == 0))
  {
      /* set main clock source to RC oscillator */
      //HAL_SLEEP_SET_MAIN_CLOCK_RC();
      clockSetMainSrc(CLOCK_SRC_HFRC);

      intState = halIntLock();  
    
      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();
      }  
            
      HAL_SLEEP_IE_BACKUP_AND_DISABLE(ien0, ien1, ien2);
    
 
 	
 
    
      HAL_SLEEP_IE_RESTORE(ien0, ien1, ien2);

      // disable sleep timer interrupt
      HAL_SLEEP_TIMER_DISABLE_INT();
   
      // set main clock source to crystal
      //HAL_SLEEP_SET_MAIN_CLOCK_CRYSTAL();
      clockSetMainSrc(CLOCK_SRC_XOSC);
      
      // Calculate timer elasped 
      halAccumulatedSleepTime += HalTimerElapsed() ;
    
      if ( osal_timeout > halAccumulatedSleepTime)
      {
        osal_timeout -= halAccumulatedSleepTime;
      }
      
      osal_adjust_timers();
        
      if ( timeout == 0 || InterrupcioLF || InterrupcioPulsador 
#if !defined(TZ2TAG)           
          || InterrupcioReed 
#endif            
            ) break;
      
      if ( osal_timeout < PM_MIN_SLEEP_TIME )
      {
        halSleepWait(osal_timeout * 1000);
        halAccumulatedSleepTime += osal_timeout;
        osal_timeout = halAccumulatedSleepTime;
      }    
  }
  
  // Es reactiva la radio del CC2530 i del CC2591. Sense aix� el consum en sleep �s de 3.6mA segons datasheet CC2591.
  halRfInit();
  
  halIntUnlock(intState);
}

/**************************************************************************************************
 * @fn          halSleepSetTimer
 *
 * @brief       This function sets the CC2530 sleep timer compare value.  First it reads and
 *              stores the value of the sleep timer; this value is used later to update OSAL
 *              timers.  Then the timeout value is converted from 320 usec units to 32 kHz
 *              period units and the compare value is set to the timeout.
 *
 * input parameters
 *
 * @param       timeout - Timeout value in 320 usec units.  The sleep timer compare is set to
 *                        this value.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
void halSleepSetTimer(uint32 timeout)
{
  uint32 ticks;

  /* read the sleep timer; ST0 must be read first */
  ((uint8 *) &ticks)[UINT32_NDX0] = ST0;
  ((uint8 *) &ticks)[UINT32_NDX1] = ST1;
  ((uint8 *) &ticks)[UINT32_NDX2] = ST2;
  ((uint8 *) &ticks)[UINT32_NDX3] = 0;

  /* Compute sleep timer compare value.  The ratio of 32 kHz ticks to 320 usec ticks
   * is 32768/3125 = 10.48576.  This is nearly 671/64 = 10.484375.
   */
  ticks += (timeout * 671) / 64;

  /* subtract the processing time spent in function halSleep() */
  ticks -= HAL_SLEEP_ADJ_TICKS;

  /* set sleep timer compare; ST0 must be written last */
  ST2 = ((uint8 *) &ticks)[UINT32_NDX2];
  ST1 = ((uint8 *) &ticks)[UINT32_NDX1];
  ST0 = ((uint8 *) &ticks)[UINT32_NDX0];
}

/**************************************************************************************************
 * @fn          TimerElapsed
 *
 * @brief       Determine the number of OSAL timer ticks elapsed during sleep.
 *              Deprecated for CC2530 and CC2430 SoC.
 *
 * input parameters
 *
 * @param       None.
 *
 * output parameters
 *
 * None.
 *
 * @return      Number of timer ticks elapsed during sleep.
 **************************************************************************************************
 */
uint32 TimerElapsed( void )
{
  return ( halAccumulatedSleepTime );
}

uint32 HalTimerElapsed( void )
{
  uint32 ticks;

  /* read the sleep timer; ST0 must be read first */
  ((uint8 *) &ticks)[UINT32_NDX0] = ST0;
  ((uint8 *) &ticks)[UINT32_NDX1] = ST1;
  ((uint8 *) &ticks)[UINT32_NDX2] = ST2;

  /* set bit 24 to handle wraparound */
  ((uint8 *) &ticks)[UINT32_NDX3] = 0x01;

  /* calculate elapsed time */
  ticks -= halSleepTimerStart;

  /* add back the processing time spent in function halSleep() */
  ticks += HAL_SLEEP_ADJ_TICKS;

  /* mask off excess if no wraparound */
  ticks &= 0x00FFFFFF;

  /* Convert elapsed time in milliseconds with round.  1000/32768 = 125/4096 */
  return ( ((ticks * 125) + 4095) / 4096 );
}

/**************************************************************************************************
 * @fn          halRestoreSleepLevel
 *
 * @brief       Restore the deepest timer sleep level.
 *
 * input parameters
 *
 * @param       None
 *
 * output parameters
 *
 *              None.
 *
 * @return      None.
 **************************************************************************************************
 */
void halRestoreSleepLevel( void )
{
  /* Stubs */
}

/**************************************************************************************************
 * @fn          halSleepTimerIsr
 *
 * @brief       Sleep timer ISR.
 *
 * input parameters
 *
 * None.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
HAL_ISR_FUNCTION(halSleepTimerIsr, ST_VECTOR)
{
  HAL_ENTER_ISR();
  HAL_SLEEP_TIMER_CLEAR_INT();

#ifdef HAL_SLEEP_DEBUG_POWER_MODE
  halSleepInt = TRUE;
#endif
  
  CLEAR_SLEEP_MODE();
  HAL_EXIT_ISR();
}

/**************************************************************************************************
 * @fn          halSleepWait
 *
 * @brief       Perform a blocking wait for the specified number of microseconds.
 *              Use assumptions about number of clock cycles needed for the various instructions.
 *              This function assumes a 32 MHz clock.
 *              NB! This function is highly dependent on architecture and compiler!
 *
 * input parameters
 *
 * @param       duration - Duration of wait in microseconds.
 *
 * output parameters
 *
 * None.
 *
 * @return      None.
 **************************************************************************************************
 */
#pragma optimize=none
void halSleepWait(uint16 duration)
{
  duration >>= 1;

  while (duration-- > 0)
  {
    ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP;
    ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP; ASM_NOP;
  }
}