I'm coding a SAMD21 based board. The upload will contain both code and application config data. (Note this is NOT the chip config data.) The application config data will be declared using the PROGMEM directive aand the initial upload will contain default values for this.
At some stage after the node has been deployed I'd like to be able to overwrite the app config data using an across the wire protocol (see Omzlo: The NoCAN protocol specification). There are tools in this protocol to specify the starting address, length and payload of firmware (app config data) to upload.
What I need to know is the address of the memory block that is allocated by the compiler when the PROGMEM directive is specified so I can set the starting address correctly.
To my knowledge, a SAMD21 has no progmem. You can find a memory location by using the printf family and using the %p format specifier.
char buffer1[64];
int param1 = 1;
int param2 = 2;
void setup() {
Serial.begin(115200);
while (!Serial)
;
sprintf(buffer1, "address of param1 = %p", ¶m1);
Serial.println(buffer1);
sprintf(buffer1, "address of param2 = %p", ¶m2);
Serial.println(buffer1);
}
void loop() {
// put your main code here, to run repeatedly:
}
Output:
address of param1 = 0x10e
address of param2 = 0x10c
I'm not familiar with the SAMD21 but I'm reasonably sure that you can emulate EEPOM in flash; it might be a far easier solution. The flow would be something like
Verify (emulated) EEPROM CRC
If CRC fails, use program defaults
If CRC success, use EEPROM values
You can store your parameters in emulated EEPROM and update those.
It has no PROGMEM, all you need is a 'const' variable and it will be stored in Flash.
The 'PROGMEM' keyword is added to be compatible, but it is an empty keyword.
It has also no EEPROM, but you can mimic the EEPROM behaviour with a library that uses Flash memory. A few other processor can do that same trick. But it is a trick.
The ATmega microcontroller is optimized a lot. As a result it can not execute code from ram and it can not read data from Flash. There is no data-pointer that can point to data in Flash. With special instructions it is possible to read data from Flash and the compiler can be told that a pointer should be treated as a pointer to Flash memory, so with a detour or two, the PROGMEM became possible.
So if I declare a lump of const memory, is there any way I can definitively determine where the compiler will store that, or do I need to do something a bit more clever and have a function that would return the address of the start of the memory address, given that the optimising compiler could move it between version of my program.
Sort of building on your first answer sterretje
Be clever and use 'const'.
Don't over-think it or try to over-clever it, that will mess it up.
Make a block of data and look at the compiler output.
With 'const':
Sketch uses 29368 bytes (11%) of program storage space.
Global variables use 2400 bytes (7%) of dynamic memory
Without 'const'
Sketch uses 29376 bytes (11%) of program storage space.
Global variables use 20464 bytes (62%) of dynamic memory
Without 'const', the data is still in Flash, because the data has to be somewhere. During startup, it is copied to a global variable, and the SRAM usage goes from 2400 bytes to 20464 bytes.
For a processor, it is common that a 'const' goes into Flash memory and nowhere else. That is the rule. That is what the 'const' keyword is supposed to do. The ATmega is the exception because it has no pointer to 'const' data in Flash as I wrote before, so that is where the trickery stuff is used, but it is not common.
For a processor, it is common that a 'const' goes into Flash memory and nowhere else. That is the rule. That is what the 'const' keyword is supposed to do.
That's got me on the right track (I'm new to this level of programming). I'll follow up by figuring out where my const block gets stored in the hex file and can work things out from there.
This post looks useful
I hope that that is the emulated EEPROM stuff, else I think that you're wasting your time. Don't get me wrong, it's always nice to learn things but if you want to get your project finished ...
Thanks for your help, I've learnt a lot in the last few hours and no doubt I'm only starting to climb this wall.
I agree, it's good to learn new things, but don't forget the finish line. I often have to remind my team of this at work - hit a road block so just put a "mock" solution in to get past that block while you get the rest of the project working. You can always come back to put the correct solution in place as needed
So things have evolved I'm using a bootloader and an external protocol which enables the "application" code to be reflashed over a communications network.
The bootloader currently puts the application code at address 0x2000. I could change this to put the code at a higher address, say 0x2400 which would free up 1k bytes of flash to store configuration data (this block will not be overwritten if a new application hex is uploaded).
So the problem now becomes how to get the Arduino code to access memory at a negative offset, some typical of virtual mapping perhaps ?
Yeah, with 100 odd nodes across the network having to recompile code for each one to change the configuration is not an option. As well as being too error prone.
I'll be writing a separate PC utility that will provide a GUI for configuration of each device, outputting the data in a bytewise format that can then be uploaded across the network to targetted devices.
So the issue to be solved here is to provide an emulation of EEPROM reading that reads from the memory at 0x2000. I can handle the writing of that memory via the network protocol. So obviously not a pointer but a lower level assembly instruction then.
Arduino is just a layer of software. Everything below it can be used, every C++, C and assembly code you want.
If the normal way would be "int var __attribute__((at(0x20000000)));" then that can be used in Arduino. I see most of the time that the compiler is told about a "section" at a certain location. There is a note about the GCC compiler and you need a pointer to Flash memory. Let us know if it works.
So my code to define the configuration data will look something like ...
// use following to stash config data at a particular location
typedef struct {
byte channel;
byte id;
} config_test;
const config_test configData[2] __attribute__((at(0x2000))) = {{10, 1}, {11, 0}};
and the code will be accessed during the execution of the program
int _delay = configData[0].channel;
The board definition linker_scripts flash_with_bootloader.ld file that defines the offset for the application code will need to be amended as well, so the HEX file contains the correct address.
Time to take a look at the bootloader because that starts the application, and it expects to find it at 0x2000, rather than the new location of 0x2400