Go Down

Topic: why use callbacks (Read 4820 times) previous topic - next topic

joeblogs

carry on some dumbass didnt set up the header file properly, just when i thought newbie mistakes had left the system
Code: [Select]

void Attach_Isr_Function(void (*isr)(uint32_t reg));//forgot the uint32_t reg

westfw

Code: [Select]
typedef void (*Callback_OneArg_t)(uint32_t reg);
is likely to make things easier (or... something like that, anyway.)

joeblogs

what would be the advantage of using typedef over just void in this situation

BulldogLowell

what would be the advantage of using typedef over just void in this situation
easier to create the function pointer and you wouldn't then mix up the function signature, like you did above:


Code: [Select]

typedef void (*Callback)(uint32_t arg);

Callback myCallback;
Callback myOtherCallback;

joeblogs

where abouts above buldog

BulldogLowell

Post 30 where you say:

Code: [Select]

;//forgot the uint32_t reg

Jiggy-Ninja

#36
Jun 02, 2017, 04:42 pm Last Edit: Jun 02, 2017, 04:43 pm by Jiggy-Ninja
In addition to all of the good discussion, there are a number of things I feel compelled to call out.
Callbacks are essentially a software equivalent of interrupts.
No they aren't. Callbacks can be used as part of providing an interface to ISR functions (like attachInterrupt), but you don't need callbacks to make an ISR, and callbacks have many more uses outside of ISRs. There is no inherent equivalence between callbacks and interrupts.

It's like saying "circles are essentially equivalent to cars" because wheels are round.

Quote
An more modern alternative to callbacks is using pure virtual methods.
Polymorphism is basically the same thing as callbacks, but covered in the OO gift wrapping of inheritance hierarchies.

There's nothing wrong with using abstract classes for callbacks since they can be much more sophisticated than a single callback and enforce type safety. However, of the Big 3 OOP concepts, polymorphism was definitely the one I needed to have explained to me the most times. I'm comfortable with it now, but it's definitely more than a lot of people can chew at once.
Hackaday: https://hackaday.io/MarkRD
Advanced C++ Techniques: https://forum.arduino.cc/index.php?topic=493075.0

BulldogLowell

...you don't need callbacks to make an ISR...
Isn't an ISR a callback?


How then would you do that (in C++/Arduino), for example? 

Jiggy-Ninja

#38
Jun 02, 2017, 08:57 pm Last Edit: Jun 02, 2017, 08:58 pm by Jiggy-Ninja
Isn't an ISR a callback?
No. An interrupt is the way most microprocessors allow asynchronous execution of code independent of the main thread in response to certain trigger conditions. The trigger causes the code to branch to a specific memory address, commonly called the interrupt vector.

Interrupt handlers vary in sophistication. Many mid-range PICs (like a PIC12F1612) have only one interrupt vector to service every interrupt in the microcontroller. You need to examine which interrupt flags are set to determine what action needs to be done.

Others (like the PIC18 series) have multiple vectors in a defined priority order, and let you select which interrupts are which priority. You still have the issue of multiple interrupts sharing the same vector.

AVRs have a separate interrupt vector for each individual interrupt, but the vector address is hardcoded and cannot be changed. The priority is also predefined and unchangeable.

Even higher on the scale of sophistication (PIC24) are vector tables, where the interrupt handler peripheral has registers that let you store a function address to use for each interrupt source. This implementation of an interrupt handler can properly be thought of as using callbacks, but the others listed above do not. If callback functionality is desired for them, it needs to be implemented manually by the programmer (like it is with attachInterrupt in Arduinos).

In case you hadn't noticed, I've recently been getting into PICs.
Quote
How then would you do that (in C++/Arduino), for example? 
As a matter of fact, I recently coded a very small and simple project for an ATtiny84 that uses exactly 2 interrupts (Pin Change 0 and ADC), and neither uses a callback function.
Code: [Select]
ISR(PCINT0_vect)
{
  pin_change_int_enable = 0; // diasble pin change interrupt;
}

Code: [Select]
ISR(ADC_vect)
{
  // Clear interrupt flag for Timer1 so it can trigger again.
  TIFR1 |= _BV(TOV1);
 
  static uint8_t index = 0;

  *(write_locations[index]) = ADC;
  ADMUX = adc_mux_values[index];

  index = 1-index;
}

The pin change interrupt is just being used to wake the controller up from Power Down sleep mode, and the ADC interrupt is automatically switching between reading two inputs (the internal reference for battery measurement, and an external potentiometer) and is also used to wake up the controller from Idle. The ADC is set to auto-trigger off Timer1.

No callbacks. They just do what I need them to do.
Hackaday: https://hackaday.io/MarkRD
Advanced C++ Techniques: https://forum.arduino.cc/index.php?topic=493075.0

BulldogLowell

No callbacks. They just do what I need them to do.
It's all just semantics...

Code: [Select]
ISR(ADC_vect)
{
  // blah...
}


that's a callback...

every function is at a "specific memory address"

Jiggy-Ninja

#40
Jun 02, 2017, 09:54 pm Last Edit: Jun 02, 2017, 09:55 pm by Jiggy-Ninja
It's all just semantics...
You say that like semantics are unimportant.

Quote
Code: [Select]
ISR(ADC_vect)
{
  // blah...
}


that's a callback...

every function is at a "specific memory address"

It's as much a callback as setup() and loop() are. Which is to say it's not a callback at all.

The key feature of a callback is that it is a function pointer (not a function, but a pointer to a function) whose value you can modify to point to different functions at run time, and that you can store and pass around as a variable parameter.

Let's pull a snippet out of WInterrupts.c in the Arduino core. A macro is used to consistently define the multiple interrupt handlers needed for attachInterrupt:
Code: [Select]
#define IMPLEMENT_ISR(vect, interrupt) \
  ISR(vect) { \
    intFunc[interrupt](); \
  }

#if defined(__AVR_ATmega32U4__)

IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1)
IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_2)
IMPLEMENT_ISR(INT3_vect, EXTERNAL_INT_3)
IMPLEMENT_ISR(INT6_vect, EXTERNAL_INT_4)

#elif defined(EICRA) && defined(EICRB)

IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_2)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_3)
IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_4)
IMPLEMENT_ISR(INT3_vect, EXTERNAL_INT_5)
IMPLEMENT_ISR(INT4_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT5_vect, EXTERNAL_INT_1)
IMPLEMENT_ISR(INT6_vect, EXTERNAL_INT_6)
IMPLEMENT_ISR(INT7_vect, EXTERNAL_INT_7)

#else

IMPLEMENT_ISR(INT0_vect, EXTERNAL_INT_0)
IMPLEMENT_ISR(INT1_vect, EXTERNAL_INT_1)

#if defined(EICRA) && defined(ISC20)
IMPLEMENT_ISR(INT2_vect, EXTERNAL_INT_2)
#endif

This snippet has callbacks in it, but it's not the ISR(vect) declarations. The ISRs access an array of callbacks (intFunc), but the ISRs themselves are not callbacks. They are just the functions that are automatically called when the corresponding interrupt is triggered.

Once more for emphasis: ISRs can use callbacks (and are often great places to do so), but an ISR is not in and of itself a callback.
Hackaday: https://hackaday.io/MarkRD
Advanced C++ Techniques: https://forum.arduino.cc/index.php?topic=493075.0

BulldogLowell

Once more for emphasis: ISRs can use callbacks (and are often great places to do so), but an ISR is not in and of itself a callback.
I will concede that at the bare-metal (by that I mean hardware interrupt handler) there are routines that are driven by interrupts that do such-and-such and this-and-that.  I would not refer to that as a callback, nor would I call it an ISR... I'd call that a plain old interrupt handler.

Once you are at the software level (where we are here) and we have an event (that may or may not be triggered by an interrupt) and that function is defined by the software and called by the software, we are in a territory referred to as callback functions even when triggered by the bare metal interface between the mcu and the software.

That said, ISR's or Interrupt Service Routines are blocks of code (software defined) so therefore:

Quote
So, why do witches burn?
Cuz they're made of... wood?
:-X





westfw

Quote
There is no inherent equivalence between callbacks and interrupts.
We may have to agree to disagree.  Both are mechanisms for execution of code out-of-sequence with the flow of the main program, so I see them as very similar.   Perhaps you're only considering a simple case ala single-threaded Arduino, where the only way you can get to a callback is to explicitly call the function that invokes the callback, while I'm thinking of a more general case where ISRs and/or the OS can cause a callback when the running thread is least expecting it.

The Arduino attachInterrupt function puts its own code at the ROM-based vector that you talked about, but then invokes a callback that is provided by the user (function pointer, stored in RAM.)  So essentially it implements a more refined/complex level of ISR even though the hardware is limited.  I think I would have preferred a weak symbol, and to have had SerialEvent be a callback rather than a weak symbol, but...


zhomeslice

We may have to agree to disagree.  Both are mechanisms for execution of code out-of-sequence with the flow of the main program, so I see them as very similar.   Perhaps you're only considering a simple case ala single-threaded Arduino, where the only way you can get to a callback is to explicitly call the function that invokes the callback, while I'm thinking of a more general case where ISRs and/or the OS can cause a callback when the running thread is least expecting it.

The Arduino attachInterrupt function puts its own code at the ROM-based vector that you talked about, but then invokes a callback that is provided by the user (function pointer, stored in RAM.)  So essentially it implements a more refined/complex level of ISR even though the hardware is limited.  I think I would have preferred a weak symbol, and to have had SerialEvent be a callback rather than a weak symbol, but...
I'm seeing your point about callbacks they are used all the time to give control to the programmer for such things as mouse clicks or key press. onclick ( callback function ) but because they can be used outside of the interrupt in the main() loop as a pointer to a function we have created,  makes me shift if favor @BullDogLowell and @Jiggy-Ninja point that they different than and not directly related to ISRs and interrupts although callbacks are commonly used by them:
Quote
Once more for emphasis: ISRs can use callbacks (and are often great places to do so), but an ISR is not in and of itself a callback.
Help me understand your view better @westfw 
This discussion has helped me grasp this concept. Thanks Everyone :)
Z
HC

Jiggy-Ninja

#44
Jun 03, 2017, 08:14 pm Last Edit: Jun 03, 2017, 08:14 pm by Jiggy-Ninja
Both are mechanisms for execution of code out-of-sequence with the flow of the main program, so I see them as very similar.
This is where you are wrong. What you have described is interrupts, not callbacks.
Quote
Perhaps you're only considering a simple case ala single-threaded Arduino, where the only way you can get to a callback is to explicitly call the function that invokes the callback
No, I am not. I am fully aware of how interrupts work and how they are useful.
Quote
while I'm thinking of a more general case where ISRs and/or the OS can cause a callback when the running thread is least expecting it.
That is not a "more general case", it is a different case.
Quote
The Arduino attachInterrupt function puts its own code at the ROM-based vector that you talked about, but then invokes a callback that is provided by the user (function pointer, stored in RAM.)  So essentially it implements a more refined/complex level of ISR even though the hardware is limited.
This is exactly what I have said before. attachInterrupt uses callbacks to increase the functionality of the interrupt system, but you can't take that to claim that interrupts and callbacks are the same thing. They are not. It would be like claiming that cheese, tomato sauce, and bread are all the same thing because they are the main ingredients of pizza. They may be used together in pizza, but they are also useful in other foods (tomato sauce with pasta, cheese with tacos, etc). They are not themselves pizza.

attachInterrupt is interrupts + callbacks. They are not the same thing, they are two different things that are being used together.

Here is the way callbacks are defined on Wikipedia:
Quote
In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. This execution may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback. In all cases, the intention is to specify a function or subroutine as an entity[clarification needed] that is, depending on the language, more or less similar to a variable.

Programming languages support callbacks in different ways, often implementing them with subroutines, lambda expressions, blocks, or function pointers.
This matches my understanding of callbacks. Note that the second paragraph mentions function pointers and lambda expressions, but not interrupts. Callbacks can be used in ISRs. This is true. It is also true that you can write ISRs without callbacks, like I showed in one of my previous posts. You can also use callbacks outside of ISRs. Callbacks are not ISRs.

I can give another example so that this is a little less abstract and a little more concrete. The Standard Template Library is chock full of functions that use callbacks that have nothing to do with interrupts. Here is just one example:
Code: [Select]
template<class InputIterator, class UnaryPredicate>
  bool all_of (InputIterator first, InputIterator last, UnaryPredicate pred)
{
  while (first!=last) {
    if (!pred(*first)) return false;
    ++first;
  }
  return true;
}

The all_of function checks all the elements of an array (bounded by the first and last pointers) and evaluates them with the pred function. It returns true only if pred returns true for all of the elements. pred is a callback function so that you can implement whatever test criteria you want and pass it into the function, it's not hardcoded in.

Take this sketch:
Code: [Select]
template<class InputIterator, class UnaryPredicate> bool all_of (InputIterator first, InputIterator last, UnaryPredicate pred)
{
  while (first!=last) {
    if (!pred(*first)) return false;
    ++first;
  }
  return true;
}

#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))

bool isEven( int16_t val )
{
  return (val%2)==0;
}

int16_t allEven[] = {0, 6, 8, 12, 4, 16, 100 };
int16_t oneOdd[] = {0, 5, 6, 2, 8, 22, 100, 44 };

void setup() {
  // put your setup code here, to run once:
  Serial.begin(250000);

  Serial.print("allEven: ");
  Serial.println(all_of(allEven, allEven+ARRAY_SIZE(allEven), isEven) ? "True" : "False");
  Serial.println();
 
  Serial.print("oneOdd: ");
  Serial.println( all_of(oneOdd, oneOdd+ARRAY_SIZE(oneOdd), isEven) ? "True" : "False");
  Serial.println();
}

void loop() {
  // put your main code here, to run repeatedly:

}

It runs the all_of function on the two different arrays, testing their elements with the isEven function. The first array is composed of all even numbers, and the second is composed of mostly even numbers with one odd number. The result is printed to Serial Monitor.

Using a callback as a function argument means the behavior can be modified just be changing the function I pass into it. I could change the test to be for odd numbers, for numbers within a certain range, for strings of a certain length, or anything else that I can write a function for.

TL;DR: Callbacks can be used in ISRs to allow their behavior to be changed at run time. Callbacks can also be used outside of ISRs to increase the flexibility of normal functions. Callbacks and ISRs are different things. If my pizza ingredients analogy doesn't help, frankly I don't know a better one.
Hackaday: https://hackaday.io/MarkRD
Advanced C++ Techniques: https://forum.arduino.cc/index.php?topic=493075.0

Go Up