【问题标题】:Understanding I/O memory location in embedded C了解嵌入式 C 中的 I/O 内存位置
【发布时间】:2021-06-14 07:31:10
【问题描述】:

• 声明一个指针,

int *p;

• 将 I/O 内存位置的地址分配给 指针

 p = (int*) 0x30610000;

我想访问内存位置0x30610000,它有一个我想配置的寄存器。 为什么要键入强制类型转换呢? p =0x30610000; 不应该指向该位置吗?

【问题讨论】:

  • 尝试在没有强制转换的情况下进行编译,看看编译器是怎么说的。
  • “初始化使指针从整数变为没有强制转换”。我无法理解为什么当我输入 cast 时它会起作用。
  • 好的,现在搜索该错误消息并阅读它。我想说的是,您可以在发布新问题之前进行基础研究。很多时候,它会比发帖更快,并且学习进行有效的研究是一项有用的技能。
  • 好的,我理解为什么我们需要类型转换指针的概念。在此,RHS 是我想要访问的地址,为什么不强制转换它就无法工作。我能找到的是,最初的十六进制值是一个数字,类型转换是让它成为一个地址。我希望得到更详细的解释。
  • 0x30610000 是一个整数。将整数分配给指针通常是错误的,至少应该生成编译器警告。将其显式转换为指针向编译器表明您希望将其解释为指针,并且它不仅仅是一个错误(通常是这样)。因此,它完全符合您的要求。

标签: c pointers embedded


【解决方案1】:

int *p;

p = (int*) 0x30610000;

方法和实现都不好。

  1. 使用宏(不是指针变量)来访问映射到地址空间的硬件寄存器。这是更有效的方式。
  2. 使用固定大小的整数。
  3. 将寄存器声明为volatile。它们肯定容易产生副作用:)
#define  REG1  (*(volatile uint32_t *)0x30610000)

及用法:

REG1 = 0x456;
foo(REG1);

大多数外设都有不止一个寄存器。最好使用结构来访问它们(注意 volatile 关键字所在的位置。不要使整个结构 volatile):

typedef struct
{
    volatile uint32_t DR;
    volatile uint32_t CR;
    volatile uint32_t SR;
}UART_type;

#define UART1 ((UART_type *)0x45678000)

及用法:

UART1 -> CR = 0x45676;

在 C 语言中,需要强制转换来使警告静音,因为它是一个 UB(或者可能只是定义为 @Clifford 建议的实现)。即使实现(如 ARM-gcc)以可预测的方式实现 - 它仍然会发出警告。

现在,为什么拥有指针对象的效率低于#define。这是因为编译器需要更多的指令来检索寄存器的地址。

【讨论】:

  • 肯定是 实现定义 而不是 undefined 行为?此类转换取决于编译器,但应与编译后的可执行文件运行所在系统的寻址结构一致。
  • @Clifford 可能你是对的。将其添加到答案中。
  • 感谢您的额外提示。我主要对为什么编译器不会将十六进制值视为地址而不进行类型转换感到困惑。只是为了澄清问题,不可能做像int *p=0x30001000 这样的事情并期望p 是指向0x30001000 的指针。
  • It's implementation defined as per C17 6.3.2.3/5 "一个整数可以转换为任何指针类型。除非前面指定,结果是实现定义的,可能没有正确对齐,可能没有指向被引用类型的实体,并且可能是一个陷阱表示。”。 然而这是假设编译器完全不知道该位置存储了什么。如果编译器认为其他类型不同的变量存储在那里,你会得到严格的别名 UB。这主要是 gcc 的问题。
  • 总的来说,我更喜欢#define UART1_CR (*(volatile uint32_t*)0x45678000u) 使用单独的寄存器和对应于外设的前缀。通过对齐、填充和严格的锯齿排除所有潜在的问题。
猜你喜欢
  • 1970-01-01
  • 2015-05-24
  • 2015-02-14
  • 1970-01-01
  • 2022-12-29
  • 2016-05-27
  • 1970-01-01
  • 2011-09-13
  • 2017-06-08
相关资源
最近更新 更多