Tell me more ×
Electrical Engineering Stack Exchange is a question and answer site for electronics and electrical engineering professionals, students, and enthusiasts. It's 100% free, no registration required.

I am looking for C++ abstractions for hardware I/O points or pins. Things like in_pin, out_pin, inout_pin, maybe open_collector_pin, etc.

I surely can come up with such a set of abstractions myself, so I am not looking for 'hey, you might do it this way' type of answers, but rather the 'look at this library that has been used in this and this and this project'.

Google did not turn up anything, maybe because I don't know how others would call this.

My aim is to build I/O libraries that are based on such points, but also provide such points, so it would be easy to for instance connect an HD44780 LCd to either the IO pins of the chip, or an I2C (or SPI) I/O extender, or any other point that can somehow be controlled, without any change to the LCD class.

I know this is on the electronics/software edge, sorry if it does not belong here.

@leon: wiring That is a big bag of software, I will need to look closer. But is seems they do not use a pin abstraction like I want. For instance in the keypad implementation I see

digitalWrite(columnPins[c], LOW);   // Activate the current column.

This implies that there is one function (digitalWrite) that knows how to write to an I/O pin. This makes it impossible to add a new type of I/O pin (for instance one that is on an MCP23017, so it has to be written via I2C) without rewriting the digitalWrite function.

@Oli: I googled an Arduino IO example, but the seem to use about the same approach as the Wiring library:

int ledPin = 13;                 // LED connected to digital pin 13
void setup(){
    pinMode(ledPin, OUTPUT);      // sets the digital pin as output
}
share|improve this question
What microcontroller are we talking here? – Majenko Sep 4 '11 at 16:20
That is irrelevant; for a particular microcontroller the io pins of that uC wil will implement the appropriate interfaces. But this is for C++, so think of 32-bit chips like ARM, Cortex and MIPS. – Wouter van Ooijen Sep 4 '11 at 17:26
I've never used one, but doesn't Arduino abstract all the pins like this? You may (or may not) get some useful info looking at the way they have done things. – Oli Glaser Sep 4 '11 at 17:42
That is exactly the Arduino interface quoted there ;) And yes, it does matter which uC you are using as different ones will provide a different interface to the hardware. For instance the PIC uses memory mapped registers, and I often do something like: unsigned char *MyTRIS = ⧍ - often in association with a struct: struct pin { unsigned char *tris; unsigned char *lat; unsigned char *port; unsigned char pinno; }; struct pin pins[8] = { {&TRISA,&LATA,&PORTA,0}, {&TRISB,&LATB,&PORTB,3}...}; That is specific to PIC and I doubt it would work on ARM, or Atmel, etc. – Majenko Sep 4 '11 at 22:07
1  
And as for rewriting the digitalWrite function - look at "overloading" in C++. I have just a few moments ago written an overloaded digitalWrite function for an IO expander board for the Arduino. As long as you use different parameters (I replaced the first "int" with a "struct") it will pick your digitalWrite in preference to the default one. – Majenko Sep 4 '11 at 22:10
show 3 more comments

3 Answers

up vote 3 down vote accepted

Short answer: sadly, there is no library to do what you want. I've done it myself numerous times but always in non open-source projects. I'm considering putting something up on github but I'm not sure when I can.

Why C++? 1. compiler is free to use dynamic word-size expression evaluation. C propagates to int. Your byte mask/shift can be done faster/smaller. 2. inlining 3. templatizing operations lets you vary word size and other properties, with type-safety. -G

share|improve this answer

The Wiring project uses abstraction like that:

http://wiring.org.co/

and the compiler is written in C++. You should find plenty of examples in the source code. The Arduino software is based on Wiring.

share|improve this answer
answered in the question body – Wouter van Ooijen Sep 4 '11 at 19:09

In C++, it's possible to write a class so that you can use I/O ports as though they were variables, e.g.

  PORTB = 0x12;  /* Write to an 8-bit port */
  if (RB3) LATB4 = 1;  /* Read one I/O bit and conditionally write another */

without regard for the underlying implementation. For example, if one is using a hardware platform which doesn't support bit-level operations but does support byte-level register operations, one could (probably with the aid of some macros) define a static class IO_PORTS with an inline read-write properties called bbRB3 and bbLATB4, such that the last statement above would turn into

  if (IO_PORTS.bbRB3) IO_PORTS.bbLATB4 = 1;

which would in turn be processed into something like:

  if (!!(PORTB & 8)) (1 ? (PORTB |= 16) : (PORTB &= ~16));

A compiler should be able to notice the constant expression in the ?: operator and simply include the "true" part. It might be possible to reduce the number of properties created by having the macros expand to something like:

  if (IO_PORTS.ppPORTB[3]) IO_PORTS.ppPORTB[4] = 1;

or

  if (IO_PORTS.bb(addrPORTB,3)) IO_PORTS.bbPORTB(addrPORTB,4) = 1;

but I'm not sure a compiler would be able to in-line the code as nicely.

I by no means wish to imply that using I/O ports as though they are variables is necessarily a good idea, but since you mention C++ it's a useful trick to know. My own preference in C or C++, if compatibility with code that uses the aforementioned style was not required, would probably be to define some type of macro for each I/O bit, and then define macros for "readBit", "writeBit", "setBit", and "clearBit" with the proviso that the bit-identifying argument passed to those macros must be the name of an I/O port intended for use with such macros. The above example, for instance, would be written as

  if (readBit(RB3)) setBit(LATB4);

and translated as

  if (!!(_PORT_RB3 & _BITMASK_RB3)) _PORT_LATB4 |= _BITMASK_LATB4;

That would be a bit more work for the preprocessor than would the C++ style, but it would be less work for the compiler. It would also allow optimal code generation for many I/O implementations, and decent code implementation for almost all.

share|improve this answer
2  
A quote from the question: "I am not looking for 'hey, you might do it this way' type of answers" ... – Wouter van Ooijen Sep 5 '11 at 6:12
I guess I'm not clear quite what you are looking for. Certainly I would expect that many people who are interested in classes for I/O pin reconstruction would also be interested to know that using properties one can make code which is written for one style of I/O use just about anything else. I've used properties to make a statement like "LATB3=1;" send out an I/O request to a TCP stream. – supercat Sep 5 '11 at 23:48
I tried to be clear in my question: I want to be able to accomodate new types of IO pins without rewriting the code that uses IO pins. You write about user-defined type conversions and assignment operators, which are surely interesting, I use them all the time, but not a solution to my problem. – Wouter van Ooijen Sep 6 '11 at 5:33
@Wouter van Ooijen: What "new types of I/O pins" would you be anticipating? If the source code is written with syntax like "if (BUTTON_PRESSED) MOTOR_OUT = 1;", I would expect that for just about any mechanism by which the processor might read a button control or a motor one could write a library so the above source code would turn on the motor if the button is pushed. Such a library might not represent the most efficient way of turning on the motor, but it should work. – supercat Sep 6 '11 at 15:55
@Wouter van Ooijen: One could perhaps improve efficiency if one required that the source code invoke an UPDATE_IO() or UPDATE_INPUTS() macro sometime before reading any input, and perform an UPDATE_IO() or UPDATE_OUTPUTS() some time after any output, with the semantics that inputs could be sampled either at the code that reads them, or at the previous UPDATE_INPUTS()/UPDATE_IO() invocation. Likewise outputs could either occur immediately or be deferred. If an I/O is implemented using something like a shift register, deferring actions would allow multiple operations to be consolidated. – supercat Sep 6 '11 at 16:02
show 3 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.