【问题标题】:Initializing a variable and specifying the storage address the same time: is it possible?初始化一个变量,同时指定存储地址:有可能吗?
【发布时间】:2012-08-02 05:10:06
【问题描述】:

Atmel 处理器的 codevision 编译器中,可以指定全局变量的存储地址,例如

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

当然,按照标准 C,变量可以在声明时初始化

int a=42;

但是,我没有找到任何可能同时做这两个。 int a @0x100 = 42int a = 42 @0x100; 不起作用,它们会导致编译器错误。

你可能会问为什么这样做如此重要,因为一个人可以简单地拥有

int a @0x100;

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

但是,如果我在 EEPROM 中有变量,我需要初始化它们,因为这是自动生成包含值的 eeprom 文件的唯一方法。我以后不能分配这些值,因为在这种情况下,它实际上会在程序每次启动时将值写入 eeprom。

【问题讨论】:

  • 哪个特定的 Atmel 处理器系列?这个帖子对avrfreaks.net/…有帮助吗?
  • ATmega,但 eeprom 处理是相同的。该线程仅更详细地描述了我在上一段中写的内容(如果我在声明 eeprom int a=42; 处为 eeprom 变量赋值,这意味着该值将被添加到生成的 eeprom 文件中,该文件只会写入一次设备。它不会在每次程序启动时实际写入 eeprom,这是一个非常好的功能)。如果我在单独的命令中分配了值,它将被编译为写入 eeprom 的指令。感谢您的链接,如果我在这里没有得到任何可用的东西,我会尝试在那里询问。
  • 请解释一下你实际上想要做什么,听起来你走错了路,可以做一些事情。 Atmel 与此无关。您想控制特定地址的 eeprom 空间中的值吗?您想控制不在 eeprom 中的寄存器/内存中的值吗?如果后者你想控制地址,以便你的程序一旦启动就可以有一个指向该寄存器/内存位置的变量(不在 eeprom 中)?
  • @dwelch:同一个 eeprom 文件将被多个程序使用。我想保证在所有程序中都会读取相同的内容。

标签: c embedded avr


【解决方案1】:

看看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; 

【讨论】:

    【解决方案2】:

    虽然我不知道如何直接将 EEPROM 变量分配给特定地址并对其进行初始化,但我发现此链接非常有用:EEPROM data at fixed address

    我使用的解决方案是在 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 变量,只要您将它们添加到结构的末尾,以免干扰已经建立的这些变量的地址。

    【讨论】:

      【解决方案3】:

      我知道你在说什么,我自己也遇到过同样的问题。问题在于,使用带有与变量本身内联的地址的 @ 符号是大多数工具链的附加功能。虽然许多嵌入式工具链都支持它,因此您可以显式调用 SFR 或其他寄存器的位置,但这不是标准 C 语言的正常行为。

      虽然我不熟悉您使用的特定编译器,但我知道大多数编译器都提供了一种更复杂的方式来指定内存映射。例如,来自 Atmel 的 ATmega 系列提供了在项目设置中指定自定义内存部分的功能。例如,在 GNU 工具链上,这些部分用作变量声明的一部分,方法是使用带有变量的 section 属性:

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

      对于 ATmega 系列,您可以在 EEPROM 中找到任何内存,方法是在变量声明的同一行(之前或之后,只要它位于赋值中的“=”之前)包含以下文本:

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

      如果您想保证 EEPROM 中的特定内存地址设置为二进制映像的一部分,因此它只在第一次写入映像时被编程一次,您可以在您的项目设置(如果您在 Atmel Studio 中开发,它位于“工具链”设置的“内存设置”下)。

      例如,我通过在内存设置的 EEPROM 部分声明“.tune_data”部分(按照提供的关于地址偏移等的文档)然后声明一个变量,如以下:

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

      显然它会略有不同,因为您没有使用 GNU 编译器,也可能没有使用 Atmel Studio。但是,如果您仔细研究一下,几乎每个嵌入式编程工具链都提供了一些方法来声明自定义内存部分,然后可以通过编译指示(或 GNU 工具链的属性)将其附加到代码中的变量。规范应该可以通过命令行参数和/或使用命令行选项修改标准链接描述文件来指定非默认链接描述文件。我知道第二种方法是在 IAR 提供的工具链上执行此操作的标准方法,也是唯一方法。

      【讨论】:

        【解决方案4】:

        您可以使用指向绝对地址的指针:

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

        然后您可以使用解引用运算符* 访问该地址的值,如下所示:

        int value = *pa;
        

        *pa = 0x10;
        

        编辑:没有办法声明一个变量指向一个特定的区域,同时给那个区域赋值。除非编译器有允许它的扩展。

        【讨论】:

        • 这只是int a @0x100; 的另一种方式(这很危险,因为编译器可能会在该地址分配其他东西)。它将如何初始化该地址的 at 值?此外,正如问题中所述,在 eeprom 变量的情况下它是最重要的。
        • @vsz 嵌入式系统的编译器在内存和内存位置方面非常严格。嵌入式系统具有用于堆、堆栈和其他数据段的特定区域。只有当系统配置错误时,编译器才会使用它不应该使用的内存地址范围。
        • 据我所知,ANSI C 不支持 int a @0x100;。当然,AVR 编译器(包括 codevision 和 AVR-GCC 以及可能的其他编译器)都有用于此目的的扩展。
        • @dwelch:但这不是我想做的。这个答案显示了如何使用位于确切内存位置的变量,很好。但同样的事情是通过我的示例实现的:int a @0x100;,这是 AVR 编译器的一个特殊功能。而且更安全,因为在指针解决方案中,不同的普通变量也可以自动映射到同一地址。我寻求的是同时拥有一个绝对地址 初始化变量,因为初始化是我知道的唯一方法是 EEPROM 只初始化一次,而不是在程序的每次启动时.
        • @dwelch:不,和开头一样。我不希望两个地址相互指向。我想要一个变量,它保证位于预定义的内存位置,并且被初始化。我可以用int a @0x100;“保证位置”,我可以用int a = 42;初始化 但是我想两者都做,但我发现没办法它。 int a = 42 @0x100;int a @0x100 = 42; 和其他排列会导致语法错误。我只是告诉 eeprom 我需要它来证明我为什么需要它。否则int a @0x100; a=42; 就可以了。
        猜你喜欢
        • 2018-06-23
        • 1970-01-01
        • 1970-01-01
        • 2021-01-02
        • 2015-01-17
        • 2016-02-24
        • 1970-01-01
        • 2014-01-20
        • 1970-01-01
        相关资源
        最近更新 更多