8 years, 8 months ago.

How can I refactor my static logic into a generic class that implements callback functions

Hi there, I've written a program to call a function based on the duration of a button press. Now I'd like to wrap this into a class and make it more generic so I could use it in a similar way

myduration buttonDuration();
myduration.add(after1, 1);
myduration.add(after3, 3);
myduration.add(after8, 8);

I've posted a question on stackexchange as I though this would be a more C++ generic question but the advise I got make use of STL classes such as iostream or maps which can't or shouldn't be used on mbed.

Any hint would be of great help!

Thanks, Jens

My program looks like this:

#include "mbed.h"
 
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);

Serial pc(USBTX, USBRX);
Ticker ticker;
 
InterruptIn pb(p17);

void state1(void){
 led2 = !led2;
 }
 
 void state2(void){
 led3 = !led3;
 }
 
 void state3(void){
 led4 = !led4;
 }
 
 
// Global count variable
int volatile counter = 0;
int volatile STATE = 0;

void countCallback(void) {
    counter = counter + 1;
}
void stateEval(void) {
    if(counter >=1 && counter <=3){
        STATE = 1; 
        state1();
        }
    if (counter >= 4 && counter <=7){
        STATE = 2;
        state2();
        }
    if (counter >=8 && counter <=10){ö
        STATE = 3;
        state3();
        }
    pc.printf("STATE %d \n", STATE);
}
 
// pb Interrupt routine - is interrupt activated by a falling edge of pb input
void pb_hit_interrupt (void) {
    ticker.attach(countCallback, 1);
    counter = 0;         
}

void pb_release_interrupt (void) {
    ticker.detach();
    stateEval();
    counter = 0;
    STATE = 0;
   // pc.printf("Counter: %d \n", counter);
}

int main() {
    // Use internal pullup for pushbutton
    pb.mode(PullUp);
    // Delay for initial pullup to take effect
    wait(.01);
    // Attach the address of the interrupt handler routine for pushbutton
    pb.rise(&pb_release_interrupt);
    pb.fall(&pb_hit_interrupt);
    // Blink led1 in main routine forever while responding to pb changes
    // via interrupts that activate pb_hit_interrupt routine
    while (1) {
        led1 = !led1;
        wait(.5);
    }
}

This is the ButtonPressDuration Library so far. For some reason the "press()" and "release()" functions attached to ButtonPin.raise and fall are never executed.

#include "mbed.h"

Serial pc(USBTX, USBRX);

struct action
{
    int counter_limit;
    void (*transition)(void);
};

struct action_list
{
    size_t size;
    const action *table;
};

class ButtonHandler {
public:

    ButtonHandler(std::size_t table_size, const action* begin, PinName pin, float seconds ): buttonPin(pin) {
        mTable.size = table_size;
        mTable.table = begin;
       // buttonPin(pin);
        intervalInSeconds = seconds;
        pc.printf("class initialized \n");
    }

    void react(int counter) const {
        const action *a = mTable.table;
        for (std::size_t i=mTable.size; i; --i, ++a) {
            if (counter < a->counter_limit) {
                a->transition();
                return;
            }
        }
    }
   void enable() {
       buttonPin.mode(PullDown);
       wait(0.01);
       buttonPin.rise(this, &ButtonHandler::release);
       buttonPin.fall(this, &ButtonHandler::press);
       buttonPin.enable_irq();
        pc.printf("enable() function executed\n");
    }
    void disable() {
        buttonPin.disable_irq();
        ticker.detach();
    }
    virtual ~ButtonHandler() {
        disable();
    }
protected:
    
    void press() {
        pc.printf("button 1 pressed\n");
        counter = 0;
        ticker.attach(this, &ButtonHandler::secondsCount, intervalInSeconds);
        
    }
    void secondsCount() {
        ++counter;
    }
    void release() {
        ticker.detach();
        react(counter);
        counter = 0;
    }
private:
    volatile unsigned counter;
    InterruptIn buttonPin;
    float intervalInSeconds;
    action_list mTable;
    Ticker ticker;
};

void state1() {
    //std::cout << __func__;
    pc.printf("state1");
}
void state4() {
    //std::cout << __func__;
    pc.printf("state4");
}
void state8() {
    //std::cout << __func__;
    pc.printf("state8");
}


const action action_table[] =
{
    { 4, state1 },
    { 8, state4 },
    { 11, state8 },
};

int main()
{
    ButtonHandler green(
            sizeof(action_table)/sizeof(action_table[0]),
            action_table,
            p17,
            1
    );
    green.enable();

    while(1){
        printf("alive\n");
        wait(5);
     }
}

Ok, problem is solved. I've made a library out of this available from here: https://developer.mbed.org/users/jensstruemper/code/PushDuration/

posted by Jens Strümper 28 May 2016

1 Answer

8 years, 8 months ago.

A relative simple library which uses callbacks is for example: https://developer.mbed.org/users/Sissors/code/WakeUp/ (the attaching/adding is done in the .h file, the .cpp files call the actual functions, the LPC812 is a quite simple one to see what it does).

Then your only other job is to add the InterruptIn to that library.

Accepted Answer

Thanks Erik! After reading your example library I understand how to implement the callback part but I struggle with the routine to map callback functions to the actual timer values. Since "map" does not exist on med and I can't do something like:

using state_caller = std::map<int , std::function<void()>>;

Having a 2 dimensional array with different datatypes doesn't seem to work either. Any idea?

Cheers, Jens

posted by Jens Strümper 18 Mar 2016

Since it seems to be a standard C++ function (and not only for latest C++) I would expect that std::map can work.

You can make a 2D array based on pointers, or 2 1D arrays (easier), one with time values, other one with FunctionPointers. Downside is you need to know beforehand how many you can add to your list. Otherwise you can for example use a LinkedList (there is a good library for that floating around), but I would prefer to just make (semi) fixed number of callbacks you can attach.

posted by Erik - 18 Mar 2016

With quite some help from stackreview I got the library almost ready. I still have problems with the InterruptIn setup in function enable() as press() and release()functions are never being triggered. (I've updated the initial post with the current code)

posted by Jens Strümper 31 Mar 2016

I'm not sure what is going to happen to the interrupt handlers once the program has exited. At the end of main add while(true) wait(100); so that it will sit in a loop doing nothing rather than exiting.

posted by Andy A 01 Apr 2016

Hmpf, I guess this will never work when it just exits. Thanks! I added a while loop to the end of the program. Still the callback functions get never triggered. At least I know that the program is not crashing.

posted by Jens Strümper 01 Apr 2016