Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Anyone managed to get nanoWatt or nanoXatt XLP consumption from a PIC?.

Status
Not open for further replies.
Well, I've only been using XC8 a short while so I'm still learning. Before XC8 I used BoostC for several years and it automatically supports bit access, such as "pmd4.uart1md = 1" or "pmd4 = 1<<uart1md", using the same constants that I would use in assembler. It seemed very clean and intuitive.

Cheerful regards, Mike
 
XC8 won't evaluate my constant expressions to a constant. That is, XC8 generates a whole bunch of bit-shift code for statements like this;
Hi Mike,

Do you have a simple example of this? I tried it and XC8 appears to just generate a constant.

Mike.
 
here's an excerpt and the generated output;

Code:
 /*
  *  x_16f15325_oled.c excerpt (XC8 V2.00 build -1524193055)
  */
     PMD4 = 1<<UART2MD |    // UART2 module off
            1<<UART1MD |    // UART1 module off
            1<<MSSP1MD |    // MSSP1 module off
            1<<CWG1MD;      // CWG1 module off
 
     LATA = 0x00;                   //
Code:
   667                           ;x_16f15325_oled.c: 266: PMD4 = 1<<UART2MD |;x_16f15325_oled.c: 267: 1<<UART1MD |;x_16f1
      +                          5325_oled.c: 268: 1<<MSSP1MD |;x_16f15325_oled.c: 269: 1<<CWG1MD;
   668  06FE  3001                   movlw    1
   669  06FF  00F5                   movwf    ??_main
   670  0700  3000                   movlw    0
   671  0701  181A                   btfsc    26,0    ;volatile
   672  0702  3001                   movlw    1
   673  0703  0A89                   incf    9,f
   674  0704  2F06                   goto    u134
   675  0705                     u135:
   676  0705  35F5                   lslf    ??_main,f
   677  0706                     u134:
   678  0706  0B89                   decfsz    9,f
   679  0707  2F05                   goto    u135
   680  0708  3001                   movlw    1
   681  0709  00F6                   movwf    ??_main+1
   682  070A  3000                   movlw    0
   683  070B  1A1A                   btfsc    26,4    ;volatile
   684  070C  3001                   movlw    1
   685  070D  0A89                   incf    9,f
   686  070E  2F10                   goto    u144
   687  070F                     u145:
   688  070F  35F6                   lslf    ??_main+1,f
   689  0710                     u144:
   690  0710  0B89                   decfsz    9,f
   691  0711  2F0F                   goto    u145
   692  0712  3001                   movlw    1
   693  0713  00F7                   movwf    ??_main+2
   694  0714  3000                   movlw    0
   695  0715  1B1A                   btfsc    26,6    ;volatile
   696  0716  3001                   movlw    1
   697  0717  0A89                   incf    9,f
   698  0718  2F1A                   goto    u154
   699  0719                     u155:
   700  0719  35F7                   lslf    ??_main+2,f
   701  071A                     u154:
   702  071A  0B89                   decfsz    9,f
   703  071B  2F19                   goto    u155
   704  071C  3001                   movlw    1
   705  071D  00F8                   movwf    ??_main+3
   706  071E  3000                   movlw    0
   707  071F  1B9A                   btfsc    26,7    ;volatile
   708  0720  3001                   movlw    1
   709  0721  0A89                   incf    9,f
   710  0722  2F24                   goto    u164
   711  0723                     u165:
   712  0723  35F8                   lslf    ??_main+3,f
   713  0724                     u164:
   714  0724  0B89                   decfsz    9,f
   715  0725  2F23                   goto    u165
   716  0726  0878                   movf    ??_main+3,w
   717  0727  0477                   iorwf    ??_main+2,w
   718  0728  0476                   iorwf    ??_main+1,w
   719  0729  0475                   iorwf    ??_main,w
   720  072A  009A                   movwf    26    ;volatile
   721                       
   722                           ;x_16f15325_oled.c: 271: LATA = 0x00;
   723  072B  0140                   movlb    0    ; select bank0
   724  072C  0198                   clrf    24    ;volatile
Also... when I add assembler code to the XC8 program, none of the SFR bit names are defined/recognized so I have to use the actual bit number;
Code:
/*                                                                      *
 *   test a 10x14 font driver, switch to 'vertical' addressing mode     *
 *                                                                      */
     i2c_start();                   // send I2C 'start'
     i2c_write(0x78);               // send I2C 'address'
     i2c_write(0x00);               // send SSD 'control' Co=0 D/C=0
     i2c_write(0x20);               // send SSD 'address mode" command
     i2c_write(0x01);               // send SSD 'vertical' mode param
     i2c_write(OLED_SETCOLADDR);    //
     i2c_write(0);                  //
     i2c_write(127);                //
     i2c_write(OLED_SETPAGEADDR);   // set page (row) address
     i2c_write(1);                  // start page (row) 1 (line 2)
     i2c_write(2);                  // end page (row) 2 (line 3)
     i2c_restart();                 //
     i2c_write(0x78);               // send I2C 'address'
     i2c_write(0x40);               // send SSD 'control' Co=0 D/C=1
     NVMADR = 0x1E00;               //
     i2c_write(0xFF);               // left border line
     i2c_write(0xFF);               //  "
     i2c_write(0); i2c_write(0);    // column 001
     i2c_write(0); i2c_write(0);    // column 002
     i2c_write(0); i2c_write(0);    // column 003
     i2c_write(0); i2c_write(0);    // column 004
     do {
     unsigned char count = 10;      //
     asm("fontlp:               "); //
     asm("movlb  NVMCON1/128    "); // bank 16                        |16
     asm("bcf    NVMCON1,6      "); // NVMREGS = 0 (not config)       |16    <---
     asm("bsf    NVMCON1,0      "); // RD = 1 (initiate read)         |16    <---
     asm("incf   NVMADRL,F      "); // bump NVMADR                    |16
     asm("lslf   NVMDATL,W      "); //                                |16
     asm("call   _i2c_write     "); //                                |14
     asm("movlb  NVMDATL/128    "); // bank 16                        |16
     asm("rlf    NVMDATH,W      "); //                                |16
     asm("call   _i2c_write     "); //                                |14
     asm("decfsz main@count,F   "); //                                |??
     asm("bra    fontlp         "); //                                |??
     i2c_write(0); i2c_write(0);    // 2 blank lines between chars
     i2c_write(0); i2c_write(0);    //  "
     } while(NVMADRL < 100);        //
     i2c_write(0); i2c_write(0);    // column 125
     i2c_write(0); i2c_write(0);    // column 126
     i2c_write(0xFF);               // right boarder line
     i2c_write(0xFF);               //
     i2c_stop();                    //
 
Last edited:
OK, back to current consumption :D

I went on a short MicroChip course yesterday, about the new SAML10/11 ARM processors - part of which was specifically about low power consumption.

The development boards we were using actually include current monitoring, so you can check the consumption in specific modes. These chips are also 'officially' the lowest powered ones available, according to independent testing in real world conditions - the second best processor (another MicroChip product) only scores about half what this one does.

There's a ludicrous amount of power reducing facilities on the chip, including the capability of shutting down sections of RAM, and three different types of internal regulator for feeding the core.

We were running at 8MHz, and active mode was about 200uA - spec is 25.3uA/MHz - so pretty spot on.

Anyway, Idle mode didn't impress me greatly, only dropping it to about 120uA - but I've never really considered idle mode for what I'm doing anyway - spec is 15.2uA/MHz.

But 'proper' sleep dropped it to around 400nA - this is highly dependent on temperature, and is listed as 500nA at 25C.

There's also a fairly stupid 'off' mode, not really a sleep mode at all - and this reduces the power to 40nA - but you need a hardware reset to start it back up. I suppose it saves you needing to add hardware to actually switch the power off to the chip?.

So pretty impressive devices, and cheap as well (£1.74 from RS) - but pretty complicated as well - if you think modern PIC's have too many clock options, these are even worse :D
 
Well, I've only been using XC8 a short while so I'm still learning. Before XC8 I used BoostC for several years and it automatically supports bit access, such as "pmd4.uart1md = 1" or "pmd4 = 1<<uart1md", using the same constants that I would use in assembler. It seemed very clean and intuitive.

Cheerful regards, Mike

Try pmd4bits.uart1md = 1 (include header for the requisite pic).
 
Try pmd4bits.uart1md = 1 (include header for the requisite pic).
That works fine but it uses more memory so I did it the ugly way;

Code:
/************************************************************************
 *                                                                      *
 ************************************************************************/
   void main()
   { 
     PMD0 = 0<<_PMD0_SYSCMD_POSN |  // Clock network 'on'
            1<<_PMD0_FVRMD_POSN  |  // FVR module off
            0<<_PMD0_NVMMD_POSN  |  // NVM module 'on'
            1<<_PMD0_CLKRMD_POSN |  // CLKR module off
            1<<_PMD0_IOCMD_POSN;    // IOC module off

     PMD1 = 1<<_PMD1_NCOMD_POSN  |  // NCO module off
            1<<_PMD1_TMR2MD_POSN |  // TMR2 module off
            1<<_PMD1_TMR1MD_POSN |  // TMR1 module off
            1<<_PMD1_TMR0MD_POSN;   // TMR0 module off
     
     PMD2 = 1<<_PMD2_DAC1MD_POSN |  // DAC1 module off
            1<<_PMD2_ADCMD_POSN  |  // ADC module off
            1<<_PMD2_CMP2MD_POSN |  // C2 module off
            1<<_PMD2_CMP1MD_POSN |  // C1 module off
            1<<_PMD2_ZCDMD_POSN;    // ZCD module off

     PMD3 = 1<<_PMD3_PWM6MD_POSN |  // PWM6 module off
            1<<_PMD3_PWM5MD_POSN |  // PWM5 module off
            1<<_PMD3_PWM4MD_POSN |  // PWM4 module off
            1<<_PMD3_PWM3MD_POSN |  // PWM3 module off
            1<<_PMD3_CCP2MD_POSN |  // CCP2 module off
            1<<_PMD3_CCP1MD_POSN;   // CCP1 module off

     PMD4 = 1<<_PMD4_UART2MD_POSN | // UART2 module off
            1<<_PMD4_UART1MD_POSN | // UART1 module off
            1<<_PMD4_MSSP1MD_POSN | // MSSP1 module off
            1<<_PMD4_CWG1MD_POSN;   // CWG1 module off

     PMD5 = 1<<_PMD5_CLC4MD_POSN |  // CLC4 module off
            1<<_PMD5_CLC3MD_POSN |  // CLC3 module off
            1<<_PMD5_CLC2MD_POSN |  // CLC2 module off
            1<<_PMD5_CLC1MD_POSN;   // CLC1 module off
 
That works fine but it uses more memory so I did it the ugly way;

Code:
/************************************************************************
*                                                                      *
************************************************************************/
   void main()
   {
     PMD0 = 0<<_PMD0_SYSCMD_POSN |  // Clock network 'on'
            1<<_PMD0_FVRMD_POSN  |  // FVR module off
            0<<_PMD0_NVMMD_POSN  |  // NVM module 'on'
            1<<_PMD0_CLKRMD_POSN |  // CLKR module off
            1<<_PMD0_IOCMD_POSN;    // IOC module off

     PMD1 = 1<<_PMD1_NCOMD_POSN  |  // NCO module off
            1<<_PMD1_TMR2MD_POSN |  // TMR2 module off
            1<<_PMD1_TMR1MD_POSN |  // TMR1 module off
            1<<_PMD1_TMR0MD_POSN;   // TMR0 module off
    
     PMD2 = 1<<_PMD2_DAC1MD_POSN |  // DAC1 module off
            1<<_PMD2_ADCMD_POSN  |  // ADC module off
            1<<_PMD2_CMP2MD_POSN |  // C2 module off
            1<<_PMD2_CMP1MD_POSN |  // C1 module off
            1<<_PMD2_ZCDMD_POSN;    // ZCD module off

     PMD3 = 1<<_PMD3_PWM6MD_POSN |  // PWM6 module off
            1<<_PMD3_PWM5MD_POSN |  // PWM5 module off
            1<<_PMD3_PWM4MD_POSN |  // PWM4 module off
            1<<_PMD3_PWM3MD_POSN |  // PWM3 module off
            1<<_PMD3_CCP2MD_POSN |  // CCP2 module off
            1<<_PMD3_CCP1MD_POSN;   // CCP1 module off

     PMD4 = 1<<_PMD4_UART2MD_POSN | // UART2 module off
            1<<_PMD4_UART1MD_POSN | // UART1 module off
            1<<_PMD4_MSSP1MD_POSN | // MSSP1 module off
            1<<_PMD4_CWG1MD_POSN;   // CWG1 module off

     PMD5 = 1<<_PMD5_CLC4MD_POSN |  // CLC4 module off
            1<<_PMD5_CLC3MD_POSN |  // CLC3 module off
            1<<_PMD5_CLC2MD_POSN |  // CLC2 module off
            1<<_PMD5_CLC1MD_POSN;   // CLC1 module off
A perfectly valid and efficient way to do it. I used to do the same thing in asm and was criticized for writing bad code.

Mike.
 
OK, back to current consumption :D

I went on a short MicroChip course yesterday, about the new SAML10/11 ARM processors - part of which was specifically about low power consumption.

The development boards we were using actually include current monitoring, so you can check the consumption in specific modes. These chips are also 'officially' the lowest powered ones available, according to independent testing in real world conditions - the second best processor (another MicroChip product) only scores about half what this one does.

There's a ludicrous amount of power reducing facilities on the chip, including the capability of shutting down sections of RAM, and three different types of internal regulator for feeding the core.

We were running at 8MHz, and active mode was about 200uA - spec is 25.3uA/MHz - so pretty spot on.

Anyway, Idle mode didn't impress me greatly, only dropping it to about 120uA - but I've never really considered idle mode for what I'm doing anyway - spec is 15.2uA/MHz.

But 'proper' sleep dropped it to around 400nA - this is highly dependent on temperature, and is listed as 500nA at 25C.

There's also a fairly stupid 'off' mode, not really a sleep mode at all - and this reduces the power to 40nA - but you need a hardware reset to start it back up. I suppose it saves you needing to add hardware to actually switch the power off to the chip?.

So pretty impressive devices, and cheap as well (£1.74 from RS) - but pretty complicated as well - if you think modern PIC's have too many clock options, these are even worse :D

Hey Nigel, making your choice from the somewhat overwelming list of options could be complicate but once done the micro looks more or less like the "old" ones, isn't it?
 
Hey Nigel, making your choice from the somewhat overwelming list of options could be complicate but once done the micro looks more or less like the "old" ones, isn't it?

That applies to most of the newer PIC's as well, I've just ordered some 18F27K40 chips, 128K of program memory :D

But just as importantly, they incorporate the facilities of the new enhanced 16F devices, including PPS (which I use), switchable schmitt trigger inputs (which I use), individual bi-directional interrupts on I/O pins (which I use) etc.

Just as importantly, the devices are available as DIP28, as our production runs are fairly small we hand build them all, so SM devices are avoided where ever possible as it takes much longer to hand build. The previous version (which was designed by someone else, and we don't have source code for) was entirely SM, so took ages to build each one - it also used a lot more parts, and didn't use a PIC :D

As far as the set-up options go, I tend to use MCC to set them up, then strip the code from the MCC generated files - which was fine until the header files were incorrect, which caused me loads of headaches.
 
Yup, the newer 16F devices are super flexible in pin arrangement. I use all those as well, though what are "bi-directional interrupts"?

I use interrupt on-change but can't see why throwing an interrupt if the PIC changes the value is useful. Maybe if a peripheral changes the output but usually they have there own interrupts....
 
Yup, the newer 16F devices are super flexible in pin arrangement. I use all those as well, though what are "bi-directional interrupts"?

I use interrupt on-change but can't see why throwing an interrupt if the PIC changes the value is useful. Maybe if a peripheral changes the output but usually they have there own interrupts....

Perhaps 'bi-directional' wasn't a good choice of word, but basically you can generate an interrupt either negative going, positive going, or both (so either direction).

For those of you who might be interested in hardware debouncing?, you've perhaps seen the usual configuration of two resistors and a capacitor, plus sometimes a diode across the second resistor (the one that connects to the capacitor), feeding a schmitt trigger. The new PIC's are great for this, as you can set the inputs to be schmitt or TTL. Anyway, I generally don't bother with the diode, it's an extra component that may well not be needed - BUT - I was testing input frequency response the other week, so did some investigation in to it.

I've got a reed relay (in a DIP encapsulation), and feed this directly from the squarewave output of a small function generator, this gives me a nice 'bouncy' switch signal. I wrote a quick ISR that detects the negative going transition of the pin (and turns an I/O pin ON - feeding an LED), and that also detects the positive going transition and turns the LED back off (hence the 'bi-directional' comment). I feed this signal to one input of the scope, and sync from that - the other input goes to the top of the capacitor, or the switch if you want - but with the capacitor you can see the slope up and down as it charges and discharges.

Anyway, without a diode, and using two 100K resistors and a 0.1uF capacitor it worked up to about 30Hz (which is more than actually required), and as the frequency increases the output gets more and more asymmetrical until it never triggers at all. Watching the scope makes it obvious that the charging time (through the two resistors) is the limiting factor, as the discharge is much quicker (just through one). Adding the diode across the second resistor means it charges through just one resistor, plus the drop across the diode.

Testing with the added diode raised the input to over 70Hz, more than double the previous, just for the cost of a diode - it still produces an asymmetrical output, but not as much as before.

So, as a 'rule of thumb', if you're just debouncing a simple manual key input you don't really need a diode, but if you're wanting to debounce streams of pulses then adding the diode considerably increases your frequency range.

BTW, in keeping with the title of this thread, the debounce resistors are 100K as one of them is permanently connected across the supply when the switch contacts are closed, which will be 50% of the time - so 33uA/2 - 16.5uA.

Obviously, I'll be trying to get the sleep consumption down on the 18F27K40 which I'm now going to be using, so solidly back on theme for the thread. Hopefully I'll have as much luck as I did with the 16F18446.
 
I prefer to do debounce in software. This way you can use the internal pullups and turn them off when not needed.

Following the consumption progress with interest.

Mike.
 
I prefer to do debounce in software. This way you can use the internal pullups and turn them off when not needed.

Doing hardware debouncing means you don't have problems missing input pulses, which can be important in our application - doing it in software means running delays in the ISR, which isn't a good idea when there's a lot of ISR stuff going on.

I don't see how turning off the internal pullups would help?, as it would disable the input until you turned them back on again.
 
You only need to turn on the pullups when you read the port. No need for any delays, you just poll the pin 20-50 times a second.

Mike.
 
The pic will still wake from sleep without the hardware debounce but you will need a high value pullup - probably more efficient with a pulldown(?). One project I did, the pic woke 20 times a second to check a pin and then went back to sleep.

Mike.
 
That's sure not quality sleep! ;)
I still have the prototype hanging around. It's probably about a year since it was last charged and it still works fine. It is however a 1Ah Lipo battery powering it.

Mike.
 
The pic will still wake from sleep without the hardware debounce but you will need a high value pullup - probably more efficient with a pulldown(?). One project I did, the pic woke 20 times a second to check a pin and then went back to sleep.

Mike.

That obviously takes 20 times the current, pull up or pull down makes no difference, as in either case it's across the supply when the contacts are closed. Hardware debouncing is more efficient, and only takes an extra resistor and capacitor, plus an optional diode.

Consumption is fairly critical, as we're wanting 5+ years battery life - although the exact project we're discussing at the moment only requires transmission once a month (less often than we would normally do), so shouldn't be an issue - as long as we can get sleep current down.
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top