Clocking and PLL0 example

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Clocking and PLL0 example

2,634 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Rob65 on Mon Jul 04 12:21:22 MST 2011
The FreeRTOS port that is included with the new LPCXpresso 4.0.5 does not use CMSIS per default. This means that the system is running on the 4 MHz internal RC oscillator without a PLL active.
I found this is a nice moment to remove the need for CMSIS in my application and take control of power and clocking myself.

Clock switching and PLL programming is not always as easy as it looks. As soon as you make one little mistake the system stops working and debugging is not possible anymore.

I tried to create a simple interface using just two functions:

[LIST]
[*]LPC_UseOscillator(osc)
switches the clock to the requested oscillator, being LPC_INTRC_OSC, LPC_MAIN_OSC or LPC_RTC_OSC.
[*]LPC_SetPLL0(cclk)
Programs PLL0 with the proper values to get the requested cclk (cpu clock).
cclk must be in range of the defined values in the table (I currently defined 10-100 MHz in 10 MHz steps).
[/LIST]
Both functions also reprogram the SysTick counter such that FreeRTOS keeps running at a 1 msec tick interval (as defined through configTICK_RATE_HZ)

It all seems to work, except for the RTC_OSC ...
I thought I had RTC_OSC working this morning but somehow I cannot reproduce this anymore...

Some explanation with the code could be nice.
I tried to capture most of the stuff in comments but there are some key points that might be worth mentioning:

[LIST]
[*]Before switching oscillators the PLL is disabled and stopped.
changing the input clock with a running PLL will unlock the PLL which results in a very strange clock behaviour.
[*]The same goes for PLL (re)programming. During switching the PLL will unlock and that's not something we like to present the Cortex core with.
[*]During clock switching or PLL repogramming the SysTick is disabled.
[*]The values needed to reprogram the PLL are given in the tables MainOscPLL0conf, IntRCOscPLL0conf and RTCOscPLL0conf.
Add values when needed.
[/LIST]
Adding a new value is easy, just calculate the numbers needed.
Suppose we want to have a frequency of 1.8432 MHz (for thos who still remember: that's 16 * 115200, resulting in a very nice clock for the uart).

[LIST=1]
[*]Find a Fcco frequency that is in range (275 - 550 MHz):
200 * 1.8 MHz = 368.64 MHz
[*]Find values for N that result in an integer M value :D
M = Fcco x N / (2 x Fin)
with a 12 MHz crystal: Fcco / (2 x Fin) = 15.36
N = 14 gives an M of 215.04 which is very close but
N = 25 gives an M of 384
[/LIST]
Check with the formula in the user manual:
Fcco = (2 x M x Fin) / N = (2 x 384x 12) / 25 = 368.64

Now with a CCLKCFG of 200 we get a cclk of: 368.64 / 200 = 1.8432 MHz

Since this frequency may be too slow, we can also define the cclk frequencies that are 2x 4x or 8x this value. Just use CCLKCFG of 100, 50 and 25

It all seems to work properly, switching back and forth between the main crystal and internal RC oscillator and programming the PLL at different values all behaves as expected.
Oh ... did I mention the PLL main oscillator values are for a 12 MHz crystal ???
You will need to change the table when using a different crystal.

Regards,

Rob
0 Kudos
Reply
1 Reply

2,173 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Rob65 on Fri Jul 08 23:27:06 MST 2011
Just discovered that I had forgotten to include the .c files ...

Meanwhile I have progressed a bit further to get more insight in power management. NXP's Application note (AN10915 "Using the LPC1700 power modes") is a nice document to look at when creating battery operated devices.

There are some very easy things you can do to preserve power:

[LIST=1]
[*]Reduce cclk, the SystemCoreClock
[*]Disable USB
[*]Disable peripherals that are not used in PCONP
[*]Stop the CPU clock when it is not used.
[/LIST]
My demo board contains an lpc1754, a DisplayTech 64128N LCD and a 24C08 EEPROM and all is power by 5V using a IRU1117-33 linear regulator.
Using a few simple steps I was able to reduce the board's supply current from 59 to 27 mA when running at 100 MHz (rediculous speed).

[B]PLL Programming[/B]

My first step was to look at PLL programming.
What is the impact of cclk on the current drawn from the chip. Running from the 4 MHz internal base clock, the current is 7.9 mA. With cclk at 10 MHz 16.7 mA and with 100 mA 59.a mA - so it may be clear that there is a relation between cclk and power consumption.
The higher cclk, the higher the current. [B]No !!![/B]

hav a look at the following numbers:

 cclk   current     Fcco
 (MHz)  (mA)        (MHz)
 
  10     16.7       280
  20     23.2       360
  30     27.7       360
  40     33.1       400
  50     44.8       400
  60     [COLOR=Red][B]41.0[/B]       [B]360[/B][/COLOR]
  70     [COLOR=Red][B]37.5[/B]       [B]280[/B][/COLOR]
  80     50.4       400
  90     [COLOR=Red][B]46.2[/B]       [B]360[/B][/COLOR]
 100     59.1       400
 
When looking at 50 MHz and 70 MHz there we see that at 70 MHz less current is used: 40% more speed, 16% less current.
The Fcco values could give you a hint: lower Fcco consumes less power (sound logical to me).
So some optimalizations can be done there. When you do not need a CPU speed to be exact a certain round value, then why not use one PLL setting and only use the cclkcfg to lower the frequency.

[B]Do we need USB ?[/B]

If we need USB, the power limits are relaxed a bit. USB hosts mostly have large batteries we can operate from. If we do not use USB we can easily save 4 mA current by disconnecting the clock to the USB block (apparently programming the PCONP bit only stops the peripheral clock to the USB block, not the USB clock...).
AN10915 tells us we can have PLL1 connected but not enabled. This differs from the User Manual where it is clearly stated that the result of this is the same as not connecting so that must be a bug in the UM (since it does work).

[B]Looping Idle[/B]

A lot of times we see stuff like
while (1) ; 
or any other form of a while loop where we are waiting for something to happen. If the event we are waiting for is interrupt driven (and the event is updated within the interrupt routine) then we can also do
while(1) __WFI;
With any RTOS you should be able to define the point where the OS is just waiting for an event to happen. The __WFI will put the Cortex M3 in sleep mode, stopping the clock to the core and its peripherals.
There are (at least) two queues in the OS; one with ready or active tasks and one with waiting tasks. If the ready list is empty, the OS is just waiting for an interrupt to generate an event that places a task in the ready list.
When you find this loop the just add the wfi there.

Here is where you could do this in FreeRTOS 7.0.1 (just after line 2149 in tasks.h in the FreeRTOS project in LPCXpresso 4.0.5):
static void prvCheckTasksWaitingTermination( void )
{
    #if ( INCLUDE_vTaskDelete == 1 )
    {
        portBASE_TYPE xListIsEmpty;

        /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called
        too often in the idle task. */
        if( uxTasksDeleted > ( unsigned portBASE_TYPE ) 0 )
        {
            vTaskSuspendAll();
                xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination );
            xTaskResumeAll();

            if( xListIsEmpty == pdFALSE )
            {
                tskTCB *pxTCB;

                taskENTER_CRITICAL();
                {
                    pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xTasksWaitingTermination ) );
                    vListRemove( &( pxTCB->xGenericListItem ) );
                    --uxCurrentNumberOfTasks;
                    --uxTasksDeleted;
                }
                taskEXIT_CRITICAL();

                prvDeleteTCB( pxTCB );
            }
        } else
[COLOR=Red][B]        {
            asm volatile ("wfi");
        }
[/B][/COLOR]    }
    #endif
}
[B]PCONP: stop clocks to unused peripherals[/B]

The last (easy) thing to do is to stop clocks to unused peripherals.
I did some measurements with only the RTC and GPIO clocked. Saves a bit of power but not that much.
Here is the final table of all measurements I did:
 cclk   current USB off WFI  PCONP
 (MHz)  (mA)     (mA)  (mA)   (mA)
 
   4      7.7    7.7    6.9    6.5
  10     16.7   12.6   10.2    9.9
  20     23.2   18.2   13.1   12.0
  30     27.7   22.9   15.3   14.5
  40     33.1   27.8   17.8   16.8
  50     44.8   40.0          21.9
  60     41.0   36.6   21.8   20.3
  70     37.5   34.2   20.3   18.9
  80     50.4   45.8   26.4   24.4
  90     46.2   42.0   24.3   22.5
 100     59.1   54.7   30.6   27.3
 
Stopping the USB clock is a very huge power saver when running at lower frequencies since this saves ~4 mA independent of cclk. Adding the wfi will save power depending on your application: I measured with a small program with two tasks (one toggling an I/O pin, the next one displaying the frequency on my LCD). Bigger tasks that do more work will use more power.

Attached is my clocking code to program the oscillators and PLLs.
You may need to change this to suit your own needs, I've changed the license to allow any LPCXpresso user to use and modify this code as you like.
Just one thing: if you discover something that might be worth to this community, please spread the word!

Have fun!

Rob
0 Kudos
Reply