Jump to content
43oh

Design puzzle in abstracting GPIO with port support


Recommended Posts

As you all know the two usual ways to access GPIO is either through the msp430.h defined P1OUT etc. variables, or through Energia (or similar) digitalWrite() functions.

P1OUT |= BIT3 | BIT4;

digitalWrite(6, HIGH);
digitalWrite(7, HIGH);

The problem I have now is I'd like to use the advantages of port writing with an abstract interface. So I want to be able to write multiple pins at once, but I don't want to bother with ports.

What I want is a way to tell "write this series of values to this series of pins", without the need to consider that these pins are on the same port.

My current attempt on a C++ abstraction of pins looks somewhat like this.

class Pin
{
  public:
    virtual void high() const = 0;
    virtual void low() const = 0;
    virtual void floating() const = 0;
    //virtual void pullup() const = 0;
    //virtual void pulldown() const = 0;
};

class MSP430Pin;

class MSP430Port
{
  public:
    MSP430Port(READ_ONLY DEFXC& in, DEFXC& out, DEFXC& dir, DEFXC& ifg, DEFXC& ies, DEFXC& ie, DEFXC& sel, DEFXC& ren)
    : in(in), out(out), dir(dir), ifg(ifg), ies(ies), ie(ie), sel(sel), ren(ren) {}
  private:
    friend class MSP430Pin;
    READ_ONLY DEFXC& in;
    DEFXC& out;
    DEFXC& dir;
    DEFXC& ifg;
    DEFXC& ies;
    DEFXC& ie;
    DEFXC& sel;
    DEFXC& ren;
};

class MSP430Pin: public Pin
{
  public:
    MSP430Pin(const MSP430Port& port, const uint8_t pin): port(port), pin(pin){}
    void high() const {port.out |= pin; port.dir |= pin;}
    void low() const {port.out &= ~pin; port.dir |= pin;}
    void floating() const {port.dir &= ~pin;}
  private:
    const uint8_t pin;
    const MSP430Port& port;
};

MSP430Port P1(P1IN, P1OUT, P1DIR, P1IFG, P1IES, P1IE, P1SEL, P1REN);
MSP430Pin P1_3(P1, BIT3);
MSP430Pin P1_4(P1, BIT4);

I need to do some rewriting, as the current relation is MSP430Pin "uses an" MSP430Port. This should be MSP430Port "has an" MSP430Pin.

But still, how could I introduce some consistent implementation to set a set of pins in parallel.

I'd love to have some syntax like

// P1_3 and P1_4 are approachable through the Pin interface
(P1_3 + P1_4).high();

// iterative version
void makePinsHigh(Pin[] pinarray, int n)
{
PinSet pins;
for(int i = 0; i < n; ++i)
{
  pins += pinarray[i];
}
pins.high();
}

As far as I've found, nobody has ever done this (on any processor platform). Are there any tips or insights that might help mecreate such an abstraction?

Link to post
Share on other sites

As far as I've found, nobody has ever done this (on any processor platform). Are there any tips or insights that might help me create such an abstraction?

I've actually done some work in that area. My focus has been creating optimized read/write to the GPIO pins using C++ templates. I have two constructs that abstract the ports and pins. This creates a solution that works for the different types of MSP430 ports and I've also got it working with the LPC1114 Cortex-M0 chip. You can see the msp430 implementation here:

 

https://github.com/RickKimball/fabooh/blob/master/include/msp430/core/gpio.h

 

Directly to your point though, I haven't come up with a nice solution for dealing with pins on the same port. I hacked in a solution using compile time logic and defines so I do something like the code below. The is_same_port<> check is done at compile time and if they are the same port results in one instruction that changes the PDIR register :

 

/**
 * blink.cpp - blink test fabooh style
 *
 * Pins Used: RED_LED, GREEN_LED, PUSH2
 */

#include <fabooh.h>

#define SMALL_INIT4 /* don't bother to initialize .bss and .data sections we don't use either */
#include <main.h>

void setup() {
  if( is_same_port<RED_LED,GREEN_LED>::yes_no ) {
    port_mode(RED_LED,GREEN_LED,OUTPUT);
  }
  else {
    RED_LED::set_mode(OUTPUT);
    GREEN_LED::set_mode(OUTPUT);
  }
  PUSH2::set_mode(INPUT_PULLUP);

  RED_LED::high();
  GREEN_LED::low();
}

void loop() {
  // block loop if user holds down the button
  if ( PUSH2::is_pushed()) {
    do {
      delay_msecs(10); // debounce the button
    } while(PUSH2::is_pushed());
  }

  if ( is_same_port<RED_LED, GREEN_LED>::yes_no ) {
    port_toggle(RED_LED,GREEN_LED);
  }
  else {
    RED_LED::toggle();
    GREEN_LED::toggle();
  }

  delay_msecs(100);
}
This results in optimized code even though it doesn't appear it would. The code above creates only uses 100 bytes of flash code. The other advantage to using templates is that I use no data or bss memory. All the code distills down to operations that work on the SFR memory locations using registers and immediates constants.

blink.elf:     file format elf32-msp430


Disassembly of section .init9:

00000200 <main>:
  static const unsigned long MCLK_FREQ = FREQ;

  static void init_clock(void) {
    if (MCLK_FREQ == 16000000) {
#if !defined(__MSP430G2231__)
      BCSCTL1 = CALBC1_16MHZ;
 200:	d2 42 f9 10 	mov.b	&0x10f9,&0x0057	
 204:	57 00 
      DCOCTL = CALDCO_16MHZ;
 206:	d2 42 f8 10 	mov.b	&0x10f8,&0x0056	
 20a:	56 00 

#define SMALL_INIT4 /* don't bother to initialize .bss and .data sections we don't use either */
#include <main.h>

void setup() {
  WDTCTL = WDTPW | WDTHOLD;
 20c:	b2 40 80 5a 	mov	#23168,	&0x0120	;#0x5a80
 210:	20 01 
  ALWAYS_INLINE static void setmode_inputpulldown(const uint8_t mask) {
    pdir &= ~mask;  clear_pins(mask); pren |= mask;
  }

  ALWAYS_INLINE static void setmode_output(const uint8_t mask ) {
    pdir |= mask;
 212:	f2 d0 41 00 	bis.b	#65,	&0x0022	;#0x0041
 216:	22 00 

  /*
   *
   */
  ALWAYS_INLINE static void setmode_inputpullup() {
    PORT::PDIR() &= ~MASK;
 218:	f2 f0 f7 ff 	and.b	#-9,	&0x0022	;#0xfff7
 21c:	22 00 

  /*
   *
   */
  ALWAYS_INLINE static void high() {
    PORT::POUT() |= MASK;
 21e:	f2 d2 21 00 	bis.b	#8,	&0x0021	;r2 As==11
   *
   */
  ALWAYS_INLINE static void setmode_inputpullup() {
    PORT::PDIR() &= ~MASK;
    high();
    PORT::PREN() |= MASK;
 222:	f2 d2 27 00 	bis.b	#8,	&0x0027	;r2 As==11

  /*
   *
   */
  ALWAYS_INLINE static void high() {
    PORT::POUT() |= MASK;
 226:	d2 d3 21 00 	bis.b	#1,	&0x0021	;r3 As==01
  /*
   *
   */
  ALWAYS_INLINE static void low() {
    if ( MASK )
      PORT::POUT() &= ~MASK;
 22a:	f2 f0 bf ff 	and.b	#-65,	&0x0021	;#0xffbf
 22e:	21 00 

  /*
   *
   */
  ALWAYS_INLINE static unsigned is_low() {
    return (PORT::PIN() & MASK ) == 0;
 230:	5f 42 20 00 	mov.b	&0x0020,r15	
 234:	3f f2       	and	#8,	r15	;r2 As==11
  GREEN_LED::low();
}

void loop() {
  // block loop if user holds down the button
  if ( PUSH2::is_pushed()) {
 236:	0a 20       	jnz	$+22     	;abs 0x24c
    do {
      delay_msecs(10); // debounce the button
 238:	3f 40 54 d0 	mov	#-12204,r15	;#0xd054
 23c:	1f 83       	dec	r15		
 23e:	fe 23       	jnz	$-2      	;abs 0x23c
 240:	03 43       	nop			
 242:	03 43       	nop			
 244:	5f 42 20 00 	mov.b	&0x0020,r15	
 248:	3f f2       	and	#8,	r15	;r2 As==11
}

void loop() {
  // block loop if user holds down the button
  if ( PUSH2::is_pushed()) {
    do {
 24a:	f6 27       	jz	$-18     	;abs 0x238

  /*
   *
   */
  ALWAYS_INLINE static void toggle_pins(const uint8_t pins_mask) {
    PORT::POUT() ^= pins_mask;
 24c:	f2 e0 41 00 	xor.b	#65,	&0x0021	;#0x0041
 250:	21 00 
  else {
    RED_LED::toggle();
    GREEN_LED::toggle();
  }

  delay_msecs(100);
 252:	3e 40 09 00 	mov	#9,	r14	;#0x0009
 256:	3f 40 4b 23 	mov	#9035,	r15	;#0x234b
 25a:	1f 83       	dec	r15		
 25c:	fe 23       	jnz	$-2      	;abs 0x25a
 25e:	1e 83       	dec	r14		
 260:	fc 23       	jnz	$-6      	;abs 0x25a
 262:	e6 3f       	jmp	$-50     	;abs 0x230

Hex dump of section '.rodata':
  0x0000c000 00000000                            ....
Other projects have done work in this area. I really like the approach the mbed project took with the BusOut class. It however suffers from the problem that any real class scheme suffers. They all use memory and bss and runtime decisions. http://mbed.org/handbook/BusOut

 

So with my template scheme you define your ports and pins in a header file called pins.h. This allows you to flip between chips and boards creating appropriate prototypes for the abstract pins like RED_LED, PUSH2, MISO .. etc. By making it a template I can also special some instances ( the P2 xtal pins are an example ) so that setting them to gpio works differently than everything else.

 

/* * pins.h - msp430g2253 20-pin DIP GPIO port and pins * * Created: Nov 20, 2012 *  Author: rick@kimballsoftware.com *    Date: 03-02-2012 * Version: 1.0.1 * * ========================================================================= *  Copyright 
Link to post
Share on other sites

Roadrunner,

 

I am a big fan of abstraction and automation.  I see plenty of room for accidental defects using the iomacros (i.e. P1OUT, P1REN, etc).  A good abstraction could help push the defect discovery process back to compilation.  So, I've been thinking about ways to solve your challenge.

 

Like Rick, I'm going to solve the problem with templates.  But I'm going to take it a step farther.  I'm going to make it simpler on the library user's end, but at the cost of a more complicated library.

 

For example, this--or something similar--is what I want to do:

int main(int,char**)
{
   // disable watchdog, setup clocks removed to simplify legability

   typedef define_pin<port1,6> green; // P1.6
   typedef define_pin<port1,0> red;   // p1.0

   enable_output<green,red>();
   set_high<green>();
   set_low<red>();

   while(1)
   {
      delay_300ms(); // delay 300ms
      toggle_pins<green,red>();
   }
}

Here's how I did it:

// need to add reinclusion protection
#include msp430.h
#include <stdint.h>

// convinience / dispatch type
template<typename PORT, uint8_t PIN>
struct define_pin
{
   typedef PORT port;
   enum pin_ { pin = (1 << PIN) };
};


// port definitions
struct port_none { ; };

struct port1
{
   static inline void enable_output(const uint8_t& bits)
   {
      P1DIR |= bits;
   }

   static inline void set_high(const uint8_t& bits)
   {
      P1OUT |= bits;
   }

   static inline void set_low(const uint8_t& bits)
   {
      P1OUT &= ~bits;
   }

   static inline void toggle(const uint8_t& bits)
   {
      P1OUT ^= bits;
   }
};

struct port2
{
   static inline void enable_output(const uint8_t& bits)
   {
      P2DIR |= bits;
   }

   static inline void set_high(const uint8_t& bits)
   {
      P2OUT |= bits;
   }

   static inline void set_low(const uint8_t& bits)
   {
      P2OUT &= ~bits;
   }

   static inline void toggle(const uint8_t& bits)
   {
      P2OUT ^= bits;
   }
};


//
// ENABLE OUTPUT
//

// default template
template<typename PORT1>
inline void enable_output1_impl(const uint8_t& p1)
{
   PORT1::enable_output(p1);
}


// default template
template<typename PORT1, typename PORT2>
inline void enable_output2_impl(const uint8_t& p1, const uint8_t& p2)
{
   PORT1::enable_output(p1);
   PORT2::enable_output(p2);
}



// default template
template<typename PP1, typename PP2=port_none>
struct enable_output
{
   enable_output() { enable_output2_impl<typename PP1::port, typename PP2::port>(PP1::pin,PP2::pin); }
};

template<typename PP1>
struct enable_output<PP1,port_none>
{
   enable_output() { enable_output1_impl<typename PP1::port>(PP1::pin); }
};

// specialized
template<>
inline void enable_output2_impl<port1,port1>(const uint8_t& p1, const uint8_t& p2)
{
   port1::enable_output(p1 | p2);
}

template<>
inline void enable_output2_impl<port2,port2>(const uint8_t& p1, const uint8_t& p2)
{
   port2::enable_output(p1 | p2);
}


//
// SET HIGH
//

// default template
template<typename PORT1>
inline void set_high1_impl(const uint8_t& p1)
{
   PORT1::set_high(p1);
}

// default template
template<typename PORT1, typename PORT2>
inline void set_high2_impl(const uint8_t& p1, const uint8_t& p2)
{
   PORT1::set_high(p1);
   PORT2::set_high(p2);
}

// specialized templates
template<>
inline void set_high2_impl<port1,port1>(const uint8_t& p1, const uint8_t& p2)
{
   port1::set_high(p1 | p2);
}

template<>
inline void set_high2_impl<port2,port2>(const uint8_t& p1, const uint8_t& p2)
{
   port2::set_high(p1 | p2);
}


// default template
template<typename PP1, typename PP2=port_none>
struct set_high
{
   set_high() { set_high2_impl<typename PP1::port, typename PP2::port>(PP1::pin,PP2::pin); }
};

// specialized template
template<typename PP1>
struct set_high<PP1,port_none>
{
   set_high() { set_high1_impl<typename PP1::port>(PP1::pin); }
};



//
// SET LOW
//

// default template
template<typename PORT1>
inline void set_low1_impl(const uint8_t& p1)
{
   PORT1::set_low(p1);
}

// default template
template<typename PORT1, typename PORT2>
inline void set_low2_impl(const uint8_t& p1, const uint8_t& p2)
{
   PORT1::set_low(p1);
   PORT2::set_low(p2);
}

// specialized templates
template<>
inline void set_low2_impl<port1,port1>(const uint8_t& p1, const uint8_t& p2)
{
   port1::set_low(p1 | p2);
}

template<>
inline void set_low2_impl<port2,port2>(const uint8_t& p1, const uint8_t& p2)
{
   port2::set_low(p1 | p2);
}


// default template
template<typename PP1, typename PP2=port_none>
struct set_low
{
   set_low() { set_low2_impl<typename PP1::port, typename PP2::port>(PP1::pin,PP2::pin); }
};

// specialized template
template<typename PP1>
struct set_low<PP1,port_none>
{
   set_low() { set_low1_impl<typename PP1::port>(PP1::pin); }
};


//
// TOGGLE
//

// default template
template<typename PORT1>
inline void toggle1_impl(const uint8_t& p1)
{
   PORT1::toggle(p1);
}

// default template
template<typename PORT1, typename PORT2>
inline void toggle2_impl(const uint8_t& p1, const uint8_t& p2)
{
   PORT1::toggle(p1);
   PORT2::toggle(p2);
}

// specialized templates
template<>
inline void toggle2_impl<port1,port1>(const uint8_t& p1, const uint8_t& p2)
{
   port1::toggle(p1 | p2);
}

template<>
inline void toggle2_impl<port2,port2>(const uint8_t& p1, const uint8_t& p2)
{
   port2::toggle(p1 | p2);
}


// default template
template<typename PP1, typename PP2=port_none>
struct toggle_pins
{
   toggle_pins() { toggle2_impl<typename PP1::port, typename PP2::port>(PP1::pin,PP2::pin); }
};

// specialized template
template<typename PP1>
struct toggle_pins<PP1,port_none>
{
   toggle_pins() { toggle1_impl<typename PP1::port>(PP1::pin); }
};

This is a great example, I think, of generic programming using dispatch tags and specialized templates.

 

This code works great to operate on 1 or 2 pins at a time.  It needs to be extended (easy, but tedious) to handle more.  Maybe 8 or 10.  Also, needs to be extended to enable input, enable resistors, and read input.  Again, easy and tedious.

 

But what about comparing the resulting assembly to C based iomacro only?  The same code, only using P1DIR and P1OUT, produces the EXACT same assembly output.  (Note that I am using mspgcc and have optimization turned on.  But, I wouldn't expect it any other way.  Well written, abstract C++ is often as optimal as boring old C.   ;-)  )

 

I'm sure if I thought about it longer, I could change the function call to 'enable_output(green,red)', even if only through a macro.

Link to post
Share on other sites

I like it ScottyB. I do tend to lean towards Noun->Verb, as in RED_LED::high(). However, in this case unless the noun is something like 'DataBus' it doesn't make much sense. Does the code above actually work? I didn't try to compile it.

 

How about a meta programming approach to recursively use multiple pins as arguments? I had started to look at this and realized, that would probably be overkill for most situations. The 'G' series chips only have 2 ports in the 20 pin package so why kill yourself right? Coming up with a more general purpose solution makes sense if you are using chips with 64 pins.

 

I still do like the idea of a DataBus structure that allows multiple pins to be aggregated and treated as one thing.

 

roadrunner, did you come up with a solution that works for you?

 

-rick

Link to post
Share on other sites

You solutions present quite a nice interface to the user and produce very efficient code. The limitation (and in my case, the problem) is that it will only work for hardcoded pin instructions. It is (as far as I can see) impossible to pass an array/vector of pins to a class and tell let that class instruct all pins to go high, except by iterating over all pins.

I am currently on to a solution for this, I found it by asking the question "who is responsible for grouping these pins together, the using class, the pins themselves or the port they're part of?" The answer turned out to be "the array the pins are part of". So I'm building a PinSet class that holds an amount of pins, then I subclass PinSet to MSP430SinglePortPinSet, which can take any amount of pins which are part of the same port. This class will provide methods high(), low() and floating() (and maybe pullup() and pulldown() in the future). In addition, each of these methods may accept a single pin by index or pointer in itself to exclude that pin from the command.

So I could call set.high(4) which will set all pins in "set" high, except for the 4th pin in its contained pins.

I am aware that this solution is a little bit less efficient than the generic programming solution presented above, but it does allow me to write (the origin of my question: ) a charlieplexing driver class that is totally unaware of the environment. One could even run the same code on an ARM or AVR with only implementing the Pin interface (and for efficiency may implement the ARMSinglePortPinSet).

 

Oh, if I'd implement both a MSP430SinglePortPinSet and a MSP430TwoPortPinSet you'd have full flexibility to combine any number of pins on the value line chips.

Link to post
Share on other sites

I like it ScottyB. I do tend to lean towards Noun->Verb, as in RED_LED::high(). However, in this case unless the noun is something like 'DataBus' it doesn't make much sense. Does the code above actually work? I didn't try to compile it.

 

How about a meta programming approach to recursively use multiple pins as arguments? I had started to look at this and realized, that would probably be overkill for most situations. The 'G' series chips only have 2 ports in the 20 pin package so why kill yourself right? Coming up with a more general purpose solution makes sense if you are using chips with 64 pins.

 

I still do like the idea of a DataBus structure that allows multiple pins to be aggregated and treated as one thing.

 

roadrunner, did you come up with a solution that works for you?

 

-rick

 

 

Rick,

 

I'm trying to use the STL and Boost libs for inspiration.  I'm thinking variadic template parameters.  But I've not ventured quite that far, yet.   I'll probably write a little helper tool to generate the template specializations to allow 3 ports.  We'll see.  It's a lot of work for not all that much gain, I suspect.  Fiddling around with it I completed much of the port interface (i.e. enable_input, enable_pullup, enable_interupt_rising, enable_interupt_falling, etc).  If I take it much farther I'll put it on github.

 

You solutions present quite a nice interface to the user and produce very efficient code. The limitation (and in my case, the problem) is that it will only work for hardcoded pin instructions. It is (as far as I can see) impossible to pass an array/vector of pins to a class and tell let that class instruct all pins to go high, except by iterating over all pins.

 

 

Virtual classes--while great for some things--certainly consume more cycles and space than the C style iomacros.  And without great care, you will quickly eat up any efficiency gains over the wiring/Arduino style interface.  You'll need to balance these factors in writing your library.

 

 

My code is a first stab at general port+pin abstraction.  In fact, I'm certain it will work for AVR by providing the right port structures to the pin definition.  Probably for ARM, too.  It's really quite cool what can be done with templates!  Of course it's no where near done.

 

Please keep us posted on how you come along with your library! 

 

SB

Link to post
Share on other sites

I'll try to post as much code as needed to have a working library. This is my current design.

Basically there are 6 components:

  1. The Pin Interface
  2. The Pin implementation (MSP430Pin)
  3. The PinSet class
  4. The child class MSP430PinSet
  5. The MSP430Port class
  6. The user of the Pin and PinSet interfaces

I'll start with the Pin and PinSet definitions:

 

 

class Pin
{
  public:
    virtual void high() const = 0;
    virtual void low() const = 0;
    virtual void floating() const = 0;
    //virtual void pullup() const = 0;
    //virtual void pulldown() const = 0;
};

template <size_t N>
class PinSet
{
  public:
    virtual ~PinSet(){}
    Pin*& operator[](size_t index){return pins[index];}
    Pin*const& operator[](size_t index) const {return pins[index];}
    virtual void high(){for(Pin** iter = pins; iter != pins + N; ++iter) (*iter)->high();}
    virtual void low(){for(Pin** iter = pins; iter != pins + N; ++iter) (*iter)->low();}
    virtual void floating(){for(Pin** iter = pins; iter != pins + N; ++iter) (*iter)->floating();}
    virtual void high(Pin& exclude){for(Pin** iter = pins; iter != pins + N; ++iter) if (*iter != &exclude) (*iter)->high();}
    virtual void low(Pin& exclude){for(Pin** iter = pins; iter != pins + N; ++iter) if (*iter != &exclude) (*iter)->low();}
    virtual void floating(Pin& exclude){for(Pin** iter = pins; iter != pins + N; ++iter) if (*iter != &exclude) (*iter)->floating();}
    virtual PinSet<N>& operator=(const std::bitset<N>& bits); // drives using noninverting push-pull
    virtual PinSet<N>& assignOpenCollector(const std::bitset<N>& bits); // drives using noninverting float-pull
    virtual PinSet<N>& assignOpenEmitter(const std::bitset<N>& bits); // drives using noninverting push-float
  protected:
    Pin* pins[N];
};

template <size_t N>
PinSet<N>& PinSet<N>::operator=(const std::bitset<N>& bits)
{
  size_t i = 0;
  for(Pin** iter = pins; iter != pins + N; ++iter)
  {
    if (bits[i])
    {
      (*iter)->high();
    }
    else
    {
      (*iter)->low();
    }
    ++i;
  }
}

template <size_t N>
PinSet<N>& PinSet<N>::assignOpenCollector(const std::bitset<N>& bits)
{
  size_t i = 0;
  for(Pin** iter = pins; iter != pins + N; ++iter)
  {
    if (bits[i])
    {
      (*iter)->floating();
    }
    else
    {
      (*iter)->low();
    }
    ++i;
  }
}

template <size_t N>
PinSet<N>& PinSet<N>::assignOpenEmitter(const std::bitset<N>& bits)
{
  size_t i = 0;
  for(Pin** iter = pins; iter != pins + N; ++iter)
  {
    if (bits[i])
    {
      (*iter)->high();
    }
    else
    {
      (*iter)->floating();
    }
    ++i;
  }
}

 

 

 

The PinSet allows us to command a set of pins to go high or low, additionally, I find myself often wanting to target a certain set, excluding one pin. So I supported it here. I could have implemented a operator-() to do even fancier stuff, but I don't have the need for it, plus it would cause a temorary object to be instantiated.

 

The MSP430PinSet is used to access a bunch of pins in parallel, but it requires that all pins must be on the same port.

 

 

 

class MSP430Pin;
template <size_t N>
class MSP430PinSet;

class MSP430Port
{
  public:
    MSP430Port(READ_ONLY DEFXC& in, DEFXC& out, DEFXC& dir, DEFXC& ifg, DEFXC& ies, DEFXC& ie, DEFXC& sel, DEFXC& ren)
    : in(in), out(out), dir(dir), ifg(ifg), ies(ies), ie(ie), sel(sel), ren(ren) {}
  private:
    friend class MSP430Pin;
    template<size_t X> friend class MSP430PinSet;
    READ_ONLY DEFXC& in;
    DEFXC& out;
    DEFXC& dir;
    DEFXC& ifg;
    DEFXC& ies;
    DEFXC& ie;
    DEFXC& sel;
    DEFXC& ren;
};

class MSP430Pin: public Pin
{
  public:
    MSP430Pin(const MSP430Port& port, const uint8_t pin): port(port), pin(pin){}
    void high() const {port.out |= pin; port.dir |= pin;}// port.ren &= ~pin;}
    void low() const {port.out &= ~pin; port.dir |= pin;}// port.ren &= ~pin;}
    void floating() const {port.dir &= ~pin;}// port.ren &= ~pin;}
    //void pullup() const {port.ren |= pin; port.out |= pin; port.dir &= ~pin;} // if current is floating and previous was low, this will fail
  private:
    template<size_t X> friend class MSP430PinSet;
    const uint8_t pin;
    const MSP430Port& port;
};

template <size_t N>
class MSP430PinSet : public PinSet<N>
{
  public:
    MSP430PinSet(MSP430Port& port): port(port), pinmask(0) {}
    ~MSP430PinSet(){}
    bool consolidate();
    virtual void high(){port.out |= pinmask; port.dir |= pinmask;}
    virtual void low(){port.out &= ~pinmask; port.dir |= pinmask;}
    virtual void floating(){port.dir &= ~pinmask;}
    virtual void high(Pin& exclude){uint8_t mask = pinmask & ~(static_cast<MSP430Pin&>(exclude).pin); port.out |= mask; port.dir |= mask;}
    virtual void low(Pin& exclude){uint8_t mask = pinmask & ~(static_cast<MSP430Pin&>(exclude).pin); port.out &= ~mask; port.dir |= mask;}
    virtual void floating(Pin& exclude){uint8_t mask = pinmask & ~(static_cast<MSP430Pin&>(exclude).pin); port.dir &= ~mask;}
  protected:
    using PinSet<N>::pins;
  private:
    uint8_t pinmask;
    MSP430Port& port;
};

template <size_t N>
bool MSP430PinSet<N>::consolidate()
{
  for(Pin** iter = pins; iter != pins + N; ++iter)
  {
    pinmask |= static_cast<MSP430Pin*>(*iter)->pin;
    if (&static_cast<MSP430Pin*>(*iter)->port != &port)
    {
      pinmask  = 0;
      return false;
    }
  }
  return true;
}

////// MSP430 definitions ///////
MSP430Port P1(P1IN, P1OUT, P1DIR, P1IFG, P1IES, P1IE, P1SEL, P1REN);
//MSP430Port P2(P2IN, P2OUT, P2DIR, P2IFG, P2IES, P2IE, P2SEL, P2REN);
MSP430Pin P1_0(P1, BIT0);
MSP430Pin P1_1(P1, BIT1);
MSP430Pin P1_2(P1, BIT2);
MSP430Pin P1_3(P1, BIT3);
MSP430Pin P1_4(P1, BIT4);
MSP430Pin P1_5(P1, BIT5);
MSP430Pin P1_6(P1, BIT6);
MSP430Pin P1_7(P1, BIT7);

 

 

 

A user may now use these classes in a manner like

  MSP430PinSet<3> pinmap(P1);
  pinmap[0] = &P1_1;
  pinmap[1] = &P1_2;
  pinmap[2] = &P1_3;
  pinmap.consolidate();
 
  std::bitset<N> bits;
  pins.assignOpenEmitter(bits);

I still want to implement a method to add pins to the pinset, instead of the current operator[]() implementation, then I can save the execution of the consolidate() method.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...