假设有一个简单的测试代码

typedef struct
{
    int first;
    int second;
    int third;
} type_t;

#define ADDRESS 0x12345678

#define REGISTER ((type_t*)ADDRESS)

const int data = (int)(&REGISTER->second)*2;

int main(void)
{
    volatile int data_copy;

    data_copy = data;

    while(1) {};
}

这是在CodeSourcery的G ++(GCC 4.3.2),用于裸金属ARM编译。它也有一个很标准的链接脚本。

当用C编译(如main.c中)对象“数据”进入闪存,如所预期。当用C ++编译(如main.cpp中)这个对象进入RAM,和额外的代码加入其中确实不外乎复制从Flash中的值到RAM(该值已计算,只需复制!)。因此,编译器可以计算地址,但不知何故不希望“刚 使用它的”问题的根源是地址的乘法 - 无。‘* 2’乘法两个版本按预期方式工作 - ‘数据 - 数据‘被声明为’被放置在Flash中同样,当’:

const int data = (int)(REGISTER)*2;

也一切都很好。

C和C ++编译所有的文件是相同的,唯一的区别是在调用编译器 - 克++为main.cpp中,GCC为main.c中(与警告的水平的差异,和C ++有RTTI和异常禁用)

有没有简单和优雅的方式来解决这个“C ++的问题”?我确实需要这样的操作中的Cortex-M3的位段区创建位地址常量数组。这是一个错误,或者也许是C ++编译器的有些异样限制?

我知道我可以在“C”文件创建数据对象,只是“外部”在C ++ -Include他们,但是这不是很优雅[;

感谢您的所有帮助!

有帮助吗?

解决方案 2

在合适的解决方案是使用 offsetof()宏从STDDEF.H报头。

基本上这样:

const int data = (int)(&REGISTER->second)*2;

具有一个与替换

#include <stddef.h>
const int data = (int)(REGISTER + offsetof(type_t,second))*2;

和然后将对象放置在Flash既为C和C ++。

其他提示

您有几个问题。你为什么要服用一个地址,将其转换为整数,乘以2?你不应该乘以地址或地址存储为整数。你应该永远做指针唯一算术减去他们(以获得偏移),或与添加的指针偏移得到另一个指针。

在C ++中,对全局值初始化的规则比C.在C稍微更宽松,需要的值被初始化为编译时间常数;其结果是,编译器可以在只读存储器中放置const全局值。在C ++中,值可以被初始化为它们不一定编译时间常数表达式,允许编译器在运行时生成的代码,以计算初始值。这个初始化代码进入main()之前调用,类似于构造函数的全局对象。

既然你与常量地址的工作,你知道的int有多大是你的平台上(我假设32位),你应该做这样的事情:

接下来,你的volatile关键字的使用是完全错误的。 volatile说,编译器不会保存变量在寄存器 - 它应该从内存在每次阅读时间被重新加载,应该每次写入的时间完全写入存储器。声明局部变量data_copy作为volatile几乎是没用的,除非你期待着另一个线程或信号处理程序开始修改它意外(高度怀疑)。此外,data_copy仅仅是一个地址的副本,而不是寄存器的你想阅读的内容。

什么应该做的是声明REGISTER作为指针到一个易失性 - 这是volatile的明确目的之一,用于存取存储器映射的寄存器。这里是你的代码应该是什么样子:

#define REGISTER (*(volatile type_t *)ADDRESS)
#define DATA (*(const volatile int *)((ADDRESS+4)*2))

这使得它如此,当你做这样的事情:

REGISTER.first = 1;
REGISTER.first = 2;
int x = REGISTER.second;
int y = DATA;

它总是这样合适的事:1写入到0×12345678,2〜0×12345678写入,从0x1234567c读取,并从0x2468acf8读取。该volatile关键字确保这些读取和写入总是发生,他们没有得到优化:编译器将不删除第一次写入REGISTER.first,如果它是一个普通变量,它是多余的。

修改

在回答您的评论,见安德鲁医学生的回应您的评论 - 你真的相乘的区别的两个指针之间通过2,这是确定的。只是要小心你的数据类型。我也从来没有提过关于内核任何东西。

您可以得到GCC把变量中的特定数据部分用的 section属性

const volatile int *data __attribute__((section("FLASH")) = /* whatever */;

使用适当的节名。如果你不知道那是什么,拿上它由C编译器(你说把它放在正确的部分)生成的目标文件,运行nm,看看C编译器把哪一部分在它

您拍照时看看GCC 变量属性,或许“部分”以及帮助你在可变的位置。

  1. 您不应该乘以指针。
  2. 您不应该被存储指针作为 “INT”。
  3. 我敢肯定有一个合法的C ++的方式做你想要什么,但我不能完全理解这是你发布的代码是什么。

修改

您说你要乘的指针,但是这是非常可能是错误的。请记住:(int)(REGISTER) * 2将等于(int)(0x12345678 * 2)这可能等于0x2468ACF0不可以你想要什么。你可能想:REGISTER + sizeof(int) * 2这是2点的整数过去该结构的最后一个字节

<强>原始回答:

这看起来像一个失败的尝试做了“结构破解”(该示例使用C99风格,但它在C89工作得很好太,只需要具有1个作为最后元件的阵列)。也许你想要的是这样的:

typedef struct {
    int first;
    int second;
    int third;
    unsigned char data[1]; /* we ignore the length really */
} type_t;

type_t *p = (type_t *)0x12345678;

p->data[9]; /* legal if you **know** that there is at least 10 bytes under where data is */

这样做的常见的用法是这样的malloc分配的结构:

type_t *p = malloc((sizeof(int) * 3) + 20) /* allocate for the struct with 20 bytes for its data portion */

我想说对于外围读最安全的接入写你应该只使用volatile定义和偏移定义。铸造外围地址作为一个结构没有给出任何类型的对准或偏移量保证。

#define UART_BASE_REG ((volatile uint32_t*)0x12341234)
#define UART_STATUS_REG (UART_BASE_REG + 1)

...

如果我理解正确的话,你的总体目标是在总结这一段:

  

我确实需要这样的操作在Cortex-M3的的位段区域中产生的位的地址常量阵列。这是一个错误,或者也许是C ++编译器的有些异样限制?

我使用Keil编译为STM32,以及随之而来包含用于设置和清零位带状位宏示例之一:

#define RAM_BASE       0x20000000
#define RAM_BB_BASE    0x22000000

#define  Var_ResetBit_BB(VarAddr, BitNumber)    \
 (*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 0)

#define Var_SetBit_BB(VarAddr, BitNumber)       \
 (*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 1)

#define Var_GetBit_BB(VarAddr, BitNumber)       \
 (*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)))

如果这些到底是不是你要找什么,我想他们可以进行修改,以满足您的需求。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top