3 years, 1 month ago.

Tickers and Queues on F411RE

Hi guys,

I'm trying to use an Mbed Ticker object to push 'events' into a Queue object. It seems like this should be fairly simple, however it appears the callback attached to the Ticker never gets called.

Here is an abstract of my code:

DigitalOut userLed(LED1);
Queue<int, 8> eventQueue;
Ticker        drawTimeInt;
Ticker        checkAlarmInt;

void mainLoop(){
    time_t     seconds = time(NULL);
    struct tm* curTime = localtime(&seconds);
    osEvent    event;
    int*       eventCode;
    
    drawTimeInt.attach(&scheduleDrawTime, 1.0f);
    checkAlarmInt.attach(&scheduleCheckAlarms, ((60.0) - (curTime->tm_sec)));
while(1){
        seconds = time(NULL);
        curTime = localtime(&seconds);
        event = eventQueue.get(50);
        if(event.status == osEventMessage){
            eventCode = (int*) event.value.p;
            switch(*eventCode){
            case EVENT_DRAW_TIME:
                drawTime(seconds);
                break;
            case EVENT_CHECK_ALARMS:
                checkAlarms(curTime);
                checkAlarmInt.attach(&scheduleCheckAlarms, 60.0);
                break;
            default:
                debugWrite(DEBUG_ER, "Invalid event code: %d", *eventCode);
                break;
            }
        }
        else{
            debugWrite(DEBUG_IN, "Nothing...");
        }
}

void scheduleDrawTime(){
    int event = EVENT_DRAW_TIME;
    userLed = !userLed;
    eventQueue.put(&event, 1000);
}


void scheduleCheckAlarms(){
    int event = EVENT_CHECK_ALARMS;
    eventQueue.put(&event, 1000);
}

I have tried only using 1 Ticker and removing any of the queue interaction and I still see no activity from the LED. This leads me to believe the timer interrupt is simply never triggering.

Anyone have any ideas for what could be causing this?

Also is it even possible to use the Queue object from interrupt context? How many Ticker objects can be in use at once?

Thanks, Russell Bateman

1 Answer

3 years, 1 month ago.

I tried to make a more generic example that is runnable. This works for me. However, I am still worried about access to the shared variable event_number.

#include "mbed.h"

#define EVENT_NULL 0
#define EVENT1     1
#define EVENT2     2

DigitalOut    led(LED1);
Queue<int, 8> queue;
Ticker        ticker1;
Ticker        ticker2;

int event_number = EVENT_NULL;

void event1Isr();
void event2Isr();

int main() {
    osEvent os_event;
    int    *event_received;

    ticker1.attach(&event1Isr, 1.0f);
    ticker2.attach(&event2Isr, 1.1f);
    
    printf("\n\nStart\n");
    
    while (1) {
        os_event = queue.get(100);
        
//        printf("OS event status %d\n",os_event.status);
        
        if (os_event.status == osEventMessage) {
            event_received = (int *)os_event.value.p;

            switch (*event_received) {
                case EVENT_NULL:
                    printf("null event\n");                
                    break;
                case EVENT1:
                    printf("event1\n");
                    break;
                case EVENT2:
                    printf("event2\n");
                    break;
                default:
                    printf("Invalid event code: %d\n", *event_received);
                    break;
            }
        }
        else {
            printf(".\n");
        }
    }
}

void event1Isr() {
    event_number = EVENT1;
    led          = !led;
    queue.put(&event_number);
}

void event2Isr() {
    event_number = EVENT2;
    queue.put(&event_number);
}




Accepted Answer

Hi Graham, thanks for looking at this for me. I can confirm your code does indeed work! However I can't see any massive differences between this code and what I'm doing. I was wondering why you made the event number a shared global instead of having a local variable to the ISR. Is this normally best practise?

If the ISR's trigger at fairly different paces then I cant imagine it creates much of an issue. I was just wondering if there was any rationale behind this choice or if it was just your preference.

Anyway thanks for the code and I'll try and get my program working from it!

posted by Russell Bateman 18 Mar 2018

You can't pass a pointer to a local variable into the queue because the variable goes out of scope when the ISR exits and no longer exits by the time main gets to it. Another way to do it is to use a mailbox, which is like a queue but with storage. In this simple case, a global boolean flag would work almost as well as the queue but would introduce a little bit of delay since main() would wait out the 50ms delay every time whereas with the queue it runs immediately. But the global volatile bool flag would be simpler.

I also removed the timeout in the call to queue.put because it was causing me trouble and didn't seem necessary anyway.

posted by Graham S. 18 Mar 2018

Ah okay, I understand your point. The reference passed into the queue would be trashed when the ISR returns, having it global would prevent that. Thanks for your help and explanations!

posted by Russell Bateman 19 Mar 2018