bootloader callable from sketch?

retrolefty:
I have no need to convince you, as I have little interest if your design is successful or not.

There's not much one can say to that, is there? Searching through nearly 4000 pages of Atmel documentation I'm unable to find the reference you're so coy about giving. I did find a post on AVRfreaks by somebody called Retrolefty, but he didn't seem to know the answer either.

I find this very interesting. I'm also insatiably curious, and tend toward the "trust, but verify" side of taking common knowledge at face value. After all, that's how cargo-cult habits get to where they are.

If I had to guess, I would've figured the reset pin was latched, so that by the time the pins were tri-stated, the dominoes would be falling and there's no going back. I don't know if I ever would've had an occasion to use such a trick, but I love that I happened to stumble upon the topic.

I hope you guys are patient enough to both accept wisdom and challenge the status quo. There's much to be gained by seeing for yourself if, how, or why something works. Maybe not in this case, but often enough. 8)

http://support.atmel.com/bin/customer.exe?=&action=viewKbEntry&id=21

Lefty

Thanks for the link. That specific statement from Atmel doesn't match my own measurements, so I've asked their tech support to clarify the issue for us.

Whether I'm right or wrong doesn't matter a great deal to me. I wouldn't be very good at my job if I attached my ego to every test or measurement I make - especially one as trival as this is.

What has provoked me, and caused this thread to be as long as it is, is Grumpy Mike's attempt to trump an observation with his supposed seniority. This is known as Proof by Assertion Proof by assertion - Wikipedia, and it is very offensive to anybody that loves science or technology.

To anybody still reading this topic, I offer the following code. It generates reset pulses of varying durations, and indicates which if any are successful. After reset it checks the state of the registers and lists any which do not match the expected values. Read the comments before running.

/*
Self-reset test sketch
Made for 16MHz ATmega328-based Arduino

To use:
- Compile, upload, and launch the serial monitor (57600 baud)
- Update the register values in checkreg() to those reported.
- Recompile, re-upload, and check there are no "Register" messages
- Cut the auto-reset connection if present (the track between the solder pads marked "RESET-EN on Uno boards)
- Connect Pin-2 to Reset
- Optionally, connect Pin-3 to an oscilloscope and a resistive load to show when the port is reset.

Disabling Auto-Reset is optional, but required for testing with very short pulses <1ms (do this *after* uploading and checking this sketch)
To re-enable Auto-Reset on an Uno, dab a spot of solder between the pads where the track was cut.

NB. Whilst Pin-2 is connected to Reset and the sketch is running, do not push the reset button (will short Pin-2 to ground)

*/
#define reg_is(x,y) if (x != y) {\
  Serial.print("Register ");\
  Serial.print(#x);\
  Serial.print(" = ");\
  Serial.println(x,HEX);\
  }


void setup() {
  Serial.begin(57600);
  Serial.println("-");
  Serial.println("Started (do not push the Reset button)");
  checkreg();
  PORTD = 0x04; // Pin-2 high, Pin-3 low
  DDRD = 0x0C;

  delay(1000);

  PORTD = 0x08; // Pin-2 low, Pin-3 high
  PORTD = 0x04; // Pin-2 high, Pin-3 low
  Serial.println("Failed to reset after a  60ns pulse (this is normal)");

  delay(1000);

  PORTD = 0x08; // Pin-2 low, Pin-3 high
  asm volatile("nop\n");
  PORTD = 0x04; // Pin-2 high, Pin-3 low
  Serial.println("Failed to reset after a 120ns pulse (this is normal)");

  delay(1000);

  PORTD = 0x08; // Pin-2 low, Pin-3 high
  asm volatile("nop\nnop\n");
  PORTD = 0x04; // Pin-2 high, Pin-3 low
  Serial.println("Failed to reset after a 180ns pulse (this is normal)");

  delay(1000);

  PORTD = 0x08; // Pin-2 low, Pin-3 high
  asm volatile("nop\nnop\nnop\n");
  PORTD = 0x04; // Pin-2 high, Pin-3 low
  Serial.println("Failed to reset after a 250ns pulse (this is normal)");

  delay(1000);

  PORTD = 0x08; // Pin-2 low, Pin-3 high
  asm volatile("nop\nnop\nnop\nnop\n");
  PORTD = 0x04; // Pin-2 high, Pin-3 low
  Serial.println("Failed to reset after a 300ns pulse (within spec, but differs from Tim's result)");

  delay(1000);

  PORTD = 0x08; // Pin-2 low, Pin-3 high
  asm volatile("nop\nnop\nnop\nnop\nnop\nnop\nnop\n");
  PORTD = 0x04; // Pin-2 high, Pin-3 low
  Serial.println("Failed to reset after a 500ns pulse (within spec, but differs from Tim's result)");

  delay(1000);

  PORTD = 0x08; // Pin-2 low, Pin-3 high
  asm volatile("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n");
  asm volatile("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n");
  asm volatile("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n");
  asm volatile("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n");
  PORTD = 0x04; // Pin-2 high, Pin-3 low
  Serial.println("Failed to reset after a 2.5us pulse (out of spec - is autoreset disconnected?)");

  delay(1000);

  PORTD = 0x08; // Pin-2 low, Pin-3 high
  delay(1);
  PORTD = 0x04; // Pin-2 high, Pin-3 low
  Serial.println("Failed to reset after a   1ms pulse (this shouldn't happen!)");

  delay(1000);

  DDRD = 0x00;
  Serial.println("Stopped (you may now push the reset button)");

}

void loop() {
}

void checkreg() {
 // This routine compares the register contents to values after a "normal" reset
 // Normal values vary from board to board -- update the code below as necessary

 Serial.println("Looking for uninitialised registers...");
//  reg_is(PINB,    0) // floating inputs can be random
  reg_is(DDRB,    0)
  reg_is(PORTB,   0)
//  reg_is(PINC,    0) // floating inputs can be random
  reg_is(DDRC,    0)
  reg_is(PORTC,   0)
//  reg_is(PIND,    0) // floating inputs can be random
  reg_is(DDRD,    0)
  reg_is(PORTD,   0)
//  reg_is(TIFR0,   0) // timer
//  reg_is(TIFR1,   0) // timer
//  reg_is(TIFR2,   0) // timer
  reg_is(PCIFR,   0)
  reg_is(EIFR,    0)
  reg_is(EIMSK,   0)
  reg_is(GPIOR0,  0)
  reg_is(EECR,    0)
  reg_is(EEDR,    0)
  reg_is(EEAR,    0x03DF)
  reg_is(EEARL,   0x00DF)
  reg_is(EEARH,   0x0003)
  reg_is(GTCCR,   0)
  reg_is(TCCR0A,  0x0003)
  reg_is(TCCR0B,  0x0003)
//  reg_is(TCNT0,   0) // timer
  reg_is(OCR0A,   0)
  reg_is(OCR0B,   0)
  reg_is(GPIOR1,  0)
  reg_is(GPIOR2,  0)
  reg_is(SPCR,    0)
  reg_is(SPSR,    0)
  reg_is(SPDR,    0)
  reg_is(ACSR,    0x0030)
  reg_is(SMCR,    0)
  reg_is(MCUSR,   0x0005)
  reg_is(MCUCR,   0)
  reg_is(SPMCSR,  0)
  reg_is(WDTCSR,  0)
  reg_is(CLKPR,   0)
  reg_is(PRR,     0)
  reg_is(OSCCAL,  0x0084)
  reg_is(PCICR,   0)
  reg_is(EICRA,   0)
  reg_is(PCMSK0,  0)
  reg_is(PCMSK1,  0)
  reg_is(PCMSK2,  0)
  reg_is(TIMSK0,  0x0001)
  reg_is(TIMSK1,  0)
  reg_is(TIMSK2,  0)
  reg_is(ADC,     0)
  reg_is(ADCW,    0)
  reg_is(ADCL,    0)
  reg_is(ADCH,    0)
  reg_is(ADCSRA,  0x0087)
  reg_is(ADCSRB,  0)
  reg_is(ADMUX,   0)
  reg_is(DIDR0,   0)
  reg_is(DIDR1,   0)
  reg_is(TCCR1A,  0x0001)
  reg_is(TCCR1B,  0x0003)
  reg_is(TCCR1C,  0)
//  reg_is(TCNT1,   0) // timer
//  reg_is(TCNT1L,  0) // timer
//  reg_is(TCNT1H,  0) // timer
  reg_is(ICR1,    0)
  reg_is(ICR1L,   0)
  reg_is(ICR1H,   0)
  reg_is(OCR1A,   0)
  reg_is(OCR1AL,  0)
  reg_is(OCR1AH,  0)
  reg_is(OCR1B,   0)
  reg_is(OCR1BL,  0)
  reg_is(OCR1BH,  0)
  reg_is(TCCR2A,  0x0001)
  reg_is(TCCR2B,  0x0004)
//  reg_is(TCNT2,   0) // timer
  reg_is(OCR2A,   0)
  reg_is(OCR2B,   0)
  reg_is(ASSR,    0)
  reg_is(TWBR,    0)
  reg_is(TWSR,    0x00F8)
  reg_is(TWAR,    0x00FE)
  reg_is(TWDR,    0x00FF)
  reg_is(TWCR,    0)
  reg_is(TWAMR,   0)
  reg_is(UCSR0A,  0)
  reg_is(UCSR0B,  0x00B8)
  reg_is(UCSR0C,  0x0006)
  reg_is(UBRR0,   0x0010)
  reg_is(UBRR0L,  0x0010)
  reg_is(UBRR0H,  0)
  reg_is(UDR0,    0)
  Serial.println("...finished register check");
}

Oscilloscope traces from an Uno r2:
Yellow = pin-3 (connected via 10kOhm to ground)
Blue = pin-2 (connected to Reset, and two resistors (internal + 10kOhm) in parallel to Vcc)

A 250ns pulse is insufficient to cause reset and sketch execution continues. Note the rapid trailing-edge of the pulses.

Pulses 300ns or longer trigger reset. After 300ns the port outputs go high-impedance. Note the slower voltage decay.

A 250ns pulse is insufficient to cause reset
:
After 300ns the port outputs go high-impedance.

Eh. Violate the datasheet at your own risk. It says reset should be 2.5us (Section 26.5: System and Reset Characteristics)

Your measurements are interesting in that they confirm that you do NOT get a 2.5us reset pulse if you set a connected output pin to LOW and allow the chip to (begin) resetting.

The datasheet has other information hinting at the complexity of the reset process. See section 6.2.2 "Clock Startup Sequence" and Section 8 "System Control and Reset."

I too wondered why reset was starting earlier than 2.5us. Reading the datasheet more closely, it says that there is some minimum reset pulse-width, tRST. And the maximum value of tRST is 2.5us (table 29-12). Typical and minimum values are not given. So my value of tRST = 300ns does not contradict the datasheet.

I also checked the startup timing:

  • Following the release of the Reset button, there's a delay tOUT for Vcc to stabilise (figure 11-4). With the normal Arduino fuse settings tOUT is ~65ms (table 9-2), but with an error of ~10% (the 128kHz clock is "not designed for high accuracy", section 9-7).
  • This is followed by 16000 clock cycles (~1ms) to allow the crystal/resonator to stabilise.
  • A few clock-cycles later the bootloader starts toggling pin-13 to show it is working.

I measured a total startup time of 62.8ms. More importantly startup-time did not vary, implying that the proper startup sequence is being followed even after very short reset pulses. See traces below.

PS. My datasheet seems to have different section numbers to yours. The references above are for the latest version.

Oscilloscope trace legend

  • Yellow = reset-pin : connected to Reset-Button, or Pin-2 for the auto-reset examples
    Ports are tri-stated 300ns after falling edge
    Reset sequence starts on rising edge
  • Blue = pin-3 : connected to gnd via 10k, initial state = high output
    Falling-edge shows end of sketch execution and tri-stating of ports
  • Purple = pin-13 : connected to Vcc via 10k and gnd via LED, initial state = input
    First falling-edge indicates start of bootloader operation, followed by LED flashes

Normal button-push reset (210ms)

Long button-push reset (470ms)

Short button-push reset (60ms)

Auto reset (300ns)

Normal button-push reset (5x zoom)

Auto reset (5x zoom)

Normal button-push reset (100,000x zoom)

Auto reset (100,000x zoom)

westfw:
It would probably be easier to create a custom bootloader with a longer timeout. Up to 8s is an easy change.

Sorry to hijack the thread, but creating a bootloader with an altered timeout is something I really want. I've looked for advice on how to start going about this, posted some threads and got nowhere. Can anyone please suggest a starting point where I can read up on what I need to do ?

To make your modified bootloader, find the source code in .\arduino-1.0\hardware\arduino\bootloaders\optiboot. Make a backup copy of this directory and keep it somewhere safe. Then modify line 311 of optiboot.c from

  watchdogConfig(WATCHDOG_1S);

to

  watchdogConfig(WATCHDOG_8S);

This will give you an 8s time-out. To compile, open a command window in the optiboot directory and type omake clean followed by omake atmega328 (I'm assuming that your Arduino has an ATmega328 processor clocked at 16Mz).

Unless you have a dedicated programmer, you'll need a second Arduino to upload the new bootloader. Follow the instructions at http://arduino.cc/en/Tutorial/ArduinoISP.
Good luck,

tim7:
To make your modified bootloader, find the source code in .\arduino-1.0\hardware\arduino\bootloaders\optiboot. Make a backup copy of this directory and keep it somewhere safe. Then modify line 311 of optiboot.c from

  watchdogConfig(WATCHDOG_1S);

to

  watchdogConfig(WATCHDOG_8S);

This will give you an 8s time-out. To compile, open a command window in the optiboot directory and type omake clean followed by omake atmega328 (I'm assuming that your Arduino has an ATmega328 processor clocked at 16Mz).

Unless you have a dedicated programmer, you'll need a second Arduino to upload the new bootloader. Follow the instructions at http://arduino.cc/en/Tutorial/ArduinoISP.
Good luck,

Wow. Thank you so much - everything I need to get going. I'm sending much internet karma your way.

tim7:
And the maximum value of tRST is 2.5us (table 29-12). Typical and minimum values are not given. So my value of tRST = 300ns does not contradict the datasheet.

How is that value labeled? Why would there be a maximum pulse width? What happens to a processor when the maximum pulse width is exceeded (something I do every time I program an ATtiny processor)? Is it possible the person who wrote the document placed the value in the wrong column? That the value is the minimum pulse width rather than the maximum?

Between the Reset pin and the internal reset logic there is a glitch filter (see the diagram I posted earlier), and I'm guessing that tRST is the threshold for pulses passing through that filter. In other words, the minimum reset-pulse-width is tRST ... and the specified value of tRST is not greater than 2.5us.

It's the maximum value of the minimum reset pulse width. Ie, you will never need a pulse longer than 2.5us to cause the AVR to reset. In the particular case of the Arduino with its particular fuse settings, at 5V and room temperature with a 16MHz ceramic resonator that is running at the time, it seems that a much shorter pulse also succeeds in resetting the AVR; that's OK - the datasheet does not list a minimum time for Trst

Cool stuff. I suppose it's safe to say that component differences may affect these results greatly, but on your particular board, it took 300nS before the pins were tri-stated, and that amount of time was sufficient to trigger its own reset. Nifty. I might try this myself just to see if it works. I don't think I would count on it for a design yet. :wink:

westfw:
It's the maximum value of the minimum reset pulse width. Ie, you will never need a pulse longer than 2.5us to cause the AVR to reset. In the particular case of the Arduino with its particular fuse settings, at 5V and room temperature with a 16MHz ceramic resonator that is running at the time, it seems that a much shorter pulse also succeeds in resetting the AVR; that's OK - the datasheet does not list a minimum time for Trst

Although you can no doubt go lower then 2.5us, 300ns seems much too short.
What works on one chip, clock, pin etc... may not work on another.
Now that I think about it, wouldn't using a single NPN transistor between reset and GND with the base triggered with a high state from a pin and a capacitor on the base to lengthen the pulse.

FWIW I did a few tests. With this sketch:

const byte RESET = 8;
const byte LED = 9;

void setup ()
{
  digitalWrite (LED, LOW);
  digitalWrite (RESET, HIGH);
  
  pinMode (LED, OUTPUT);
  pinMode (RESET, OUTPUT);
 
  for (byte i = 0; i < 5; i++)
    {
    delay (20);
    digitalWrite (LED, HIGH);
    digitalWrite (LED, LOW);
    }
    
  digitalWrite (RESET, LOW);
  
}  // end of setup

void loop () {}

I connected D8 to /RESET, and measured timings. The loop of 5 was to identify when we were about to pull reset low. I found that reset went low for 937.5 nS. This was on a "bare bones" board with no capacitor on the reset line.

The datasheet, page 422, appears to indicate that the minimum reset pulse varies per the supply voltage. At 5V it can be between 200 nS and 400 nS depending on temperature.

However the datasheet on page 318 says that the "maximum" minimum reset pulse (whatever a maximum minimum is) is 2.5 uS. This appears to contradict the chart on page 422.

The other point though is that I got rather strange results when I also had D8 connected via an LED to ground. The processor never seemed to get into flashing the "indicator" pin at all.

And I think this is probably the heart of the problem. When the processor powers up, the pins are probably in an indeterminate state. Now if you have one of the pins connected direct to /RESET, before the processor has time to set the pins high impedance, the pin you choose might assert reset again, going into some strange loop of being reset before it can escape from reset.

tim7:
And the maximum value of tRST is 2.5us (table 29-12). Typical and minimum values are not given. So my value of tRST = 300ns does not contradict the datasheet.

Actually it's the maximum minimum. Because, tRST is "Minimum pulse width on /RESET Pin".

So in the absence of minimum and typical, I would read that as the figure which is the minimum reset pulse. Notwithstanding that it contradicts the chart on page 422.

It could just be a documentation error. After all, what's a maximum minimum anyway? Maybe whoever drew up that chart thought that a minimum minimum would be confusing.

11.4 External Reset

An External Reset is generated by a low level on the RESET pin. Reset pulses longer than the minimum pulse width (see ”System and Reset Characteristics” on page 324) will generate a reset, even if the clock is not running. Shorter pulses are not guaranteed to generate a reset.

Table 29-12. Reset, Brown-out and Internal Voltage Characteristics(1)

Symbol Parameter Min Typ Max Units
tRST Minimum pulse width on RESET Pin 2.5 ?s

It could just be a documentation error.

That's where I'm putting my money.

Of course. What else could it be? There is no 'maximum reset pulse length, as one can mash down on the reset switch for a week and upon release the reset process will complete properly. They simply don't define the minimum minimum reset pulse length for a proper reset, and rather caution that at some value less then 2.5us value it is possible to start a reset process, pins go tri-state, yet not be able to properly complete the reset process. What some seem to want is a Atmel listed specification on reset pulse length that is guareented to cause a faulty reset condition, which they do not and will not supply.

Lefty