Unusual thread hang in Serial::RxIrq attached method

21 Nov 2018

Hi All,

I'm working with OS2 and the Nucleo-F411RE. Seeing a strange thread hang on a Serial::RxIrq where the received event hangs the main thread and the LED being turned on/off in the method gets 'stuck' between states (flickering). I have buttons on InterruptIn which remain functional during the hang.

I'm not sure why, but the number of receives before a hang is random. At first I thought the number of 'strcmp' functions could be using up memory, so I output the 'free RAM' value to check that, and it appears unchanged at 109700 Bytes, so no runaway buffers. Anyone have any ideas? See below code and video of the problem.

main.cpp

#include "mbed.h"
#include "LM75B.h"
#include "TSL2561.h"
#include "BME280.h"
#include <stdio.h>
#include <stdlib.h>

#define FREEMEM_CELL 100

bool _debug = true;
Serial pc(USBTX, USBRX);
void Rx_interrupt();
char Buffer[20];
DigitalOut status(LED1);

LM75B temp(PB_9, PB_8);
TSL2561 Lux(PB_9, PB_8);
BME280 sensor(PB_4, PA_8);

PwmOut led1(D6);
PwmOut led2(D4);
PwmOut led3(D3);
PwmOut led4(D2);
float led1OnValue = 0.6f;
float led2OnValue = 0.6f;
float led3OnValue = 0.6f;
float led4OnValue = 0.6f;

PwmOut heater(PA_1);
float heaterOnValue = 0.0f;

InterruptIn btn1(PA_9, PullUp);
InterruptIn btn2(PC_4, PullUp);
InterruptIn btn3(PB_6, PullUp);
InterruptIn btn4(PA_7, PullUp);

bool btn1Pressed = false;
bool btn2Pressed = false;
bool btn3Pressed = false;
bool btn4Pressed = false;
void btnCheck();
void btn1Event();
void btn2Event();
void btn3Event();
void btn4Event();

struct elem { /* Definition of a structure that is FREEMEM_CELL bytes  in size.) */
    struct elem *next;
    char dummy[FREEMEM_CELL-2];
};

int FreeMem(void) {
    int counter;
    struct elem *head, *current, *nextone;
    current = head = (struct elem*) malloc(sizeof(struct elem));
    if (head == NULL)
        return 0;      /*No memory available.*/
    counter = 0;
   // __disable_irq();
    do {
        counter++;
        current->next = (struct elem*) malloc(sizeof(struct elem));
        current = current->next;
    } while (current != NULL);
    /* Now counter holds the number of type elem
       structures we were able to allocate. We
       must free them all before returning. */
    current = head;
    do {
        nextone = current->next;
        free(current);
        current = nextone;
    } while (nextone != NULL);
   // __enable_irq();

    return counter*FREEMEM_CELL;
}

int main()
{
    pc.baud(9600);
    status = 0;
    led1.period(0.003f);
    led2.period(0.003f);
    led3.period(0.003f);
    led4.period(0.003f);
    heater.period(0.003f);
    led1 = led1OnValue;
    led2 = led2OnValue;
    led3 = led3OnValue;
    led4 = led4OnValue;
    heater = 1.0f;

    btn1.rise(&btn1Event);
    btn2.rise(&btn2Event);
    btn3.rise(&btn3Event);
    btn4.rise(&btn4Event);
    pc.attach(&Rx_interrupt, Serial::RxIrq);

    while (true) {
        pc.printf("\r\n{Data:{Led1:%.3f,Led2:%.3f,Led3:%.3f,Led4:%.3f,Heater:%.3f,Temp:%.3f,Lux:%.1f,hPa:%04.2f,Hum:%2.2f}}",1-led1.read(), 1-led2.read(), 1-led3.read(), 1-led4.read(), 1-heater.read(), temp.read(), Lux.lux(), sensor.getPressure(), sensor.getHumidity());
        pc.printf("\r\n{Free Memory:%d}", FreeMem());
        wait(0.5);
    }
}

void btn1Event()
{
    if(btn1.read() == 0) {
        if(btn1Pressed == false) {
            wait(0.2);
            if(btn1.read() == 0) {
                if(led4 == led4OnValue) {
                    led4 = 1.0f;
                } else {
                    led4 = led4OnValue;
                }
                btn1Pressed = true;
            }
        }
    } else {
        btn1Pressed = false;
    }
}

void btn2Event()
{
    if(btn2.read() == 0) {
        if(btn2Pressed == false) {
            wait(0.2);
            if(btn2.read() == 0) {
                if(led3 == led3OnValue) {
                    led3 = 1.0f;
                } else {
                    led3 = led3OnValue;
                }
                btn2Pressed = true;
            }
        }
    } else {
        btn2Pressed = false;
    }
}

void btn3Event()
{
    if(btn3.read() == 0) {
        if(btn3Pressed == false) {
            wait(0.2);
            if(btn3.read() == 0) {
                if(led2 == led2OnValue) {
                    led2 = 1.0f;
                } else {
                    led2 = led2OnValue;
                }
                btn3Pressed = true;
            }
        }
    } else {
        btn3Pressed = false;
    }
}

void btn4Event()
{
    if(btn4.read() == 0) {
        if(btn4Pressed == false) {
            wait(0.2);
            if(btn4.read() == 0) {
                if(led1 == led1OnValue) {
                    led1 = 1.0f;
                } else {
                    led1 = led1OnValue;
                }
                btn4Pressed = true;
            }
        }
    } else {
        btn4Pressed = false;
    }
}

void Rx_interrupt()
{
    status = 1;
    pc.scanf("%17s",Buffer);

    if(strcmp(Buffer, "{Command:{L1:3}}") == 0) {
        if(led1 == led1OnValue) {
            led1 = 1.0f;
        } else {
            led1 = led1OnValue;
        }
    } else if(strcmp(Buffer, "{Command:{L1:0}}") == 0) {
        led1 = 1.0f;
    } else if(strcmp(Buffer, "{Command:{L1:1}}") == 0) {
        led1 = led1OnValue;
    } else if(strcmp(Buffer, "{Command:{L2:3}}") == 0) {
        if(led2 == led2OnValue) {
            led2 = 1.0f;
        } else {
            led2 = led2OnValue;
        }
    } else if(strcmp(Buffer, "{Command:{L2:0}}") == 0) {
        led2 = 1.0f;
    } else if(strcmp(Buffer, "{Command:{L2:1}}") == 0) {
        led2 = led2OnValue;
    } else if(strcmp(Buffer, "{Command:{L3:3}}") == 0) {
        if(led3 == led3OnValue) {
            led3 = 1.0f;
        } else {
            led3 = led3OnValue;
        }
    } else if(strcmp(Buffer, "{Command:{L3:0}}") == 0) {
        led3 = 1.0f;
    } else if(strcmp(Buffer, "{Command:{L3:1}}") == 0) {
        led3 = led3OnValue;
    }
    status = 0;
}
21 Nov 2018

Hello Angus,

I'm not sure, but I think the issue could result from the assumption that the size of a pointer is two bytes:

struct elem { /* Definition of a structure that is FREEMEM_CELL bytes  in size.) */
    struct elem *next;
    char dummy[FREEMEM_CELL-2];
};

However, it is rather four bytes. So try:

struct elem { /* Definition of a structure that is FREEMEM_CELL bytes  in size.) */
    struct elem *next;
    char dummy[FREEMEM_CELL-sizeof(struct elem*)];
};

or

struct elem { /* Definition of a structure that is FREEMEM_CELL bytes  in size.) */
    struct elem *next;
    char dummy[FREEMEM_CELL-4];
};

and see how it works.

07 Dec 2018

Zoltan Hudak wrote:

Hello Angus,

I'm not sure, but I think the issue could result from the assumption that the size of a pointer is two bytes:

struct elem { /* Definition of a structure that is FREEMEM_CELL bytes  in size.) */
    struct elem *next;
    char dummy[FREEMEM_CELL-2];
};

However, it is rather four bytes. So try:

struct elem { /* Definition of a structure that is FREEMEM_CELL bytes  in size.) */
    struct elem *next;
    char dummy[FREEMEM_CELL-sizeof(struct elem*)];
};

or

struct elem { /* Definition of a structure that is FREEMEM_CELL bytes  in size.) */
    struct elem *next;
    char dummy[FREEMEM_CELL-4];
};

and see how it works.

Hi Zoltan,

Thanks for your reply. While it may have been outputting the wrong memory value, it seems that the thread hangs before it can output the next value. I can't therefore tell if memory is the issue or not, but thanks for pointing this out.

I'm sending {Command:{L1:3}}\n, and the *scanf* is waiting for the *\n* to complete a read of the buffer. The only other time I've seen this hang behaviour is when using *gets* and its waiting to fill the buffer before continuing. Anyone see any reason a *\n* might not be getting through?

07 Dec 2018

Hello Angus,

Nice project! Try to avoid scanf and make the ISR as short as possible by moving the logics from ISR to the main function. For example:

//...

RawSerial pc(USBTX, USBRX);

//...

void Rx_interrupt()
{
    int  i = 0;
    char c = 0;

    while pc.readable() {
        c = pc.getc();
        Buffer[i++]= c;
        if ((i == 18) || (c == '\n'))
            break;
    }

    if (c == '\n') {
        Buffer[i - 1] = 0;  // mark the end of a C-style string (replace '\n' with null char)
        status = 1;         // flag that a complete command has been received
    }
}

int main()
{
//...

    while (true) {
        if (status == 1) {
            // new command received
            status = 0; // reset the flag for next use in ISR
            
            if (strcmp(Buffer, "{Command:{L1:3}}") == 0) {

            //...

            pc.printf("\r\n{Data:{Led1:%.3f,Led2:%.3f,Led3:%.3f,Led4:%.3f,Heater:%.3f,Temp:%.3f,Lux:%.1f,hPa:%04.2f,Hum:%2.2f}}",1-led1.read(), 1-led2.read(), 1-led3.read(), 1-led4.read(), 1-heater.read(), temp.read(), Lux.lux(), sensor.getPressure(), sensor.getHumidity());
            pc.printf("\r\n{Free Memory:%d}", FreeMem());
        }
    }
}