使用_SFR_IO_ADDR作为常量表达式,AVR GCC编译错误

时间:2017-04-30 17:46:39

标签: gcc avr

在C ++程序中,我需要将I / O寄存器的地址作为常量表达式获取,该表达式可以作为模板参数中的整数传递。在GCC 5中,这有效:

static constexpr uint16_t Address = _SFR_IO_ADDR(PORTA);

但是,GCC 6.3.0会产生以下错误:

error: reinterpret_cast from integer to pointer

以下是AVR libc中定义的摘要:

#define __SFR_OFFSET 0x20

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

#define _SFR_MEM_ADDR(sfr) ((uint16_t) &(sfr))
#define _SFR_IO_ADDR(sfr) (_SFR_MEM_ADDR(sfr) - __SFR_OFFSET)

#define PORTA _SFR_IO8(0x02)

将寄存器地址提取为常量表达式整数是什么工作方法?

我怀疑通过“解析”定义可以通过宏来完成某些事情,但我只看到如何删除第一组括号而不是如何处理星号。

编辑:实际失败的情况是:

#include <avr/io.h>
struct PortDef {
    static const uint32_t Address = _SFR_IO_ADDR(PORTA);
};

这是使用的命令和错误:

$ ~/avrgcc/bin/avr-g++ -v
Using built-in specs.
Reading specs from /nix/store/43k8vl3dzm102wj1jsdb1sil9k5b0k6r-avr-gcc-libc/lib/gcc/avr/6.3.0/device-specs/specs-avr2
COLLECT_GCC=/home/ambro/avrgcc/bin/avr-g++
COLLECT_LTO_WRAPPER=/nix/store/43k8vl3dzm102wj1jsdb1sil9k5b0k6r-avr-gcc-libc/libexec/gcc/avr/6.3.0/lto-wrapper
Target: avr
Configured with: ../configure --target=avr --prefix=/nix/store/43k8vl3dzm102wj1jsdb1sil9k5b0k6r-avr-gcc-libc --disable-nls --disable-libssp --with-dwarf2 --disable-install-libiberty --with-system-zlib --enable-languages=c,c++
Thread model: single
gcc version 6.3.0 (GCC) 

$ ~/avrgcc/bin/avr-g++ -mmcu=atmega2560 test.cpp -c -o test.o
In file included from /nix/store/43k8vl3dzm102wj1jsdb1sil9k5b0k6r-avr-gcc-libc/avr/include/avr/io.h:99:0,
                 from test.cpp:1:
test.cpp:3:37: error: reinterpret_cast from integer to pointer
     static const uint32_t Address = _SFR_IO_ADDR(PORTA);

在这些情况下它不会失败(所以这些是至少在GCC 6.3中的简单解决方法):

  • 地址在命名空间范围内定义,而不是在类中定义。
  • 它被定义为类的一部分,但值是在外部定义的。

2 个答案:

答案 0 :(得分:1)

我不明白<h2>@(Model.Movie==null?"New Movie": "Edit Movie")</h2> constexpr之间的区别,但const似乎适用于这种情况。以下代码为我编译:

const

我在this library成功使用了该宏。顺便说一句,我认为您可以使用#include <avr/io.h> static const uint16_t address = _SFR_IO_ADDR(PORTB); 代替uint8_t

以下是来自Arch Linux系统的shell会话,显示上面的代码可以使用avr-g ++ 6.3.0进行编译:

uint16_t

答案 1 :(得分:0)

我通过生成定义特定寄存器的数字地址的头文件解决了这个问题。这是actual code

它的工作原理如下:

  • 有一个文件包含不同寄存器宏的使用(例如PORTA)。它不是有效的C ++,但在预处理之后会产生REG_PORTA:<definition>;行,其中<definition>PORTA扩展到的行。
  • 预处理文件转换为static uint16_t const Address_PORTA = <address>;等定义,其中<address>是通过剥离原始表达式中的所有强制转换而提取的数字地址。

我使用与编译时相同的定义和编译器标志进行预处理,以确保使用正确的寄存器定义。这比解析原始设备特定的头文件更便携,因为您需要知道哪个头文件是正确的。

注意:结果是内存地址,相当于_SFR_MEM_ADDR。要获取IO寄存器的IO地址(_SFR_IO_ADDR),必须减去__SFR_OFFSET