【问题标题】:Writing pointers to the address of GPIO out pin将指针写入 GPIO 输出引脚的地址
【发布时间】:2019-09-16 05:57:00
【问题描述】:

嵌入式系统的绝对菜鸟。现在我正在尝试在C 中编写一个名为nRF52832 的设备。我知道GPIO 的基地址是0x50000000,而OUT 寄存器的偏移量是0x504。如何创建指向 GPIO OUT 地址的指针并打印地址?

我做吗

volatile unsigned int* GPIO_BASE = (unsigned int*) 0x50000000;
volatile unsigned int* GPIO_OUT = GPIO_BASE + 0x504; ?

【问题讨论】:

    标签: c embedded gpio


    【解决方案1】:

    请记住,指针运算总是以指针基类型为单位进行的。

    这意味着表达式GPIO_BASE + 0x504 将添加0x504 * sizeof *GPIO_BASE 的字节偏移量,即0x504 * sizeof(int)(在大多数系统上将是5136 字节的偏移量,而不是1284 字节)。

    如果你想添加0x504的偏移量字节你需要将它除以指针基类型的大小:

    volatile unsigned int* GPIO_OUT = GPIO_BASE + (0x504 / sizeof *GPIO_BASE);
    

    除了这个问题,您的代码是正确的,并且是访问硬件寄存器的一种非常常见的方式。

    【讨论】:

    • GPIO_BASE + (0x504 / sizeof *GPIO_BASE); 是胡言乱语,你永远不会在调用应用程序中编写这样的代码。偏移量应该在宏内部计算,而不是在运行时计算,如本例所示。
    • 我认为像#define GPIO_OFFSET(x, y) (unsigned int *) ((char *) (x) + (char *) (y)) 然后简单地使用volatile unsigned int* GPIO_OUT = GPIO_OFFSET(GPIO_BASE, 0x504) 就可以了。
    【解决方案2】:

    我做吗

    volatile unsigned int* GPIO_BASE = (unsigned int*) 0x50000000;
    volatile unsigned int* GPIO_OUT = GPIO_BASE + 0x504;
    

    不,你没有。这意味着地址是在运行时计算的,因为volatile 限定符意味着程序必须在运行时更新变量,无论何时使用它。

    这是你应该做的:

    // assuming 32 bit registers and 0x504 being the offset in bytes
    #define GPIO_BASE 0x50000000u
    #define GPIO_OUT  (*(volatile uint32_t*)(GPIO_BASE + 0x504u))
    #define GPIO_X    (*(volatile uint32_t*)(GPIO_BASE + 0x508u)) // some other related register 
    
    ...
    GPIO_OUT = something;
    

    在本例中,寄存器地址是在编译时计算的,这是唯一正确的方法。

    通过取消引用宏内部的地址,我们现在可以使用GPIO_OUT,几乎就像它是一个普通变量一样。

    然而,GPIO 外设似乎不太可能有数百个内存映射寄存器,因此0x504 的值很奇怪。


    另外请注意,在嵌入式系统中使用默认的 C 类型如 intchar 是幼稚的。这些类型带有各种可移植性问题,并且还具有与之相关的各种形式的定义不明确的行为。专业系统只使用来自stdint.h 的类型,从不使用其他类型。

    【讨论】:

      【解决方案3】:
      volatile unsigned int* GPIO_BASE = (unsigned int*) 0x50000000;
      volatile unsigned int* GPIO_OUT = GPIO_BASE + 0x504; 
      

      如果OUT 寄存器的offsetBASE 相距0x504 字节,则需要将GPIO_BASE 类型转换为char *

      volatile unsigned int* GPIO_OUT = ((char *)GPIO_BASE) + 0x504; 
      

      否则一切正常。

      【讨论】:

      • 感谢您的回复!您能否详细说明我们为什么需要(char *)?我以为你可以添加十六进制地址
      • @user3084686 这是因为指针算法。请在线阅读。
      【解决方案4】:

      由于指针运算,指针 GPIO_OUT 的值将是 GPIO_BASE + (0x504 * sizeof(int))。如果 sizeof(int) 是 4 个字节,你会得到指向 0x50000000 + (0x504 * 4) = 0x50001410 的指针,而 OUT 寄存器的实际地址是 0x50000504。您可以将指针类型转换为 unsigned char* 或更好地使用宏。

      #define GPIO_BASE 0x50000000

      #define GPIO_OUT GPIO_BASE + 0x504

      volatile unsigned int* gpio_out = GPIO_OUT;

      【讨论】:

      • 但是,volatile unsigned int* gpio_out = GPIO_OUT; 不是有效的 C。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-03
      相关资源
      最近更新 更多