ISR and classes

Hello folks,

I am trying to figure out how to use class member fields with ISRs.

Came across some code:

extern "C"
void com_recv_isr() __attribute__((interrupt));

class communication
{
	public:
		communication() : recv_buf(0U), has_recv(false) { }
		~communication() { }
			
		bool send_byte(const std::uint8_t by) const
		{
			*reinterpret_cast<volatile std::uint8_t*>(tbuf)
			= by;
		}
		bool recv_ready() const { return has_recv; }
		std::uint8_t recv_byte()
		{
			if(has_recv)
			{
				has_recv = false;
				return recv_buf;
			}
			return 0U;
		}
		private:
			static constexpr std::uint8_t tbuf = 0xAAU;
			static constexpr std::uint8_t rbuf = 0xAEU;
			std::uint8_t recv_buf;
			bool has_recv;
			communication(const communication&) = delete;
			const communication& operator=(const communication&)
			= delete;
		friend
			void com_recv_isr() __attribute__((interrupt));
};

I don't understand this part

extern "C"
void com_recv_isr() __attribute__((interrupt));

This function is supposed to be called when interrupt occurs, but how does it know at what location should it jump, when it doesn't receive any parameter such as address for it's jump (vector address).
Also I really don't understand this "extern" word.

Thank you.

Here's my code for interrupts. I have simplified it as much as possible and still do what I need.

Now this uses Lambda functions (anonymous functions). My apologies if this is confusing.

But it uses a class and interrupts as you are interested in.
This code below handles encoders, Ping Sensors Push Button Switches, RC radio input. This plus any addition on any pin including analog.

#include "Interrupts.h"
InterruptsClass Interrupt;
volatile long EncoderCounter = -2;
float microsecondsToInches(long microseconds)
{
  // According to Parallax's datasheet for the PING))), there are
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
  // second).  This gives the distance travelled by the ping, outbound
  // and return, so we divide by 2 to get the distance of the obstacle.
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return (float) microseconds / 74 / 2;
}

float microsecondsToCentimeters(long microseconds)
{
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.
  // The ping travels out and back, so to find the distance of the
  // object we take half of the distance travelled.
  return (float)microseconds / 29 / 2;
}

void setup() {
  Serial.begin(115200); //115200
  // put your setup code here, to run once:
  pinMode(7,OUTPUT);
  Interrupt.onInterrupt([ = ](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
    sei(); // re enable other interrupts at this point,
    Interrupt.PinCallBack(Time, PinsChanged, Pins);
  });
  Interrupt .onPin(4, INPUT_PULLUP, [ = ](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
              if (Interrupt.CheckPin(4)) { // rising
                Serial.println(" Pin 4 Rising \t");
              } else { // Falling
                Serial.println(" Pin 4 Falling \t");
              }
          
            }).onPin(5, INPUT, [ = ](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
              uint16_t RCTime =  Interrupt.RCRemote(5, Time, Pins, true);
              if (RCTime) {
                Serial.print(" RC Time:");
                Serial.print(RCTime);
              } 
            }).onPin(8, INPUT, [ = ](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
              unsigned int PingTime = Interrupt.Ping(8, Time, Pins);
              if (PingTime) {
                Serial.print("Ping \t");
                Serial.print(microsecondsToCentimeters(PingTime));
                Serial.println("cm");
              }
            }).onPin(9, INPUT_PULLUP, [ = ](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
              Serial.print("Switch \t");
              Serial.println(Interrupt.Switch(9, Time, 1000, false));
          
            }).onPin(10, INPUT_PULLUP, [ = ](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
              Serial.print("Switch \t");
              Serial.println(Interrupt.Switch(10, Time, 1000, false));
          
            }).onPin(11, INPUT_PULLUP, [ = ](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
              Serial.print("Switch \t");
              Serial.println(Interrupt.Switch(11, Time, 1000, false));
          
            }).onPin(12, INPUT, [ = ](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
              EncoderCounter += Interrupt.Encoder(12, 13, Pins, true);
              Serial.print("Count ");
              Serial.print(EncoderCounter);
              Serial.print("\t Encoder \t");
              Serial.println(Interrupt.Encoder(12, 13, Pins, true));
          
            }).onPin(13, INPUT, [ = ](uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
              EncoderCounter -= Interrupt.Encoder(12, 13, Pins, true);
              Serial.print("Count ");
              Serial.print(EncoderCounter);
              Serial.print("\t Encoder \t");
              Serial.println(-1 * Interrupt.Encoder(12, 13, Pins, true));
            });
}

void loop() {
  // put your main code here, to run repeatedly:
  int t = 100;
    static unsigned long _ETimer;
  if ( millis() - _ETimer >= (t)) {
    _ETimer += (t);

      digitalWrite(7, LOW);
      delayMicroseconds(1);
     // digitalWrite(7, HIGH); // Trigger another pulse
      delayMicroseconds(5);
      digitalWrite(7, LOW);
        }
}

Interrupts.cpp (5.93 KB)

Interrupts.h (5.3 KB)

Do you have embedded C example,
since I write most of my code for Arduino in C, with some arduino functions.

This is my code

#ifndef F_CPU
#define F_CPU 1000000UL
#endif


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define TRIGGER_DDR		DDRA
#define ECHO_DDR		DDRD
#define TRIGGER_PORT	PORTA
#define TRIGGER			PINA6
#define ECHO			PIND6

extern "C" void TIMER1_COMPA_vect(void) __attribute__((interrupt));
extern "C" void TIMER1_CAPT_vect(void) __attribute__((interrupt));

class HCSR04
{
	public:
		HCSR04(const unsigned long);
	
	public:
	
		static volatile unsigned char triggerSent;
		static volatile unsigned int risingEdge;
		static volatile unsigned int fallingEdge;
		static volatile uint8_t	distance;
		static unsigned char usPerTick;	
		
		
		void TIMER1_init(void);
		
	private:
		friend void TIMER1_COMPA_vect(void);
		friend void TIMER1_CAPT_vect(void);
		unsigned char getDistance(void);
};

HCSR04::HCSR04(const unsigned long FCPU)
{
	if (FCPU == 1000000UL)
		usPerTick = 8;
		 
	ECHO_DDR &= ~(1 << ECHO);
	TRIGGER_DDR |= (1 << TRIGGER);
	TIMER1_init();
}

void HCSR04::TIMER1_init()
{
	TCCR1B |= (1 << WGM12) | (1 << ICES1);					// CTC mode
	//Input Capture Edge Select = Rising edge;
	TCCR1B |= (1 << ICNC1);									//Input Capture Noise Canceler
	TCCR1B |= (1 << CS11);									//Prescaler = 8
	TIMSK1 |= (1 << ICIE1) | (1 << OCIE1A);					//Input Capture Interrupt Enable;
	//Output Compare A Match Interrupt Enable

	OCR1A = 8749;											//Outpur Compare Register for Timer interrupt occuring every 70ms
	TCNT1 = 0;
		
	usPerTick = 8;											// 8 us per 1 CPU tick for XTAL = 1MHz and prescaler = 8
	sei();	
}

uint8_t HCSR04::getDistance(void)
{
	return distance;
}


ISR(TIMER1_CAPT_vect)
{
	if (TCCR1B & (1 << ICES1))
	{
		HCSR04::risingEdge = ICR1;
		TCCR1B &= ~(1 << ICES1);
	}
	else
	{
		HCSR04::fallingEdge = ICR1;
		TCCR1B |= (1 << ICES1);
		HCSR04::distance = (((unsigned int)HCSR04::fallingEdge - (unsigned int)HCSR04::risingEdge) * HCSR04::usPerTick ) / 58;
		HCSR04::triggerSent = 0;
		TCNT1 = 0;
	}
	
}


ISR(TIMER1_COMPA_vect)
{
	TRIGGER_PORT |= (1 << TRIGGER);
	_delay_us(10);
	TRIGGER_PORT &= ~(1 << TRIGGER);
	HCSR04::triggerSent = 1;
}




int main(void)
{
    HCSR04 hcsr04(F_CPU);
	Serial.begin(9600);
    while (1) 
    {
		Serial.println(hcsr04.getDistance());
    }
}

This is the error I get

https://postimg.org/image/9kxti5ef3/cdf75dad/

I would like if someone could explain it to me how do I implement ISRs with C++, I am not a C++ expert and I rarely use it. Primarly C and java.

SimpleThings:
Do you have embedded C example,
since I write most of my code for Arduino in C, with some arduino functions.

I would like if someone could explain it to me how do I implement ISRs with C++, I am not a C++ expert and I rarely use it. Primarly C and java.

I simplified it more Added lots of notes and placed everything into a sketch for you it is set to be with a ping sensor hcsro04
trigger is on pin 7
echo is on pin 8

The code is a single sketch. I removed all Lambda other confusing parts.
It does use Callbacks just like attachInterrupt() does only difference is that I pass several variables to the functions

static void nothing(void) {};
typedef void (*voidFuncPtr)(uint32_t, uint32_t, uint32_t); // Create a type to point to a funciton.
typedef void (*voidFuncTimerPtr)();// Create a type to point to a funciton.
volatile voidFuncPtr Int_CB = nothing;

union Mask {
  volatile uint32_t All;
  volatile uint8_t  Port[4];
};
Mask _Pins;

volatile uint32_t _PinMask; //volatile uint8_t  rcPinMask[3];          //Pin Mask for active RC Pins
volatile uint32_t _PCintLast; //volatile uint8_t  PCintLast[3];         // looking fo pin changes using bianary

/************ static functions common to all instances ***********************/
// port change Interrupt

void ISR_Exe(void);
ISR(PCINT2_vect) { //this ISR pins 0-7
  ISR_Exe();
}
ISR(PCINT1_vect) { //this ISR s A0~A5
  ISR_Exe();
}
ISR(PCINT0_vect) { //this ISR is common to every receiver channel, it is call everytime a change state occurs on a pins 8-13 only need 9-11
  ISR_Exe();
}
void ISR_Exe() {
  _Pins.Port[0] = PIND; // 0-7
  _Pins.Port[1] = PINB; // 8-13
  _Pins.Port[2] = PINC; // A0-A6
  uint32_t _cTime = micros();
  _Pins.Port[3] = 1;    // Placeholder
  uint32_t _Changed = _Pins.All ^ _PCintLast;// doing a ^ between the current interruption and the last one indicates wich pin changed
  uint32_t _Masked = _Changed & _PinMask;// Has one of the pins we need changed
  if (!_Masked)return;                   //No Data move on
  if (Int_CB) {
    Int_CB(_cTime, _Masked, _Pins.All); // Callback Function
  }
  _PCintLast = _Pins.All;          // we memorize the current state of all PINs in group
}

class InterruptsClass {
  public:
    //   Interrupts();
    uint8_t  BitNumber[20] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21}; // Key = Pin Val = Position List of pins that could be used as ping inputs:
    static void nothing(void) {};
    typedef void (*voidFuncPtr)(uint32_t, uint32_t, uint32_t); // Create a type to point to a funciton.
    voidFuncPtr  PinCB[20] = {nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing}; // Key = Pin Val = Position List of pins that could be used as ping inputs:
    void AddPin(uint8_t pin, uint8_t InputType = INPUT);
    void onInterrupt(void (*CB)(uint32_t, uint32_t, uint32_t));
    void onPin(int Pin, int InputType, void (*CB)(uint32_t, uint32_t, uint32_t));
    void PinCallBack(uint32_t Time, uint32_t PinsChanged, uint32_t Pins);
    bool CheckPin(uint8_t Pin);
    volatile uint32_t EdgeTime[20]; // Time Storage
    // Ping
    volatile int16_t Ping(uint8_t Pin, uint32_t cTime, uint32_t Pins);
};


/****************** end of static functions ******************************/
// Install Pin change interrupt for a pin, can be called multiple times

void InterruptsClass::AddPin(uint8_t Pin, uint8_t InputType) {
  pinMode(Pin, InputType);// enable interrupt for pin...
  if (bitRead(_PinMask, Pin))
    bitWrite(_PinMask, BitNumber[Pin], 1);
  *digitalPinToPCMSK(Pin) |= bit (digitalPinToPCMSKbit(Pin));  // enable pin
  PCIFR  |= bit (digitalPinToPCICRbit(Pin)); // clear any outstanding interrupt
  PCICR  |= bit (digitalPinToPCICRbit(Pin)); // enable interrupt for the group
}

void InterruptsClass::onInterrupt(void (*CB)(uint32_t, uint32_t, uint32_t)) {
  Int_CB = CB;
}

void InterruptsClass::onPin(int Pin, int InputType, void (*CB)(uint32_t, uint32_t, uint32_t)) {
  AddPin( Pin,  InputType);
  PinCB[Pin] = CB;
}

void InterruptsClass::PinCallBack(uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
  for (byte Pin = 0; Pin < 20; Pin++) {
    if (bitRead(PinsChanged, Pin)) {
      if (PinCB[Pin]) {
        PinCB[Pin](Time, PinsChanged, Pins);
      }
    }
  }
}

bool InterruptsClass::CheckPin(uint8_t Pin) {
  return (bitRead(_Pins.All, Pin));
}


volatile int16_t InterruptsClass::Ping(uint8_t Pin, uint32_t cTime, uint32_t Pins) {

  if (CheckPin(Pin)) {
    EdgeTime[Pin] = cTime; //Pulse went HIGH store the start time
    return (0);
  } else {                         // Pulse Went low calculate the duratoin
    uint16_t dTime = cTime - EdgeTime[Pin]; // Calculate the change in time
    return (dTime);    // Lets send any duration up to 65535 micro seconds
  }
}




InterruptsClass Interrupt;


float microsecondsToInches(long microseconds)
{
  // According to Parallax's datasheet for the PING))), there are
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
  // second).  This gives the distance travelled by the ping, outbound
  // and return, so we divide by 2 to get the distance of the obstacle.
  // See: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
  return (float) microseconds / 74 / 2;
}

float microsecondsToCentimeters(long microseconds)
{
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.
  // The ping travels out and back, so to find the distance of the
  // object we take half of the distance travelled.
  return (float)microseconds / 29 / 2;
}

void AnyInterruptCallbackFunction(uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
  sei(); // re enable other interrupts at this point, Makes it so we can print from interrupts if we don't go crazy and call them too much!!!!
  Interrupt.PinCallBack(Time, PinsChanged, Pins); //Something triggered an interrupt lets see what it was
}

void UltrasonicPingCallback(uint32_t Time, uint32_t PinsChanged, uint32_t Pins) { //connect the echo pin to pin 8 and pin 7 will be the trigger
  unsigned int PingTime = Interrupt.Ping(8, Time, Pins);
  if (PingTime) {
    Serial.print("Ping \t");
    Serial.print(microsecondsToCentimeters(PingTime));
    Serial.println("cm");
  }
}
/*
  void UltrasonicPingCallbackPin9(uint32_t Time, uint32_t PinsChanged, uint32_t Pins){ //connect the echo pin to pin 8 and pin 7 will be the trigger
      unsigned int PingTime = Interrupt.Ping(9, Time, Pins);
    if (PingTime) {
      Serial.print("Ping \t");
      Serial.print(microsecondsToCentimeters(PingTime));
      Serial.println("cm");
    }
  }
*/
void setup() {
  Serial.begin(115200); //115200
  pinMode(7, OUTPUT); // for ping trigger
  Interrupt.onInterrupt(AnyInterruptCallbackFunction); // basic trigger for all other functions it calls the function AnyInterruptCallBackFunction on all interrupts
  Interrupt.onPin(8, INPUT, UltrasonicPingCallback); // Setup interrupt on pin 8, it is a standard INPUT rathere than one with pullup (INPUT_PULLUP) and wee are calling this function each time the interrupt is triggered: UltrasonicPingCallback

  // Add as many pins as you would like just make a function to use the same pin
  // Interrupt.onPin(9, INPUT, UltrasonicPingCallbackPin9); // Setup interrupt on pin 9, it is a standard INPUT rathere than one with pullup (INPUT_PULLUP) and wee are calling this function each time the interrupt is triggered: UltrasonicPingCallbackPin9
}

void loop() {
  // put your main code here, to run repeatedly:
  int t = 100;
  static unsigned long _ETimer;
  if ( millis() - _ETimer >= (t)) {
    _ETimer += (t);


    digitalWrite(7, LOW);
    delayMicroseconds(1);
    // digitalWrite(7, HIGH); // Trigger another pulse for ping sensor trigger on pin 7
    delayMicroseconds(5);
    digitalWrite(7, LOW);
  }
}

Z

I managed to compile the code using static class pointer and using interrupt handlers as friend functions.
I didn't even know about the AVR feature of pin change interrupts, I thought only Input capture pins could do it.

Also I have to ask why don't you use input capture pin like I did, isn't that much easier plus it has option for noise cancelling.

Here is my code:

#ifndef F_CPU
#define F_CPU 1000000UL
#endif


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include "uart.h"

#define TRIGGER_DDR			DDRA
#define ECHO_DDR			DDRD
#define TRIGGER_PORT		PORTA
#define TRIGGER				PINA6
#define ECHO				PIND6

#define UART_BAUD_RATE		4800

extern "C" void TIMER1_COMPA_vect(void) __attribute__((interrupt));
extern "C" void TIMER1_CAPT_vect(void) __attribute__((interrupt));

class HCSR04
{
	public:
		HCSR04(const unsigned long);
		static HCSR04* HCSR04_pointer;  
		
	public:
		 volatile unsigned char triggerSent;
		 volatile unsigned int risingEdge;
		 volatile unsigned int fallingEdge;
		 volatile unsigned int	distance;
		 unsigned char usPerTick;	
	
	public:
		friend void TIMER1_COMPA_vect(void);
		friend void TIMER1_CAPT_vect(void);
		void TIMER1_init(void);
		inline unsigned int getDistance(void);
};

HCSR04::HCSR04(const unsigned long FCPU)
{
	if (FCPU == 1000000UL)
		usPerTick = 8;
		 
	ECHO_DDR &= ~(1 << ECHO);
	TRIGGER_DDR |= (1 << TRIGGER);
	TIMER1_init();
}

void HCSR04::TIMER1_init()
{
	TCCR1B |= (1 << WGM12) | (1 << ICES1);					// CTC mode
	//Input Capture Edge Select = Rising edge;
	TCCR1B |= (1 << ICNC1);									//Input Capture Noise Canceler
	TCCR1B |= (1 << CS11);									//Prescaler = 8
	TIMSK1 |= (1 << ICIE1) | (1 << OCIE1A);					//Input Capture Interrupt Enable;
	//Output Compare A Match Interrupt Enable

	OCR1A = 8749;											//Outpur Compare Register for Timer interrupt occuring every 70ms
	TCNT1 = 0;
		
	HCSR04::usPerTick = 8;											// 8 us per 1 CPU tick for XTAL = 1MHz and prescaler = 8
	sei();	
}

inline unsigned int HCSR04::getDistance(void)
{
	return distance;
}

HCSR04* HCSR04::HCSR04_pointer;

ISR(TIMER1_CAPT_vect)
{
	if (TCCR1B & (1 << ICES1))
	{
		HCSR04::HCSR04_pointer->risingEdge = ICR1;
		TCCR1B &= ~(1 << ICES1);
	}
	else
	{
		HCSR04::HCSR04_pointer->fallingEdge = ICR1;
		TCCR1B |= (1 << ICES1);
		HCSR04::HCSR04_pointer->distance = (((unsigned int)HCSR04::HCSR04_pointer->fallingEdge - (unsigned int)HCSR04::HCSR04_pointer->risingEdge) * HCSR04::HCSR04_pointer->usPerTick ) / 58;
		HCSR04::HCSR04_pointer->triggerSent = 0;
		TCNT1 = 0;
	}
	
}


ISR(TIMER1_COMPA_vect)
{
	TRIGGER_PORT |= (1 << TRIGGER);
	_delay_us(10);
	TRIGGER_PORT &= ~(1 << TRIGGER);
	HCSR04::HCSR04_pointer->triggerSent = 1;
}




int main(void)
{
    HCSR04 hcsr04(F_CPU);
	//Serial.begin(9600);
	uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
	unsigned char buffer[7];
		
	sei();
		
		
	while (1)
	{
		
		itoa(hcsr04.getDistance(), buffer, 10);
		uart_puts(buffer);
		uart_puts("\n\r");
		_delay_ms(100);
	}
}


	
[\code]

SimpleThings:
I managed to compile the code using static class pointer and using interrupt handlers as friend functions.
I didn't even know about the AVR feature of pin change interrupts, I thought only Input capture pins could do it.

Also I have to ask why don't you use input capture pin like I did, isn't that much easier plus it has option for noise cancelling.

Thanks for sharing your code :slight_smile:
I'm not sure exactly what you mean by input capture pin If you could show me an example I would be grateful.
How I capture the input pins and the time of the interrupt is as follows:

// This union stores the states of all the pins at the time of the interrupt
union Mask{
	volatile uint32_t All;
	volatile uint8_t  Port[4];
};
Mask _Pins;

void ISR_Exe(){
	_Pins.Port[0] = PIND; // 0-7  Stores pins 0~7
	_Pins.Port[1] = PINB; // 8-13 Stores pins 8-13
	_Pins.Port[2] = PINC; // A0-A7 Stores pins 14-20 (Some Uno's have 8 analog pins)
	uint32_t _cTime = micros();   // Store the time of the interrupt
//...
}

Now that I have all the information I need for other calculations including ones that require the state of other pins and I can make the calculations at a later time allowing me to release the interrupts
The interrupt is still in play so nothing is updating until I release it.

I've written explanation and it didnt post...
It took me more than 1 hour to write it with whole explanation and now it didn't post.
Could you send me a pm, so I can explain it to you.