Skip to content

Instantly share code, notes, and snippets.

@RickKimball
Created January 11, 2012 22:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RickKimball/1597239 to your computer and use it in GitHub Desktop.
Save RickKimball/1597239 to your computer and use it in GitHub Desktop.
msp430 template based gpio experiment

This example code is a test using c++ templates to try and implement single instruction bit flipping with abstraction for GPIO. This is useful for getting compact code with msp430-gcc, however CCS seems to generate efficient code without all this hand waving. However, you can still see some value in GPIO abstraction.

Tested with msp430g2553 and msp430g2231

-rick

/*
* 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_ */
/**
* gpiotest.cpp - experimental c++ template based gpio abstraction
*
* author: rick@kimballsoftware.com
*
* 2012-01-14 - this version is about 168 bytes using msp430-gcc -Os
*/
#include <msp430.h>
#include <stdint.h>
#include "gpio.h"
GPIO<0> LED1; // P1.0
GPIO<6> LED2; // P1.6
/**
* millis - access to milliseconds since startup
*
* Note: because the value is stored in a 16 bit int,
* it rolls over every 65536 milliseconds or 65.536 seconds
*/
static uint16_t millis;
int main(void) {
WDTCTL = WDT_MDLY_0_5; // Configure Watchdog as .5 second interval timer
// use default ~1.024 MHz DCO clock
digitalWrite(LED1,HIGH);
digitalWrite(LED2,LOW);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
IE1 |= WDTIE; // Enable WDT interrupt
while(1) {
// Go to sleep until the Watchdog wakes us
_BIS_SR(LPM0_bits + GIE);
// The WDT_INTERVAL_ISR brings the CPU out of sleep mode
// and we end up here, every 8 seconds or so
if ( millis % 8192 == 0 ) {
volatile int i=0;
i=i;
}
}
return 0;
}
/**
* WDT_INTERVAL_ISR -Watchdog Timer ISR handler
*
* Note: assumes MCLK is 1.024MHz and we are using the
* WatchDog Interval timer mode of WDT_MDLY_0_5
*/
#ifdef __GNUC__
__attribute__( (interrupt (WDT_VECTOR)) )
#else
#pragma vector = WDT_VECTOR
__interrupt
#endif
void WDT_INTERVAL_ISR(void) {
static uint8_t _500uSCount= 0; // count each .5mSec tick
// every 2 ticks update the millis counter
if ( (++_500uSCount % 2) == 0) {
millis++;
if ( millis % 64 == 0) { // toggle every 64 mSec, uses power of 2 value for smaller code
LED1.toggle(); // our 2 leds are out of sync
LED2.toggle(); // one is on when the other is off
_BIC_SR_IRQ(LPM0_bits); // leave the ISR and exit sleep mode
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment