Erroneous key echo after printf in timer handler

09 Feb 2010 . Edited: 13 Feb 2010

This problem can be reproduced with the mbed and PC connected via USB serial (no hardware needed).

The code below is a simplification of the program referenced in this thread. The problem appears to be related to the first key press after a printf from within a timer handler (local echo and the Terminal class are not relevant).

When running the code, one should see the "!" character as the one shot in the timer handler triggers. After that, a key press (eg. the letter "a") is displayed along with the ">" character. Further key presses just result in the ">" character (with no echo). When the one shot is reset with the "r" key, a "!" should be displayed and then the next key press results in an echo (and the ">"). Further key presses just result in ">".

An example display is as follows:

!a>>>>>>>>>>>>>>>!a>>>>>>>>>>>>!a>>>>>
#include "mbed.h"

Serial term(USBTX, USBRX);
volatile bool one_shot = false;

void HandleTimer(void){
    if (!one_shot) {
        one_shot = true;
        term.printf("!");  
    }
}
     
int main() {
    Ticker timer;
    timer.attach(&HandleTimer, 0.1);

    while(true){                  
        int key = term.getc();
        term.printf(">");
        if (key == 'r')
            one_shot = false;   
    }       
}
16 Feb 2010

Hi mbed team

Can I bump this in case it was overlooked in all the excitement of the compiler upgrade? Alternatively, it would be nice to know if it is a feature by design and not a bug.

Thanks
Daniel

16 Feb 2010 . Edited: 16 Feb 2010

Hi Daniel,

Thanks for the bump; very interesting observation!

So, I had a theory, and here is what I tried:

#include "mbed.h"

Serial term(USBTX, USBRX);
volatile bool one_shot = false;

void HandleTimer(void){
    if (!one_shot) {
        one_shot = true;
        term.printf("!");  
    }
}
     
int main() {
    Ticker timer;
    timer.attach(&HandleTimer, 0.1);
    wait(1); // wait so we know handler triggers before we hang on getc()
    while(true){                  
        int key = term.getc();
        term.printf(">");
        if (key == 'r')
            one_shot = false;   
    }       
}

And the result:

!>>>>>>>>!a>>>>>>>>>!a>>>>

Interesting :) No 'a' the first time.

So here is my initial theory that led me to this test. When you start this code, it all goes fine and ends up hanging on getc() waiting for a character, in the depths of stdio. Then, when the interrupt fires, printf enters in to stdio to do things as well, and changes some state within the stdio libs. When getc() finally gets a character, something is a little confused and means the result is incorrect. i.e. it is a re-entrancy problem.

So, you don't really want to be in stdio when an interrupt happens (as you'd probably expect). As a simple "workaround" to prove the point, here is some code that doesn't hang on getc():

#include "mbed.h"

Serial term(USBTX, USBRX);
volatile bool one_shot = false;

void HandleTimer(void){
    if (!one_shot) {
        one_shot = true;
        term.printf("!");  
    }
}
     
int main() {
    Ticker timer;
    timer.attach(&HandleTimer, 0.1);

    while(true){                  
        if(term.readable()) {
            int key = term.getc();
            term.printf(">");
            if (key == 'r')
                one_shot = false;   
        }
    }       
}

This works as I think you are expecting, but of course doesn't totally eliminate the potential for a race condition.

So whilst this probably gets you what you need, it also asks the question about if and how we should support re-entrancy/resouce sharing. This will need more investigation/specification, and I'd be interested in opinions at what level people would "expect" it to be solved (it just works vs RTOS/resouce sharing etc). It feels like a good candidate for "it just works", where works is it trying to do the most "expected" behaviour.

16 Feb 2010

Hi Simon

Thanks very much for the investigation. Could I trouble you to look at this post on a related issue? I'm looking at this particularly from the point of view of writing serial drivers, but the issue is a more general one of when to use an RTOS.

My own suggestion is for the standard mbed libraries to "just work". However, I would also like to see more official support for an RTOS on the mbed if feasible. My main concern is how much RAM you have left, after an RTOS and an http library and so on.

What about a lightweight task switcher, with some semaphores and mutexes? Michael mentioned that here. Should I be able to write my own? I can understand the ability to raise an interrupt (eg. timer) but not what would be involved in switching to another interrupt if the first one was blocked. Would it be re-inventing the wheel (and not very robust if I did it myself)?

Thanks
Daniel

19 Feb 2010
Simon Ford wrote:
This works as I think you are expecting, but of course doesn't totally eliminate the potential for a race condition.

Hi Simon

I believe you can eliminate the race condition by disabling the timer interrupt before checking the readable status (I hope so because I'm going to rely on this technique in the absence of an RTOS for handling serial interrupts). Also, will a __WFI will stop the aimless looping? My original code blocked on the getc (not sure of this really saves power).

#include "mbed.h"

Serial term(USBTX, USBRX);
volatile bool one_shot = false;

void HandleTimer(void){
    if (!one_shot) {
        one_shot = true;
        term.printf("!");  
    }
}
     
int main() {
    Ticker timer;
    timer.attach(&HandleTimer, 0.1);

    while(true){
        
        /* wait for interrupt eg. timer or serial */
        __WFI();
        
        /* enter critical section on mbed timer*/
        NVIC_DisableIRQ(TIMER3_IRQn);
                              
        if(term.readable()) {
            int key = term.getc();
            term.printf(">");
            if (key == 'r')
                one_shot = false;   
        }
        
        /* exit critical section on mbed timer*/
        NVIC_EnableIRQ(TIMER3_IRQn);
        
    }       
}

I'm still interested in an answer to my previous post above about RTOS/task switching. I hope you don't mind if I keep bumping this until you notice ;-)

Regards
Daniel

26 Feb 2010

hmm, why not just adding a second flag and set it in the Timer handler function and then using the stdio-functions only from main (not from an ISR). Ok, i must admit that i come from lower level µCs (8 and 16 Bit), where one often put much effort avoiding anything too time or ressource consuming in an ISR (especially things like printing via UART or display to LCD and such). Mostly one just sets or resets some flags and the rest goes into the main loop, which then does the code flow control based on the flags.

#include "mbed.h"

Serial term(USBTX, USBRX);
volatile bool one_shot = false;
volatile bool print_exmark = false;

void HandleTimer(void){
    if (!one_shot) {
        one_shot = true;
        print_exmark = true;
    }
}
     
int main() {
    Ticker timer;
    timer.attach(&HandleTimer, 0.1);

    while(true){                  
        if(term.readable()) {
            int key = term.getc();
            term.printf(">");
            if (key == 'r')
                one_shot = false;   
        }
        if(print_exmark) {
            print_exmark = false;
            term.printf("!");
        }
    }       
}

Best regards
Neni

26 Feb 2010
Nenad Milosevic wrote:
why not just adding a second flag and set it in the Timer handler function and then using the stdio-functions only from main

Hi Neni

Thanks for the response. For me personally, I have spent more time recently programming for the PC (in C#) and now find it very appealing to put all the code for something into a class and not have to rely on the programmer remembering to make calls from a main loop to make things happen. For example, I want to be able to create an object to talk to a serial device with one line of code as below.

Dacio *dacio;
dacio = new Dacio(tx_pin, rx_pin);

After that, I would call methods on the object to interact with the device in the main loop (eg. to print values from it or send commands) but I would not have to call dacio.Poll() every second from the main loop, and also I would not want to worry about RPC calls interacting with the object either.

I think it is quite a challenge to write code in this way without a multitasking environment, hence my questions above and in other threads about RTOS (simple or otherwise) that still go unanswered.

At the end of the day, mbed is not a PC (although I'm sure we'd like 1 GB of memory ... or even 1 MB), so I may have to be realistic about my programming style. However, I did assembly language and plain C and industrial PLC programming with ladder logic, and I'm not sure I want to go there again.

Regards
Daniel