C指针转换为操作

时间:2016-09-02 09:10:34

标签: c pointers casting

我正在摆弄Raspberry Pi上的GPIO访问,所以当然会涉及内存。

我的想法是我有一个基地址:(gpios + GPIO_INPUT_READ_OFFSET)。根据我想要做的事情,我会在某个地址上找到更远的地址并更改一些位。

(gpios + GPIO_INPUT_READ_OFFSET + 4)

在这个例子中,我想读取引脚的逻辑值。为此,有两个寄存器,第一个在地址volatile uint32_t* input = (uint32_t*) ((uint32_t)gpios + GPIO_INPUT_READ_OFFSET + 4*(gpio/32)); 用于32个第一个gpios,第二个在地址gpio用于剩余的gpios。

我总是写这样的操作:

volatile uint32_t* input = gpios + GPIO_INPUT_READ_OFFSET/4 + (gpio/32);

Application.CommandBars("Records").Controls(4).Enabled = False Application.CommandBars("Menu Bar").Controls("Data").Enabled = False Application.CommandBars("Menu Bar").Controls("Filter").Enabled = False 只是在当前函数中操作的gpio数字。

但是我一直在想,如果我没弄错的话,很可能就是这样写的:

DoCmd.ShowToolbar "Ribbon", acToolbarNo

因为指针shenanigans。

我喜欢这两种,但我无法确定哪种优先于另一种优先。

是否有最佳实践"关于这种类型的地址摆弄?

1 个答案:

答案 0 :(得分:3)

IMO在C中最明智的方法是使用指向结构的指针。查看RPi register documentation我可以看到GPIO寄存器是:

0x7E20 0000 | GPFSEL0 GPIO Function Select 0 | 32 | R/W
0x7E20 0004 | GPFSEL1 GPIO Function Select 1 | 32 | R/W
0x7E20 0008 | GPFSEL2 GPIO Function Select 2 | 32 | R/W
0x7E20 000C | GPFSEL3 GPIO Function Select 3 | 32 | R/W
0x7E20 0010 | GPFSEL4 GPIO Function Select 4 | 32 | R/W
0x7E20 0014 | GPFSEL5 GPIO Function Select 5 | 32 | R/W
0x7E20 0018 | - Reserved                     | -  | -
0x7E20 001C | GPSET0 GPIO Pin Output Set 0   | 32 | W
0x7E20 0020 | GPSET1 GPIO Pin Output Set 1   | 32 | W
0x7E20 0024 | - Reserved                     | -  | -
0x7E20 0028 | GPCLR0 GPIO Pin Output Clear 0 | 32 | W
0x7E20 002C | GPCLR1 GPIO Pin Output Clear 1 | 32 | W 
0x7E20 0030 | - Reserved                     | -  | -
0x7E20 0034 | GPLEV0 GPIO Pin Level 0        | 32 | R
0x7E20 0038 | GPLEV1 GPIO Pin Level 1        | 32 | R
....

我要做的是按照这种布局创建一个结构:

typedef struct {
    volatile       uint32_t gpfsel0;
    volatile       uint32_t gpfsel1;
    volatile       uint32_t gpfsel2;
    volatile       uint32_t gpfsel3;
    volatile       uint32_t gpfsel4;
    volatile       uint32_t gpfsel5;
    volatile       uint32_t : 32;
    volatile       uint32_t gpset0;
    volatile       uint32_t gpset1;
    volatile       uint32_t : 32;
    volatile       uint32_t gpclr0;
    volatile       uint32_t gpclr1;
    volatile       uint32_t : 32;
    volatile const uint32_t gplev0;
    volatile const uint32_t gplev1;
    ....
} RPiGpio;

然后将RPiGpio指针指向基址并更改/读取一些位:

RPiGpio* rPiGpio = (RPiGpio*)(0x7E200000);

rPiGpio->gpclr1 = 0x0001;
uint32_t pins = rPiGpio->gplev0;

读取GPIO引脚

如果你想根据gpio引脚号选择要读取的寄存器,那么我可能会做的是将相应的struct成员更改为数组并以这种方式访问​​寄存器:

// Change this
typedef struct {
    ....
    volatile const uint32_t gplev0;
    volatile const uint32_t gplev1;
    ....
} RPiGpio;

// To this
typedef struct {
    ....
    volatile const uint32_t gplev[2];
    ....
} RPiGpio;

// Read a pin
bool isPinHigh(unsigned pinNum)
{
    const RPiGpio* rPiGpio = (const RPiGpio*)(0x7E200000);

    return rPiGpio->gplev[pinNum / 32] & (1 << (pinNum % 32));
}

pinNum是GPIO引脚号0-63(我不知道你可以在Raspberry Pi上实际访问多少个引脚,所以我只假设gpio 0到gpio 63)。

pinNum / 32选择适当的寄存器(0或1)。

1 << (pinNum % 32)选择适当的位。

&将屏蔽其他位,留下所需位的状态。

一些注意事项:

结构中的成员被声明为volatile。这在读取寄存器时非常重要,因为寄存器值可能会在读取调用之间发生变化。如果不包含volatile,那么编译器可能会优化远离读取,因为编译器的眼睛值不会改变。 Volatile告诉编译器该值可能会意外地发生变化。

只读寄存器被声明为volatile const。这样可以防止您意外地写入内存的只读部分(或者至少抛出编译器错误)。

对于保留字段,使用uint32_t : 32;,这是一个未命名的32位位域。 Bitfields很少有用,因为实际上对它们的保证很少,但在这里它们工作正常。