Oct 7, 2013

Understanding the NetBurner PinIO Class

The PinIO class in the NetBurner library is very convenient, clever and both misunderstood and misused. PinIO  is most often accessed through the array subscription operator on the global variables J1 and J2. That is, you can read the value of pin 42 very easily with

const int PIN_J1_42 = 42;
int value = J1[PIN_J1_42];

However, this is not the equivalent (in terms of efficiency) of reading the pin directly with the underlying port:

If you find this article helpful check out my Eclipse Guided Tour course on Pluralsight. I’m currently working on part 2 which should be released in December of  2013.

int value = sim.pcdat & BIT_MASK; //assume BIT_MASK is set for pin 42

The distinction doesn’t matter too much unless speed and efficiency matter. Reading through J1 or J2 as done in the first example performs the following steps each time:

  1. Invokes the array subscription operator on the PinIOJ1Array class and returns a solid object of type PinIO. (Note, operator[] is not written in the canonical form to return a reference and there is no const form. Given what this class does this seems like a reasonable design decision but it probably does lead to the misunderstanding and misuse). 
  2. The array subscription operator in turn invokes the private constructor for PinIO (It can do this because PinIOJ1Array is a friend of PinIO).
  3. Since an integer value was requested the  operator int() conversion function of PinIO is automatically invoked which calls read()  and casts the result to an integer.
  4. The destructor on the temporary PinIO class that was created is invoked.

If you thought it just read sim.pcdat for pin 42 all of that might be a surprise. The overhead is not that bad, the destructor is just the default C++ destructor and the constructor is pretty lightweight, it just assigns two integer values. If you’re doing a single read or write it probably doesn’t matter much (unless you’re looking to save tens of microseconds). It’s when you’re doing multiple reads and writes where the time can really start to add up. 

I’m not suggesting you avoid the PinIO class, just that you understand it a little better. There is no good reason to write a loop like :

while (J1[PIN_J1_42])
{
}
 

or

while (some_condition) //or a for loop
{
    
if (J1[PIN_J1_42]) doSomething();
}
 

The operator[]  is written to return the underlying PinIO class. When you want to check the value of the same pin multiple times, keep a copy of the returned value.

PinIO j1_42 = J1[PIN_J1_42];  //save the returned instance of PinIO
while (j1_42) //the bool() operator is called and invokes read() and casts the result to bool
{
    doSomething();
}

That way you’ll only pay for the overhead costs once, instead of every time. If you need the fastest possible GPIO toggle times then stick with direct manipulation of sim.pcdat (or its siblings).

Technorati Tags: ,

About Me

My photo
Tod Gentille (@todgentille) is now a Curriculum Director for Pluralsight. He's been programming professionally since well before you were born and was a software consultant for most of his career. He's also a father, husband, drummer, and windsurfer. He wants to be a guitar player but he just hasn't got the chops for it.