Looking at the forums, quite a few people seem to have problems with getting the PIC32 to operate at low power, many including me have spent hours wondering why when I issue a PowerSaveIdle() or PowerSaveSleep() command the device is still consuming tens of milliamps.
Like almost everything when your doing embedded design its normally down to a few stupid assumptions and oversights, so if you want a few tips on getting your pic32 to play happily in the microamp range read on..
For a battery powered device to work for a decent length of time, say a week of normal use, we need to do a little maths. regarding power source (AA batteries) boost converter efficiencies and minimum operational voltage and expected usage of the different modules, also how long the user is doing real work against doing nothing. Once you have done this you normally find that most handheld devices spend 99.9% of their time waiting for a user response, during that time you are generally wanting to be consuming as little power as possible, if possible during this time you will also we checking for timeouts such as if the user has decided to go home for the night so you can safely power off the device. If you can get your microcontroller and devices under 50uA during sleep then you can generally dispense with power switches, if you cannot achieve this for any reason then you want the ability to power off the devices when not required.
The notes below refer to a version of my data collection terminal schematic shown below, it has all the problems above. Because the WiFi and Barcode parts chosen are not that power efficient and do not have very good power management features, the terminal switches off power to them when not in use using P channel MOSFETs, on the Vcc line, the buzzer and backlight are also MOSFET controlled, thus when powered off we should only have to worry about the PIC32, the LCD power consumption or do we?
Lets assuming you’ve done some operation in the above circuit and now want the pic to drop into a low power mode and wait for some event like an interrupt or keypress. You generally issue either a PowerSaveIdle() or a PowerSaveSleep() command.
Before you do this you will have first turned off all the interrupts you dont want to wake the device as any interrupt (with a priority greater than the CPU) will do this. This is the first mistake most people often make and it is easy to check by issuing an asm(“DI”) instruction before the powersavesleep() to ensure nothing else has woken it such as a timer, if you do this and the consumption goes down then all well and good, but what if it doesn’t go down or only drops by a few milliamps, is the pic faulty, are microchip lying?
Its usually only mentioned in passing, but the actual biggest consumer of power is your IO Pins, input pins generally are not the issue (as long as they are tied to a high/low state and not floating), however unless your output pin is driving a high impedance source such as a MOSFET (like above) then you are very likely supplying current into whatever you are driving and this includes such things as UART transmit pins, SPI enables, etc. For example in the circuit I remove power from the barcode MT700 module by setting M2’s gate high, I then remove power from the Wifi module by setting M1’s gate high, the backlight and buzzer are also completely disabled by setting their gates low (N-Channel MOSFETS) I then issue a PowerSaveIdle() and zap the power drops to under a milliamp?
Actually no, if thats all I did above the power actually only drops from 150mA to about 50mA! So why is that? Basically because I have forgot to also account for the pin states of the other inputs to these modules. Both devices are have active low resets, triggers and tx pins, this means the PIC which has these pins high during normal operation is now sourcing the full current the pin can sink into these pins when the power is removed (dohhh!). If we set these pins to the same level (ie set them to 0) then the power drops to 7mA. This can also be achieved by using resistors in series to unknown inputs to reduce the current the port will sink/source in an unknown state.
Now, 7mA is better but its still going to waste your battery 10 times as quick, as say, 700uA which should be achievable. So discarding the LCD (which plays good actually takes about 400uA normally) where is the rest of this current going?
Strangely (and maddeningly) enough as I use the ICSP lines for other functions, about 4mA of that 7mA is actually being drawn by my PICKIT programmer when it is connected, which it normally is during development. This little nugget of information would have saved me hours of work a few years back! So really your hardware is only actually taking about 2-3mA.
We are still using 2-3mA because PowerSaveIdle() removes the CPU clock but the peripherals UART, SPI, ADC, I2C etc are still powered and running at the peripheral clock frequency. Luckily the PIC32 lets you power down unused peripherals, eg I use:
PMD1bits.AD1MD = 1; // ADC Pwr off PMD1bits.CTMUMD = 1; // CTMU Off PMD1bits.CVRMD = 1; // CVR Off PMD2bits.CMP1MD = 1; // Comparator 1 off PMD2bits.CMP2MD = 1; // Comparator 2 off PMD2bits.CMP3MD = 1; // Comparator 3 off PMD3bits.IC1MD = 1; // Input compare 1 off PMD3bits.IC2MD = 1; // Input compare 2 off PMD3bits.IC3MD = 1; // Input compare 3 off PMD3bits.IC4MD = 1; // Input compare 4 off PMD3bits.IC5MD = 1; // Input compare 5 off PMD3bits.OC1MD = 1; // output compare 1 off PMD3bits.OC2MD = 1; // output compare 2 off PMD3bits.OC3MD = 1; // output compare 3 off PMD3bits.OC4MD = 1; // output compare 4 off PMD3bits.OC5MD = 1; // output compare 5 off PMD4bits.T2MD = 1; // timer2 off PMD4bits.T3MD = 1; // timer3 off PMD4bits.T4MD = 1; // timer4 off PMD4bits.T5MD = 1; // timer5 off PMD5bits.I2C1MD = 1; // I2C Off PMD5bits.I2C2MD = 1; // I2C Off PMD5bits.SPI2MD = 1; // SPI 2 off PMD6bits.PMPMD = 1; // Parallel Master Port off PMD6bits.REFOMD = 1; // ref clk out off
In my device startup to removes another milliamp or so.
Great, now we are down to around 1-2mA.. Most people would be happy with this, the PIC is working the LCD is working and we can still receive interrupts and get going immediately any work to do and we get a day or so on a battery, but what if there is another way we could squeeze that last milliamp out? Remember removing that last milliamp will potentially double, treble, quadruple the battery life, this is the area where you can now work on a weeks battery life as opposed to a day so it is really worth the effort.
Well issuing PowerSaveSleep() will definately remove that last milliamp and get you down to around 600uA (including the LCD) this is because it removes the peripheral clock too, the CPU can still be woken up by watchdog timer (which uses the LPRC oscillator @ 31.25Khz) or an external interrupt, but remember all other peripherals and there interrupts are off. But what if you still need to do some work, such as say timing the inactivity to power off the device after 30 minutes of no use or timing out the backlight properly after say 10 seconds? Well then you will need to do some more programming.
PowersaveSleep() is the lowest power mode and therefore the best way is to issue that and use the watchdog timer or the keyboard scan lines on an external interrupt to wake the CPU and do a few quick calculations then put it back to sleep. The watchdog timer really should only be enabled before sleep as if it is triggered during normal operation it resets the CPU, the watchdog timer is covered to death in the documentation and internet. But there is also an alternative way which allows you to keep the CPU running and no PowerSaveIdle() or PowerSaveSleep() is required, and this is to change the CPU clock right down to the lowest possible frequency to do the task you want and change it back up again when an external event is detected.
Now you need to be a bit careful if doing this as the Peripheral clock is derived from the system clock and timings for baud rates etc will be thrown out completely when the clock changes, also any code that does run will run really (really) slow so best to turn off all interrupts just in case. The code snippet below drops the power to 680uA, only 20uA more than using PowerSaveSleep() by using the LPRC (Low power Internal RC 31.25Khz) oscillator as the clock source while waiting for a keypress or timeout event then switching back to the normal clock when the event is detected.
uint8 WaitEvent(uint32 timeout) { static uint32 PowerOffTimer,BacklightTimeout;
// power off all modules you can and disable the interrupts asm("di");
// change default states of output pins to peripheral devices so // that we are not accidentally supplying power through them IFS1bits.U1RXIF=0; // clear any current interrupt flags IFS1bits.U2RXIF=0; // clear any current interrupt flags
// change down to 31khz for power save while waiting SYSKEY = 0xAA996655; // Write Key1 to SYSKEY SYSKEY = 0x556699AA; // Write Key2 to SYSKEY // OSCCON is now unlocked OSCCONbits.NOSC=0x5; // change to LPRC @ 31.25Khz OSCCONbits.OSWEN=1; while(OSCCONbits.OSWEN); // wait for osc switch to complete SYSKEY = 0; // ensure OSCCON is locked after
// now working really SLOOOOOOOW without interrupts, // so nothing complicated, just wait for keypress do timeouts PowerOffTimer = prg.OffTimeoutMs; BacklightTimeout = prg.DimTimeoutMs; while(1) // loop @ 31Khz.. { BACKLIGHT(BacklightTimeout--); // time out backlight if(KEYSTROBE0 || KEYSTROBE1 || KEY_SCAN) break; // detected key if(!PowerOffTimer--) PWR_HOLD(0); // no activity so power off }
// resume proper clock SYSKEY = 0xAA996655; // Write Key1 to SYSKEY // resume proper clock SYSKEY = 0xAA996655; // Write Key1 to SYSKEY SYSKEY = 0x556699AA; // Write Key2 to SYSKEY // OSCCON is now unlocked OSCCONbits.NOSC=0x2; // restore PRI HS Osc no pll OSCCONbits.OSWEN=1; while(OSCCONbits.OSWEN); // wait for osc switch to complete SYSKEY = 0; // ensure OSCCON is locked // back to normal CPU speed so enable interrupts again asm("EI"); BACKLIGHT(1);//restore any pins to normal states if required // put uart tx pins back under uart control U1STAbits.UTXEN=1; U2STAbits.UTXEN=1; return }
Whether you use this or the watchdog timer depends upon what your doing during the idle time and how frequently you are doing it. I have yet to decide which method to use for the terminal but I thought I’d share these thoughts in case they can help anyone..