Using the AVR RESET Pin as an Input

Here's a handy little trick you can use to put the AVR Reset pin for something other than just resetting an AVR micro controller.  You may not know it, but the AVR series micro controllers remembers what type of event caused a reset and makes this information available in a register named MCUSR (see image below.)  Bit 1 of this register is for a bit named EXTRF and this bit is set whenever the reset was triggered by the reset pin.   If, instead, the PORF bit is set, it means that the AVR micro was reset as a result of being powered on.  The other two bits, WDRF and BORF, are for indicating when the reset happened due to the watchdog timer expiring, or a voltage brown out.  If we are careful about how we configure the AVR micro we can use this behavior to our advantage.

The circuit below shows my test setup.  I'm using an ATTiiny85 because this trick is most useful in cases where the number of available I/O pins is limited and you don't have a spare pin you could otherwise use as a switch input.  The reset pin (pin 1) is connected to a 10k pull up resistor and a pushbutton switch that can pull the pin LOW and trigger a reset.  The 3 LEDs are each wired through a 330 ohm resistor to limit the current.

There's another trick we need to use in order to make use of the reset pin, but let's take a look at the code for the demo circuit so I can explain why this trick is required.  Note: for this example, I'm using my ATTiny10IDE program (newly updated to also support the ATTinyx4 and ATTinyx5 series micros) to compile and upload the code rather than the Arduino IDE.  It's possible to use the Arduino IDE, but it requires more setup work.  I'll talk a bit more about this later in the article.

//                 +====+

//  RESET/A0/PB5 1 |*   | 8 Vcc

//        A3/PB3 2 |    | 7 PB2/A1/SCK

//       A2/~PB4 3 |    | 6 PB1~/MISO

//           Gnd 4 |    | 5 PB0~/MOSI

//                 +====+

#include <avr/io.h>

#pragma chip attiny85

#pragma lfuse 0x62  // default value is 0x62

#pragma hfuse 0xDF  // default value is 0xDF

#pragma efuse 0xFF  // default value is 0xFF

#define EXT_RESET (1 << EXTRF)

volatile unsigned char count __attribute__((section(".noinit"))); // Not cleared on RESET

int main () {

// Any type of RESET clears the IO Regs, so we always need to reconfigure them

DDRB = 7;

if ((MCUSR & EXT_RESET) != 0) {

// If External RESET then increment count and output to pins

MCUSR = MCUSR & ~EXT_RESET;

PORTB = ++count;

} else {

// Else, set count to zero and output to pins

PORTB = count = 0;

}

while (true) {

// Forever

}

}

Notice the declaration used for the "count" variable.  Normally, the startup code (the code that's called before the main() method is called and which does things like setting up the stack pointer) clears all the available RAM to zero before it calls main().  But, by using this rather odd-looking declaration, we can tell the compiler to generate startup code that does not to clear the RAM where the count variable resides.  This is important because we need some way to remember the state of the micro before the reset button is pushed.  You'll also notice that the first thing the main() method does is to set DDRB (data direction register for port B) to 7, which configure the three pins connected to the LEDs as outputs.  This is needed because all the I/O-related registers are also cleared by a reset.

How does it work?

If the AVR micro is simply powered on, the test "if ((MCUSR & EXT_RESET) != 0)" will fail and the code will execute the else case which simply sets count to zero and than copies this state to the output pins by writing to the PORTB register.  So, on power up, all the LEDs should be off.  But, when the reset pin is pushed, the test will be true and the code will not take the else case.  Instead, it first writes a ZERO to the EXTRF bit (any ANDing it with ~(1 << EXTRF), or 0xFD) to clear the EXTRF bit.  Then, it increments count and writes the result to the LEDs via the PORTB register. So, with this code programmed, the LEDs should all be off if the ATTiny85 is powered up without touching the reset pin.  But, each time you press and release the pushbutton, the LEDs should count up in binary from 1 to 7 and then cycle back to zero.  

What is this good for?

Offhand, you may think that this is a lot of work to go through for such a simple function.  After all, why go to all this trouble to use the reset pin like this?  Here are some practical idea you might consider:

You might also consider using the "Feature Selection" idea as a way to make your ideas accessible to people who like to experiment with electronics but don't like to program a micro controller, or haven't yet learned.  So, let's imagine you've developed some really nifty code that can flash LEDs in dozens of different patterns (such as the famous Knight Rider scanner) and you want to see this to people in the form of a preprogrammed micro controller they can just wire in and use, or perhaps even packaged on a custom PCB.  How will the end user select the pattern they want.  You could use DIP switches, or jumper pins, but this approach uses up pins you could otherwise used for connecting more LEDs.

Some Tricky Bits

One thing you need to consider when using the approach, is that it's important that the RESET pin is HIGH before the power on delay counter tries to do a Power-on Reset (POR).  With the default fuse setting on an ATTiny85, the power on delay is rather long, so this won't happen with this test circuit.  But, if you change the fuses to use a shorter power on delay, or a faster clock rate, or you don't use a pull up resistor on the reset pin, or if there is a capacitive load on the reset pin, it'ss possible that the MCUSR bits might show an external reset rather than a power on reset.

Can I use this Trick with an Arduino?

I said earlier that I'd talk about using the Arduino IDE to wrote code that employs this trick.  Unfortunately, the Arduino boot loader also uses a similar trick to tell when the Arduino IDE is trying to upload code so this rules out using it with code uploaded via the boot loader.  If you an ISP Programmer, it is possible to use it to upload the code directly to the Arduino and bypass the boot loader.  But, this is an advanced technique and it will also overwrite the boot loader and you'll have to write it back again to regain use of the boot loader.  Also, the process to upload a sketch using an ISP Programmer is not well documented and requires a bit of research to figure out.  I may write an article about this at a future date but, for the moment, I decided to devote my efforts to improving my ATTiny10IDE program instead, as I hope to make programming with the AVR Tiny series of chips easier and more accessible to people.