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.

CC1110 change sleep interval in PM2

Other Parts Discussed in Thread: CC2510

All  CC1110 do is wakes up every second and check a pin status. Every 3 minutes transmits a message. 

during the field trials 25% of the units go into a weird state during which the transmit interval becomes a few hours for a few days then recover and go back to a normal 3 minutes interval.

Any ideas what can go wrong.  The units are running in automotive environment.


  • Hi

    Make sure that you are following the CC1110 errata for how to enter/exit PM2 and PM3.

    BR

    Siri

  • Here is the code we are using. I copied from the errata and added a few things.
    
    #pragma optimize=none
    void EnterPowerMode2( BYTE byIntervalOffsetTime, BYTE flags )
    {
    volatile uint8 storedDescHigh = DMA0CFGH;
    volatile uint8 storedDescLow = DMA0CFGL;
    volatile int8 temp;
    volatile WORD wEvent0;
    if( ( flags > 2 ) || ( byIntervalOffsetTime > MAX_INTERVAL_OFFSET ) )
    {
    FaultRecord();
    SoftwareReset();
    }
    if( flags )
    {
    setup_sleep_interrupt();
    wEvent0 = EVENT_WORD + byIntervalOffsetTime;
    }
    // Switch system clock source to HS RCOSC and max CPU speed:
    // Note that this is critical for Power Mode 2. After reset or
    // exiting Power Mode 2 the system clock source is HS RCOSC,
    // but to emphasize the requirement we choose to be explicit here.
    SLEEP &= ~SLEEP_OSC_PD;
    while( !( SLEEP & SLEEP_HFRC_S ) ) ;
    CLKCON = ( CLKCON & ~CLKCON_CLKSPD ) | CLKCON_OSC | CLKSPD_DIV_2;
    while( !( CLKCON & CLKCON_OSC ) ) ;
    SLEEP |= SLEEP_OSC_PD;

    // Set LS XOSC as the Sleep Timer clock source (CLKCON.OSC32 = 0)
    CLKCON &= ~CLKCON_OSC32;

    ///////////////////////////////////////////////////////////////////////
    ////////// CC111xFx/CC251xFx Errata Note Code section Begin ///////////
    ///////////////////////////////////////////////////////////////////////

    // Store current DMA channel 0 descriptor and abort any ongoing transfers,
    // if the channel is in use.
    storedDescHigh = DMA0CFGH;
    storedDescLow = DMA0CFGL;
    DMAARM |= ( DMAARM_ABORT | DMAARM0 );

    // Update descriptor with correct source.
    dmaDesc[0] = ( uint16 ) & PM2_BUF >> 8;
    dmaDesc[1] = ( uint16 ) & PM2_BUF;
    // Associate the descriptor with DMA channel 0 and arm the DMA channel
    DMA0CFGH = ( uint16 ) & dmaDesc >> 8;
    DMA0CFGL = ( uint16 ) & dmaDesc;
    DMAARM = DMAARM0;

    // NOTE! At this point, make sure all interrupts that will not be used to
    // wake from PM are disabled as described in the "Power Management Control"
    // chapter of the data sheet.

    // The following code is timing critical and should be done in the
    // order as shown here with no intervening code.
    // The update rate of the WORTIME0 register depends on the Sleep Timer resolution,
    // configured through WORCTRL.WOR_RES. , that's why the WORCTL have to be reset,
    // before aligning with the positive 32 kHz edge.

    WORCTL = 0X04;
    // Align with positive 32 kHz clock edge as described in the
    // "Sleep Timer and Power Modes" chapter of the data sheet.
    temp = WORTIME0;
    while( temp == WORTIME0 );

    temp = WORTIME0;
    while( temp == WORTIME0 );

    // Set Sleep Timer Interval - time = EVENT0 / 1024
    WORCTL = ( WORCTL & ~WORCTL_WOR_RES ) | WORCTL_WOR_RES_1;

    if( flags )
    {
    // Enable Sleep Timer CPU Interrupt (IEN0.STIE = 1)
    STIE = 1;
    // set to 0.999847412 sec + offset time
    WOREVT0 = (BYTE)wEvent0;
    WOREVT1 = (BYTE)(wEvent0>>8);
    }

    // Make sure HS XOSC is powered down when entering PM{2 - 3} and that
    // the flash cache is disabled.
    MEMCTR |= MEMCTR_CACHD;
    SLEEP = 0x06;

    // Enter power mode as described in chapter "Power Management Control"
    // in the data sheet. Make sure DMA channel 0 is triggered just before
    // setting [PCON.IDLE].
    asm("NOP");
    asm("NOP");
    asm("NOP");
    if( SLEEP & 0x03 )
    {
    asm("MOV 0xD7,#0x01");
    // DMAREQ = 0x01;
    asm("NOP");
    // Needed to perfectly align the DMA transfer.
    asm("ORL 0x87,#0x01");
    // PCON |= 0x01 -- Now in PM2;
    asm("NOP");
    // First call when awake
    }
    // End of timing critical code
    // Enable Flash Cache.
    MEMCTR &= ~MEMCTR_CACHD;

    // Update DMA channel 0 with original descriptor and arm channel if it was
    // in use before PM was entered.
    DMA0CFGH = storedDescHigh;
    DMA0CFGL = storedDescLow;
    DMAARM = DMAARM0;

    ///////////////////////////////////////////////////////////////////////
    /////////// CC111xFx/CC251xFx Errata Note Code section End ////////////
    ///////////////////////////////////////////////////////////////////////


    // Wait until HS RCOSC is stable
    while( !( SLEEP & SLEEP_HFRC_S ) )
    ;

    // Set LS XOSC as the clock oscillator for the Sleep Timer (CLKCON.OSC32 = 0)
    CLKCON &= ~CLKCON_OSC32;

    }


    void setup_sleep_interrupt( void )
    {
    // Clear Sleep Timer CPU Interrupt flag (IRCON.STIF = 0)
    STIF = 0;

    // Clear Sleep Timer Module Interrupt Flag (WORIRQ.EVENT0_FLAG = 0)
    WORIRQ &= ~WORIRQ_EVENT0_FLAG;

    // Enable Sleep Timer Module Interrupt (WORIRQ.EVENT0_MASK = 1)
    WORIRQ |= WORIRQ_EVENT0_MASK;

    // Enable Global Interrupt (IEN0.EA = 1)
    EA = 1;
    }

    #pragma vector = ST_VECTOR
    __interrupt void sleep_timer_isr( void )
    {
    // Clear Sleep Timer CPU interrupt flag (IRCON.STIF = 0)
    STIF = 0;

    // Clear Sleep Timer Module Interrupt Flag (WORIRQ.EVENT0_FLAG = 0)
    WORIRQ &= ~WORIRQ_EVENT0_FLAG;

    // Clear the [SLEEP.MODE] bits, because an interrupt can also occur
    // before the SoC has actually entered Power Mode 2.
    SLEEP &= ~SLEEP_MODE;

    }
  • Hi

    The first thing I noticed is that you have added code in the part of the errata code that is marked as time critical: From the errata: "The code marked blue below is timing critical and should be done in the order as shown here with no intervening code."

    char temp = WORTIME0;
    while( temp == WORTIME0);

    MEMCTR |= 0x02;
    SLEEP = 0x06;

    asm("NOP");
    asm("NOP");
    asm("NOP");

    if( SLEEP & 0x03 ) {
        asm(“MOV 0xD7,#0x01”); // DMAREQ = 0x01;
        asm(“NOP”); // Needed to perfectly align the DMA transfer
        asm(“ORL 0x87,#0x01”); // PCON |= 0x01;
        asm("NOP");
    }

    BR

    Siri

  • Hi, 

    We implemented this and tested 12 units in the office for a month and none of them went into the 6h mode. 

    We tested also 11 units with the old code and 2 of them went into the 6h mode.

    We haven't done any field trials yet but from the lab tests it seems that this may be the fix.

    Thanks

  • Hi

    I have experienced the same problem (that the processor suddently hangs after about 2-4 days with 1 sec. cycles in PM2) and wonder why  your example code "powermode2.c" (found in cc1110_cc2510_Basic Software_Examples : http://www.ti.com/product/cc1110f32#toolssoftware ) looks like shown below:

    // The following code is timing critical and should be done in the
    // order as shown here with no intervening code.

    // Align with positive 32 kHz clock edge as described in the
    // "Sleep Timer and Power Modes" chapter of the data sheet.
    temp = WORTIME0;
    while(temp == WORTIME0);

    // Set Sleep Timer Interval
    WOREVT0 = EVENT0_HIGH;
    WOREVT1 = EVENT0_LOW;

    // Make sure HS XOSC is powered down when entering PM{2 - 3} and that
    // the flash cache is disabled.
    MEMCTR |= MEMCTR_CACHD;
    SLEEP = 0x06;

    // Enter power mode as described in chapter "Power Management Control"
    // in the data sheet. Make sure DMA channel 0 is triggered just before
    // setting [PCON.IDLE].
    asm("NOP");
    asm("NOP");
    asm("NOP");
    if(SLEEP & 0x03)
    {
    asm("MOV 0xD7,#0x01"); // DMAREQ = 0x01;
    asm("NOP"); // Needed to perfectly align the DMA transfer.
    asm("ORL 0x87,#0x01"); // PCON |= 0x01 -- Now in PM2;
    asm("NOP"); // First call when awake
    }
    // End of timing critical code

    The code that you wrote in your last ansver did not have the following code:

    ---------------------

    // Set Sleep Timer Interval
    WOREVT0 = EVENT0_HIGH;
    WOREVT1 = EVENT0_LOW;

    ---------------------

    Can this be the problem, since it is present in the time critical section?

    if yes, where shall WOREVT0/1 then be set in the code?

    Please answer ASAP because I already have spend many days looking to solving the problem and have several costumer waiting.

    Regards Allan

  • Hi Allan

    Updating EVENT0 should be done before the time critical section. The update of EVENT0 must also be align to a 32 kHz edge:

    // Alignment of updating EVENT0 to a positive edge on the 32 kHz clock source

    char temp = WORTIME0;

    while(temp == WORTIME0);           // Wait until a positive 32 kHz edge

    WOREVT1 = desired event0 >> 8;  // Set EVENT0, high byte

    WOREVT0 = desired event0;          // Set EVENT0, low byte

    // Align with positive 32 kHz clock edge as described in the
    // "Sleep Timer and Power Modes" chapter of the data sheet.
    temp = WORTIME0;
    while(temp == WORTIME0);

     // Make sure HS XOSC is powered down when entering PM{2 - 3} and that
    // the flash cache is disabled.
    MEMCTR |= MEMCTR_CACHD;
    SLEEP = 0x06;

    // Enter power mode as described in chapter "Power Management Control"
    // in the data sheet. Make sure DMA channel 0 is triggered just before
    // setting [PCON.IDLE].
    asm("NOP");
    asm("NOP");
    asm("NOP");
    if(SLEEP & 0x03) {
        asm("MOV 0xD7,#0x01"); // DMAREQ = 0x01;
        asm("NOP"); // Needed to perfectly align the DMA transfer.
        asm("ORL 0x87,#0x01"); // PCON |= 0x01 -- Now in PM2;
        asm("NOP"); // First call when awake
    }
    // End of timing critical code

    It is also very important that you remember to clear the MODE bits in all ISR for interrupts used to wake the chip from PM2/PM3 (SLEEP timer ISR, Port ISR etc.). This is stated in the data sheet.

    BR

    Siri

  • Hi Siri

    Thank you for your quick reply :-)

    1) If the WOREVT0/1 has to be done before the time-critical section, you have an example on your website that has to be updated (I mentioned it in last mail)

    2) Why is the alignment of the 32KHz edge that important?

    3) In another app I have created (where the unit has to sleep up to 0xFFFF sec., typ. 4h to 24h between awake), the alignment (as suggested) cost up to 1 sec busywait, consuming valuable current, and now you say that I have to do this twice for every PM2 sleep?...that is not acceptable in my app...could you ask your app. team how to avoid this busy-wait.

    3) This going dead problem is very serious, We have already sold many thousend units and some of them suddently goes dead. I have struggeled and struggeled with this problem for a very long time now!

    BR

    Allan

  • Hi Allan

    There are issues related to doing specific operation if they are done on a 32 kHz edge, so the aligning is simply to avoid the edges. One of the things that could happen if you for example enter PM2 on an edge is that the sleep interval, tEvent0, will be 128 times longer than programmed.

    You cannot avoid doing the alignment. However, if you look at the assembly code generated you can find out how many cycles the different operation takes. If you can guarantee that from the edge of the 32 kHz until you enter PM2 (set PCON.IDLE = 1) there are no more than 14.42 us (the time until your next edge) it should be safe to only do one alignment. The main thing is that updating EVENT0 and entering PM2 must not happen on an edge.

    I agree that the code example should be updated, but if one follows the errata note + the data sheet there will not be any issues related to entering PM.

    Please be aware that the CC1110 errata note was updated last week.

    BR

    Siri

  • Hi Siri

    - If the worst thing that could happend is that the sleeptime is 128 times longer than intended, it cannot be the problem I'm looking for, since the device hangs forever!!!

    - I have seen the new errata rev.C, but was not sure that the crystal startup-problem could generate the "hang" problem, are you agree?

    - Is there som source-code that implements the work-around (sec. 1.2), since I'm a bit confused about solving this problem!!!

    - Below is my sleep-routine, is this correct implemented?

    void sleepTimer(unsigned short sec)
    {

     uint32 tempSec = sec*4;
    // Initialization of source buffers and DMA descriptor for the DMA transfer
    // (ref. CC111xFx/CC251xFx Errata Note)
    uint8 PM2_BUF[7] = {0x06,0x06,0x06,0x06,0x06,0x06,0x04};
    uint8 dmaDesc[8] = {0x00,0x00,0xDF,0xBE,0x00,0x07,0x20,0x42};

    int8 EVENT0_HIGH = tempSec>>8;
    int8 EVENT0_LOW = tempSec;

    volatile uint8 storedDescHigh = DMA0CFGH;
    volatile uint8 storedDescLow = DMA0CFGL;
    int8 temp;

    if(!sec)
    return;

    WORCTRL |= 1;

    // Switch system clock source to HS RCOSC and max CPU speed:
    // Note that this is critical for Power Mode 2. After reset or
    // exiting Power Mode 2 the system clock source is HS RCOSC,
    // but to emphasize the requirement we choose to be explicit here.
    SLEEP &= ~SLEEP_OSC_PD;
    while( !(SLEEP & SLEEP_HFRC_S) );
    CLKCON = (CLKCON & ~CLKCON_CLKSPD) | CLKCON_OSC | CLKSPD_DIV_2;
    while ( !(CLKCON & CLKCON_OSC) ) ;
    SLEEP |= SLEEP_OSC_PD;

    // Set LS XOSC as the Sleep Timer clock source (CLKCON.OSC32 = 0)
    CLKCON &= ~CLKCON_OSC32;

    // Wait some time in Active Mode, and set SRF04EB LED1 before
    // entering Power Mode 2
    // for(activeModeCnt = 0; activeModeCnt < ACT_MODE_TIME; activeModeCnt++);
    // P1_0 = 1;

    ///////////////////////////////////////////////////////////////////////
    ////////// CC111xFx/CC251xFx Errata Note Code section Begin ///////////
    ///////////////////////////////////////////////////////////////////////

    // Store current DMA channel 0 descriptor and abort any ongoing transfers,
    // if the channel is in use.
    storedDescHigh = DMA0CFGH;
    storedDescLow = DMA0CFGL;
    DMAARM |= (DMAARM_ABORT | DMAARM0);

    // Update descriptor with correct source.
    dmaDesc[0] = (uint16)&PM2_BUF >> 8;
    dmaDesc[1] = (uint16)&PM2_BUF;
    // Associate the descriptor with DMA channel 0 and arm the DMA channel
    DMA0CFGH = (uint16)&dmaDesc >> 8;
    DMA0CFGL = (uint16)&dmaDesc;
    DMAARM = DMAARM0;

    // Align with positive 32 kHz clock edge as described in the
    // "Sleep Timer and Power Modes" chapter of the data sheet.
    WORCTL = 0X04;
    temp = WORTIME0;
    while( temp == WORTIME0 );
    temp = WORTIME0;
    while( temp == WORTIME0 );
    // Set Sleep Timer Interval
    WOREVT0 = EVENT0_HIGH;
    WOREVT1 = EVENT0_LOW;

    // NOTE! At this point, make sure all interrupts that will not be used to
    // wake from PM are disabled as described in the "Power Management Control"
    // chapter of the data sheet.

    // The following code is timing critical and should be done in the
    // order as shown here with no intervening code.

    // Align with positive 32 kHz clock edge as described in the
    // "Sleep Timer and Power Modes" chapter of the data sheet.

    temp = WORTIME0;
    while(temp == WORTIME0); // Wait until a positive 32 kHz edge

    // Make sure HS XOSC is powered down when entering PM{2 - 3} and that
    // the flash cache is disabled.
    MEMCTR |= MEMCTR_CACHD;
    SLEEP = 0x06;

    // Enter power mode as described in chapter "Power Management Control"
    // in the data sheet. Make sure DMA channel 0 is triggered just before
    // setting [PCON.IDLE].
    asm("NOP");
    asm("NOP");
    asm("NOP");
    if(SLEEP & 0x03)
    {
    asm("MOV 0xD7,#0x01"); // DMAREQ = 0x01;
    asm("NOP"); // Needed to perfectly align the DMA transfer.
    asm("ORL 0x87,#0x01"); // PCON |= 0x01 -- Now in PM2;
    asm("NOP"); // First call when awake
    }
    // End of timing critical code

    // Enable Flash Cache.
    MEMCTR &= ~MEMCTR_CACHD;

    // Update DMA channel 0 with original descriptor and arm channel if it was
    // in use before PM was entered.
    DMA0CFGH = storedDescHigh;
    DMA0CFGL = storedDescLow;
    DMAARM = DMAARM0;

    ///////////////////////////////////////////////////////////////////////
    /////////// CC111xFx/CC251xFx Errata Note Code section End ////////////
    ///////////////////////////////////////////////////////////////////////

    // Wait until HS RCOSC is stable
    while( !(SLEEP & SLEEP_HFRC_S) );

    // Set LS XOSC as the clock oscillator for the Sleep Timer (CLKCON.OSC32 = 0)
    CLKCON &= ~CLKCON_OSC32;

    }

    BR

    Allan

  • Hi Allan

    Your code is correct except that WORCTL = 0x04; should be changed to WORCTL |= 0x04;

    When you are entering PM2, are the SLEEP timer interrupt the only interrupt enabled or are there other interrupts that can be used to wake the chip from PM2?

    Have you remembered to clear the MODE bits in all the ISR’s run when waking the chip?

    When you say that the device hang forever, are you sure that it is stuck in PM2 (have you for example measured the current consumption to verify this?)

    The bug in the errata is the only thing we know can make the device hang in PM, and as far as I can see you have implemented the work-around correctly (if the MODE bits are cleared in the ISR).

    BR

    Siri

  • Hi again

    I have another app where the device is required to sleep for an amount of time (up to 24 hours.). After a normal wakeup, it has to sleep a minimum time (900 s) where another part of the hw is charging before doing a new wakeup that requires a lot of current. During the sleep it has to be able to register if an input is changing from low to high. If this happens the sleeptime (PM2) must be changed to the minimum required minus already elapsed time (900 [s] - T_elapsed_in_pm2 [s]), else it shall be in PM2 until sleeptimer interrupt occurs (ex. 24 hours).

    I read the WORTIME0 and WORTIME1 in the ext IRQ ISR, but I cannot read the correct amount time already spent in PM2 (I reset the WORTIME reg each time before entering the sleep-routine). Below is my sleep-routine with 1 sec resolution.

    Note, I have made som changes to speed-up (in this app) the wait-time when synchronizing to the clock.

    //==================================================================================

    void sleepTimer(unsigned short Sec)
    {
    // Initialization of source buffers and DMA descriptor for the DMA transfer
    // (ref. CC111xFx/CC251xFx Errata Note)
    uint8 PM2_BUF[7] = {0x06,0x06,0x06,0x06,0x06,0x06,0x04};
    uint8 dmaDesc[8] = {0x00,0x00,0xDF,0xBE,0x00,0x07,0x20,0x42};
    // uint32 tempSec = Sec*4<<8;
    uint32 tempSec = Sec;
    int8 EVENT0_HIGH = tempSec>>8;
    int8 EVENT0_LOW = tempSec;
    volatile uint8 storedDescHigh = DMA0CFGH;
    volatile uint8 storedDescLow = DMA0CFGL;
    //APL-to remove warning...MAX 2 volatile values in a statement volatile int8 temp;
    int8 temp;

    if(!Sec)
    return;

    ST_TimeElapsed=0;

    //apl-130924 WORCTRL |= 3;

    // Switch system clock source to HS RCOSC and max CPU speed:
    // Note that this is critical for Power Mode 2. After reset or
    // exiting Power Mode 2 the system clock source is HS RCOSC,
    // but to emphasize the requirement we choose to be explicit here.
    SLEEP &= ~SLEEP_OSC_PD;
    while( !(SLEEP & SLEEP_HFRC_S) );
    CLKCON = (CLKCON & ~CLKCON_CLKSPD) | CLKCON_OSC | CLKSPD_DIV_2;
    while ( !(CLKCON & CLKCON_OSC) ) ;
    SLEEP |= SLEEP_OSC_PD;

    // Set LS XOSC as the Sleep Timer clock source (CLKCON.OSC32 = 0)
    CLKCON &= ~CLKCON_OSC32;

    // Wait some time in Active Mode, and set SRF04EB LED1 before
    // entering Power Mode 2
    // for(activeModeCnt = 0; activeModeCnt < ACT_MODE_TIME; activeModeCnt++);
    // P1_0 = 1;

    ///////////////////////////////////////////////////////////////////////
    ////////// CC111xFx/CC251xFx Errata Note Code section Begin ///////////
    ///////////////////////////////////////////////////////////////////////

    // Store current DMA channel 0 descriptor and abort any ongoing transfers,
    // if the channel is in use.
    storedDescHigh = DMA0CFGH;
    storedDescLow = DMA0CFGL;
    DMAARM |= (DMAARM_ABORT | DMAARM0);

    // Update descriptor with correct source.
    dmaDesc[0] = (uint16)&PM2_BUF >> 8;
    dmaDesc[1] = (uint16)&PM2_BUF;
    // Associate the descriptor with DMA channel 0 and arm the DMA channel
    DMA0CFGH = (uint16)&dmaDesc >> 8;
    DMA0CFGL = (uint16)&dmaDesc;
    DMAARM = DMAARM0;

    // NOTE! At this point, make sure all interrupts that will not be used to
    // wake from PM are disabled as described in the "Power Management Control"
    // chapter of the data sheet.

    // The following code is timing critical and should be done in the
    // order as shown here with no intervening code.

    // Align with positive 32 kHz clock edge as described in the
    // "Sleep Timer and Power Modes" chapter of the data sheet.
    WORCTRL |= 0x04; // Reset Sleep Timer
    WOREVT0 = 0xAA;
    temp = WORTIME0;
    while(temp == WORTIME0); // Wait until a positive 32 kHz edge
    WORCTRL = (WORCTRL & ~0x03)|0x3; // Set Resolution

    // Set Sleep Timer Interval

    WOREVT0 = EVENT0_LOW;
    WOREVT1 = EVENT0_HIGH;

    // Make sure HS XOSC is powered down when entering PM{2 - 3} and that
    // the flash cache is disabled.
    MEMCTR |= MEMCTR_CACHD;
    SLEEP = 0x06;

    // Enter power mode as described in chapter "Power Management Control"
    // in the data sheet. Make sure DMA channel 0 is triggered just before
    // setting [PCON.IDLE].
    asm("NOP");
    asm("NOP");
    asm("NOP");
    if(SLEEP & 0x03)
    {
    asm("MOV 0xD7,#0x01"); // DMAREQ = 0x01;
    asm("NOP"); // Needed to perfectly align the DMA transfer.
    asm("ORL 0x87,#0x01"); // PCON |= 0x01 -- Now in PM2;
    asm("NOP"); // First call when awake
    }
    // End of timing critical code

    //apl-130924
    WORCTRL &= ~0x03; // Set fastest Resolution to set fastest clk sync at next entry

    // Enable Flash Cache.
    MEMCTR &= ~MEMCTR_CACHD;

    // Update DMA channel 0 with original descriptor and arm channel if it was
    // in use before PM was entered.
    DMA0CFGH = storedDescHigh;
    DMA0CFGL = storedDescLow;
    DMAARM = DMAARM0;

    ///////////////////////////////////////////////////////////////////////
    /////////// CC111xFx/CC251xFx Errata Note Code section End ////////////
    ///////////////////////////////////////////////////////////////////////

    // Wait until HS RCOSC is stable
    while( !(SLEEP & SLEEP_HFRC_S) );

    // Set LS XOSC as the clock oscillator for the Sleep Timer (CLKCON.OSC32 = 0)
    CLKCON &= ~CLKCON_OSC32;

    }

    //==================================================================================

    _Pragma("vector=0x7B") __near_func __interrupt void IO_P1EventISR(void) //Port 1
    {
     ST_TimeElapsed=WORTIME1<<8;
     ST_TimeElapsed+=WORTIME0;

    // Note that the order in which the following flags are cleared is important.
    P1IFG = 0;


    // Clear CPU Interrupt Flag for P0 (IRCON.P0IF = 0)
    P1IF = 0;

    // Clear the [SLEEP.MODE] bits, because an interrupt can also occur
    // before the SoC has actually entered Power Mode 3.
    #if defined(DEBUG_USE_DEBUGGER_MODE)
    TIMER_Running=false;
    #else
    SLEEP &= ~SLEEP_MODE;
    #endif
    // Put Action here
    // CT_UserIntervention=true;
    }

    //==================================================================================

    __interrupt void sleep_isr(void)
    {

     ST_TimeElapsed=WORTIME1<<8;
     ST_TimeElapsed+=WORTIME0;

    // Clear Sleep Timer CPU interrupt flag (IRCON.STIF = 0)
    STIF = 0;

    // Clear Sleep Timer Module Interrupt Flag (WORIRQ.EVENT0_FLAG = 0)
    WORIRQ &= ~WORIRQ_EVENT0_FLAG;

    // Clear the [SLEEP.MODE] bits, because an interrupt can also occur
    // before the SoC has actually entered Power Mode 2.
    SLEEP &= ~SLEEP_MODE;

    // Set SRF04EB LED1 to indicate Power Mode 2 exit
    //P1_0 = 0;
    }

    //==================================================================================

    Hope you can help

    Regards

  • Hi

    First of all you are not following the data sheets recommendations for resetting the WOR timer even if your comment in your code says you do. A reset should be followed by aligning to two clock edges, no intervening code:

    // Reset timer, update EVENT0, and enter PM{0 – 2}

    WORCTRL |= 0x04; // Reset Sleep Timer

    char temp = WORTIME0;

    while(temp == WORTIME0); // Wait until a positive 32 kHz edge

    temp = WORTIME0;

    while(temp == WORTIME0); // Wait until a positive 32 kHz edge

    WOREVT1 = desired event0 >> 8; // Set EVENT0, high byte

    WOREVT0 = desired event0; // Set EVENT0, low byte

    PCON |= 0x01; // Enter PM{0 – 2}

    How do you know that you are reading the wrong value and how are you testing this?

    I am not sure I understand what you are trying to achieve and how it is failing.

    Maybe you can provide me with a flow chart or something to explain once more what you want to achieve and what the problem is

    Sorry that I could not be of any help at the moment.

    Br

    Siri

  • Hi Siri

    Thank you for your answer. I'm fully aware of the things you pointed out, but my app is very time-critical, so I try to minimize idle wait-time on the WORTIME0 reading. The code I send you is running in 1 s. resoulution (has to be able to seleep uptil 14 days, which of couse is impossible, but with a minimum  ), and the waiting on WORTIME0 does  

    Shortly...my goal is to be able to wake-up the cc1110 in PM2, using P0.0  (interrupt on positive edge) and read the time (ST_TimeElapsed) that the CC1110 already has been in PM2. When exit from PM2 I use the ST_TimeElapsed to correct some manually updated timer-variables.

    //============================================================

    #define MAX_SLEEP_TIME 65535 //max sec to sleep

    Timer1=ChargeTime; //ChargeTime user controllable

    while(1) //main loop

    {

      //Calculate sleeptime

      T1=(Timer1>MAX_SLEEP_TIME )?MAX_SLEEP_TIME:Timer1;

      //Sleep and correct Timer1

      Timer1-=SleepPM2(T1); 

      //Sleep Time finished to secure a charging process

      if(Timer1<=0)

      {

        //Do some work requiring a lot of current

        Timer1=ChargeTime;

      }

      //IRQ Flag set if sleep was interruptet on P0.0

      if(P0_IRQ_Flag) 

      {

        //Do som quick work and return to sleep

        P0_IRQ_Flag=false;

      }

    }

    //============================================================

    So the "SleepPM2" must return the time already spent in PM2. If the unit has not been interrupted by P0.0, The above "code" is of course very simplified :-) 

    During several tries, I haven't been able-to read anything usable out of WORTIME0/1. In one try, I read constant 0, in another constant 1 and else totally random values.

    I hope the above description clarifies the problem. I would appreciate If you could tell me the "right" way to be able to read the time spent in PM2, and with very little busy-waiting in the sleep-routine since every little current-consumption is important in this application.

    Regards Allan

  •  Hi Allan

    Once again I must recommend that you follow the errata.

    When it comes to the reading of the WORTIME registers I have tested this and it works as it should.

    I do not have time to implement exactly what you are trying to do but the code below show you that it is possible to read the SLEEP timer values so hopefully it will be useful. Just remember that it is not possible to run the debugger and setting breakpoints when using power modes.

    The code below toggles a LED every 0.5 s (SLEEP timer gives an interrupt every 0.5 s)

    unsigned char __xdata PM2_BUF[7] = {0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x04};

    unsigned char __xdata dmaDesc[8] = {0x00, 0x00, 0xDF, 0xBE, 0x00, 0x07, 0x20, 0x42};

    unsigned short event0;

    unsigned short _0_5_s = 0x43B5;

    void main(void) {
        
        unsigned char temp;
        
        CLKCON = CLKCON_13_MHZ_HSRCOSC;
        while(!(CLKCON & 0x40)); // wait until clock switch has occured (=stable)
        SLEEP |= 0x04;           // power down the unused oscillator

        // Init LEDs for debug purposes
        P1DIR = 0x01;       // P1_0 (Green LED)

        // Update descriptor with correct source
        dmaDesc[0] = (unsigned int)& PM2_BUF >> 8;
        dmaDesc[1] = (unsigned int)& PM2_BUF;

        // Associate the descriptor with DMA channel 0 and arm the DMA channel
        DMA0CFGH = (unsigned int)&dmaDesc >> 8;
        DMA0CFGL = (unsigned int)&dmaDesc;

        // Enable Sleep Timer interrupt    
        
        // 1. Clear interrupt flags
        STIF = 0;             // IRCON.STIF = 0
        WORIRQ &= ~0x01;      // WORIRQ.EVENT0_FLAG = 0

        // 2. Set individual interrupt enable bit in the peripherals SFR, if any
        WORIRQ |= 0x10;     // WORIRQ.EVENT0_MASK = 1

        // 3. Set the corresponding individual, interrupt enable bit in the
        // IEN0, IEN1, or IEN2 registers to 1
        // HAL_INT_ENABLE(INUM_ST, INT_ON);  // IEN0.STIE = 1

        // 4 . Enable global interrupt by setting the IEN0.EA = 1
        INT_GLOBAL_ENABLE(INT_ON);

        CLKCON = CLKCON_26_MHZ_XOSC;
        while (CLKCON & 0x40)   // Wait until the clock switch has occurred (=stable)
        SLEEP |= 0x04;          // Power down unused oscillator

        WOREVT1 = 0x43; // tEVENT0 = 0.5 s
        WOREVT0 = 0xB5;

        while (TRUE) {

            delay_10_ms(); // Introduce delay to stay awake some time before going back to PM2

            // Selesct HS RCOSC as system clock and turn off the HS XOSC.
            CLKCON = CLKCON_13_MHZ_HSRCOSC;
            while(!(CLKCON & 0x40)); // wait until clock switch has occured (=stable)
            SLEEP |= 0x04;           // power down the unused oscillator
            STIE = 1;  // IEN0.STIE = 1

            DMAARM = 0x01;

            temp = WORTIME0;            // Align with positive 32 kHz clock edge
            while (temp == WORTIME0);

            // Disable flash cache
            MEMCTR |= 0x02;

            // Enter PM2
            SLEEP |= 0x02;
            asm("NOP");
            asm("NOP");
            asm("NOP");
            if(SLEEP & 0x03) {
                asm("MOV 0xD7,#0x01");  // DMAREQ = 0x01;
                asm("NOP");             // Needed to perfectly align the DMA transfer
                asm("ORL 0x87,#0x01");  // PCON |= 0x01;
                asm("NOP");
            }
        }
    }

    #pragma vector=ST_VECTOR

    __interrupt void st_IRQ(void) {

        MEMCTR &= ~0x02;

        // Clear interrupt flags
        STIF = 0;        // IRCON.STIF = 0
        WORIRQ &= ~0x01; // WORIRQ.EVENT0_FLAG = 0

        CLKCON = CLKCON_26_MHZ_XOSC;
        while (CLKCON & 0x40); // wait until the clock switch has occured (=stable)
        SLEEP |= 0x04;         // power down unused oscillator
        
        STIE = 0;

        // Note: It is necessary to clear the MODE bits before returning from all
        // ISRs associated with interrupts that can be used to wake the device from
        // PM{1 - 3}.
        SLEEP &= ~0x03; // SLEEP.MODE = 00
        P0_1 = ~P0_1;
    }

     

    Adding a reset of the WOR timer before entering PM2 will make the LED toggle every 510 ms instead of every 500 ms.
    This is because the sleep timer has run for 10 ms before resetting it.

    To compensate for this you can read the sleep timer and update EVENT0 before entering PM2. This is shown below:

    void main(void) {

        unsigned char temp;

        CLKCON = CLKCON_13_MHZ_HSRCOSC;
        while(!(CLKCON & 0x40)); // wait until clock switch has occured (=stable)
        SLEEP |= 0x04;           // power down the unused oscillator

        // Init LEDs for debug purposes
        P1DIR = 0x01;       // P1_0 (Green LED)

        // Update descriptor with correct source
        dmaDesc[0] = (unsigned int)& PM2_BUF >> 8;
        dmaDesc[1] = (unsigned int)& PM2_BUF;

        // Associate the descriptor with DMA channel 0 and arm the DMA channel
        DMA0CFGH = (unsigned int)&dmaDesc >> 8;
        DMA0CFGL = (unsigned int)&dmaDesc;

        // Enable Sleep Timer interrupt    
        // 1. Clear interrupt flags
        STIF = 0;             // IRCON.STIF = 0
        WORIRQ &= ~0x01;      // WORIRQ.EVENT0_FLAG = 0

        // 2. Set individual interrupt enable bit in the peripherals SFR, if any
        WORIRQ |= 0x10;     // WORIRQ.EVENT0_MASK = 1

        // 3. Set the corresponding individual, interrupt enable bit in the
        // IEN0, IEN1, or IEN2 registers to 1
        // HAL_INT_ENABLE(INUM_ST, INT_ON);  // IEN0.STIE = 1

        // 4 . Enable global interrupt by setting the IEN0.EA = 1
        INT_GLOBAL_ENABLE(INT_ON);

        CLKCON = CLKCON_26_MHZ_XOSC;
        while (CLKCON & 0x40)   // Wait until the clock switch has occured (=stable)
        SLEEP |= 0x04;          // Power down unused oscillator
        
        WOREVT1 = 0x43; // tEVENT0 = 0.5 s
        WOREVT0 = 0xB5;

        while (TRUE) {

            delay_10_ms(); // Introduce delay to stay awake some time before going back to PM2

            // Selesct HS RCOSC as system clock and turn off the HS XOSC.
            CLKCON = CLKCON_13_MHZ_HSRCOSC;
            while(!(CLKCON & 0x40)); // wait until clock switch has occured (=stable)
            SLEEP |= 0x04;           // power down the unused oscillator
            
            STIE = 1;  // IEN0.STIE = 1

            DMAARM = 0x01;

            // Read WORTIME to find out how long it has run (~10 ms in this case) and calculate the new
            // SLEEP time (500 ms – 10 ms. The 4 is added since this is the reset value of the timer
            event0 = WORTIME1 << 8;
            event0 |= WORTIME0;
            event0 = _0_5_s - event0 + 4;

            WORCTRL |= 0x04;            // Reset Sleep Timer
            temp = WORTIME0;            // Align with positive 32 kHz clock edge
            while (temp == WORTIME0);
            temp = WORTIME0;            // Align with positive 32 kHz clock edge
            while (temp == WORTIME0);

            // Write back the new value
            WOREVT1 = event0 >> 8; // Set EVENT0, high byte
            WOREVT0 = event0; // Set EVENT0, low byte

            // Disable flash cache
            MEMCTR |= 0x02;

            // Enter PM2
            SLEEP |= 0x02;
            asm("NOP");
            asm("NOP");
            asm("NOP");
            if(SLEEP & 0x03) {
                asm("MOV 0xD7,#0x01");  // DMAREQ = 0x01;
                asm("NOP");             // Needed to perfectly align the DMA transfer
                asm("ORL 0x87,#0x01");  // PCON |= 0x01;
                asm("NOP");
            }
        }
    }

    BR

    Siri

  • Hi Siri

    Thank you for your effort trying to solve the problem I have. What you describe is how to have a ST that follows a certain 0.5 s timetick correctly, but my problem is to read WORTIME0/1 (what is elapsed time) if we have an external interrupt that wakesup the uP (eg. a input pin irq). This is because I have to be able to register an event on an Input-pin, but also be able to sleep a specific minimum time (if interrupted). That is why I need to know exactly how long we already have been sleeping if interrupted, hopefully by reading WORTIME0/1 (which I haven't been able to until now. After Input-pin irq, the WORTIME0/1 does not contain the elapsed time I expect).

    Please do not hesitate to ask if my problem description is unclear :-)

    Regards Allan