|
/* |
|
* gpio.h - experimental template based c++ gpio abstraction |
|
* |
|
* Created on: Jan 14, 2012 |
|
* Author: kimballr |
|
* |
|
* 01/14/2012 - kimballr added volatile to SFR registers and force inline |
|
* 01/16/2012 - kimballr more functions more experimentation |
|
* 01/28/2012 - kimballr more complete implementation dealing with a 28 pin msp430g2553 |
|
*/ |
|
|
|
#ifndef GPIO_H_ |
|
#define GPIO_H_ |
|
|
|
#ifdef __GNUC__ |
|
#define _INLINE_ __attribute__((always_inline)) |
|
#else |
|
#define _INLINE_ inline |
|
#endif |
|
|
|
enum pin_value { |
|
LOW = 0x0, HIGH = 0x1 |
|
}; |
|
|
|
enum pin_mode { |
|
INPUT = 0x00, OUTPUT = 0x01, PULLDOWN_INPUT = 0x02, PULLUP_INPUT = 0x04 |
|
}; |
|
|
|
/** |
|
* PORT_memmap0_t - memory mapped port registers for P1 and P2 |
|
*/ |
|
typedef struct { |
|
volatile uint8_t _IN; // 0 IN value |
|
volatile uint8_t _OUT; // 1 OUT value |
|
volatile uint8_t _DIR; // 2 Direction mask |
|
volatile uint8_t _IFG; // 3 Interrupt Flag |
|
volatile uint8_t _IES; // 4 Interrupt Edge Select |
|
volatile uint8_t _IE; // 5 Interrupt Enable |
|
volatile uint8_t _SEL; // 6 Primary/Alternate Selection Mask |
|
} PORT_memmap0_t; |
|
|
|
/** |
|
* PORT_memmap1_t - memory mapped port registers for all other ports |
|
*/ |
|
typedef volatile struct { |
|
volatile uint8_t _IN; // 0 IN value |
|
volatile uint8_t _OUT; // 1 OUT value |
|
volatile uint8_t _DIR; // 2 Direction mask |
|
volatile uint8_t _SEL; // 3 Primary/Alternate Selection Mask |
|
} PORT_memmap1_t; |
|
|
|
/** |
|
* PORT_memmap2 - memory mapped Resistor Enabled base address varies |
|
*/ |
|
typedef volatile struct { |
|
volatile uint8_t _REN; |
|
} PORT_memmap2_t; |
|
|
|
/** |
|
* PORT_memmap3_t - memory mapped Selection 2 base address varies |
|
*/ |
|
typedef volatile struct { |
|
volatile uint8_t _SEL2; // 0x41 [0] Selection 2 .. need to look at this in depth more |
|
} PORT_memmap3_t; |
|
|
|
#define P1BASEADDR 0x20 |
|
#define P2BASEADDR 0x28 |
|
#define P3BASEADDR 0x18 |
|
|
|
//-------------------------------------------------------------------------------- |
|
// use C++ metatemplate programming to compute base addresses at compile time |
|
|
|
// PORT1 0-7, PORT2 8-15, PORT3 9-23 |
|
// |
|
|
|
/** |
|
* PORTINFO - holds the base addresses for the start of the base,sel,ren memory locations |
|
* |
|
*/ |
|
template<uint8_t PINNUM> |
|
struct PORTINFO { |
|
static int const BASE_ADDR = PINNUM < 8 ? P1BASEADDR |
|
: PINNUM < 16 ? P2BASEADDR |
|
: PINNUM < 24 ? P3BASEADDR |
|
: 0x20 /* value is outofbounds*/; |
|
static int const SEL2_ADDR = 0x41 + (PINNUM / 8); |
|
static int const REN_ADDR = PINNUM < 8 ? 0x27 |
|
: PINNUM < 16 ? 0x2f |
|
: PINNUM < 24 ? 0x10 |
|
: 0x27 /* value is outofbounds */; |
|
static int const MASK = 1 << PINNUM % 8; |
|
}; |
|
|
|
/** |
|
* specialization for pin 0, not needed just here as an example of how to specialize |
|
*/ |
|
template<> |
|
struct PORTINFO<0> { |
|
static int const BASE_ADDR = P1BASEADDR; |
|
static int const SEL2_ADDR = 0x41; |
|
static int const REN_ADDR = 0x27; |
|
static int const MASK = 1 << 0; |
|
}; |
|
|
|
/** |
|
* GPIO - template class |
|
*/ |
|
template<uint8_t PIN, typename PORT_DEF = PORT_memmap0_t> |
|
struct GPIO { |
|
|
|
// PIN non-inlined methods |
|
|
|
inline void pulse(uint16_t pulseCycle); |
|
inline void pulse(int32_t pulseCycle, int dummyArg); |
|
void setPinMode(pin_mode mode); |
|
void setValue(pin_value value); |
|
|
|
_INLINE_ uint8_t PIN_MASK() { |
|
return PORTINFO<PIN>::MASK; |
|
} |
|
|
|
// PIN inline methods |
|
|
|
_INLINE_ void configureAsInput() { |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_DIR &= ~(PIN_MASK()); |
|
} |
|
|
|
_INLINE_ void configureAsOutput() { |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_DIR |= PIN_MASK(); |
|
} |
|
|
|
_INLINE_ pin_value getValue() { |
|
return (reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT & PIN_MASK()) ? HIGH : LOW; |
|
} |
|
|
|
_INLINE_ void setHigh() { |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT |= PIN_MASK(); |
|
} |
|
|
|
_INLINE_ void setLow() { |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT &= ~(PIN_MASK()); |
|
} |
|
|
|
_INLINE_ void toggle() { |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT ^= PIN_MASK(); |
|
} |
|
|
|
/** |
|
* enableGPIOMode - disable _SEL bits and use a GPIO port pin |
|
*/ |
|
_INLINE_ void enableGPIOMode() { |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_SEL &= ~PIN_MASK(); |
|
// TBD: probably need to deal with PXSEL2 |
|
} |
|
|
|
/** |
|
* enableGPIOMode - enable _SEL bits for primary peripheral mode |
|
*/ |
|
_INLINE_ void enablePeripheralMode() { |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_SEL |= PIN_MASK(); |
|
// TBD: probably need to deal with PXSEL2 |
|
} |
|
|
|
/** |
|
* GPIO assigment operatore - allow for GPIO<0> P1.0 = HIGH|LOW; |
|
*/ |
|
_INLINE_ GPIO& operator=(int value) { |
|
setValue(static_cast<pin_value>(value)); |
|
return *this; |
|
} |
|
|
|
// PORT level static methods |
|
static _INLINE_ void setPortMode(uint8_t mask) { |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_DIR = mask; |
|
} |
|
|
|
}; |
|
|
|
/** |
|
* GPIO::pulse() - toggle a pin, delay, and toggle back 0-65k version |
|
*/ |
|
template<uint8_t PIN, typename PORT_DEF> |
|
void GPIO<PIN, PORT_DEF>::pulse(uint16_t pulseCycle) { |
|
void delay(uint16_t); |
|
|
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT ^= PIN_MASK(); |
|
delay(pulseCycle); |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT ^= PIN_MASK(); |
|
} |
|
|
|
/** |
|
* GPIO::pulse() - toggle a pin, delay, and toggle back 0-2M version |
|
*/ |
|
template<uint8_t PIN, typename PORT_DEF> |
|
void GPIO<PIN, PORT_DEF>::pulse(int32_t pulseCycle, int dummyArg) { |
|
void ldelay(uint32_t); |
|
|
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT ^= PIN_MASK(); |
|
ldelay(pulseCycle); |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT ^= PIN_MASK(); |
|
} |
|
|
|
/** |
|
* GPIO::setValue() - set a pin High or Low |
|
*/ |
|
template<uint8_t PIN, typename PORT_DEF> |
|
void GPIO<PIN, PORT_DEF>::setValue(pin_value value) { |
|
switch (value) { |
|
case LOW: |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT &= ~(PIN_MASK()); |
|
break; |
|
case HIGH: |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT |= PIN_MASK(); |
|
break; |
|
} |
|
} |
|
|
|
/** |
|
* GPIO::setPinMode() - configure a pin for a specific mode of operation |
|
*/ |
|
template<uint8_t PIN, typename PORT_DEF> |
|
void GPIO<PIN, PORT_DEF>::setPinMode(pin_mode mode) { |
|
switch (mode) { |
|
case INPUT: |
|
configureAsInput(); |
|
break; |
|
case OUTPUT: |
|
configureAsOutput(); |
|
break; |
|
case PULLUP_INPUT: |
|
configureAsInput(); |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT |= PIN_MASK(); |
|
reinterpret_cast<PORT_memmap2_t *>(PORTINFO<PIN>::REN_ADDR)->_REN |= PIN_MASK(); |
|
break; |
|
case PULLDOWN_INPUT: |
|
configureAsInput(); |
|
reinterpret_cast<PORT_DEF *>(PORTINFO<PIN>::BASE_ADDR)->_OUT &= ~PIN_MASK(); |
|
reinterpret_cast<PORT_memmap2_t *>(PORTINFO<PIN>::REN_ADDR)->_REN |= PIN_MASK(); |
|
break; |
|
} |
|
} |
|
|
|
#if 0 |
|
GPIO<0> P1_0; // P1BASE is 0x20 |
|
GPIO<1> P1_1; |
|
GPIO<2> P1_2; |
|
GPIO<3> P1_3; |
|
GPIO<4> P1_4; |
|
GPIO<5> P1_5; |
|
GPIO<6> P1_6; |
|
GPIO<7> P1_7; |
|
GPIO<8> P1_8; |
|
|
|
GPIO<8> P2_0;// P2BASE is 0x28 |
|
GPIO<9> P2_1; |
|
GPIO<10> P2_2; |
|
GPIO<11> P2_3; |
|
GPIO<12> P2_4; |
|
GPIO<13> P2_5; |
|
GPIO<14> P2_6; |
|
GPIO<15> P2_7; |
|
|
|
GPIO<16> P3_0;// P3BASE is 0x10 |
|
GPIO<17> P3_1; |
|
GPIO<18> P3_2; |
|
GPIO<19> P3_3; |
|
GPIO<20> P3_4; |
|
GPIO<21> P3_5; |
|
GPIO<22> P3_6; |
|
GPIO<23> P3_7; |
|
#endif |
|
|
|
#if defined(__MSP430G2553__) || defined(__MSP430G2452) || defined(__MSP430G2231__) |
|
// good I know this works |
|
#else |
|
#error "hmm .. check this stuff for your chip!") |
|
#endif |
|
|
|
#define digitalWrite(pin, value) (pin.setValue(value)) |
|
#define pinMode(pin, mode) (pin.setPinMode( mode )) |
|
|
|
#endif /* GPIO_H_ */ |