【问题标题】:Memory mapping peripheral registers using pointer array使用指针数组的内存映射外设寄存器
【发布时间】:2019-03-09 20:13:45
【问题描述】:

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

我对内存映射有点困惑

从这个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 方面没有太多经验。 我知道如果宏指的是内存,那么在定义指针数组时我不必使用 & 。 如何知道宏(eg: PORTB, IOPIN0) 是指地址还是值??

【问题讨论】:

  • 这在很大程度上取决于SFR_IO8 是什么以及它对0x05 值的影响。

标签: c pointers memory-mapping


【解决方案1】:

在编写寄存器映射时,通常使用一种将寄存器保留为变量的方法:

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

左边的* 取消引用指向的地址,这意味着您现在可以像使用任何普通变量一样使用REGISTER。这就是为什么你必须写&PORTB,在宏扩展之后会变成&*pointer。并且这保证等同于 pointer,由 C (c17 6.5.3.2):

一元 & 运算符产生其操作数的地址。 /--/
如果操作数是一元 * 运算符的结果,则该运算符和 & 运算符都不会被计算,结果就像两者都被省略了一样

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

顺便说一句,你的演员阵容很可疑。不需要(uint8_t*)&PORTB。这表明某些限定符与 volatileconst 不匹配。一般来说,从指针转到限定指针总是可以的,但反过来就不行。

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

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

【讨论】:

  • 你能解释一下限定词不匹配的问题吗?可能有一些小例子
  • @Athul 例如,假设原始指针定义为const volatile uint8_t*,但您希望将其存储在volatile uint8_t* 的数组中。你会得到不兼容的指针:你总是可以从更少的限定符到更多的限定符,但不能反过来。当出现更大的设计错误时,通常会出现此类错误:未能事先正确定义哪些指针指向硬件寄存器 (volatile*),哪些指向只读数据 (const*),哪些指向将存储在ROM中(*const)
  • const volatile uint8_t* 所以如果我有这样的东西,那么我只能存储在const volatile uint8_t* 中,对吗? volatile uint8_t* 也可以存储在const volatile uint8_t* 中,对吗??我有一个像这样的函数 void Adc_RegisterWrite(uint8_t const Address, uint8_t const Value) { uint8_t * const RegisterPointer = (uint8_t *) Address; *RegisterPointer = Value; } 并且编译器给了我警告cast to pointer from integer of different size 在这里投射的正确方法是什么?
  • @Athul 是的,是的。所以不需要演员表,这就是为什么他们是可疑的。但是,您在此处的函数没有任何意义,因为它们是普通的 uint8_t 而不是指针。也许您可以将该部分作为单独的问题发布(带有实际代码的复制/粘贴)。
  • 函数接受一个uint8_t 类型变量,并且在函数体中这个非指针值被类型转换为指针对吗?如果调用函数时传递的Address 变量实际上是一个地址,如0x28 之类的。
【解决方案2】:

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;

【讨论】:

  • 这是我最先想到的。 (uinit8_t*)&(_SFR_IO8 (0x05)) 这按预期工作,但是当我写 (uinit8_t*)0x05 时,代码不起作用。为什么??根据您的解释,它应该,对吗?或者我应该试试(uinit8_t*)0x0C
  • @Athul:你的线到底长什么样? IE。 volatile uint8_t * p = (volatile uint8_t*)0x05; 应该可以工作(如果 __AVR_ARCH__ 至少是 100)。根据您的编译器,您应该能够获得预处理后的输出(例如-E 选项in GCC)。这将向您展示在所有宏都被替换后、编译前的结果代码的样子。
  • uint8_t volatile * const portsout[NUM_PORTS] = { (uint8_t*)0x05, (uint8_t*)&PORTC, (uint8_t*)&PORTD, };我试过这种方法但没有用,但uint8_t volatile * const portsout[NUM_PORTS] = { (uint8_t*)&(_SFR_IO8 (0x05)), (uint8_t*)&PORTC, (uint8_t*)&PORTD, };这有效
  • 这很奇怪,我已经测试过here 并且没有任何警告。您绝对应该使用-E 并检查您的宏是如何实际定义的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-02
相关资源
最近更新 更多