Is it possible to insert your own routine into the Reset procedure, ATMega328

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 code is loaded, or any pre-C initialization is done. If this is possible, I’d like to know (A) how to do it, and (B) how much time my inserted function would have to complete, to ensure original reset functionality is not adversely affected. This thread has caused some confusion, so I am editing it with the inclusion of a simple example sketch illustrating what I would LIKE to do. Obviously it won’t do what I want as written, because I don’t yet have 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() as part of the reset process, before anything within code or data space is affected. 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.

#include <EEPROM.h>

static uint8_t number =0;

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

void loop() {}

void magic(void)
{
 EEPROM.write(0, ++number);
}

NOTE: As I said in the original post before editing) I’d like to keep replies focused on the actual request. That way if it is NOT impossible to do it, the lack of replies will at least alert anyone seeking (or Googling ) this subject that it probably is not possible. “Why” questions or suggested alternatives to my request would be best posted in a similar thread I posted back in March, here.[/b]

This is an example of the XY problem.

Why would you want to do this?

Oh and what do you mean by "reset procedure".

setup() is part of the "reset procedure" as is/are the creation of objects.

If you want better answers then YOU MUST PROVIDE MORE DETAIL! Telling us not to ask questions we feel need answers just does not work!

Mark

Put your code in one of the ".initX"sections, which execute before any of the C code. You DO have to be very aware of what is and isn't available in the immediately post-reset state. Note that this will always be running after the bootloader runs, but that is pretty minimal in its interference, especially if you get the latest Optiboot

An example usage is interfacing with the bootloader to get the reset cause:
https://github.com/Optiboot/optiboot/blob/master/optiboot/examples/test_reset/test_reset.ino

(alternately, you could presumably create your own bootloader, with your custom code inside it.)

holmes4:
This is an example of the XY problem.

Why would you want to do this?

Oh and what do you mean by "reset procedure".

setup() is part of the "reset procedure" as is/are the creation of objects.

If you want better answers then YOU MUST PROVIDE MORE DETAIL! Telling us not to ask questions we feel need answers just does not work!

Mark

  1. X/Y problem... That is correct. If you want to discuss whether I'm asking the correct question, see the other post i referenced, and read it through to the end.

  2. If you want to know the "WHYs", again, please see the older referenced post, and comment there if you wish.

  3. "reset procedure"... Take a new Arduino out of the box, power it up or plug in a USB. Internally a boot loader program begins to run. It does several things before ultimately calling the user's Setup() code, and Loop() code. That is what mean by the "reset procedure". It will always run.

Many embedded systems include a NV read/write memory location that the processor will use for this starting point. This enables an application to insert its own vector, pointing to alternate code. The next time a hard reset occurs, that a new piece of user generated code will run first. If that code is properly written, it will jump back to the original reset vector location when its done. If such a thing is possible, I'd like to attempt it.

westfw:
Put your code in one of the ".initX"sections, which execute before any of the C code. You DO have to be very aware of what is and isn't available in the immediately post-reset state. Note that this will always be running after the bootloader runs, but that is pretty minimal in its interference, especially if you get the latest Optiboot

An example usage is interfacing with the bootloader to get the reset cause:
https://github.com/Optiboot/optiboot/blob/master/optiboot/examples/test_reset/test_reset.ino

(alternately, you could presumably create your own bootloader, with your custom code inside it.)

Thanks... I'll look into that. The main thing I'd want is for the inserted code to run before user 'dynamic' memory is initialized. I assume it is initialized because most 'unstuffed' global vars seem to reliably default to zero. If my inserted code can't run before memory is wiped, or if memory at that point is random due to loss of strobes ( does it behaves like static ram or true dynamic ram that needs RAS and CAS stobes?), then it wouldn't be super valuable to me either. I'm hoping to at least be able to write several dozen bytes from dynamic memory to EEprom before memory is wiped.

1+2 NO and NO I'll ask questions relating to this thread here and I expect answers her not any where else if you want help YOU must make an effort - it's a two may street!

3 SO stick your code in setup()!

@westfw - init is the third(ish) thing to run - it is init() you mean by initX section?

Mark

I assume it is initialized because most 'unstuffed' global vars seem to reliably default to zero

That's a requirement (abet a silly one) of the ANSI C/C++ statndard

Mark

The memory is static ram!

Mark

PeterPan321:
is possible to both read and replace the RESET vector for Arduinos having the ATMeg328 chip

Yes.

@westfw - init is the third(ish) thing to run - it is init() you mean by initX section?

Nope. init() is just a function withing the arduino code. The .init sections are a separate compiler-level thing (follow the link I provided.)
If all you want is some chunk of memory that isn't overwritten with zeros when arduino starts up, there is also the possibility of putting variables in the ".noinit" section:

uint8_t persistent_data[100] __attribute__ ((section(".noinit")));

westfw:
Nope. init() is just a function withing the arduino code. The .init sections are a separate compiler-level thing (follow the link I provided.)
If all you want is some chunk of memory that isn’t overwritten with zeros when arduino starts up, there is also the possibility of putting variables in the “.noinit” section:

uint8_t persistent_data[100] __attribute__ ((section(".noinit")));

Oh that’s an interesting thing to know! I’ll have to experiment to see how well it works. But I’ll still need that INIT example, because it seems to be able to reveal what kind of reset is taking place. I have all my state variable in a structure of course, and normally I initialize it with what I’d call sensible “factory defaults”. Then, once the user has made a real setup, it all gets saved to EEprom. I already can detect when power is fading and save the whole structure, so on power up I can recall both user settings, and process states. So if in addition I can detect when a reset has come from either the RTS line pulled low (same as what the serial chip does), I could theoretically decide not to initialize the structure OR recover EEprom data, because in that case the “persitant data” would already contain a valid setup.

Then again I’m getting ahead of myself… I don’t know exactly how to put my structure in this persistent data area yet, and it does complicate things because I’ll now need another copy of the structure.

So back to that Init() hook you mentioned. When it is called, has the reset already cleared its dynamic data area? If not, the easiest thing for me to do is simply save my struct to EEPROM, as I normally do when power is going away. It would seem to be the easiest addition, because afterward, the code could proceed normally, as it would in a power up. But again, that involves knowing whether data space is cleared before or after the init() hooks you described.

In any case, at least I have some potential solutions!

PeterPan321:
Additionally, for future upgrades/improvements, it would be an advantage (and one less point of error for a field installer) if all that was necessary was to jack in a USB port and perform the upgrade, knowing that the end users scheduled and ongoing processes would resume as expected.

I am very sceptical that this would be reliable. It may be OK in laboratory conditions but it is not something I would design into a product in the hope that it would work. Because if it does not work for the customer you will have a monumental mess.

Finally, since I am making the USB port accessible, its always possible some curious user might jack it into a PC. If that is done, the resulting reset could once again cause an issue with current daily scheduled processes, because states were not saved.

That is an entirely different problem and should be treated as such - probably by disabling the auto-reset or by making it impossible to plug into the USB port by accident. Or by making such an action void any warranty.

...R

has the reset already cleared its dynamic data area?

There is no "dynamic data area" cleared by reset itself. Only by the C startup code. The .init sections happen before the C startup code, so ... nothing is cleared yet.
Various IO registers are set to specific values, but nothing is done to either RAM or CPU registers.
(power-up and power-down tend to be more destructive, and in fact it's a pretty common bug for someone programming in assembly language to forget to initialize register or memory, in which case sometimes it works after power-on, but not after hitting the reset button.)

westfw:
There is no "dynamic data area" cleared by reset itself. Only by the C startup code. The .init sections happen before the C startup code, so ... nothing is cleared yet.
Various IO registers are set to specific values, but nothing is done to either RAM or CPU registers.
(power-up and power-down tend to be more destructive, and in fact it's a pretty common bug for someone programming in assembly language to forget to initialize register or memory, in which case sometimes it works after power-on, but not after hitting the reset button.)

So if that's the case (of course I'll test what you're saying), I could use that to my advantage. A variable, or short array stuffed with a complex 'magic number' string would be highly unlikely to still contain that string after a power cycle, regardless of whether it is sitting in a 'persistent_data' section, but it certainly 'SHOULD' still be intact on a user reset or serial event. So I'd like to do two things then with my added init() code... test the 'magic' string for validity and if it IS intact, use my existing call to save my structure to EEPROM. Sound reasonable?

So I have two things to try, both of which I'd like to master for this project or a future one. First, say I have a simple typedef C structure. Its currently 80 bytes long. If I wanted to put something like that in a persistent_data section, how would I do that? (Or do you have a reference to where this is explained so I can read for myself)? Second, looking at that example init code, the example seems to be written in assembler. I don't mind delving into learning the processor's native tongue if I have to, but you also mentioned init() being "just a function withing the arduino code". Is it possible for me to write the bulk of my "test memory and save to eeprom" in code, and patch it in without assembler, as you might do for installing an interrupt?

Robin2:
I am very sceptical that this would be reliable. It may be OK in laboratory conditions but it is not something I would design into a product in the hope that it would work. Because if it does not work for the customer you will have a monumental mess.
That is an entirely different problem and should be treated as such - probably by disabling the auto-reset or by making it impossible to plug into the USB port by accident. Or by making such an action void any warranty.

...R

Well thanks, and of course I intend to test and re-test any solution like this I come up with. But the way the code is constructed, the worst that could happen would be what happen now after a hardware reset... I fail to save my most resent data, and so some of the daily processes which have already been done (or partially done) are repeated. That's not awful, but its still a 'mess' in my opinion, so maybe this would be an improvement.

Here's another example. It looks like the data is more "persistent" than I would have thought, sticking around through actual powerdowns, uploading new copies of the code, etc...

 struct {
  volatile uint32_t magicnumber;
  char string[80];
  volatile uint32_t resetcount;
} persistent __attribute__((section (".noinit")));

void setup() {
  Serial.begin(115200);
  while (!Serial) ;
  if (persistent.magicnumber == 0x12349876) {
    Serial.println("Magic Number present");
    Serial.print("String has: ");
    Serial.println(persistent.string);
    Serial.print("reset count has ");
    Serial.println(persistent.resetcount);
    persistent.resetcount++;
  } else {
    Serial.println("No persistent data detected.  Initializing.");
    persistent.magicnumber = 0x12349876;
    sprintf(persistent.string, "This should stay here after reboots");
    persistent.resetcount = 0;
  }
}

void loop() {
  if (Serial.read() == 'x')  // option to clear the data
    persistent.magicnumber = 0;
}

westfw:
Here's another example. It looks like the data is more "persistent" than I would have thought, sticking around through actual powerdowns, uploading new copies of the code, etc...

Actually took a break from this stuff for a day (always good to do!). But holy smokes! Really? It held through a power-down? For how long? I guess it makes sense if its static ram and there exists some residual charge within the CPU. Battery backed static RAM, after all, is often good for many years. Interesting! So all you did was add "attribute((section (".noinit")));"after the structure declaration? Same if the the struct was already #typedef, and declared by its type?

Could you help me with a mental sanity check here. In my case I have a circuit mechanism to detect loss of power, and drop a digital input at least a second before the CPU loses its power. That's plenty of time to take various actions, which for me is mainly saving my structure to EEPROM. So let's say I try to put my entire program structure in this persistent data area. First, I'll add a "powerDownSave" boolean flag to it, and a magic number (or maybe better, a checksum or CRC). I'll only set that flag TRUE when I actually do a power-down EEPROM save. When my program starts, before I assume I need to pull my structure copy from EEPROM as usual, I'll check that flag. IfI see the flag is TRUE, I'll recover EEPROM data as I always have, whether I really need to or not. But if I see the flag is false, I'll check my magic number or CRC, and if all is well there, I can assume my data is already intact, and skip pulling the data from EEPROM. Naturally the first things I'd do at this point is clear the flag. So now, if someone hits the reset button or causes reset through the serial port, I will still get interrupted and restart. But the CRC checked persistent data (and clear power-down flag) will tell me my structure in memory is really the most current.

So what could go wrong....go wrong....go wrong....go wrong....go wrong....go... :wink:

…the easiest thing for me to do is simply save my struct to EEPROM, as I normally do when power is going away…

Unless, i’m reading this wrongly - you seem to miss the point that all your RAM content becomes invalid when power ‘goes away’. Restarting after any power loss will leave nothing viable in RAM.

You can’t rely on anything being valid after a power cycle - that’s what NVRAM or EEPROM is all about.

lastchancename:
Unless, i’m reading this wrongly - you seem to miss the point that all your RAM content becomes invalid when power ‘goes away’. Restarting after any power loss will leave nothing viable in RAM.

You can’t rely on anything being valid after a power cycle - that’s what NVRAM or EEPROM is all about.

No... that part is actually not an issue. As I attempted to describe, I have an electric circuit solution for this, which I'd be happy to share in detail if my explanation isn't enough. I have a 12V power supply. There is a diode on the positive side, and there I have a significant capacitor (2200uF). That is the point in the circuit that feeds VCC. At the same time, I have a pair of resistors connected to the supply BEFORE the diode. Those resistors form a voltage divider that delivers about 5V to a digital input. (I have a SMALL capacitor on the pin (100nF) to filter noise.) So the result is that the voltage on the digital input pin will drop very quickly when power is disconnected, because the diode prevents the larger capacitor from holding the voltage high at the digital pin. That larger capacitor, however, continues to feed the VIN pin, keeping the MCU running for well over a second. So as long as I am always monitoring the digital input pin for a drop to LOW state, and immediately saving my data to EEPROM at that point, I am assured that the EEPROM save has been done long before power to the MCU is lost.

What is being discussed here are ways to ensure data is preserved when a reset is caused by something other than a fresh power up, such as when a serial event or the reset button pulls the RST line low.

This works vey well - TWO supply rails shown - you only need the 5V
Sense the voltage drop BEFORE the regulator(s)
volt-sense.JPG
Poll the analog input regularly - I use this and have time to save about 1.5K to EEPROM
Quite a few in the field.