I’ve been working with the AVR microprocessor for a couple months now. I decided to switch from the PIC to AVR because the AVR supports C, which is a lot easier to program in than assembly (and I’ve become a pretty lazy programmer over the years). Though C is far more cumbersome to use than Java, it’s a lot easier to design and implement than assembly.
One issue have I have not been able to over come until recently is the use of interrupt vectors in a C++ class. I could not get the right combination of header includes, extern declarations, and C++ anti-mangle syntax going. I found an thread on avrfreaks that I thought might solve the problem, but it was not exactly what I was looking for (and it was quite old).
I then found a blog post that I thought would solve my problem, but alas, it was not complete. I think found a follow up post that gave me the additional details I needed. But, of course, I was not completely satisfied with the solution. I’ve never been a fan of pointers in C and the indirection syntax makes my head hurt (as well as makes the program nearly incomprehensible for beginners). The solution used a static pointer to a static class, and then “reached” into the class to access the member variables. It works, but was very C and not very C++.
Then it occurred to me – if the external routine can access the member variable via pointer, why can’t it just use the static class declaration directly, and access the variables using normal C++ “dot” notation. That is, more like a public instance variable in Java. Since I already had a static class defined for use outside the library (and to avoid a malloc), I should be able to use the instance variable using a simple dereference. And it worked.
So, here are the key points of the code. In the header for the class (e.g., Foo.h) that contains the ISR handler, declare the handler extern for C, as follows:
extern "C" void TIMER2_COMPA_vect(void) __attribute__ ((signal));
Then declare the ISR handler as a public friend of the class:
friend void TIMER2_COMPA_vect();</code>
That takes care of “exposing” the member variables to the ISR handler and declaring the ISR handler as a valid method. Though the handler is not a method on the class, it can access the private variables as if it where a method. To ensure the class can be used from C as well as C++ routines, you must define the class as a void struct in C as follows:
typedef struct Foo Foo;
The whole header file looks something like this:
#ifdef __cplusplus extern "C" void TIMER2_COMPA_vect(void) __attribute__ ((signal)); class Foo { private: // Insert private variables and methods here public: // Insert public methods here void calculateFooBar(); friend void TIMER2_COMPA_vect(); }; #else typedef struct Foo Foo; #endif // end if c++
In order to access the methods on the class, you must define a static class instance in the class definition (e.g., Foo.cpp). Example:
/** Define the static variable for Foo */ Foo FooVar; // Foo class for "external" use
One final thing. To allow C routines to access the class and the methods, you must also declare the methods in the header file (Foo.h) as external to “C”, as such:
#ifdef __cplusplus extern "C" { #endif // Insert methods here void calculateFooBar(); // The static class definition. extern Foo FooVar; #ifdef __cplusplus } // end extern C #endif
Note the definition of the method calculateFooBar as well as the definition of FooVar as an external variable. This allows C components to become aware of the static class “instance” of Foo. Now C++ methods and C routines can access an instance of Foo via FooVar. Moreover, the ISR method, since it is a “friend” of Foo, can access the private instance variable of Foo by doing something like this:
// Set barVar to zero FooVar.barVar = 0;
The key for the ISR handler is being a friend of the class that has all the methods and variables. The other option would be to create getters and setters for each variable and letting the ISR handler call the getter/setter. However, this is a method call, rather than direct variable access and had a good deal of overhead, plus it adds a fair amount of extra code that can be avoided. Either case will work.
I hope this help someone out there.