简体   繁体   中英

Initializing a variable and specifying the storage address the same time: is it possible?

In the codevision compiler for Atmel processors, there is a possibility to specify the storage address of a global variable, for example

int a @0x100; // will place the variable at the address 0x100 in RAM

Of course, as per standard C, variables can be initialized upon declaration

int a=42;

However, I did not find any possibility to do them both. int a @0x100 = 42 or int a = 42 @0x100; don't work, they cause compiler errors.

You might ask why it is so important to do it, because one could simply have

int a @0x100;

int main()
{
    a = 42;
    //...
}

However, if I have variables in the EEPROM , I need to initialize them, because this is the only way to automatically generate the eeprom file with the values in it. I can't assign those values later, because in that case it would actually write the values into the eeprom at each start of the program.

While I know of no way to directly assign an EEPROM variable to a specific address and initialize it, I found this link very helpful: EEPROM data at fixed address .

The solution I used was that of declaring a struct in EEPROM and having all EEPROM variables in your program be a member of that struct. The order you define the members of the struct will be the order they are placed in the EEPROM address space by the linker. Since the struct will be the only global EEPROM declaration it is safe to say it will be addressed to address 0x0000. Therefore, you will know the address of each EEPROM variable.

Example:

 typedef eeprom struct EEvars
{
    eeprom char    foo1;   // will be located at EEPROM address 0x0000
    eeprom char    foo2;   // will be located at EEPROM address 0x0001
    eeprom short   foo3;   // will be located at EEPROM address 0x0002
    eeprom long    foo4;   // will be located at EEPROM address 0x0004
    eeprom char[3] fooArr; // fooArr[0] @ 0x0008; fooArr[1] @ 0x0009; 
                           // fooArr[2] @ 0x000A 
} EEVARS;

You can then initialize the variables in the declaration of the structure. When you compile, this will create the .eep file with the initialized values at the known EEPROM addresses.

eeprom EEVARS eepromInit = {0xAA, 0xBB, 0xCCDD, 0xEEEEFFFF, {0xF0, 0xF1, 0xF2}};

This works especially well in scenarios where the bootloader section of the AVR is used to upgrade the FLASH and the new program needs to access the stored EEPROM variables. It even allows you to add EEPROM variables across software updates as long as you add them to the end of the struct so as not to disturb the addresses of those variables already established.

I know what you're talking about and have had the same problem myself. The issue is that using the @ symbol with an address inline with the variable itself is a bolt-on addition to most toolchains. While it's supported for a lot of embedded toolchains so you can explicitly call out where SFRs or other registers are located, it's not a normal behavior for standard C.

While I'm not familiar with the particular compiler you're using, I do know that most compilers provide a more complex way of specifying the memory map. The ATmega series from Atmel for example provides the capability of specifying custom memory section in the project settings. On a GNU toolchain for example, these sections are used as part of the variable declarations by using the section attribute with the variable:

 __attribute__((__section__("<section_name>")))

For the ATmega series you locate any memory in EEPROM by including on the same line as your variable declaration (either before or after, so long as it comes before the '=' in an assignment) the text:

__attribute__((__section__(".eeprom")))

If you'd like to guarantee that the specific memory address in EEPROM is set to a value as part of your binary image, so it only gets programmed once when the image is first written, you can declare a custom memory section in your project settings (it's under the "Memory Settings" of the "Toolchain" settings if you're developing in Atmel Studio).

For example, I've done exactly what you describe with a block of data by declaring section ".tune_data" in the EEPROM portion of the memory settings (following provided documentation as to address offsets, etc) then declaring a variable like the following:

const __attribute__((__section__(".tune_data))) Tune_Data_s as_tune_data = { <all_my_data> };

Clearly it's going to be slightly different since you're not using a GNU compiler and may not be using Atmel Studio. If you look into it however, pretty much every toolchain for embedded programming provides some way to declare a custom memory section that can then be attached to a variable in the code via a pragma (or attribute for GNU toolchains). The specification should be available through a command-line argument and/or modification of a standard linkerscript with a command-line options to specify a non-default linkerscript. I know the second method is the standard and only way to do it on an IAR provided toolchain.

Just a look at CodeVisionAVR help,

"The following procedure must be used if a global variable, placed at a specific address using the @ operator , must be initialized during declaration:

/* the variable will be stored in EEPROM at address 0x10 */

eeprom int abc @0x10;

/* and it will be initialized with the value 123 */ 

eeprom int abc=123; 

You can use pointer that point to an absolute address:

volatile int *pa = (int *) 0x100;

Then you can use the dereferencing operator * to access the value at that address like so:

int value = *pa;

or

*pa = 0x10;

Edit: There is no way of declaring a variable to point to a specific area and at the same time assign a value to that area. Not unless the compiler has extensions that allow it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM