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.

How to preserve USB controller during deep sleep mode?

Other Parts Discussed in Thread: TM4C123GE6PM

I'm trying to put a TIVAC TM4C123GE6PM into deep sleep mode when there isn't really anything going on.  The catch is I still need to service a timer and the USB controller.  In my main loop I'm using this:

		if((WTimer0_running || ADC_running) == 0){
			SysCtlDeepSleepClockSet(SYSCTL_DSLP_DIV_1 | SYSCTL_DSLP_OSC_MAIN | SYSCTL_DSLP_PIOSC_PD);
			TimerLoadSet(WTIMER1_BASE, TIMER_A, 1600000);
			SysCtlDeepSleep();

		}

I thought this would preserve the clock for USB since the controller uses the 16Mhz main oscillator I've got.  I also thought, from reading the users manual and what not, that this would also allow the Timer module to just pick up the clock at 16Mhz so while the ARM is asleep it is still running, but now at/expecting the correct 16Mhz clock speed.  This isn't quite working yet.  The code works fine without this....I'm strictly doing a sleeping test.  My initial clock configuration looks like this:

SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);

I reconfigure the clock with this same instruction when the ISR for the timer hits so the rest of the instructions run at 16Mhz then it goes back to sleep in the main loop. 


What am I missing here?  Is there a document that discusses the sleep modes in more detail?  I can't seem to get a good understanding form the datasheet and the peripheral driver guide.

Thanks,

Rob

  • Hi Rob,

    Are you setting the Deep Sleep Clock gating register (DCGC). You use the API SysCtlPeripheralDeepSleepEnable to ensure that the clock does not gated in Deep Sleep Mode

    Regards

    Amit

  • I tried this and it didn't help.  I think that was what you were talking about.

    		if((WTimer0_running || ADC_running) == 0){
    			
    
    			SysCtlPeripheralClockGating(true);
    
    
    			SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_USB0);
    			SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_WTIMER0);
    			SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_WTIMER1);
    
    			TimerLoadSet(WTIMER1_BASE, TIMER_A, 16000);
    			SysCtlDeepSleep();
    
    		}

    The data sheet says peripherals that are clocked are enabled in the peripheral specific DCGC registers which I've check and they DCGCUSB, DCGCWTIMER0,1 are set to 1.  It also says that the system clock source is specified in the DSLPCLKFG register.  When I check this it is set to all 0's leading me to believe the main oscilator is being used for the system clock.  The divider field override is also set to all 0's.  I would think that at least my timer would be blinking because the 16Mhz main oscillator should be driving WTimer1.  I've attached a jpg so you can see my RCC, RCC2, and DSLPCLKCFG registers in case that helps.  Any advice is appreciated and thanks so much for the reply Amit.  I'll keep working away and upon finding a solution I'll post it up.

    Note*  I actually get a "FaultISR' trap when I execute the above code.  The culprit seems to be the:

    SysCtlPeripheralClockGating(true);

    Thanks,

    Rob

  • Hi Rob,

    Is the RCGC for USB and WDTIMERx set. The function SysCtlPeripherlEnable cannot cause a FaultISR.

    Can you send a snapshot of RCGC, RCC, RCC2 and DSLPCLKCFG register before executing SysCtlDeepSleep?

    An important thing to note is that there is an Errata which places restriction on use of MOSC as deep sleep clock source.

    Regards

    Amit

  • Sorry I totally forgot to attach the screens of my registers to the last post.  Here they are!

    I'll check out the errata tonight.  Basically I run at 80Mhz normally with USB and 2x timers and need to know how to go into deep sleep mode properly to allow the functioning of those three to continue.  I'm hoping this errata doesn't say this is impossible.  It seems like it would be pretty straight forward. 

    Is my understanding correct in that entering deep sleep mode leaves the PLL on, but unused.  You then shut off the clocks to the cpu/memory and only pass the system clock to the peripherals that are active?  Also does the system clock (clock that runs everything in sleep mode) become the 16Mhz PIOSC or MOSC without the use of the PLL hence the note in the data sheet about peripherals acting differently potentially?  This would mean that my clock would be 16Mhz regardless since the external crystal is also 16Mhz..  Am I also correct in assuming that the only real modifications you can do to your system clock in deep sleep mode is divide it?

    If this is correct it seems like you shouldn't have to use any clock gating or special peripheral enables (SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_USB0) since the system clock gets passed to the active peripherals anyways. 

  • Hi Rob,

    In Deep Sleep the Main PLL is switched OFF but the USB PLL remains ON. The clock shut off to the peripherals is decided based on DCGC register and not RCGC when in deep sleep. Please note that RCGC and DCGC register are at offset 0x400FE600 and 0x400FE800. The one shown in the snapshot are not the one's that are used if you are using TIVAWare. You will need to do a memory dump of the above mentioned register address space.

    The System Clock in Deep Sleep is decided by DSLPCLKCFG register.

    Also to note is Sleep is another low power state but not as low power as Deep Sleep.

    Is it possible for you to attach the code as well?

    Regards

    Amit

  • Here are the two data files both starting at the specified offsets.  I also took two more screen shots of the registers at those offsets so you can see that the WTimer0,1 are clock-gated for both RCGC and DCGC.

      3542.0x400FE600 dump.dat

    5758.0x400FE800 dump.dat

    I can't really upload the code that I'm using as it's sensitive.  I have figured out that the arm interrupts on the WTimer1 ISR which is all I'm working with right now.  I've basically reduced this to a classic blinking LED.  If I set a break point in the ISR for WTimer1 it hits it.  The problem is after that it never comes back. 

    With this code set tonight:

    			SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_USB0);
    			SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_WTIMER0);
    			SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_WTIMER1);
    			SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_ADC1);
    
    			SysCtlPeripheralClockGating(true);
    
    			TimerLoadSet(WTIMER1_BASE, TIMER_A, 8000000);
    			SysCtlDeepSleep();

    and this ISR:

    	TimerLoadSet(WTIMER1_BASE, TIMER_A, 40000000);
    
    
    
    	GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, ~GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_1));
    
    	// Clear Interrupt Flag
    	TimerIntClear(WTIMER1_BASE, TIMER_TIMA_TIMEOUT);

    I hit the ISR once and don't get stuck in a fault ISR, but instead wait in a code cpu wait for interrupt in the cpu.c file.  Code block shown below:

    void
    CPUwfi(void)
    {
        //
        // Wait for the next interrupt.
        //
        __asm("    wfi\n");
    }
    #endif

    One thing I need someone to clarify is this.  When I go into the ISR my clock gets changed down to 16Mhz from the external crystal.  I'm ok with this, but what happens when I come out of deep sleep.  Does it jump back to 80Mhz using the PLL like what was initially configured and used before entering sleep or does it stay at 16Mhz?  If this can't be answer without my code do I check the RCGC0,1,2 since they are the runtime config registers?

  • Hi Rob,

    When the Deep Sleep Exit happens, the PLL relocks again for 80MHz. One important thing that you must know is the on TIVA Deep Sleep Entry cannot be done with Debugger Connected. You have to disconnect the debugger and power cycle the board for it to properly execute Deep Sleep Entry-Exit Sequence.

    BTW, is the timer in Periodic Mode? What do you do after Deep Sleep Exit, is it process the Interrupt and go back to deep sleep, in which case I would expect a while loop of some sort.

    Regards

    Amit

  • Hey Amit,


    Good news I finally got this up and running in deep sleep mode.  I didn't know about the debugger preventing the part from properly entering deep sleep mode, but I lucked out and ended up pulling USB (its powered off USB) and that in combination with the code below it started working.  My code executes a few instructions and then is dumped out into the main while loop.  At the bottom of the while loop I've got an if statement that catches and sends the arm back into deep sleep.  Below are the solutions to what I was doing wrong in case some else has a similar problem.  This is geared towards my code so you may have diff. peripherals etc...but the ideas the same.


    1.  Don't call the sleep config API functions multiple times.  I was doing that and it may not have been a show stopper, but it isn't a good idea in general.  I've run into problems doing this with other api calls I know.  Here were the calls I used for setting deep sleep up.

        // Enable sleep mode configurations
        SysCtlDeepSleepClockSet(SYSCTL_DSLP_DIV_1 | SYSCTL_DSLP_OSC_MAIN);
    	SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_USB0);
    	SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_WTIMER0);
    	SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_WTIMER1);
    	SysCtlPeripheralDeepSleepEnable(SYSCTL_PERIPH_ADC1);
    	SysCtlPeripheralClockGating(true);

    2.  Notice I used the:

    SysCtlDeepSleepClockSet(SYSCTL_DSLP_DIV_1 | SYSCTL_DSLP_OSC_MAIN);

    This is because my big problem was that my clock speeds were slowed down so much it took a long time for the Timer to trigger and pull the part out of sleep.  When you go into sleep mode my clock speed was dropped down to 16Mhz off my external crystal MOSC.  On top of that, the deep sleep clock division was set to 16.  This meant my part was configuring the timer at 80Mhz using 80 million as my count to value and the clock was effectively getting dropped to 1Mhz in deep sleep so I wasn't seeing the interrupt trigger because I didn't wait long enough.  I found the bits in the DSLPCLKCFG -> DSDIVORIDE and DSDIVORIDE was set to F or 15.  Thats why I used the above function call in number two to set my clock and the clock division.  This fixed my not triggering problem.  My fault ISR went away once I moved those configs out of my main loop and only called the SysCtlDeepSleep(); call.

    NOTE - if you go in and out of deep sleep mode and you are using timers you have to reconfigure the timers every time you enter and exit sleep mode because (and I don't know why...it seems odd you couldn't configure more clocks) these timers only run off system clock and using sleep mode system clock changes if you use anything from the PLL basically.  I'm not reconfiguring my timer because the change in clock rates gives me different interrupt speed which actually works out to my benefit in my design.


    3.  When using deep sleep with USB I ran into a problem where the USB controller/Stack receives a packet and interrupts upon reception.  In my main loop I process different packets with a switch statement.  From what I've seen, to actually receive a packet and process it in my state machine it takes a couple of iterations of my main loop after the first interrupt from the USB controller/stack.  If you drop into sleep mode right away like I was doing you won't be able to process your data in the packets.  A solution to this for me was to burn up some iterations of the main loop with a little counter.  After a couple iterations (looked like 4-10) I my USB packet processing worked out fine.  I guess at 80Mhz it must take some time to actually get to the point where you can process the packet from the array the USB stack sticks the data in OR the USB stack is non blocking allowing you to drop out of the stack processing for a very short period of time.  If so, you will hit the call to deep sleep before the CPU can get back into the USB stack to finish processing all the USB stuff and dump your received data into the array holding the packet for you to process. 

    Hope this is helpful.  Maybe the TI guys can clarify a bit of what I'm seeing, but things seem to be up and running now.  Using those API calls to setup your clock and peripherals (then enable clock gating) will allow you to just call the deepsleep request where ever you want and you should be good to go.  As Amit says, it will return your PLL settings after it exits sleep mode.  I actually over looked this...this little tid bit is in the data sheet as well.  I think it is in section 5.2.6.3.  All in all this looks like it works really well now!  Thanks for all the help Amit!

  • Hi Robbie,

    Glad to hear it worked.

    For the change of clock setting, there is another thing that you can do, which is use the Sleep Mode. Basically all Deep Sleep API calls need to be replaced with Sleep API call. The advantage of Sleep is that PLL will continue operating so that all such change of timer values can be avoided. The disadvantage of course would be higher Power Consumption.

    Regarding the #3 point, I am not sure that the Interrupt causing exit from Deep Sleep, requires some time for the PLL to come up which is why you need a Delay loop.

    Regards

    Amit

  • Oh ok...it was weird.  If I sent a command over USB to the device it would always work on the third time.  So, when you exit  low power mode you should spin for a short period of time to let the PLL lock? 


    Thanks again for the help I really appreciate it!  I've got another situation with the ADC, but I need to do some more reading before I really start asking questions.  I'm seeing a signal on one channel bleed over to the other channels.  Like if I run the ADC in 8 channel mode and put a square wave on channel one and nothing on the other 7.  If I save the data and look at it I see the square wave bleed over to all the channels and decrease in magnitude as it goes.