Go Down

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

BulldogLowell

You have this function that gets called out of nowhere...
I wouldn't say "called out of nowhere" they do need to be passed as arguments, after all.

They really cannot be generalized to a single example.

KeithRB

I used callbacks when I was building a user interface. I let each class build its area of the interface with various input fields and then used callbacks to get the info back to the class while the user was interacting with the gui.

joeblogs

Keith,
did you do this by using interrupts, once the fields were full the isr fired giving you the info, callback got changed to suit next lot of info?

westfw

Callbacks are essentially a software equivalent of interrupts.
Very useful for "event driven programming" (and a lot of desktop programming these days is "event driven" - do this on a mouseClick, and this other thing on a keyboard character, and so on.  So the concept is very popular.)

The code that invoke the callback is not YOURS; it's off in a library or an operating system somewhere.  So you can't just put a call to the actual function there.  Also, you want it to be able to be changed; instead of having mouseClick code that has a giant if/else chain or case statement for all the different handling cases for a mouse event (text area, menu heading, sub-menu, radio-button...), you can just update the callback.

That doesn't mean that callbacks are needed every time you see them used, but they ARE very useful.


joeblogs

chhers westfw,
i can set up and use callbacks ok, but havent built a program that needed to use them widely yet, but can see them as being real handy (especially the c++ versions).

still cant see
Quote
The code that invoke the callback is not YOURS
might work in a practical example, i thought the callback was invoked by the fact that you had put the callback in the code.  but what the callback DOES depends on the function being assigned to the callback.

im moments away from going neck level, could someone maybe write some simple psuedo to demonstrate a real practical embedded example.
                        . .>
                     .
                   .
neck level . .

KeithRB

Keith,
did you do this by using interrupts, once the fields were full the isr fired giving you the info, callback got changed to suit next lot of info?
Nope, no interrupts, my gui would just call a function that my instrument class "registered" with it. westfw explained it well.

joeblogs

Hey Keith
and simple pseudo of what you just described

KeithRB

Class Instrument:

void init()
{
gui_register(instr_function);
}

void instr_function()
{
  do gui stuff
}

End Class

Class Gui:

void gui_register(func ptr)
{
// put func pointer into array
}

void do_gui()
{
 foreach (func ptr in func pointer array)
{
ptr() // call the function in class Instr
}

// do other gui stuff
}


BulldogLowell

im moments away from going neck level, could someone maybe write some simple psuedo to demonstrate a real practical embedded example.
MsTimer2 is a nice library that uses callbacks to defer a function:
Code: [Select]
#include <MsTimer2.h>

const byte buttonPin = 5;

void setup()
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop()
{
  if(buttonFivePressed())
  {
   flashLedThirteen();
  }
}

bool buttonFivePressed(void)
{
  static unsigned long lastMillis = 0;
  static byte lastPress = HIGH;
  byte currentPress = digitalRead(buttonPin);
  if(currentPress != lastPress)
  {
    if(millis() - lastMillis < 200) return false;
    lastPress = currentPress;
    if(currentPress == LOW)
    {
      Serial.println(" button press detected!");
      lastMillis = millis();
      return true;
    }
  }
  return false;
}

// done with a lambda
//void flashLedThirteen(void)
//{
//  digitalWrite(13, HIGH);
//  Serial.println("LED ON");
//  // sets a new timer callback function
//  MsTimer2::set(5000, [] {  // the square brackets define the start of the anonymous callback function which is executed after 5000 milliseconds in this example
//    digitalWrite(13, LOW);
//    Serial.println("Timer expired...");
//    Serial.println("LED OFF");
//    MsTimer2::stop();
//  }); // the curly brace defines the end of the anonymous callback function
//  MsTimer2::start();
//}

//or with a function...
void flashLedThirteen(void)
{
  digitalWrite(13, HIGH);
  Serial.println("LED ON");
  // sets a new timer callback function
  MsTimer2::set(5000, doSomething);
  MsTimer2::start();
}

void doSomething(void)
{
  digitalWrite(13, LOW);
  Serial.println("Timer expired...");
  Serial.println("LED OFF");
  MsTimer2::stop();
}


joeblogs

thanks all for your help, i understand just need time to use them to burn them into my brain.

Dont spose you want to talk about lambdas now :o

zhomeslice

#25
Jun 01, 2017, 01:59 am Last Edit: Jun 01, 2017, 02:51 am by Coding Badly
thanks all for your help, i understand just need time to use them to burn them into my brain.

Dont spose you want to talk about lambdas now :o
Check this post out lambdas https://forum.arduino.cc/index.php?topic=436961.0
HC

PaulMurrayCbr

I use callbacks when the code I am writing will need to do a thing, but I don't know what that thing might be - it's supplied later by some other coder. For instance, if I am writing a timer or a button debouce library.

An more modern alternative to callbacks is using pure virtual methods.

Code: [Select]

// My code

class ThingDoer {
public:
  void do_it_10_times() { for(int = 0; i<10; i++) do_it();}
protected:
  virtual void do_it() = 0;
}

// your code

class Flasher : public ThingDoer {
  protected:
  void do_it() {
    digitalWrite(13, HIGH);
    delay(250);
    digitalWrite(13, LOW);
    delay(250);
  }
} flasher;

void loop() {
  flasher.do_it_10_times();
}


Another even more modern alternative is closures.
http://paulmurraycbr.github.io/ArduinoTheOOWay.html

joeblogs

could someone please tell me how to set up the callback to accept an argument

this works
in class header
Code: [Select]

void (*interrupt_cb)() = NULL;


class function in cpp file
Code: [Select]

void DTC::Attach_Isr_Function(void (*isr)())
{
interrupt_cb = isr;
}


and in a timer handler
Code: [Select]

void TC0_Handler(void)
{
    TC_GetStatus(TC0, 0);
    Timers[0].interrupt_cb();
}



but i want to have the handler like this so i can store the register inside the callback
Code: [Select]

void TC0_Handler(void)
{
    Timers[0].interrupt_cb( TC_GetStatus(TC0, 0) );//TC_getstatus returns a uint32_t
}



ive tried setting it up like
class header
Code: [Select]

void (*interrupt_cb)(uint32_t reg) = NULL;


interrupt handler
Code: [Select]

void TC0_Handler(void)
{
Timers[0].interrupt_cb( TC_GetStatus(TC0, 0) );
}

but the compiler whinges at the class function and i cant figure out how to declare it
Code: [Select]

void DueTC::Attach_Isr_Function(void (*isr)())
{
interrupt_cb = isr;
}

error: invalid conversion from 'void (*)()' to 'void (*)(uint32_t) {aka void (*)(long unsigned int)}' [-fpermissive]

Coding Badly


Changes...

Code: [Select]

void TC0_Handler(uint32_t reg)


Code: [Select]

void DueTC::Attach_Isr_Function(void (*isr)(uint32_t reg), uint32_t reg_param)
{
  interrupt_cb = isr;
  interrupt_param = reg_param;
}


Call the callback...

Code: [Select]

  interrupt_cb( interrupt_param );



joeblogs

sorry still wont work

this is how i would like it to work, this would be a function to be set as a callback
Code: [Select]

void test_func(uint32_t reg)
{
        uint32_t status = reg;    //where reg is coming from TC_GetStatus(TC0, 0) in the handler

static int state, cnt;
state = !state;
digitalWrite(13, state);
cnt++;
if(cnt == 20)
{
cnt = 0;
//Timers[0].Attach_Isr_Function(test_func2);
}
}


void TC0_Handler (void) comes from a cortex.c file
void TC0_Handler  (void) __attribute__ ((weak, alias("__halt")));
am i able to change the void void (it didnt whinge when i changed it but i didnt think you could)

Go Up