0

I'm working with ATmega328 NANO boards, and I would like to know if it is possible to INSERT my own routine, to be included in the things done during reset, BEFORE any pre-C initialization is done, or any upload of new code occurs. If this is possible, I'd like to know how. I'd want to be able to "install" this routine during setup(). I also would not expect it to work after a power loss. I'm talking about a reset caused by pressing the reset button, uploading code, opening a serial port, or any reset that was NOT caused by a loss of power.

I'm including a simple example sketch illustrating what I would LIKE to do, but it obviously won't do what I want as written, because I do not know the "magic" answer I'm seeking,

I've defined and initialized a global static byte outside of any functions. The setup() simply reads a byte from EEPROM and displays it with Serial.print(), and then stores its value in that global 'number'. Now notice I created a void(void) function called magic(). It simply increments the global number and stores it in EEPROM. I call it "magic() because what I need to do to call it is unknown. I want some way to "install" magic() during setup(), so that it runs only as part of the reset process, before anything within code or data space is affected. By install, I don't mean just call the function. It should not actually be called unless RESET occurs. Whatever "magic" has to be added to make this happen, the proof of the pudding will be that after loading the sketch. every time you press RESET, or even upload the sketch again, the printed number will be different, because it was incremented and saved when reset was pushed. NOTE: (I just edited some stupid errors here)

#include <EEPROM.h>

static uint8_t number =0;

void setup()
{
  uint8_t n = EEPROM.read(0);
  Serial.begin(57600); 
  Serial.print(n);
  number = n;

  // somehow, install "magic" routine here

}

void loop() { }

void magic(void)
{
 ++number;     
 EEPROM.write(0, &number);
}
  • 1
    why not at first line in setup()? – Juraj Aug 18 '18 at 18:02
3

Yes, it is possible. See the documentation on memory sections, from the avr-libc manual. For example:

// Run this after initializing the stack pointer and zero_reg, but
// before initializing the RAM.
void __attribute__((naked, used, section(".init3"))) magic(void)
{
    // Whatever...
}

Note that this runs after both a warm reset and a cold boot. See Gerben's answer if you want to tell the difference between these two reset conditions.

Note also that, if you just want some data to be preserved through a reset, you can put it in the .noinit section, no need to use the EEPROM.


Edit 1: In response to the OP's comment, here is a small program demonstrating the technique. It just quickly blinks the LED three times early in the program initialization, then three more times, more slowly, during setup():

#include <util/delay.h>

// Blink the LED three times, fast, early in the initialization.
void __attribute__((naked, used, section(".init3"))) magic(void)
{
    DDRB |= _BV(PB5);      // pin PB5 = digital 13 as output
    for (int i= 0; i < 6; i++) {
        PINB |= _BV(PB5);  // toggle PB5
        _delay_ms(100);
    }
}

// Blink the LED three times, slowly.
void setup()
{
    pinMode(13, OUTPUT);
    for (int i = 0; i < 6; i++) {
        delay(300);
        digitalWrite(13, !(i & 1));
    }
}

void loop(){}

I tested it on an Uno, and it works.

Note that magic() runs before the Arduino core library has been initialized. At this point, many core function, like delay(), do not work. Any job that is done here should be done low-level, using only direct port access and avr-libc calls.

Edit 2: In the previous version of this answer I forgot the used attribute. It worked with the old Arduino setup I tested with, but not with the newest IDE. Adding the used attribute fixes this.

  • Well first... I edited my "wishlist" sketch a little, because i had some stupid errors (Forgot the Serial.begin and wasn't properly formatting the EEPROM calls. But unfortunately what you've suggested compiles, but doesn't work. If you try it with the editing my corrected sketch with your suggestion, you'll see it compiles, but won't work. The proof is that "255" is always printed. I even tried substituting a short for() loop instead of the EEPROM write: "pinMode(13, OUTPUT); for (int i= 0; i < 10; i++) {digitalWrite(13, i & 1); delay(20);}" The LED doesn't flash when you press RESET. – Randy Aug 18 '18 at 19:51
  • @Randy: What I suggested does work. The Arduino core library doesn't if it has not been initialized. See my edited answer. – Edgar Bonet Aug 18 '18 at 20:23
  • Hmmm... well I don't have any UNOs, only NANO boards. Doesn't work there. I do understand the wisdom of starting with a very low level and simple example. The only thing I changed in your edited code was to make the setup blink fast (300mS delay), and the magic() one 1000mS, so i wouldn't miss it. Also tried using different init levels. It compiles, but magic() has no pixie dust here so far. ;-) I'm sure this is the best advice I've gotten yet, and most likely to work. Can you think of why I'm not having any luck? – Randy Aug 18 '18 at 20:39
  • Why would "PINB |= _BV(PB5)" alternate the LED pin state? – Randy Aug 18 '18 at 20:42
  • 2
    @Randy because the datasheet says so in §18.2.2. – tttapa Aug 18 '18 at 20:47
1

You can check the MCUSR register to see the cause of the reset. In your case you could use:

if( bit_is_set(MCUSR, EXTRF) )// reset button was pressed
{
    MCUSR = 0;// clear the reset-flag

    // "magic" routine here

}
  • Thanks. I understand this, but I don't care what caused the reset. – Randy Aug 18 '18 at 19:52
  • 2
    Are you kidding me. You specifically said reset and “NOT by loss of power”. Well; now I don’t care anymore – Gerben Aug 18 '18 at 20:01
  • I think its obvious what i meant: I wanted it to WORK when a RESET occurs, but I don't expect it work if there is a loss of power. – Randy Aug 18 '18 at 20:09
  • 1
    It was so obvious that you have two people giving you an answer to the wrong interpretation of that part of the question! – Gerben Aug 18 '18 at 20:17
0

If you use the latest version (7.0, July 2018) of Optiboot, you can use the MCUSR register to check the reset cause.

You need to use the latest version because previous versions of the bootloader cleared the MCUSR before starting the sketch (as recommended in §15.9.1 of the ATmega328P datasheet). This was changed in version 7.0 (a50f47b).

#include <EEPROM.h>

static uint8_t ch = MCUSR;

void setup() {
  MCUSR = 0;
  uint8_t n = EEPROM.read(0);
  Serial.begin(115200);
  Serial.print(n);
  if ((_BV(PORF) & ch) == 0) // not a power on reset
    magic(n);
}

void loop() { }

void magic(uint8_t &number) {
   EEPROM.put(0, ++number);
}
  • Thats interesting, but I've seen this before, and I don't really care what caused the reset. what I do want, and hope I explained, is for the routine I called magic() to run when a reset occurs, before any new sketch code is loaded, or before any pre-C cleanup or initialization of memory/variables occur. – Randy Aug 18 '18 at 19:18
  • 2
    @Randy You specifically asked: "I'm talking about a reset caused by pressing the reset button, uploading code, opening a serial port, or any reset that was NOT caused by a loss of power." What is the reason you need it to happen before the C initializations? – tttapa Aug 18 '18 at 19:38
  • I'll answer, but I don't want to side track too much into why. In a complex system, I usually put everything important into a structure (states, timers, etc.), and save it all to EEprom before turning it off. The 1st thing the code does when it starts is read the struct back out of EEprom, so it can resume in the exact same state. It would be nice if uploading a code change could first trigger my save routine too. I can find a way to do the save manually first, but it would be nice to automate it. Sadly, nothing I've tried has worked. Not even the .init sections. – Randy Aug 18 '18 at 20:15
  • @Randy That doesn't make any sense to me: when the upload is started, the MCU resets, so your configuration (states, timers) are reset and lost. – tttapa Aug 18 '18 at 20:40
  • 1
    @Randy Yes, of course, but it doesn't matter when you read back the EEPROM config, you could do it in the setup. But if you want to save the current, unsaved state before updating, you cannot do that after a reset. – tttapa Aug 18 '18 at 20:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.