Hello,

I have a problem with using IntervalTimer in a class. I know this problem is entirely based on my missing experince with C++, and I hope you can help me fix it.

I use a teensy3.6 to drive a piezo motor. The IntervalTimer sets a motor interval, which combined with a quadrature decoder counting the steps to go sets the speed. Using it in the main class worked fine. For a better overview when adding more features to the final product I want to put all the motor related stuff in a class and get it out of the main. But now the program gives me compiling issues using a non-static member function. I found some solution about using a forwarding static function but in my implementation it has undefined references. In my understanding the problem is, since the interrupt from the IntervalTimer could be fired when no instance of the motor subclass is made it does not know which function to call. There will always be only one object of the motor subclass, I read something about Singleton classes, but I don't know if that is the way to go.
Any help is highly appreciated! Does not need to the beautiful.

The main code:
Code:
#include "Tekceleo.h"



Tekceleo tekceleo(55000, 50000);
  
void setup() {
  tone(2, 2000, 200);

  Serial.begin(115200); 
  delay(1000);
 
  tone(2, 400, 200);
  delay(200);
  tone(2, 800, 200);
  delay(200);
  tone(2, 2000, 200);
  Serial.println("Start");

  tekceleo.start(50);

}

 
void loop() {

}

The header file of Tekceleo subclass
Code:
#ifndef TEKCELEO_H
#define TEKCELEO_H

#include <stdint.h>
#include "mk20dx128.h"
#include "core_pins.h"
#include "AD9837.h"
/*
* Tekceleo.h - Library for controlling the Tekceleo motor and running it for a given flowrate
*/

class Tekceleo {

	public:
		Tekceleo(unsigned long startfrq, unsigned long minfrq); //initialize, find initial frequency
		void start(float flowrate);
		void stop();



	private:

		static Tekceleo *instance;
		int cts; //cts for MOD, for motor interval
		int duration; //uS, for motor interval
		float flowrate; //
		unsigned long initialfrequency; //initial frequency, the motor turns
		unsigned long startfrequency; //starting frequency for finding initial frequency
		unsigned long minfrequency; //minimal frequency,
		unsigned long frequency; //momentary frequency

		AD9837 ad9837;

		float dutyCycle; //duty Cycle

		volatile bool motorTurning;
		volatile bool dutyCycleReached;

		void motorLoop();
		void dutyCycleLoop();

		static void motorLoopForward();
		static void dutyCycleLoopForward();

		IntervalTimer motorTimer;
		IntervalTimer dutyCycleTimer;

		int pin_motor_direction;
		int pin_test;
		int pin_PPen; //sets the push-pull converter (motorsignal)

		void findInitialFrequency(); //sets initialFrequency, do at initializing
		void quadratureDecoder();

		void set_timebase(float flw); //sets uS and cts for a given flowrate

		void ftm2_isr(void);

};

#endif
the cpp File of the Tekceleo class:
Code:
#include "Tekceleo.h"
#include <stdint.h>
#include "mk20dx128.h"
#include "core_pins.h"
#include "AD9837.h"
#include <cmath>

Tekceleo::Tekceleo(unsigned long startfrq, unsigned long minfreq) : ad9837(startfrq)
{
	instance = this;
	pin_motor_direction = 9;
	pin_test = 20;
	pin_PPen = 17;
	
    // TURN OFF PUSH PULL CONVERTER
	pinMode(pin_PPen,OUTPUT);
	digitalWrite(pin_PPen, LOW);

	pinMode(pin_test, OUTPUT);
	digitalWrite(pin_test, LOW);

  // SETUP MOTOR DIRECTION
	pinMode(pin_motor_direction,OUTPUT); // set TOGGLE to OUTPUT
	digitalWrite(pin_motor_direction, LOW);  // set high for one motor direction

	dutyCycle = 0.25;

	startfrequency = startfrq;
	minfrequency = minfreq;
	ad9837.setfrequency(startfrequency * 4);
	quadratureDecoder();

	findInitialFrequency();
}


void Tekceleo::set_timebase(float flw)
{
	float RADIUS_SYRINGE = 28.6/2;
	float P_SPINDLE = 1.0;
	float GEAR_FACTOR = 12.0/32.0;	
	float pi = 3.14159265359;	
	float VOLUME_PER_ROTATION = RADIUS_SYRINGE * RADIUS_SYRINGE * pi * P_SPINDLE * GEAR_FACTOR * 0.001;
	int ENCODER_EDGES_PER_ROTATION = 2000 * 4;
	float FLOW_PER_SECOND = flw / 3600;
	float ROTATIONS_PER_SECOND = FLOW_PER_SECOND / VOLUME_PER_ROTATION;
	double ENCODER_EDGES_PER_uSECOND = ROTATIONS_PER_SECOND * ENCODER_EDGES_PER_ROTATION / 1000000;

	bool notFound = true;
	int uS_to_GO = 100000; // minimal interval length 100mS
	double ENCODER_EDGES;
	while(notFound){
		ENCODER_EDGES = ENCODER_EDGES_PER_uSECOND * uS_to_GO;
		double MOD_EDGES = std::fmod(ENCODER_EDGES, 1);
		if(ENCODER_EDGES > 10.0 && MOD_EDGES < 0.001){
			duration = uS_to_GO;
			cts = (int) ENCODER_EDGES;
			notFound = false;
		}
		uS_to_GO += 1;
	}
}

void Tekceleo::stop() //stops the motor
{
	motorTimer.end();
	dutyCycleTimer.end();
	digitalWrite(pin_PPen, LOW);
}

void Tekceleo::quadratureDecoder() //setups the Quadrature Decoder, cts set to 10 for finding the initial frequency
{
	// FTM2 Pins
	// K20 pin 41,42
	// Bit 8-10 is Alt Assignment
	PORTB_PCR18 = 0x00000612;   //Alt6-QD_FTM2,FilterEnable,Pulldown
	PORTB_PCR19 = 0x00000612;   //Alt6-QD_FTM2,FilterEnable,Pulldown

	//Set FTMEN to be able to write registers
	FTM2_MODE=0x04;	    // Write protect disable - reset value
	FTM2_MODE=0x05;	    // Set FTM Enable

	// Set registers written in pins_teensy.c back to default
	FTM2_CNT = 0;
	FTM2_MOD = 0;
	FTM2_C0SC =0;
	FTM2_C1SC =0;
	FTM2_SC = 0x40;     //enable hardware trigger

	FTM2_FILTER=0x22;	// 2x4 clock filters on both channels
	FTM2_CNTIN=0;
	FTM2_MOD=10;	    // set counter to 10
	FTM2_CNT=0;	    	// Updates counter with CNTIN
	FTM2_QDCTRL=0b11000111;	    // Quadrature control
	//        Filter enabled, QUADEN set

	// Write Protect Enable
	FTM2_FMS=0x40;		// Write Protect, WPDIS=1

      
	NVIC_ENABLE_IRQ(IRQ_FTM2); 
}

void Tekceleo::ftm2_isr(void)
{
	if((FTM2_SC & FTM_SC_TOF) != 0){
		FTM2_SC &= ~FTM_SC_TOF;
	}
	digitalWrite(pin_PPen, LOW);
	motorTurning = true;

	digitalWrite(pin_test, LOW);
}

void Tekceleo::findInitialFrequency()
{
	frequency = startfrequency;
	motorTurning = false;
	while(!motorTurning){
		if(frequency < minfrequency){frequency = startfrequency;}
		frequency = frequency - 200;
		ad9837.setfrequency(frequency);
		delay(300);
	}
	digitalWrite(pin_PPen, LOW);
	frequency = frequency - 1000;
	initialfrequency = frequency;
	ad9837.setfrequency(frequency);
	tone(2, 400, 200);
	delay(200);
	tone(2, 800, 200);
	delay(200);
	tone(2, 2000, 200);
}

void Tekceleo::dutyCycleLoop()
{
	if(motorTurning){
		dutyCycleReached = true;
	}
	dutyCycleTimer.end();
}

void Tekceleo::motorLoop(){
	//falls Flanken NICHT erreicht!
	if(!motorTurning){
		Serial.println("Dreht nicht! Dreht zu langsam! Erreicht erforderliche Flanken nicht! Frequenz herabsetzen.");
		frequency -= 250;
		ad9837.setfrequency(frequency * 4);
	}
	//falls Duty Cycle nicht erreicht!
	else if(!dutyCycleReached){
		Serial.println("Dreht, aber Duty Cycle wurde nicht erreicht! Frequenz herabsetzen.");
		frequency -= 250;
		ad9837.setfrequency(frequency * 4);
	}
	//falls unter die Mindestfrequenz gerutscht - setze Duty Cycle rauf, nochmal von vorne!
	if(frequency <= minfrequency){
		Serial.println("Innerhalb des vorgegebenen Duty Cycle nicht möglich! Erlaube höheren Duty Cycle.");
		frequency = initialfrequency;
		dutyCycle += 0.1;
		ad9837.setfrequency(frequency * 4);
		if(dutyCycle > 0.95){
			while(1){
			tone(2, 4500, 100);
			delay(200);            
			}
		}
	}
	//ansonsten: "ganz normaler" Durchlauf, setze Variablen zurück, "starte" Motor erneut
	motorTurning = false;
	dutyCycleReached = false;
	dutyCycleTimer.begin(dutyCycleLoopForward, duration * dutyCycle);
	digitalWrite(pin_PPen, HIGH);
	digitalWrite(pin_test, HIGH);
}

void Tekceleo::start(float flowrate)
{
	set_timebase(flowrate);
	noInterrupts();
	FTM2_MOD = cts;
	FTM2_CNT = 0;
	FTM2_PWMLOAD = 0x200;
	interrupts();

	digitalWrite(pin_PPen, HIGH);
	motorTimer.begin(motorLoopForward, duration);
	dutyCycleTimer.begin(dutyCycleLoopForward, duration * dutyCycle);
}

void Tekceleo::motorLoopForward(){
	instance->motorLoop();
}

void Tekceleo::dutyCycleLoopForward(){
	instance->dutyCycleLoop();
}