Power Modes

Hi guys,

I’ve just ordered a couple of Wixels from a local distributor and I’m looking forward to using them in some wireless sensor node applications.

As noted in your product pages the existing Wixel libraries don’t make use of or support any of the low power modes. After a quick look on Texas Instrument’s website I noticed they have a fairly comprehensive application note with sample code that describes how to enter and resume those modes on the CC2511F32.

If anyone is interested you can find it here: focus.ti.com/lit/an/swra162b/swra162b.pdf

Once they arrive I was planning to give those samples a go but I’m wondering if there are any other caveats to be aware of with the Wixel hardware. I’m not too concerned about the current consumption of the 3.3V regulator but I’m considering using your LV Power Switch to turn off the sensors while the processor is sleeping. Is it worthwhile setting all the unused pins to outputs and leaving them disconnected to reduce consumption?

Anyway I’ll post my results when I get around to trying it and fork the wixel-sdk in the process. Looks like a great product for the price!

Hello.

Some tips for minimizing your Wixel’s power consumption:

You’ll definitely need to turn off the radio and any other peripherals that consume a lot of power. The radio_mac library does not support turning off the radio; if you could modify it to do so, that would be great! But alternatively you could avoid using radio_mac: I think the test_radio_signal_tx app would be a good starting point for the app you run on your sensor nodes, and the radio_sniffer app would be a good starting point for the app you run at the receiver.

We found that the Wixel’s 3.3 V regulator wastes 40-80 microamps if either its input or its output is exposed to power. You said you weren’t too concerned about that, but if you are then you can sever the output pin of the regulator (pin 5, the lower left pin if the USB connector is facing up, shown in the picture below) and power the board directly from the 3V3 line with a 2.0-3.6 V source. You will not be able to easily power the Wixel from USB after this modification.

Another way to save power is to make sure that none of the I/O lines are at some intermediate voltage. They should either be 0 V or VDD, but not in between. By default, all the pins on port 0 and port 1 except P1_0 and P1_1 are inputs with pull-ups enabled, so they are OK (you don’t have to set those pins to outputs). P1_0 and P1_1 do NOT have pull-up or pull-down resistors, so you will want to set those to be outputs. As for Port 2, the default Wixel libraries take care of enabling pull-downs on the yellow and red LED lines so you won’t have to worry about those. But the VBUS_IN (P2_4) line and the P2_0 line are allowed to float to intermediate voltages when USB is not connected, so you’ll want to do something with those lines. I enabled a pull-down on P2_4 and drove P2_0 high in the code below, but there might be other ways to take care of them.

I think the power savings you can get by managing those IO lines correctly is around 5-20 microamps, but I forget exactly how much. It was small compared to the savings we got by severing the regulator, so if you don’t sever the regulator than it might not be worth it to you.

Finally, to minimize the power consumption of the CPU, we put it in to sleep mode 2 (the deepest sleep mode where the chip can still wake itself up). Here is the test code I wrote to set up the I/O lines and go to sleep:

#include <wixel.h>
void main()
{
    P2INP = 0b10001000;
    P0INP = 0;
    P1INP = 0;
    P1 = 0;
    P1DIR |= 3;
    P2_0 = 1;
    P2DIR |= 1;
    while(1)
    {
        SLEEP = (SLEEP & ~3) | 2;    // SLEEP.MODE = 2 : Selects Power Mode 2 (PM2).
        __asm nop __endasm; __asm nop __endasm; __asm nop __endasm;
        if (SLEEP & 3)
        {
            PCON |= 1;    // PCON.IDLE = 1 : Actually go to sleep.
        }
    }
}

By doing all of this we were able to get the current consumption of the Wixel down to 0.5 microamps, just like the CC2511 datasheet claimed.

Sensor Power Control

The P1_0 and P1_1 lines on the Wixel are special high-current lines that are designed to deliver up to 20 mA each, so it would be easiest if you can just power your sensors directly from one or two of those lines. You probably shouldn’t connect the lines together because that increases the risk of a short circuit, but if each Wixel had multiple sensors you could power some of the sensors from P1_0 and some from P1_1.

If you have a sensor that draws more than 20 mA you can use a Wixel I/O line to control a MOSFET that supplies power to the sensor.

The Pololu Pushbutton Power Switches are intended for applications where the power is controlled manually by a human.


I’m glad you are going to fork the wixel-sdk. We’ll definitely take a look at the changes you make and consider pulling it in to the wixel-sdk. Even if we don’t, the code you come up with will probably be useful for other Wixel users, so I’m glad it will be online. Thanks!

–David

Thanks for the very informative reply David,

Hopefully I’ll get a chance to try all this out in the next few weeks and post back with my results.

Hi guys,

Just received my pair in the mail today - can’t believe how tiny they are! I’m particularly impressed with the Pololu Wixel Configuration Utility.

I’ve had a bit of a play around using the samples from the TI application note but they only seem to hang the microcontroller when going to sleep.

This is what I’ve got at the moment:

sleep.h
#ifndef _WIXEL_SLEEP_H
#define _WIXEL_SLEEP_H

#include <cc2511_map.h>
#include <cc2511_types.h>

ISR(ST, 1);
void sleepMode(uint16 seconds);

#endif

sleep.c

#include <sleep.h>

ISR(ST, 1)
{
   // Clear IRCON.STIF (Sleep Timer CPU interrupt flag)
   IRCON &= 0x7F;
   // Clear WORIRQ.EVENT0_FLAG (Sleep Timer peripheral interrupt flag)
   // This is required for the CC111xFx/CC251xFx only!
   WORIRQ &= 0xFE;
   
   SLEEP &= 0xFC; // Not required when resuming from PM0 
}

void sleepMode(uint16 seconds)
{
   unsigned char temp;
   unsigned short desired_event0;

   WORCTRL |= 0x03; // Set Sleep Timer to the lowest resolution (1 second)   
    
   // Power up [HS RCOSC] (SLEEP.OSC_PD = 0)
   SLEEP &= ~0x04;
   // Wait until [HS RCOSC] is stable (SLEEP.HFRC_STB = 1)
   while ( ! (SLEEP & 0x20) );
   // Switch system clock source to HS RCOSC (CLKCON.OSC = 1),
   // and set max CPU clock speed (CLKCON.CLKSPD = 1).
   CLKCON = (CLKCON & ~0x07) | 0x40 | 0x01;
   // Wait until system clock source has actually changed (CLKCON.OSC = 1)
   while ( !(CLKCON & 0x40) );
   // Power down [HS XOSC] (SLEEP.OSC_PD = 1)
   SLEEP |= 0x04;

   desired_event0 = seconds;
   
   WORCTRL |= 0x04; // Reset Sleep Timer
   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
  
   // Set SLEEP.MODE according to desired PM, e.g. PM2.
   SLEEP = (SLEEP & 0xFC) | 0x02;
   // Apply three NOPs to allow the corresponding interrupt blocking to take
   // effect, before verifying the SLEEP.MODE bits below. Note that all
   // interrupts are blocked when SLEEP.MODE ≠ 0, thus the time between
   // setting SLEEP.MODE ≠ 0, and asserting PCON.IDLE should be as short as
   // possible. If an interrupt occurs before the NOPs have completed, then
   // the enabled ISR shall clear the SLEEP.MODE bits, according to the code
   // in Figure 7.
   __asm nop __endasm; __asm nop __endasm; __asm nop __endasm;
   // If no interrupt was executed in between the above NOPs, then all
   // interrupts are effectively blocked when reaching this code position.
   // If the SLEEP.MODE bits have been cleared at this point, which means
   // that an ISR has indeed executed in between the above NOPs, then the
   // application will not enter PM{1 – 3} !
   if (SLEEP & 0x03)
   {
      // Set PCON.IDLE to enter the selected PM, e.g. PM2.
      PCON |= 0x01;
      // The SoC is now in PM and will only wake up upon Sleep Timer interrupt
      // or external Port interrupt.
      // First instruction upon exiting PM.
      __asm nop __endasm; 
      
      // Power up [HS XOSC] (SLEEP.OSC_PD = 0)
      SLEEP &= ~0x04;
      // Wait until [HS XOSC] is stable (SLEEP.XOSC_STB = 1)
      while ( ! (SLEEP & 0x40) );
      // Switch system clock source to [HS XOSC] (CLKCON.OSC = 0)
      CLKCON &= ~0x40;
      // Wait until system clock source has actually changed (CLKCON.OSC = 0)
      while ( CLKCON & 0x40 );
      // Power down [HS RCOSC] (SLEEP.OSC_PD = 1)
      SLEEP |= 0x04; 
   }
}

Mainline

#include <sleep.h>

void main()
{
    systemInit();
    
    while(1)
    {
        boardService();
        LED_YELLOW(1);
        sleepMode(10); // 10 seconds
        LED_YELLOW(0);
        sleepMode(10); // 10 seconds
    }
}

Most of the code in sleep.c is copied directly from the application notes I referenced in my initial post. Any idea where I’m going wrong?

I’m glad that you like the Wixel and its configuration utility!

According to the Sleep Timer section of the CC2511 datasheet, specifically section 12.8.4, you need to modify WORIRQ and IEN0 in order to actually enable the sleep timer interrupt. I don’t see any code in your program to do that. The datasheet does not explicitly say that enabling the interrupt is necessary in order to wake up, but I think it is worth trying.

I didn’t see any other problems in the code, but I didn’t check every line of it.

–David

Thanks David,

That was exactly what I was missing - it wasn’t particularly clear from the datasheet but I should have guessed both WORIRQ (Event0) and IEN0 (Sleep Timer) would need to be enabled. I’ve got a test application up and running and have integrated it into the Pololu libraries. All seems to be working well but I’m not sure if I’m doing a bit too much in terms of saving, disabling and then restoring interrupts when going into PM2 and whether that’s something the ‘user’ should be doing if they need it. I’ve left everything enabled for PM1 and PM3 - with PM3 requiring an external interrupt to wake up as the sleep timer is not supported in that mode. The USB device will disappear in PM2 and PM3 but the datasheet seems to suggest it could suspend and still be visible in PM1 but I haven’t been able to achieve that yet.

Anyway it’s all on my fork of wixel-sdk found here if anyone is interested: github.com/lummo/wixel-sdk
The code is more or less an implementation of Design Note DN106 with all the pieces put together - I’ve kept in the TI comments and added my own where appropriate. The strange parts with the DMA controller are recommended to work around a bug detailed in errata note SWRZ014: focus.ti.com/lit/er/swrz014c/swrz014c.pdf.

I’m glad you were able to get it working, and that you have made a fork of the wixel-sdk on github to share your contribution with the community!

Just a couple things to note:

Instead of calling switchToHSXOSC() you could call boardClockInit(), which basically does the same thing except that it chooses explicit values for all the bits in CLKCON and it enables flash caching and prefetching. I noticed you disabled the flash cache but never re-enabled it. The flash cache is very important because it makes almost every instruction run faster by 1/24 of a microsecond, and the delay routines in delay.s depend on it.

I wanted to save DMA channel 0 for the user because its configuration is more flexible than the configuration of the other channels. DMA channel 1 is being used by the radio, but you could use any of the remaining channels (2,3,4). DMA channels 1-4 are assigned to different purposes via dma.h and dma.lib.

The radio_mac library (which is used by all the higher-level radio libraries) uses WORCTRL, WOREVT1, and WOREVT0, because they are used to specify a radio RX timeout. Therefore, the values you assigned to those registers in sleepInit() will get over-written before the Wixel actually goes to sleep. We should add a function to radio_mac that shuts down the radio cleanly and allows you to use those registers. Unfortunately, the radio_mac library is currently pretty confusing because it deals with communication between an ISR and the main loop, DMA arming and disarming, and I had to add a lot of workarounds to it because of various quirks of how the CC2511’s radio works.

A while ago, I started working on getting USB suspend mode to work. You can see the results in the usbSleep function in usb.c. I think it mostly did work, except that it doesn’t yet have a way of waking up when VIN power is connected, and if you plug the Wixel in to a sleeping computer the Wixel usually fails to go in to suspend mode. This function is not being used in any of the apps yet.

Good luck on your project!

–David

Thanks for the feedback David,

I had meant to re-enable the flash cache but had obviously missed turning it back on! I’ve incorporated your suggestion of using boardClockInit() rather than my own function which has solved that problem and is likely to make things easier in the future. Noticed the difference straight away with the test application - the yellow LED is now flashing at the expected speed after sleeping.

I also hadn’t realized that the radio_mac uses the sleep timer. I’ve removed some of the setup (WORCTRL) from sleepInit and restored the DMA0 descriptor after sleeping but I’m sure there’s more that can be done there (like using DMA 2,3 or 4). Once I get a chance to do some testing with sleep modes + the radio I should be able to make some more progress as my intention is for this to be used with wireless sensor nodes so compatibility is a must.

I read this post with interest, as I too would like to be able to reduce Wixel power consumption by keeping everything in a low power state most of the time, and having both halves wake up every second or so to determine if there is anything to do.

Have there been any updates or progress in getting Lumos’ sdk fork into the mainstream?

Regards,

Frank

No, there has been no progress. --David

I recently downloaded and installed lummo’s neat power management version of the wixel sdk, and did some simple current drain experiments with a power supply and a cheap Micronta multimeter.

Using Lummo’s ‘Test_Sleep’ application:
Sleep Mode 1: About 6.4mA
Sleep Mode 2: About 2.4mA

Using the unmodified ‘IO_repeater’ app: About 23mA

Then I modified the IO_repeater app to go into sleep mode 1 or 2 for 5 seconds every 60,000 passes through the main while(1) loop, and watched the current drain.

Not in sleep mode: about 23mA (same as unmodified app)
Sleep mode 1: about 9.75mA
Sleep mode 2: Drops to 14.5mA for 1-2 sec, then 0.05mA for the rest of the period.

Anyone else tried power management using lummo’s version of the sdk? I’m not particularly conversant with the restrictions associated with the two modes - does anyone (lummo??) have a short explanation as to why one Sleep mode should be preferred over the other (other than the obvious reason that Sleep Mode2 seems to result in a MUCH lower current drain)?

On a related note: The modified sdk and the test_sleep app were both very easy to get, install, and test - thank you Lummo!!. The only problem I had was that the modified sdk doesn’t have David’s recent changes to suppress the known problem of Eclipse warnings about unresolved symbols.

David: This capability seems like a very nice addition to Wixel’s capabilities. Any chance you’ll be able to merge lummo’s work into the mainline wixel sdk so we can benefit from both yours and lummo’s efforts?

TIA,

Frank

That’s where git can help you out. After installing git, you can do:

git clone -o pololu git://github.com/pololu/wixel-sdk.git
cd wixel-sdk
git remote add lummo git://github.com/lummo/wixel-sdk.git
git pull lummo master

Then you get the latest official version of the Wixel SDK plus Lummo’s changes, merged nicely together. I just tried this myself and luckily there were no conflicts to resolve.

I’ll probably merge it in once we have something that can work nicely with the radio_mac library. The issues I mentioned a few posts ago with the sleep timer registers have not been resolved as far as I know. But the beauty of git is that you don’t have to wait for me: you can merge the changes yourself as I described above, or you can make your own fork of the Wixel SDK on github to share your changes/merges with the world.

By the way, forum user “slasi” is working on a feature of radio_mac to shut down the radio nicely so once that is working we might have something we can merge.

–David

Hmm, I might try that, although the last time I tried to install git, I got well and truly stuck to the tar-baby! :wink: Assuming I do get it installed correctly, can you give me a hint about where things wind up when the above commands are executed? I don’t see any directory information in your example, so I’m a little leery about running the command and then having to search my disk to find the resulting sdk folder. Also, I am currently using your ‘pololu-wixel-sdk-af04149’ which I thought was the latest official version (and the one containing the Eclipse fixes). Do I need to substitute that name in for ‘wixel-sdk’ above, or will the commands you show do the trick?

TIA,

Frank

The sequence of commands above creates a wixel-sdk subdirectory inside your current directory. Type “pwd” to see what your current directory is.

The commands I wrote will work. No substitutions are necessary.

–David

I haven’t had a chance to get back to this after the initial effort but thought I’d post some information I also sent to Frank directly.

If you have a look at page 11 of the CC2511F32 datasheet it tells you the differences between the different power modes:
Sleep mode 1 has the digital regulator on and an expected consumption of 220uA (wake up time of 4uS)
Sleep mode 2 has the digital regulator off and an expected consumption of 0.5uA - 1uA (wake up time of 100uS)

As far as I can tell the main impact for most applications is how fast it can wake up in those modes.
So if that’s not a major issue then there’s no reason not to go into sleep mode 2.
There’s probably a few other caveats but if you’re just using it to lower power usage and do nothing else during that time then you might as well go to mode 2.

I have been meaning to get around to fixing up the compatibility between the radio and sleep functionality but haven’t had much time for personal projects lately - hoping to get back to it in the next few weeks / months though.

Lumos,

Thanks for the explanation, and THANKS for the effort to implement these sleep modes into the wixel-sdk. Your current drain numbers are considerably lower than mine - to be expected since I was measuring the entire Wixel and your numbers are just for the CC2511 - but the difference between sleep modes 1 and 2 are consistent with my findings. For my application, the difference in wakeup times is insignificant, so I plan to implement sleep mode 2.

Frank

Hi,

I’ve done a merge of Lumos’ code and the latest Wixel SDK at github.com/slasi/wixel-sdk

I’ve made a few small changes to Lumos’ code. One of them being, removing the initSleep() function from the systemInit() function. This means that the app has to call initSleep() right after it calls systemInit(). I did this to minimize dependencies, so apps that didn’t need sleep wouldn’t need to link with the sleep code.

I’m also looking into adding suspend/resume to the the radio and hope to have something working in the next few days.

-Shehryar

I’ve added functions to put the radio to sleep before entering power modes. See the test_radio_sleep app for example code.

github.com/slasi/wixel-sdk/tree/radio

Please let me know if you find any bugs.

-Shehryar

Shehryar,

Thanks a lot for doing this. However, for us few non-Git users left in the universe, would you mind describing how to download the test_radio_sleep app, or maybe add it to the slasi-wixel-sdk-ad15b0c ZIP file (I have that one and can re-download it if necessary). :wink:

TIA,

Frank

This link should download a zip file:

github.com/slasi/wixel-sdk/zipball/radio

I’ll merge it back to my main branch as soon as I have done some more testing.

-Shehryar