[SOLVED] How do I export ISR routines into a library?

Hi all!

Brief Context:

I’ve had my Arduino UNO for a few weeks now and I’m really enjoying it. Not knowing much about programming or MCUs before I have managed to figure out how to talk to the thing and program it to do some cool blinking things.

I wouldn’t say I’ve learned to code but:

On my quest to understand the bones of this I’ve managed to get the hang of functions

I figured out how to port those functions into a library

I got a crash course on binary and figured out how to communicate with the 8bit and 16bit timers.

I’ve written an entire library from scratch dealing with blinking a light different ways depending on how the button is pressed.

The Problem:

My latest evolution involves the ability to press the button and then the user has a set amount of time to input a certain number of presses which dictates the blinking pattern.

To accomplish this, I set up a Compare Match Interrupt which triggers an ISR.

I managed to dissect a couple of tutorials about creating libraries which is how I got this far. But I’m not able to find any sources dealing with this specifically:

What is the correct way to port a working INTERRUPT into a library?

I want the user to just type in #include “MyInterrupt.h”

Up until now, I have not had problems getting code to work or porting simple functions into a working library.

Since my current code runs off my fledgling library, which you don’t have, I have simplified my example code to only include an ISR printing incremental numbers to Serial Monitor every ~0.25sec.

I am including my attempt at writing .cpp and .h files that would let me bring this into a new sketch with:

#include “TimerProto.h”

Conclusion:

If someone could break down how to port this into a library format or link to a tutorial explaining exactly this, that would be a great help.

Thanks!

TimerProto.ino
This is the working Proof of concept code that I am trying to port into a library

int x = 0;

void setup() 
{
  
  cli();
  TCCR1A = 0;
  TCCR1B = 0;
    
  OCR1A = 5192; //Output Control Register
  TCCR1B |= (1 << WGM12); //0100 CTC On
  TCCR1B |= (1 << CS10); 
  TCCR1B |= (1 << CS12);  //101
  //TCCR1B = 00001101
  TIMSK1 |= (1 << OCIE1A); //Compare Match Interupt Enable
  //TIMSK1 = 00000010
  sei();
  
  Serial.begin(9600);
}

ISR(TIMER1_COMPA_vect)
{
      Serial.println(x);
      x++;
}

void loop(){}

TimerProto.cpp
I tried to write this .cpp file using the same techniques as I have been

//Timer library prototype
//TimerProto.cpp

#include "Arduino.h"

TimerProto::protoTimer()
{
	cli();
  TCCR1A = 0;
  TCCR1B = 0;
    
  OCR1A = 5192; //Output Control Register
  TCCR1B |= (1 << WGM12); //0100 CTC On
  TCCR1B |= (1 << CS10); 
  TCCR1B |= (1 << CS12);  //101
  //TCCR1B = 00001101
  TIMSK1 |= (1 << OCIE1A); //Compare Match Interupt Enable
  //TIMSK1 = 00000010
  sei();
  
  Serial.begin(9600);
  
}

TimerProto::protoISR()
{
	ISR(TIMER1_COMPA_vect)
		{
		Serial.println(x);
		x++;
		}
}

TimerProto.h
I tried writing this .h file using the same techniques as I have been.

//Timer Prototype
//TimerProto.h

#ifndef TimerProto_h
#define TimerProto_h

#include "Arduino.h"

class TimerProto
{
	public:
		int x;
		void protoTimer();
		void protoISR();
};
#endif

LibraryTest.ino
I also tried putting the TestTimer.protoISR in between the setup() and loop() because that’s where it sits in my TimerProto.ino. Even though I knew it wouldn’t work. I also tried putting the code that exist in protoISR into the .h file.
I don’t really understand how the .cpp and .h file are processed when used in #include related to how the same commands are processed in a regular .ino file.
For instance, how would I figure this out:
If a section of code works in a regular .ino file and it exist between the setup() and loop() does it go into the .cpp or .h file and where?
```
*#include “TimerProto.h”

TimerProto TestTimer;

void setup() {
  TestTimer.protoTimer();
  TestTimer.protoISR();
}

void loop() {
 
}*
```

The header AKA #include "header.h" is just a convenient way to insert a block of code physically in front of another block of code. Compiler will process these blocks as a single chunk of code. The main convenience is that during development you do not have to navigate thru large chunk of text AKA divide and conquer!

The bad part is that it is up to you to keep track of correct insertion order of these files. But if you don't the friendly compiler will let you know _ "such and such not in scope / undefined etc. ".

In your header TimerProto.h file you declare class TimerProto and than define its functions / methods in TimerProto.cpp.

In practice all you need is one header file and put both declaration and definitions in it IN proper sequence! .

There is nothing wrong with splitting the declaration and definitions in two text files, as long as declaration precedes definitions.

You can add .h or .cpp files to your x.ino in any order anytime, but you have to put their #include(s) in correct sequence preferably in first few lines of your code.

Thanks for the reply. You did clear the question of how the #include is processed.

My trouble is still figuring out how to “port out” all the elements of TimerProto.ino into their proper places in the .cpp and .h files.

I have tried a number of different combinations, with no luck.

It would help me understand, I think if you could post examples of working .cpp and .h files based off of my TimerProto.ino

Does it matter that in the working .ino file the ISR setting falls between the setup() and loop() functions? And where does it go in the .cpp or .h files so that it runs in the right place as an included library?

Thanks!

ISRs are C, not C++, so they cannot be a method in a class at all, they have to be global
C functions - they correspond to actual hardware dispatch mechanism which is defined
at the assembler level.

So if your class wants to handle an interrupt you’ll have to code a singleton class (only
one instance) and an ISR that “trampolines” to the method you want on that singleton:

TimerProto the_timer ;

ISR(TIMER1_COMPA_vect)
{
      the_timer.handle_interrupt () ;
}

void TimerProto::handle_interrupt ()
{
    x ++ ;   // x can be an instance variable
}

BTW calling a Serial method from an ISR is one of the things that will freeze up your
Arduino - like delay().

MarkT: ISRs are C, not C++, so they cannot be a method in a class at all, they have to be global C functions - they correspond to actual hardware dispatch mechanism which is defined at the assembler level.

So if your class wants to handle an interrupt you'll have to code a singleton class (only one instance) and an ISR that "trampolines" to the method you want on that singleton:

TimerProto the_timer ;

ISR(TIMER1_COMPA_vect) {       the_timer.handle_interrupt () ; }

void TimerProto::handle_interrupt () {     x ++ ;  // x can be an instance variable }




BTW calling a Serial method from an ISR is one of the things that will freeze up your
Arduino - like delay().

MarkT

very nice explanation, thanks.

mwal: Thanks for the reply. You did clear the question of how the #include is processed.

My trouble is still figuring out how to "port out" all the elements of TimerProto.ino into their proper places in the .cpp and .h files.

I have tried a number of different combinations, with no luck.

It would help me understand, I think if you could post examples of working .cpp and .h files based off of my TimerProto.ino

Does it matter that in the working .ino file the ISR setting falls between the setup() and loop() functions? And where does it go in the .cpp or .h files so that it runs in the right place as an included library?

Thanks!

mwal: Thanks for the reply. You did clear the question of how the #include is processed.

My trouble is still figuring out how to "port out" all the elements of TimerProto.ino into their proper places in the .cpp and .h files.

I have tried a number of different combinations, with no luck.

It would help me understand, I think if you could post examples of working .cpp and .h files based off of my TimerProto.ino

Does it matter that in the working .ino file the ISR setting falls between the setup() and loop() functions? And where does it go in the .cpp or .h files so that it runs in the right place as an included library?

Thanks!

mwal: Thanks for the reply. You did clear the question of how the #include is processed.

My trouble is still figuring out how to "port out" all the elements of TimerProto.ino into their proper places in the .cpp and .h files.

I have tried a number of different combinations, with no luck.

It would help me understand, I think if you could post examples of working .cpp and .h files based off of my TimerProto.ino

Does it matter that in the working .ino file the ISR setting falls between the setup() and loop() functions?

NO - see bellow

And where does it go in the .cpp or .h files so that it runs in the right place as an included library?

See bellow

Thanks!

MarkT did an excellent job explaining how ISR "fits" into scheme of things.

The main point is - ISR is a global function , and anything outside Setup and Loop is global to Setup and Loop , and being inside the "hidden" main() not really truly global.

(Confusing , I hope I said it right.)

I also believe that IDE "automatic prototyping " is really not helping to grasp the "inside main() global" concept, but that is OT for now.

Thanks for the answers everyone! A lot of things make more sense when I am able to search for the right terms. You guys turned me onto some new words and phrases to search for. I’m including links to some of the things I read before posting these additional questions. They are at the bottom.

Also, I know it’s probably a pain in the ass, but I think it would really help to see the original working ProtoTimer.ino broken down into the appropriate .cpp, .c, .h, and .ino(usage example) files.


MarkT:
ISRs are C, not C++, so they cannot be a method in a class at all, they have to be global
C functions - they correspond to actual hardware dispatch mechanism which is defined
at the assembler level.

Thank you for taking time to explain this. I’ll try to put a (?) next to words I’m not sure I’m using correctly. This is what I think your answer means. :slight_smile:

  1. I can’t write the ISR routine into the .cpp or .h file because it is only recognized in C.
    So it needs to be written in .c and that interrupt calls a function in my .cpp file
  2. So I need a .c file into the same folder as my .cpp and .h
  3. That .c file is the code you posted**(? - refer to Question 1)**
  4. Is there an #include <TimerProto.c> added to the .h file after this?

Questions:

  1. I think I really misunderstand the example and point you are trying to make because I would expect to see something like #include “TimerProto.h” at the top of your example if I was right. But, I Also I learned that C is “top down” and is formulated step-by-step. So, if you #include the .h file will try to insert C++ code into the C code(?)

Additionally, Timerproto::handle_interrupt comes after the_timer.handle_interrupt and in the end I’m not sure what I’m looking at. :confused:

  1. Is the way I am currently trying to call the “bit-instruction”(?) through protoTimer(); in the void setup() of LibraryTest.ino correct?

  2. I hope I’m not too far off point here. Thanks again for taking the time!

So if your class wants to handle an interrupt you’ll have to code a singleton class (only
one instance) and an ISR that “trampolines” to the method you want on that singleton:

I interpret this code as the ISR handling a class or a function.

Or… are you saying “Hypothetically, IF you needed a class to handle an ISR you need to code a singleton class”.

I think I see how the ISR “trampolines” to the method, but what is this code? a .c file or an example of something I should put in my .h file or something else completely? :o

BTW calling a Serial method from an ISR is one of the things that will freeze up your
Arduino - like delay().

I have read that before, and had some trouble with it. But it is the only way I know how to debug. Is there a better way to do debug?

Vaclav:
The main point is - ISR is a global function , and anything outside Setup and Loop is global to Setup and Loop , and being inside the “hidden” main() not really truly global.

I also believe that IDE "automatic prototyping " is really not helping to grasp the “inside main() global” concept, but that is OT for now.

#include “header.h” brings all the code in that header file into the place where you type as if you copy/pasted the code directly is what I think I’m learning from this thread.

So does it matter whether a piece of code comes before or after or in between the setup() and void() loops? I think I’m starting to understand that it doesn’t. But if it is in the Hidden main() when coded in the Arduino IDE how does it still work even though it is not truly “global”? And is this really important for me to understand right now? :slight_smile:

Because it’s all included in the “hidden main();”?

Links:
Nice Breakdown of Library form in C++ [StackOverflow]

What is a singleton Class? [Code Guru]

Plain language breakdown of the differences in C and C++ [Durofy]

Good break down for the novice of OOP [Durofy]

Trampolining? [Wikipedia]

  1. You can write C in a .cpp file. It doesn't care. The difference is that you can't put the ISR inside C++ structures like classes. It has to sit outside, like a global.
  2. [see above]
  3. [addressed to other person]
  4. No, the .c or .cpp file #includes the .h file. The reason for this is the .c and .cpp files are always compiled but then there's another stage called linking before a runnable binary is produced. It is the linker which decides which functions to include in the binary and organises them to fit into the memory.

I’ve been at this for about a week now and while there are tons of great tutorials and explanations on the web and in these forums. I really had a hard time pulling out the information relevant to me from the myriad of specific examples. I will attempt to post my version of a solution using the most generic and clean code I am capable of.

I hope this helps someone down the road. I can tell you that once you start doing more complex things with this, it gets a little tricky and you may have to tweak it some, but this should get you started if you are having the same confusion as I was.

Thank you all for your answers. It turns out most of the information I needed was buried in these responses and I finally got it to work. Below are the Solution files for:

  1. Working .ino I wish to migrate to library
  2. .cpp & .h solution files
  3. Usage .ino file

The way it seems to break down is:

  1. The bit communication with the timers (turning on the CTC, ect) should be placed in a within a function in the .cpp file. Call this function in the setup() of your final .ino file.

  2. The actual ISR should also be in your .cpp file, but it should be outside of any declarations.

  3. if you are using variables in the ISR you must declare these variables volatile and place them outside of any brackets in the .cpp file as well.

I know I shouldn’t use Serial.println in an ISR, but I don’t really know any way to debug such a thing an prove it works easily. So, if you are worried about locking up your unit when you try this, you will have to change the ISR routine to your liking.

Original .ino
This code represents a sketch of an ISR program that you would like to migrate to a working library

volatile int x = 0; //declare outside of any brackets in .cpp


//put all of this into a single function in .cpp and call it in setup();
void setup() {
  cli();
  TCCR1A = 0;
  TCCR1B = 0;
    
  OCR1A = 5192; //Output Control Register
  TCCR1B |= (1 << WGM12); //0100 CTC On
  TCCR1B |= (1 << CS10); 
  TCCR1B |= (1 << CS12);  //101
  //TCCR1B = 00001101
  TIMSK1 |= (1 << OCIE1A); //Compare Match Interupt Enable
  //TIMSK1 = 00000010
  sei();
  
  Serial.begin(9600);

}

//This bit will go into your .cpp file outside of all brackets
ISR(TIMER1_COMPA_vect)
{
      x++;
	  Serial.println(x);
}

void loop() {
  
}

Solution.cpp

#include "Arduino.h"
#include "ISR_Solution.h"

volatile int x = 0;

Solution::Solution(){
}


void Solution::CTC_Setup() 
{
  
  cli();
  TCCR1A = 0;
  TCCR1B = 0;
    
  OCR1A = 5192; //Output Control Register
  TCCR1B |= (1 << WGM12); //0100 CTC On
  TCCR1B |= (1 << CS10); 
  TCCR1B |= (1 << CS12);  //101
  //TCCR1B = 00001101
  TIMSK1 |= (1 << OCIE1A); //Compare Match Interupt Enable
  //TIMSK1 = 00000010
  sei();
  
  Serial.begin(9600);
}

ISR(TIMER1_COMPA_vect)
{
      x++;
	  Serial.println(x);
}

Solution.h

#ifndef ISR_Solution_h
#define ISR_Solution_h

#include "Arduino.h"

class Solution {
	public:
		Solution();
		
		void CTC_Setup();
		
};

#endif

SolutionExample.ino
This code represents a usage case of the above library. It does the same thing as the first code posted, only it uses the Library we just created.

#include "ISR_Solution.h"


Solution Solution;

void setup() {
  Solution.CTC_Setup();
}

void loop() {
  
  }

Thanks again and I will back, I’m sure! :slight_smile:

mwal

The limitation of that approach is that you can't access properties or methods of the class from within your ISR. Go back and look at MarkT's post, #3.

The main point is - ISR is a global function , and anything outside Setup and Loop is global to Setup and Loop , and being inside the "hidden" main() not really truly global.

(Confusing , I hope I said it right.)

I don't think that is correct. Everything does NOT havr to be inside main ( ), hidden or not.

In relation to the OP's proposed scheme in reply #8, if the Serial.begin( ) isn't directly related to the purpose of that setup_CTC( ) function, I would not put it there.

I would move Serial.begin( ) back to the setup( ) function in his .ino file.