Author Topic: Moteino shortest wakeup time  (Read 10684 times)

joelucid

  • Hero Member
  • *****
  • Posts: 868
Re: Moteino shortest wakeup time
« Reply #15 on: March 23, 2016, 09:23:57 AM »
I think as long as you don't write flash or eeprom you should be fine according to the spec.

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: Moteino shortest wakeup time
« Reply #16 on: April 04, 2016, 06:59:55 PM »
Starting with a stock Moteino, I disabled BOD and also set the fuses to use the internal 8Mhz oscillator.

Code: [Select]
BODLEVEL = DISABLED
RSTDISBL = [ ]
DWEN = [ ]
SPIEN = [X]
WDTON = [ ]
EESAVE = [ ]
BOOTSZ = 512W_3E00
BOOTRST = [X]
CKDIV8 = [ ]
CKOUT = [ ]
SUT_CKSEL = INTRCOSC_8MHZ_6CK_14CK_0MS

EXTENDED = 0xFF (valid)
HIGH = 0xDC (valid)
LOW = 0xC2 (valid)

I was running the blink program, and predictably it now blinks at about a 2 second interval rather than a 1 second interval.

After making the change, I can no longer upload to the Moteino in the usual way over FTDI from the IDE.   :'(  However, I believe I can still  use the Dragon to change the program that's in flash memory so that afterward the FTDI connection will work again.  I'm guessing that somewhere F_CPU needs to be defined as 8000000  and/or I need to define a new entry in the boards.txt file for an 8Mhz Uno? 

In retrospect, I probably should have done those things, uploaded them, and only then changed the fuse settings to use the 8Mhz internal resonator.  Then it would be blinking once a second and maybe the FTDI connection would still be working.

[Edit:  "You would need to recompile the bootloader and then compile your sketches for 8mhz," according to https://lowpowerlab.com/forum/index.php/topic,650.msg3821.html#msg3821]
« Last Edit: April 04, 2016, 08:03:37 PM by WhiteHare »

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Moteino shortest wakeup time
« Reply #17 on: April 04, 2016, 08:08:44 PM »
Starting with a stock Moteino, I disabled BOD and also set the fuses to use the internal 8Mhz oscillator.

Code: [Select]
BODLEVEL = DISABLED
RSTDISBL = [ ]
DWEN = [ ]
SPIEN = [X]
WDTON = [ ]
EESAVE = [ ]
BOOTSZ = 512W_3E00
BOOTRST = [X]
CKDIV8 = [ ]
CKOUT = [ ]
SUT_CKSEL = INTRCOSC_8MHZ_6CK_14CK_0MS

EXTENDED = 0xFF (valid)
HIGH = 0xDC (valid)
LOW = 0xC2 (valid)

I was running the blink program, and predictably it now blinks at about a 2 second interval rather than a 1 second interval.

After making the change, I can no longer upload to the Moteino in the usual way over FTDI from the IDE.   :'(  However, I believe I can still  use the Dragon to change the program that's in flash memory so that afterward the FTDI connection will work again.  I'm guessing that somewhere F_CPU needs to be defined as 8000000  and/or I need to define a new entry in the boards.txt file for an 8Mhz Uno? 

In retrospect, I probably should have done those things, uploaded them, and only then changed the fuse settings to use the 8Mhz internal resonator.  Then it would be blinking once a second and maybe the FTDI connection would still be working.

[Edit:  "You would need to recompile the bootloader and then compile your sketches for 8mhz," according to https://lowpowerlab.com/forum/index.php/topic,650.msg3821.html#msg3821]
Add a section to your Moteino boards.txt file for a Moteino 8MHz.  In that section set cpu frequency to 8MHz and set your upload speed to 57600.

Tom

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: Moteino shortest wakeup time
« Reply #18 on: April 05, 2016, 11:55:59 AM »
I decided to restore the Moteino fuses to their original state, and now it's as good as new.

Instead, I'm going to use a pro mini to test for reduced wake-up time, as it is cheaper to throw-away if I screw something up.  Also, since it already works at 8Mhz, maybe (?) I don't need to mess around with its boards.txt entry.  Anyhow, without making any changes to boards.txt, I'm still able to FTDI upload to it even after shifting to the internal oscillator and removing its BOD protection:

Code: [Select]
BODLEVEL = DISABLED
RSTDISBL = [ ]
DWEN = [ ]
SPIEN = [X]
WDTON = [ ]
EESAVE = [ ]
BOOTSZ = 1024W_3C00
BOOTRST = [X]
CKDIV8 = [ ]
CKOUT = [ ]
SUT_CKSEL = INTRCOSC_8MHZ_6CK_14CK_0MS

EXTENDED = 0xFF (valid)
HIGH = 0xDA (valid)
LOW = 0xC2 (valid)

Also, I tightened up the code a bit so that it should light the LED the moment it wakes:
Code: [Select]
// Modified Sketch J
#include <avr/sleep.h>

const byte LED = 9;
 
void wake ()
{
  digitalWrite(LED, HIGH); //do immediately for measurement purposes.
  // cancel sleep as a precaution
  sleep_disable();
  // precautionary while we do other stuff
  detachInterrupt (1);
}  // end of wake

void setup ()
  {
  pinMode (3, INPUT); 
  pinMode(LED,OUTPUT); 
  digitalWrite(LED, HIGH);
  }  // end of setup

void loop ()
{

  delay(500);
  digitalWrite(LED, LOW);
 
  // disable ADC
  ADCSRA = 0; 
 
  set_sleep_mode (SLEEP_MODE_PWR_DOWN); 
  sleep_enable();

  // Do not interrupt before we go to sleep, or the
  // ISR will detach interrupts and we won't wake.
  noInterrupts ();
 
  // will be called when pin D3 goes low 
  attachInterrupt (1, wake, FALLING);
  EIFR = bit (INTF1);  // clear flag for interrupt 1
 
  // turn off brown-out enable in software
  // BODS must be set to one and BODSE must be set to zero within four clock cycles
  //MCUCR = bit (BODS) | bit (BODSE);
  // The BODS bit is automatically cleared after three clock cycles
  //MCUCR = bit (BODS);
 
  // We are guaranteed that the sleep_cpu call will be done
  // as the processor executes the next instruction after
  // interrupts are turned on.
  interrupts ();  // one cycle
  sleep_cpu ();   // one cycle

  } // end of loop
However, this particular time I compiled and FTDI uploaded using Windows Arduino IDE 1.6.8.

The result was a wake-up time of just under 20 microseconds (see attachment).  That's a big improvement, but still an order of magnitude longer  than 1 microsecond.   :o
« Last Edit: April 05, 2016, 12:07:51 PM by WhiteHare »

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: Moteino shortest wakeup time
« Reply #19 on: April 05, 2016, 12:48:20 PM »
I just now tried running Incognio's code from post #12 (above) instead, using the same Windows Arduino IDE 1.6.8, and that was a huge improvement: down to about 5.4 microseconds.

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: Moteino shortest wakeup time
« Reply #20 on: April 05, 2016, 01:03:51 PM »
So, I figured, why stop there?  I followed Incognio's lead and added OSCCAL=0xFF before the main loop, and that got the same result of 4.0 microseconds (using only Windows Arduino IDE 1.6.8 ).  So, apparently AVR Studio wasn't needed after all.   :)

But, again, why stop there?  @JoeLucid: How do we get all the way down to 1.0 microseconds?  Or, are we technically already there?  i.e. how long does the light-the-LED instruction take to execute? 
« Last Edit: April 05, 2016, 01:18:25 PM by WhiteHare »

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: Moteino shortest wakeup time
« Reply #21 on: April 05, 2016, 01:39:22 PM »
To answer that question, I modified Incognio's code to turn off a second LED on D6 immediately after the first (on D7):
Code: [Select]
//Modified Incognio code

#include <avr/io.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <avr/interrupt.h>

// using pin D7 for output
#define pinOn (PORTD) |= (1<<7)
#define pinOff (PORTD) &= ~(1<<7)
#define pinOn6 (PORTD) |= (1<<6)
#define pinOff6 (PORTD) &= ~(1<<6)

ISR(INT1_vect)
{
   // ISR code to execute here
   pinOff;
   pinOff6;
}

int main(void)
{
  // note to self: 1 = output, 0 = input
  DDRD |= (1<<7); // output pin
  DDRD |= (1<<6); // output pin
  DDRD &= ~(1<<3); // input for INT1

  // pullups
  PORTD |= (1<<7);
  PORTD |= (1<<6);
  PORTD |= (1<<3);

  EICRA &= ~(0x0C); // clear existing flags
  EICRA |= 0x08;    // set falling level interrupt
  EIMSK |= 0x02;    // enable it

  OSCCAL=0xFF;

  set_sleep_mode (SLEEP_MODE_PWR_DOWN);

  while(1) {

    cli();
    sleep_enable();
    //sleep_bod_disable();
    sei();
    sleep_cpu();
    sleep_disable();
 
    _delay_ms(50);
    pinOn;
    pinOn6;

  }

  return 1;
}

So, from the attached oscilliscope snapshot, it looks as though it takes something less than 200 nanoseconds to drive the D7 LED to LOW after waking up.  i.e. the true wake-up time is about 3.8 microseconds.  So, apparently there's more work to be done to get down to a 1 microsecond wake-up....
« Last Edit: April 05, 2016, 01:46:58 PM by WhiteHare »

joelucid

  • Hero Member
  • *****
  • Posts: 868
Re: Moteino shortest wakeup time
« Reply #22 on: April 05, 2016, 02:02:59 PM »
I think given the small improvement from the OSCCAL setting I had suggested it's likely that you're looking at a significant fixed cost (internal oscillator startup?).

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: Moteino shortest wakeup time
« Reply #23 on: April 07, 2016, 03:02:54 PM »
I can't think of anything more to try.  At any rate, I'm very happy with 3.8 microseconds.   That's a huge improvement and provides lots of possibilities.  :)  Thanks!
« Last Edit: April 07, 2016, 03:07:45 PM by WhiteHare »

incognico

  • NewMember
  • *
  • Posts: 15
  • Country: au
Re: Moteino shortest wakeup time
« Reply #24 on: July 12, 2016, 10:51:24 PM »
Hi all,

I know this thread is a bit old now, but I was implementing some watchdog/ISR stuff the other day and had to use a technique which reminded me of this thread.

AVR ISR reference: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

I copied my code from post #12 and updated it so that the ISR is NAKED. This tells the compiler to create the ISR with no prologue or epilogue code. I thought that the additional code to save state might be slowing down our wakeup test. [The OSCCAL=0xFF we came up with earlier was also added to the code.]

My result: 3.120uS (For newcomers that's time to startup from power-down sleep and toggle a pin. The previous best we got to was 4.0uS).

Note you need to add your own return instruction to a NAKED ISR.
Code: [Select]
ISR(INT1_vect, ISR_NAKED)
{
   pinOff;
   reti();
}

Initially I tried declaring it as an EMPTY_INTERRUPT and putting the pin toggle in the main loop, but this was actually slower than the NAKED version - I assume because the EMPTY version jumps back to the main loop before flipping the pin, where the NAKED one flips the pin first, then jumps back.

I also played with the pinOff macro, as in hindsight that is a read-modify-write process, but making it a direct write didn't seem to help the time any.

A small step but thought it was worth letting you guys know ;D