Go Down

Topic: How to distinguish between ‘reset’ and ‘real power loss’?? (Read 4666 times) previous topic - next topic

Darkwing

Jun 11, 2014, 04:05 pm Last Edit: Jun 11, 2014, 04:08 pm by Darkwing Reason: 1
Hi folks!

In some cases -?and I'm facing such a case now - it is not the same if you press reset on the Arduino or if you plug the power in and make the system start. I need to distinguish these two cases because of some fail-safe features in my project.

Is there some kind of built-it functionality in the Arduino I could use for this?
Or is it only possible with some external components?

I just started thinking about it?… maybe some kind of capacitor between VCC and an analog pin or the like?… and then reading out if the cap is empty or quite fully loaded?…

I just wanted to ask if anyone has done something like that before; so maybe I can avoid inventing the wheel again. ;)


Thanks!

CrossRoads

I think if you look at the interrupt types, you can differentiate between a Reset interrupt and a power loss/brown out detection interrupt.
Table 12-6 in the datasheet:
External Pin, Power-on Reset, Brown-out Reset and Watchdog System Reset

Ok, so maybe not!
So external circuit is needed then. Maybe a flip flop that is cleared with power loss, your sketch reads it to see if it's low, indicating a power loss, and clocks it to make it high. A subsequent reset leaves it high, so when your sketch reads it on a re-start and sees it high, you can take other actions.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

fungus

There's a CPU register that tells you this.

Look at MCUSR in the "System Control and Reset" section of the datasheet.

No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

CrossRoads

Well, that makes it easier:
11.9.1 MCUSR - MCU Status Register
The MCU Status Register provides information on which reset source caused an MCU reset.

Bit 7:4: Reserved
These bits are unused bits in the ATmega48A/PA/88A/PA/168A/PA/328/P, and will always read as zero.
• Bit 3 - WDRF: Watchdog System Reset Flag
This bit is set if a Watchdog System Reset occurs. The bit is reset by a Power-on Reset, or by writing a logic zero to the flag.
• Bit 2 - BORF: Brown-out Reset Flag
This bit is set if a Brown-out Reset occurs. The bit is reset by a Power-on Reset, or by writing a logic zero to the flag.
• Bit 1 - EXTRF: External Reset Flag
This bit is set if an External Reset occurs. The bit is reset by a Power-on Reset, or by writing a logic zero to the flag.
• Bit 0 - PORF: Power-on Reset Flag
This bit is set if a Power-on Reset occurs. The bit is reset only by writing a logic zero to the flag.

To make use of the Reset Flags to identify a reset condition, the user should read and then Reset the MCUSR as early as possible in the program. If the register is cleared before another reset occurs, the source of the reset can be found by examining the Reset Flags.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Darkwing

#4
Jun 11, 2014, 04:54 pm Last Edit: Jun 11, 2014, 04:56 pm by Darkwing Reason: 1
Thanks for your quick answers!

Funny: I just finished my capacitor-solution. ^^

Clever: I already use a flip-flop in this project - and never thought of digitalReading the state in, gnaarpf?… ^^ Even dumber: the whole fuss is about the state of this flip-flop in a certain system state. Silly me. ^^ Thanks for pointing me to the most obvious solution.

Awesome: I'll also look into the datasheet to find out about this register. This would be a brilliant solution too!

Darkwing

I wanted to thank you guys again!

I now managed to make use of these registers. Here is an example code in case someone in the world is looking for the same thing some later time:

Code: [Select]
void setup() {

 Serial.begin(38400);

 Serial.print("MCUSR: ");
 Serial.println(MCUSR);

 switch (MCUSR) {
   case 2: // b00000010
           // Reset button or otherwise some software reset
           Serial.println("Reset button was pressed.");
           break;
   case 7: // b00000111
           // Boot up after power loss
           Serial.println("Power loss occured!");
           break;
   }

 // Clear all MCUSR registers immediately for 'next use'
 MCUSR = 0;
}



I didn't know that accessing these registers is so damn easy! I assume that this is because some Arduino lib is doing the work.

As a follow up: can you give me an example of how this would be written in 'real' C code? Or can you point me where in which lib is the place MCUSR is handled?


Thanks!

JChristensen

Does that code actually work? I thought the bootloader cleared the MCUSR settings. Anyone know if that has changed?


I didn't know that accessing these registers is so damn easy! I assume that this is because some Arduino lib is doing the work.


Actually it's lower than that, it's the AVR-GCC compiler. It knows the register names and allows them to be used basically as variables.

Quote

As a follow up: can you give me an example of how this would be written in 'real' C code? Or can you point me where in which lib is the place MCUSR is handled?


That is real C code! (Well, C++ code.)

Darkwing

Woah, compilers baffle me all the time. Thanks, good to know!

In the meantime I ported my little condensed example sketch into 'the big project' and I can tell: this works just fine. No weird stuff happening so far. Directly after uploading a sketch, when the system starts for the first time, the register is 2 (= b0010), meaning a (software) reset was done. This behaves like one would expect.

But I can not tell what happens to the register when uploading a new bootloader (if this is what you mean). Haven't had this pleasure yet. ;)

JChristensen


But I can not tell what happens to the register when uploading a new bootloader (if this is what you mean). Haven't had this pleasure yet. ;)


I actually just meant the normal bootloader startup sequence before it passes control to the sketch. I thought MCUSR got lost in that process somewhere. Well that is interesting, I'll have to try it out. Thanks!

westfw

MCUSR normally has its contents corrupted by the bootloader.
If you get the newest version of optiboot from its source repository (https://code.google.com/p/optiboot/ ) you'll find a patch that permits optiboot to pass the original value of MCUSR on to the user application (and an example of what the application should look like to use that info: https://code.google.com/p/optiboot/issues/detail?id=66

JChristensen


MCUSR normally has its contents corrupted by the bootloader.
If you get the newest version of optiboot from its source repository (https://code.google.com/p/optiboot/ ) you'll find a patch that permits optiboot to pass the original value of MCUSR on to the user application (and an example of what the application should look like to use that info: https://code.google.com/p/optiboot/issues/detail?id=66


Thanks. I think I saw that patch when compiling the 1284P bootloader. I was pretty sure it didn't work on my Unos and clones, but none of them have the latest version of Optiboot.  I'll try it!

Tom Carpenter

#11
Jun 12, 2014, 12:07 am Last Edit: Jun 12, 2014, 12:10 am by Tom Carpenter Reason: 1
This would be a more sensible (and functional) code:
Code: [Select]

void setup() {
 Serial.begin(38400);

 Serial.print("MCUSR: ");
 Serial.println(MCUSR);
 
 if (MCUSR & _BV(EXTRF)){
     // Reset button or otherwise some software reset
     Serial.println("Reset button was pressed.");
 }
 if (MCUSR & (_BV(BORF) | _BV(PORF))){
      // Brownout or Power On
      Serial.println("Power loss occured!");
 }
 if (MCUSR & _BV(WDRF)){
      //Watchdog Reset
      Serial.println("Watchdog Reset");
 }
 // Clear all MCUSR registers immediately for 'next use'
 MCUSR = 0;
}



The case statement you had would either work if ONLY and external reset occurred, or if ALL 3 of brownout, power on and reset occurred.
The above code filters the events and detects correctly if either an external reset occurred, or if ANY of brownout or power on occurred. Plus if you press the reset button before it gets to the point of clearing MCUSR, it will allow you to see that too.

Granted you still have the problem of optiboot. Almost worth building a standalone ATMega circuit and using ISP to program it (no bootloader). The bootloader would delay the execution of the reset check code anyway.
~Tom~

fungus


I didn't know that accessing these registers is so damn easy! I assume that this is because some Arduino lib is doing the work.


No, it's a hardware feature of the chip.

No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

Tom Carpenter

#13
Jun 12, 2014, 10:37 am Last Edit: Jun 12, 2014, 10:41 am by Tom Carpenter Reason: 1

No, it's a hardware feature of the chip.

Partly, but it is also thanks to some of the abstraction from avr-libc.

This is accessing a register without that:
Code: [Select]
byte value;
asm volatile ("in %0, 0x34 \n\t" :"=r" (value) );
print("MCUSR = ");
println(value);

or possibly:
Code: [Select]
volatile byte* ptr;
ptr = (volatile byte*)0x54; //note adding 0x20 as this treats them as mapped into SRAM.
byte value = *ptr;
print("MCUSR = ");
println(value);

vs. this with the abstraction:
Code: [Select]
byte value = MCUSR;
print("MCUSR = ");
println(value);
~Tom~

JChristensen

#14
Aug 18, 2014, 02:58 pm Last Edit: Aug 18, 2014, 03:08 pm by Jack Christensen Reason: 1

MCUSR normally has its contents corrupted by the bootloader.
If you get the newest version of optiboot from its source repository (https://code.google.com/p/optiboot/ ) you'll find a patch that permits optiboot to pass the original value of MCUSR on to the user application (and an example of what the application should look like to use that info: https://code.google.com/p/optiboot/issues/detail?id=66


I finally got around to compiling Optiboot 5.0 for ATmega328P and am not seeing the results I expected WRT the MCUSR value.  When pressing the reset button, I expected to see EXTRF set in MCUSR but instead I see WDRF. Any ideas?

PS: Maybe this is working as designed? See comment #2 at https://code.google.com/p/optiboot/issues/detail?id=66

Output
Code: [Select]
0 MCUSR=0x08 WDRF


Code
Code: [Select]
//copy the MCUSR value saved in r2 by Optiboot to a global variable.

#include <Streaming.h>    //http://arduiniana.org/libraries/streaming/

uint8_t mcusr __attribute__ ((section (".noinit")));
void getMCUSR(void) __attribute__((naked)) __attribute__((section(".init0")));

void getMCUSR(void)
{
   __asm__ __volatile__ ( "mov %0, r2 \n" : "=r" (mcusr) : );
}

void setup(void)
{
   Serial.begin(115200);
   Serial << endl << millis() << F(" MCUSR=0x0") << _HEX(mcusr);
   if (mcusr & _BV(WDRF))  Serial << F(" WDRF");
   if (mcusr & _BV(BORF))  Serial << F(" BORF");
   if (mcusr & _BV(EXTRF)) Serial << F(" EXTRF");
   if (mcusr & _BV(PORF))  Serial << F(" PORF");
   Serial << endl;
}

void loop(void)
{
}

Go Up