使用指针数组的内存映射外设寄存器

时间:2018-10-04 12:25:33

标签: c pointers memory-mapping

我指的是用于编写可重复使用的固件的文档,并且本书随附的代码使用指针数组来映射内存。

我对内存映射有点困惑

从此post开始,如果将8位内存映射到0X123,则可以使用以下命令

uint8_t volatile * p_reg = (uint8_t volatile *) 0x1234;

#define PORT 0X1234
uint8_t volatile * p_reg = (uint8_t volatile *) PORT;

或在指针数组的情况下

#define PORT 0X1234
uint8_t volatile * const portsout[NUM_PORTS] =
    {
        (uint8_t*)PORTB, ....,
    };

我尝试了atmega168随书附带的代码,这是我映射内存所要做的

uint8_t volatile * const portsout[NUM_PORTS] =
{
    (uint8_t*)&PORTB, (uint8_t*)&PORTC, (uint8_t*)&PORTD,
};

PORTB在头文件“ avr / io.h”中定义为此

#define PORTB   _SFR_IO8 (0x05)

我不理解的是在指针数组中需要吗?

使用lpc2148时出现编译错误时,我向作者发送了一封邮件,并在他的回复邮件中提到了

  

然后看起来您的指针数组实际上可能不是指针。   例如:

     

(uint32_t *)&IOPIN0,(uint32_t *)&IOPIN1

     

实际上可能是

     

(uint32_t *)IOPIN0,(uint32_t *)IOPIN1

     

取决于您对IOPIN0和IOPIN1的定义方式

lpc2148的IOPIN0宏是

#define IOPIN0          (*((volatile unsigned long *) 0xE0028000))

我在C语言方面没有太多经验。 我知道宏是否指向内存,那么在定义指针数组时不必使用。 如何知道宏(例如:PORTB,IOPIN0)是引用地址还是值?

2 个答案:

答案 0 :(得分:0)

defines for C是这样的:

#if __AVR_ARCH__ >= 100
#    define __SFR_OFFSET 0x00
#else
#    define __SFR_OFFSET 0x20
#endif

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))
#define PORTB   _SFR_IO8(0x05)

这意味着PORTB扩展为:

// presuming __AVR_ARCH__ >= 100
(*(volatile uint8_t *)(0x05 + 0x00))

已被取消引用,因此需要使用&号来获取地址,即,您将必须编写:

volatile uint8_t * p = &PORTB;

答案 1 :(得分:0)

编写寄存器映射时,通常使用一种使寄存器就像变量一样离开寄存器的方法:

#define REGISTER (*(volatile uint8_t*)0x1234)

其中左边的*取消指向所指向的地址,这意味着您现在可以像使用任何普通变量一样使用REGISTER。这就是为什么您必须编写&PORTB的原因,它在宏扩展后最终显示为&*pointer。 C(c17 6.5.3.2)保证与pointer等效:

  

一元&运算符产生其操作数的地址。 /-/
  如果操作数是一元*运算符的结果,则不会对该运算符和&运算符求值,并且结果好像都被省略了

至于是否需要&,它确实取决于寄存器映射中寄存器的定义方式。无论系统如何,哪个都应作为您可以检查的头文件提供。

作为旁注,您的演员阵容可疑。不需要(uint8_t*)&PORTB。这表明某些限定词与volatileconst不匹配。通常,从指针转到合格指针总是可以的,但反之则不行。

根据您使用IOPIN0的示例,代码应如下所示:

volatile uint32_t*const portsout [NUM_PORTS] =
{
  &IOPIN0, ...
};