2016-12-10 67 views
-1

我玩了一些avr微控制器和C++ 14。當試圖爲io引腳實現一個C++包裝時,我偶然發現了一個錯誤。constexpr引用avr端口地址

這個想法是,讓包裝以sfr爲模板參數,以便編譯器輕鬆優化(不希望有一個額外的函數,並使用模板參數,我可以指示給編譯器,我的參數應該總是編譯時可評估)。我想,可以有一個引用一些編譯時已知地址的變量。但下面不工作(的定義是從AVR採取包括):

#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-effectsGet()功能。替換decltype(auto)uint8_t&(當然)並沒有幫助。

爲什麼我無法獲得指向_SFR_IO_ADDR(PORTB) = (volatile uint8_t*)(_SFR_IO8(5u) - 32u) = (volatile uint8_t*)(5u + 32u - 32u)內存位置的constexpr uint8_t參考?

+0

'constexpr'在編譯時必須是一個常量表達式 – Danh

+0

爲什麼要'Get()'是'constexpr'?它返回對一些易失性IO內存的引用。根據定義,該內存位置的值不是恆定的。我想你定義函數爲'static',而不是'constexpr'時,會得到最短的程序集。 – j6t

+0

「*用'uint8_t'代替'decltype(auto)'(當然)並沒有幫助。」好吧,GetAddr返回一個右值,所以試圖返回一個左值引用給右值_應該失敗。我不知道你實際上想要完成什麼...... – ildjarn

回答

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

+2

此解決方案的問題在於它超出了C++標準。表達式'(uint16_t)&PORTD'隱藏了'reinterpret_cast',並且這些表達式不是編譯時間常量。海灣合作委員會恰好接受他們,但他們可能在未來的版本中改變主意,拒絕它與標準一致。 – j6t