constexpr引用avr端口地址

时间:2016-12-10 15:34:07

标签: c++ c++11 c++14 avr constexpr

我正在使用avr微控制器和C ++ 14。在尝试为io引脚实现C ++包装时,我偶然发现了一个错误。

这个想法是,让包装器将sfr作为模板参数,以便于编译器的简单优化(不要在那里使用额外的函数并使用模板参数,我可以向编译器指出,我的论点应始终是编译时可评估的)。我想,可以有constexpr uint8_t& x = ...个变量引用一些编译时已知的地址。但以下不起作用(定义取自avr include):

#include <avr/io.h>

constexpr uint32_t addr[] = { _SFR_IO_ADDR(PORTB) };

constexpr uint32_t GetAddr()
{
    return addr[0];
}

constexpr decltype(auto) Get()
{
    _SFR_IO8(GetAddr());
}

int main() {
    auto addr = GetAddr();
    auto b = Get();

    _SFR_IO8(addr) &= ~(1 << 2);
    b |= (1 << 3);
}

它在error: expression '*(volatile uint8_t*)(GetAddr() + 32u)' has side-effects函数中为Get()提供了decltype(auto)。用uint8_t&取代constexpr uint8_t(当然)没有帮助。

为什么我无法获得指向_SFR_IO_ADDR(PORTB) = (volatile uint8_t*)(_SFR_IO8(5u) - 32u) = (volatile uint8_t*)(5u + 32u - 32u)内存位置的NSDataDetector引用?

2 个答案:

答案 0 :(得分:0)

我正在做这样的事情:

#include <avr/io.h>
#include <util/delay.h>

constexpr uint8_t INPUT        =0;
constexpr uint8_t OUTPUT       =1; // output + set pin LOW
constexpr uint8_t OUTPUT_LOW   =1; // output + set pin LOW
constexpr uint8_t INPUT_PULLUP =2;
constexpr uint8_t OUTPUT_HIGH  =3; // output + set pin HIGH

struct pin_setting {
    uint8_t p;        // PORTx is enough, because DDRx is allway -1, PINX -2
  uint8_t bit_mask;
}; 

//#define MEM8_OFFSET 0x100 // for megaxxx0
constexpr pin_setting pins[] {
  {(uint16_t)&PORTD, _BV(PD0)},
  {(uint16_t)&PORTD, _BV(PD1)},
  {(uint16_t)&PORTD, _BV(PD2)},
  {(uint16_t)&PORTD, _BV(PD3)},
  {(uint16_t)&PORTD, _BV(PD4)},
  {(uint16_t)&PORTD, _BV(PD5)},
  {(uint16_t)&PORTD, _BV(PD6)},
  {(uint16_t)&PORTD, _BV(PD7)},

  {(uint16_t)&PORTB, _BV(PB0)},
  {(uint16_t)&PORTB, _BV(PB1)},
  {(uint16_t)&PORTB, _BV(PB2)},
  {(uint16_t)&PORTB, _BV(PB3)},
  {(uint16_t)&PORTB, _BV(PB4)},
  {(uint16_t)&PORTB, _BV(PB5)},

  {(uint16_t)&PORTC, _BV(PC0)},
  {(uint16_t)&PORTC, _BV(PC1)},
  {(uint16_t)&PORTC, _BV(PC2)},
  {(uint16_t)&PORTC, _BV(PC3)},
  {(uint16_t)&PORTC, _BV(PC4)},
  {(uint16_t)&PORTC, _BV(PC5)}
};

inline void mDigitalSet(uint8_t pin) {
  _SFR_IO8(pins[pin].p) |= pins[pin].bit_mask;
}

inline void mDigitalClr(uint8_t pin) {
  _SFR_IO8(pins[pin].p) &= ~pins[pin].bit_mask;
}

inline void mDigitalToggle(uint8_t pin) {
  _SFR_IO8(pins[pin].p-2) = pins[pin].bit_mask;
}

inline void mDigitalWrite(uint8_t pin, bool level=true) {
  level ? mDigitalSet(pin) : mDigitalClr(pin);
}

inline bool mDigitalRead(uint8_t pin) {
  return _SFR_IO8(pins[pin].p-2) & pins[pin].bit_mask;
}

inline void mPinMode(uint8_t pin, uint8_t dir) {
  if (dir & 1) {
    _SFR_IO8(pins[pin].p-1) |= pins[pin].bit_mask;
  } else {
    _SFR_IO8(pins[pin].p-1) &= ~pins[pin].bit_mask;
    }
  mDigitalWrite(pin, dir&2);
}

它并不完美,可以做得更好。 (这只是概念的证明,从未使用过)

Arduino以同样的方式将SFR指针存储到PROGMEM - uint16_t

答案 1 :(得分:0)

我希望可以将类似的模板化编译时间减少到一条指令。我必须使用一些模板元编程才能使这项工作完成。

一个简单的例子:

#include <avr/io.h>

struct A {
  constexpr static volatile uint8_t *const PORT() { return &PORTA; }
  constexpr static volatile uint8_t *const DDR() { return &DDRA; }
};

template <class port, u1 pin> class IOpin {
  constexpr static volatile uint8_t  *DDR = port::DDR();
  constexpr static volatile uint8_t *PORT = port::PORT();
  constexpr static uint8_t mask = 1 << pin;

public:
  inline IOpin() {}

  inline static void output() { *DDR |= mask; }
  inline static void input() { *DDR &= ~mask; }
  inline static void set() { *PORT |= mask; }
}

// Use as type or instance
using myPinType = IOpin<A, 1>;
IOpin<A, 1> myPinInstance;

// Use myPin
myPinType::set(true);
myPinInstance.output();

完整实施:https://github.com/cinderblock/AVR/blob/master/AVR%2B%2B/IOpin.h