初始化变量并同时指定存储地址:是否可能?
例如,在Atmel处理器的codevision编译器中,可以指定全局变量的存储地址
int a @0x100; // will place the variable at the address 0x100 in RAM
当然,根据标准C,变量可以在声明时初始化
int a=42;
但是,我没有发现任何可能同时做到这两点。 int a @0x100 = 42
或int a = 42 @0x100;
不起作用,它们会导致编译器错误。
你可能会问为什么这么做很重要,因为人们可以这样做
int a @0x100; int main() { a = 42; //... }
但是,如果我在EEPROM中有变量,我需要初始化它们,因为这是自动生成带有值的eeprom文件的唯一方法。 我不能在以后分配这些值,因为在这种情况下,它实际上会在程序的每个开始时将值写入eeprom。
只需看看CodeVisionAVR
帮助,
“如果在声明期间必须初始化使用@ operator
放置在特定地址的全局变量,则必须使用以下过程:
/* 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;
虽然我知道无法直接将EEPROM变量分配给特定地址并对其进行初始化,但我发现此链接非常有用: 固定地址的EEPROM数据 。
我使用的解决方案是在EEPROM中声明一个结构,并且程序中的所有EEPROM变量都是该结构的成员。 您定义结构成员的顺序将是链接器将它们放置在EEPROM地址空间中的顺序。 由于该结构将是唯一的全局EEPROM声明,因此可以安全地说它将被寻址到地址0x0000。 因此,您将知道每个EEPROM变量的地址。
例:
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;
然后,您可以在结构声明中初始化变量。 编译时,这将创建.eep文件,其初始值为已知的EEPROM地址。
eeprom EEVARS eepromInit = {0xAA, 0xBB, 0xCCDD, 0xEEEEFFFF, {0xF0, 0xF1, 0xF2}};
这在AVR的引导加载程序部分用于升级FLASH并且新程序需要访问存储的EEPROM变量的情况下尤其有效。 它甚至允许您在软件更新中添加EEPROM变量,只要将它们添加到结构的末尾即可,以免干扰已建立的变量的地址。
我知道你在说什么,我自己也有同样的问题。 问题是使用带有变量本身内联地址的@符号是大多数工具链的附加function。 虽然它支持许多嵌入式工具链,因此您可以明确地调出SFR或其他寄存器所在的位置,但这不是标准C的正常行为。
虽然我不熟悉您正在使用的特定编译器,但我知道大多数编译器提供了一种更复杂的指定内存映射的方法。 例如,Atmel的ATmega系列提供了在项目设置中指定自定义内存部分的function。 例如,在GNU工具链上,通过将section属性与变量一起使用,这些部分将用作变量声明的一部分:
__attribute__((__section__("")))
对于ATmega系列,您可以通过在变量声明的同一行(在赋值之前或之后,只要它在赋值中的’=’之前)包含文本来找到EEPROM中的任何内存:
__attribute__((__section__(".eeprom")))
如果您想保证EEPROM中的特定存储器地址被设置为二进制映像的一部分,那么只有在第一次写入映像时才编程一次,您可以在项目设置中声明自定义存储器部分(如果您正在使用Atmel Studio进行开发,则它位于“工具链”设置的“内存设置”下)。
例如,我通过在内存设置的EEPROM部分中声明“.tune_data”部分(按照提供的文档解决偏移等)然后声明如下变量来完成您用数据块描述的内容:
const __attribute__((__section__(".tune_data))) Tune_Data_s as_tune_data = { };
显然,由于您没有使用GNU编译器而且可能没有使用Atmel Studio,因此会略有不同。 但是,如果你研究它,几乎每个嵌入式编程的工具链都提供了一些方法来声明一个自定义内存部分,然后可以通过编译指示(或GNU工具链的属性)将其附加到代码中的变量。 规范应该可以通过命令行参数和/或使用命令行选项修改标准linkerscript来指定非默认的linkerscript。 我知道第二种方法是在IAR提供的工具链上进行标准和唯一的方法。
您可以使用指向绝对地址的指针:
volatile int *pa = (int *) 0x100;
然后你可以使用解除引用运算符*
来访问该地址的值,如下所示:
int value = *pa;
要么
*pa = 0x10;
编辑:无法声明变量指向特定区域,同时为该区域分配值。 除非编译器具有允许它的扩展名。