I'm writing a new FlowCounters library based on the PinChangeInt library to allow multiple flow meters. I'm stuck on an error:
/Users/turgo/Documents/Arduino/libraries/FlowCounters/FlowCounters.cpp: In constructor 'FlowCounters::FlowCounters(uint8_t)':
/Users/turgo/Documents/Arduino/libraries/FlowCounters/FlowCounters.cpp:15: error: no matching function for call to 'PCintPort::attachInterrupt(uint8_t&, void (FlowCounters::*)(), int)'
/Users/turgo/Documents/Arduino/libraries/FlowCounters/PinChangeInt.h:421: note: candidates are: static int8_t PCintPort::attachInterrupt(uint8_t, void (*)(), int)
The example sketch and library files are attached below.
I had to put the PinChangeInt.h file into the same folder as the FlowCounters library for Arduino 1.5.5 to find it.
Please help, and also offer any guidance for this new library.
You get those sort of errors, where there is an inconsistency between the type of the arguments you are using in your function call, and the type of the arguments in the function declaration or definition.
Also, there is some obscure, fussy and counter-intuitive rules about where a function ( or class method ) is declared to be "static", which also may be your problem here.
In that function, you will need to determine which instance of the class should have it's interruptCount member incremented (which needs to be volatile).
Yes, you cannot call methods direct from an ISR, there is no object to call it on. An
ISR is always a plain function. You can have some global variable holding an object
and trampoline via that to a method.
Thanks all for your replies. The solution here is still beyond my understanding of the problem. For example, I tried to declare the FlowCounters::flowCountFunc() as static, but the compiler didn't like it. Could you please indicate the correct code?
My recollection is that you declare a class function to be static in the declaration but not in the definition of the function.
So, for example, in the h file
class myClass
{
myClass() ; // constructor
int afunc( int b ) ;
float cfunc( int d );
static void dfunc( );
}
and in the c ( or cxx or cpp ) file
int myClass::afunc ( int b )
{
return 2*b ;
}
float myClass::cfunc( int d )
{
return 2.0f ;
}
void myClass::dfunc( )
{
// do something unrelated to a specific instance of the class
}
The thing to note here, is that you don't put the word "static" where the word "void" is, in front of dfunc( )
Or maybe it's the other way around, I use mostly java for OOP these days.
I may be stymied by the ISR limitations requiring a single static function - the same function for all instances of the class. What I am trying to do here is to provide many instances of the FlowCounters object to handle individual flow counters. Each FlowCounters object calls PinChangeInt with a unique pin number. PinChangeInt keeps track of the pin number, sorts out which pin changed, and fires the ISR associated with it. Since I provide those ISRs in my FlowCounters object, each one needs to be a unique (and not static) function, each referencing its own instance of the interruptCount variable. How do I get around this limitation?
turgo:
I may be stymied by the ISR limitations requiring a single static function - the same function for all instances of the class. What I am trying to do here is to provide many instances of the FlowCounters object to handle individual flow counters. Each FlowCounters object calls PinChangeInt with a unique pin number. PinChangeInt keeps track of the pin number, sorts out which pin changed, and fires the ISR associated with it. Since I provide those ISRs in my FlowCounters object, each one needs to be a unique (and not static) function, each referencing its own instance of the interruptCount variable. How do I get around this limitation?
You have to determine which pin(s) changed in the ISR yourself - you always have to do with pin-change interrupts, the hardware doesn't keep track of this. You can keep a
volatile byte variable holding state of the pins at the previous interrupt and XOR.
You have to determine which pin(s) changed in the ISR yourself - you always have to do with pin-change interrupts, the hardware doesn't keep track of this. You can keep a
volatile byte variable holding state of the pins at the previous interrupt and XOR.
Then, your class needs to keep a list of instances (not trivial) and the associated pin number, so YOU can call the correct instance's method.
@Paul & Mark: The PinchangeInt (PCIntPort function) library does the work of determining which pin changes, and then fires the function provided by the user. The problem I am having is passing my function to PCIntPort.
The problem I am having is passing my function to PCIntPort.
You've needed to make some code changes. You haven't posted code since making them.
It is possible to have static methods as the callback function, and then have that static method call the correct instance's method. It's not trivial, but it is possible.
The class needs to keep a list of instances. Each instance should call a static method, perhaps called registerIntPin() that takes two arguments - the pin number and a pointer to the instance (this in the caller). The class should have an array of pointers, and the registerIntPin() method should record the pointer in the appropriate location in the array.
Then, the callback method simply accesses the nth element of the array (a pointer to an instance), and calls the appropriate method:
Thanks for your persistent help, especially PaulS. I've been reading about callback methods, and am still having a hard time understanding them. I've discovered that GreyGnome first wrote PinChangeInt library, and then AdaEncoder library which uses PinChangeInt. Therefore he has solved the architecture problems I am encountering. So I will rework the AdaEncoder Library into my FlowCounters library.
In AdaEncoder, there is a critical code section which I do not understand:
In "PCintPort::attachInterrupt(pinB, this, CHANGE);", what is "this"? I expected to see a static member function, but I do not. There is a callback method, but it is not static:
void AdaEncoder::cbmethod() {
uint8_t stateA;
uint8_t stateB;
uint8_t portState;
#ifdef DEBUGTIME
*ada_output_port|=ada_output_mask; // sets it high
#endif
portState=*this->port;
stateA=portState & bitA;
stateB=portState & bitB;
#ifdef DEBUG
printBuffer.putString(" id:");
printBuffer.put(id); printBuffer.put(' ');
printBuffer.put('A'); printBuffer.putHex(stateA); printBuffer.put(' ');
printBuffer.put('B'); printBuffer.putHex(stateB); printBuffer.putString(" T: ");
printBuffer.putDec(turning); printBuffer.putString(" C: ");
printBuffer.putDec(clicks); printBuffer.put('\n');
#endif //DEBUG
if (stateA && stateB ) { // the detent. If we're turning, mark it.
if (turning) {
clicks+=direction;
#ifdef DEBUG
printBuffer.putString("Clicks: "); printBuffer.putDec(clicks); printBuffer.put('\n');
#endif //DEBUG
}
turning=0; direction=0; // reset counters.
#ifdef DEBUGTIME
*ada_output_port&=ada_not_output_mask; // sets it low
#endif
return;
}
if (stateA == 0 && stateB == 0) { // The 1/2way point. Flag that we've reached it.
turning=1;
#ifdef DEBUGTIME
*ada_output_port&=ada_not_output_mask; // sets it low
#endif
return;
}
if (turning == 0) { // Either stateA!=0 or stateB!=0.
// We are just starting to turn, so this will indicate direction
if (stateA) { direction=1; // CCW
#ifdef DEBUG
printBuffer.put('+');
#endif
#ifdef DEBUGTIME
*ada_output_port&=ada_not_output_mask; // sets it low
#endif
return;
}; // CCW.
if (stateB) { direction=-1; // CW
#ifdef DEBUG
printBuffer.put('-');
#endif
#ifdef DEBUGTIME
*ada_output_port&=ada_not_output_mask; // sets it low
#endif
return;
}; // CW.
}
#ifdef DEBUGTIME
*ada_output_port&=ada_not_output_mask; // sets it low
#endif
}
How is it accessed thru "this"?
I've attached the entire AdaEncoder Library below.
In "PCintPort::attachInterrupt(pinB, this, CHANGE);", what is "this"?
It is a hidden member of all classes. this is a pointer to the instance that owns the pointer. Other languages use other names - self, me, etc.
It isn't clear how that works, since PCintPort::attachInterrupt seems to expect a pointer to a function, not a pointer to an instance.
Edit: After looking at the AdaEncoder header file, it is not using the PCintPort class that we have been discussing. It uses the ooPCintPort class, which I've not seen before.
I think I finally get it. 'this' is the AdaEncoder object, a member of the CallBackInterface class, which has a custom method 'cbmethod()' called by ooPinChangeInt when an interrupt happens. This is explained in detail in the ooPinChangeInt wiki: https://code.google.com/p/oopinchangeint/wiki/Logic