4 years, 12 months ago.

MBed Events in interrupts - no instance of overloaded function - argument types are: (Event)

While working on a project either in the online compiler or Mbed Studio, the example for using events in interrupts works fine. My project got rather large, and I started having issues with studio (It started to save 'incomplete' and corrupt files, causing it's linter to completely fail, but that's a separate topic) so I needed to move offline. I have tried multiple compilers, multiple code editors/ides, and I keep running into this one problem.

no instance of overloaded function "mbed::InterruptIn::rise" matches the argument list -- argument types are: (events::Event<void ()>) -- object type is: mbed::InterruptIn

The program compiles just fine as far as I can tell, but the ide doesn't know it. Frankly, while digging into the source code a little deeper, I can see why. I haven't found anywhere where Event is the same as Callback:

"InterruptIn.h

    /** Attach a function to call when a rising edge occurs on the input
     *
     *  @param func A pointer to a void function, or 0 to set as none
     */
    void rise(Callback<void()> func);

    /** Attach a member function to call when a rising edge occurs on the input
     *
     *  @param obj pointer to the object to call the member function on
     *  @param method pointer to the member function to be called
     *  @deprecated
     *      The rise function does not support cv-qualifiers. Replaced by
     *      rise(callback(obj, method)).
     */
    template<typename T, typename M>
    MBED_DEPRECATED_SINCE("mbed-os-5.1",
                          "The rise function does not support cv-qualifiers. Replaced by "
                          "rise(callback(obj, method)).")
    void rise(T *obj, M method)
    {
        core_util_critical_section_enter();
        rise(callback(obj, method));
        core_util_critical_section_exit();
    }

I'm by no means a master at c++. I come from a web development and java background, so I'm sure I'm missing something here. Regardless, I'd like to get this working properly in at least VSCode..I've tried VSCode, Atom with gcc and clang linter, eclipse, eclipse che on codeenvy, and keil uvision. With code and atom I tried both with and without using platformio configs. Here's my main.cpp.

#include "mbed.h"

InterruptIn button(USER_BUTTON);

DigitalOut led(LED1);

// create an event queue
EventQueue queue;

// create a thread that'll run the event queue's dispatch function
Thread eventThread;

double delay = 0.5; // 500 ms

void pressed()
{
    delay = 0.1; // 100 ms
}

void released()
{
    delay = 0.5; // 500 ms
}

int main()
{
    eventThread.start(callback(&queue, &EventQueue::dispatch_forever));
    // wrap calls in queue.event to automatically defer to the queue's thread
    button.fall(queue.event(&pressed));
    button.rise(queue.event(&released));

    while (1) {
        led = !led;
        wait(delay);
    }
}

I'm at a loss and out of my depth. How do I make intellisense (for example) not see this as an error? Is there something in the compiler I should be doing?

PS: I'm on windows, haven't tried this on linux yet.

2 Answers

4 years, 11 months ago.

I admit I don't fully understand all of this nested templated code. But first idea would be passing in a callback() object like it seems to want. I struggled with syntax to create a callback to a member function with an argument. It did work when I created a Callback object first with the parameter (i.e. function pointer) then passed that into rise() and fall()

Callback<void()> fall_event = queue.event(pressed);
button.fall(fall_event);

This worked in online compiler and I ran it offline in GCC 4.9 mbed 5.4.2 and it also worked. Although, your original code worked in my gcc 4.9 setup as well, so I don't know.

Maybe some clever person can tell us how to do this all in one line.

Another idea was that you could still attach pressed() so it runs in ISR context, but the only work you do there is to put something into the queue. Speculation, but the Interrupt must be firing in all cases, so something is running in ISR context. So if you let pressed() run and just put stuff in queue manually, it is probably very similar end result.

Accepted Answer

Like I said before, the code always compiled just fine, it's just that my ide doesn't seem to agree with the compiler. I guess I was mostly trying to figure out whats wrong with vscode. What I ended up doing to fix the 'error' was very round about. I created a regular ISR function that calls <<code>> queue.call(&pressed); <</code>.

To further complicate the issue, what I really is to be able to do this all within a class. AKA: the class creates it's own thread and queue and interrupt. But of course, Interrupts don't like non static member functions as callbacks, and it would appear that neither do events!? So to get around that problem, my interrupt callback is static, I have a static pointer to a reference of 'this' initialized in my classes constructor, my callback uses a static cast to pass onto the static queue to call the non static function in the static thread..... At this point my class might as well be a singleton, but my real goal is to make everything not static... I think that is a completely different question but i'm venting about the absurdity. Also these problems are really hard to fix when your ide is telling you you have problems that aren't there...

posted by Dalen Catt 01 May 2019

Dalen,

The mbed Callback class specifically to solve these types of problems. You can easily make a callback to a member function. Here’s simplified version with serial receive:

// Talks to BLE radio over serial port
class BLE {
public:
    BLE() :
        serial_(kPinBleTx, kPinBleRx, kBleBaudRate) {
        serial_.attach(callback(this, &BLE::receiveISR));
    }

private:
    void receiveISR() {
        while (serial_.readable()) {
            serial_.putc(serial_.getc());
        }
    }

    RawSerial serial_;
};

Threads can also be owned by a class. You still need to wait and call start() in main after everything is all setup. Here’s simplified version of something I use to process new requests coming in:

class API {
public:
    // Ctor
    API() :
        thread_(osPriorityNormal, 5000) {
    }

    // API Thread Loop
    void apiThread() {
        while (true) {
            osEvent evt = api_mailbox.get(1000);

            if (evt.status == osEventMail) {
                mail_t *mail = (mail_t *)evt.value.p;
                processRequest(mail->buffer);
                api_mailbox.free(mail);
            }
        }
    }

    // Call from main when you are ready to start all threads.
    void start() {
        thread_.start(callback(this, &API::apiThread));
    }

    // Other Threads can Add string to mailbox to be Processed
    void mailPut(char* buffer) {
        mail_t *mail = api_mailbox.alloc();

        if (mail == NULL) {
            printf("API mailPut         OVERFLOW\n");
        }
        else {
            strcpy(mail->buffer, buffer);
            api_mailbox.put(mail);
        }       
    }

private:
    void processRequest(char *buffer) {
        //do something with buffer
    }

    Thread thread_;

    static const int kApiMailboxSize = 1;

    typedef struct {
        char *buffer;
    } mail_t;

    Mail<mail_t, kApiMailboxSize>api_mailbox;
};

Hope this is helpful.

posted by Graham S. 02 May 2019

Well that worked pretty well. So I've got an IDE that works now. Visual studio 2019 with VisualGDB seems to be working great! As a plus, I have actual debugging capabilities now. So, last question:

I now have in my class init function:

_interruptThread.start(callback(&_interruptQueue, &EventQueue::dispatch_forever));
_interruptPin.rise(callback(this, &RF95Driver::InterruptCallback));

And then this function:

void RF95Driver::InterruptCallback() {
    _interruptQueue.call(callback(this, &RF95Driver::handleInterrupt));
}

This all works great as expected! But.... I don't really <i>need</i> the InterruptCallback function if I can call &interruptQueue.event directly, right? I can't do something like this:

_interruptPin.rise(callback(this, &RF95Driver::_interruptQueue.event(callback(this, &RF95Driver::handleInterrupt))));

Because I'm 'taking the address of a temporary object of type 'Event<void ()>''. I'm guessing I could store a Callback object as a variable and assign it the same way you did above, but that might as well be the same thing as just calling it IntterruptCallback. Can you think of any way to do this without storing the event as a variable or function? It may just be one of those necessary evils. In any case, I appreciate your time, you've been more than helpful, especially considering I drifted away from my original problems, lol.

posted by Dalen Catt 03 May 2019
4 years, 11 months ago.

Hello Dallen,

I was not able to recreate the reported issue when compiling on 64-bit Windows 7 with the QtCreator IDE .

Good to know. I haven't tried that one yet. Just as a sanity check, is it doing it in vscode for you?

posted by Dalen Catt 01 May 2019

Sorry, I don't use VSCode.

posted by Zoltan Hudak 02 May 2019

I am having the same issue with VS code on Windows 10 64-bit It compiles fine but shows an error in the IDE

No instance of overloaded function "mbed::InterruptIn::fall" matches the argument list argument types are: (events::Event<void ()>) object type is: mbed::InterruptIn

posted by Mathieu Moloney 18 Nov 2019