Mbed stops while using 3 interrupts

Hello all,

I have a problem with the sudden stop of Mbed program.

I am using PlatformIO and here is the code of platformio.ini.

[env:nucleo_f446re]
platform = ststm32@^4.0.0
board = nucleo_f446re
framework = mbed

My project needs to communicate mutually by UART with two f446re(hereafter f446_1 and f446_2).
Also one of f446re (f446_2)needs to do two more things.

  • use a ticker that fires every 50 ms and it has an interrupt function.
  • receive UART from another f446re(hereafter f446_3)

Each port of UART receive has interrupt function.

The problem is that f446_2 frequently stops in this order:

  1. receive from f446_1 stops
  2. receive from f446_3 stops
  3. timer interrupt stops
  4. ALL OF THE PROGRAM stops and f446_1 seems to stop too

One thing that I want to mention is that the above issue rarely happens just after uploading.
That problem happens when 2 or more minutes pass from the program start.

Here is the excerpt of my main.cpp of f446re_2.

int main()
{
    init();

    while (1)
    {
        input();

        /* 制御 */
        if (timer_fires)
        {
            timer_fires = false;
          /* process */
            }
        }

        output();
   
        debug();   /* send to f446re_1 */

         }
}

void input()
{
    if (RX_COMP_F)
    {
        rx_cnt_main = 0;
        for (int i = 0; i < data_num; i++)
        {
            ReceiveData[i].all = receive_data[i];
            pc.printf("%d ", ReceiveData[i].all);
        }
        RX_COMP_F = false;
    }

    if (RX_COMP_F_2)
    {
        rx_cnt_gyro = 0;
        for (int i = 0; i < data_num_gyro; i++)
        {
            ReceiveData_2[i].all = receive_data_2[i];
        }
        RX_COMP_F_2 = false;
    }
}

void init()
{
    BS.baud(57600);
    BS_gyro.baud(57600);
    BS.attach(ISR_rx_main, SerialBase::RxIrq);
    BS_gyro.attach(ISR_rx_gyro, SerialBase::RxIrq);
    NVIC_SetPriority(UART4_IRQn, 6); 
    NVIC_SetPriority(UART5_IRQn, 5); 
    NVIC_SetPriority(TIM3_IRQn, 4); 
    sampling_period.attach_us(&control, PERIOD);
}

void ISR_rx_main()   /* The structure is the same in both uart receive isr */
{
    unsigned char data1 = BS.getc(); 

    if (!RX_COMP_F)
    {
        RX_COMP_F = BS.store_data(data1, receive_data, HeadNum, foot_num, data_num - 1);
        RGB[1] = RX_COMP_F;
    }
    return;
}

I really appreciated it if anyone helps me or give me some advice.

Sincerely, Aoio

Hello Aoio,

Also one of f446re (f446_2)needs to do two more things.

  • Use a ticker that fires every 50 ms and it has an interrupt function.
  • Receive UART from another f446re(hereafter f446_3)
  • Each port of UART receive has interrupt function.

As I can see you use global variables/flags of int type to signal ISR and other threads that an interrupt has been fired… However, that is risky because reading and writing to such global variables is not an atomic operation so they can get corrupted. It could happen that after reading one byte of such variable by a thread the variable is overwritten by an ISR which is triggered at that moment. A remedy is to use global EventFlags for signalling rather than ordinary global variables.

For example:

#include "mbed.h"

const uint32_t RX_COMP_F   = 1UL << 0; // use bit 0 as signal channel for this flag 
const uint32_t RX_COMP_F_2 = 1UL << 1; // use bit 1 as signal channel for this flag 
const uint32_t TIMER_FIRES = 1UL << 2; // use bit 2 as signal channel for this flag 
...

EventFlags eventFlags;

void onInterrupt_RX_COMP_F()
{
    eventFlags.set(RX_COMP_F);  // sets on bit RX_COMP_F of the eventFlags variable
    ...
}

void onInterrupt_RX_COMP_F_2()
{
    eventFlags.set(RX_COMP_F_2);  // sets on bit RX_COMP_F_2 of the eventFlags variable
    ...
}

void onTIMER_FIRES()
{
    eventFlags.set(TIMER_FIRES);  // sets on bit TIMER_FIRES of the eventFlags variable
    ...
}

void input()
{
    eventFlags.wait_all(RX_COMP_F); // blocking until RX_COMP_F gets set (for example in ISR)
    // The wait_all function clears the RX_COMP_F flag by default after waiting.
    // RX_COMP_F doesn't have to be cleared manually here .
    rx_cnt_main = 0;
    for (int i = 0; i < data_num; i++) {
        ReceiveData[i].all = receive_data[i];
        pc.printf("%d ", ReceiveData[i].all);
    }

    eventFlags.wait_all(RX_COMP_F_2);
    rx_cnt_gyro = 0;
    for (int i = 0; i < data_num_gyro; i++) {
        ReceiveData_2[i].all = receive_data_2[i];
    }
}

void init()
{
    BS.baud(57600);
    BS_gyro.baud(57600);
    BS.attach(ISR_rx_main, SerialBase::RxIrq);
    BS_gyro.attach(ISR_rx_gyro, SerialBase::RxIrq);
    NVIC_SetPriority(UART4_IRQn, 6);
    NVIC_SetPriority(UART5_IRQn, 5);
    NVIC_SetPriority(TIM3_IRQn, 4);
    sampling_period.attach_us(&control, PERIOD);
}

void ISR_rx_main()  /* The structure is the same in both uart receive isr */
{
    unsigned char   data1 = BS.getc();

    eventFlags.wait_all(!RX_COMP_F);    // blocking until RX_COMP_F is cleared
    RX_COMP_F = BS.store_data(data1, receive_data, HeadNum, foot_num, data_num - 1);
    RGB[1] = RX_COMP_F;

    return;
}

int main()
{
    init();

    while (1) {
        input();

        /* 制御 */
        eventFlags.wait_all(TIMER_FIRES);

            /* process */
    }

    output();

    debug();    /* send to f446re_1 */
}

The EventFlags wait functions by default clear the specified event flags after waiting for them. So we don’t need to do that manually as in case of ordinary global variables. But we can change that behaviour by passing an argument (false) to the wait function.

For more details please have a look at the related Mbed documentation.

Hello Zoltan,

Thank you so much and sorry for the late response,
The situation has become more confusing.

In order to use EventFlags, I upgraded my mbed os version from os2 to os6.
I used this idea to transplant RawSerial, the base class of BusSerial_new.

But unfortunately, I could not apply EventFlags to all my interrupt points.
When I tried to apply it to entire my program, it raises RTOS event flags error.
Below is my updated code.

#include "mbed.h"
#include "MotorControler.h"
#include "QEI_speed.h"
#include "QEI_angle.h"
#include "calculation.h"
#include "defines.h"
#include "BusSerial_new.h"
#include <stdint.h>
 *
 * *************/
Timer timer0, timer1;// Serial pc(USBTX, USBRX);
BusSerial_new BS(PA_0, PA_1, &timer1);
// bool RX_COMP_F = false; // bool RX_COMP_F_2 = false;
BusSerial_new BS_gyro(PC_12, PD_2, &timer1);
Ticker sampling_period;        
/***************
 *
 *  *
 * *************/
void classSetting();
void init();
void ang_encoder_reset();
void input();
void calc_auto();
void calc_manual();
void calc_wheel_params(coordinate, double); 
void output();
void debug();
void control(); 
void ISR_rx_main();
void ISR_tx_main();
void ISR_rx_gyro();
void time_out();

const int data_num = 12;         
const int data_num_gyro = 8;     
comm_data ReceiveData[data_num]; 
comm_data ReceiveData_2[data_num_gyro];
uint8_t receive_data[data_num]; 
uint8_t receive_data_2[data_num_gyro];

const int NUM_TX_DATA = 7;
#define MAIN_ID 252
uint8_t send_data[NUM_TX_DATA];
uint8_t debug_box = 0;


// bool timer_fires = false;

bool zero_complete = false;
/* eventflags  */
// const uint32_t RX_COMP_F = 1UL << 0;   // use bit 0 as signal channel for this flag
// const uint32_t RX_COMP_F_2 = 1UL << 1; // use bit 1 as signal channel for this flag
const uint32_t TIMER_FIRES = 1UL << 2; // use bit 2 as signal channel for this flag
EventFlags eventFlags;
static bool RX_COMP_F = false;
static bool RX_COMP_F_2 = false;
/****************
 *
 *
 *
 * **************/
int main()
{
    init();
    while (1)
    {
        input();
        if (ZERO_ADJ && !zero_complete)
        {
            printf("adj\n");
            ang_encoder_reset();
        }
        else
            zero_complete = false;
       
        // if (TIMER_FIRES)
        // {
        //     TIMER_FIRES = false;
            eventFlags.wait_all(TIMER_FIRES);
            if (OPERATE_MODE == AUTO)
            {
                calc_auto();
            }
            else
            {
                RGB[0] = 1;
                calc_manual();
                RGB[0] = 0;
            }
        // }
        output();
        debug();
      }
}
/*----------------------------------------------  -------------------------------------------------*/
void init()
{
    printf("boot\n");
    
    classSetting();
    ang_encoder_reset();
    
    sampling_period.attach_us(&control, PERIOD); 
}

/*-----------------------
 * 
 * ---------------------*/
void input()
{
    /*  */
    if (RX_COMP_F)
    {
        for (int i = 0; i < data_num; i++)
        {
            ReceiveData[i].all = receive_data[i];
            printf("%d ", ReceiveData[i].all);
        }
        printf("\n");
        RX_COMP_F = false;
    }
    /* */
    if (RX_COMP_F_2)
    {
        for (int i = 0; i < data_num_gyro; i++)
        {
            ReceiveData_2[i].all = receive_data_2[i];
            printf("%d ", receive_data_2[i]);
        }
        RX_COMP_F_2 = false;
    }
}

void calc_auto()
{
    
}
void calc_manual()
{

}
void calc_wheel_params(coordinate tar_vel_xy, double tar_robo_ang_vel)
{

}

void output()
{

}
void debug()
{
    send_data[1] = ANG_ENC[0].getDegree() / decel_ratio;
    send_data[2] = CURR_POS_X_UPPER;
    send_data[3] = CURR_POS_X_LOWER;
    send_data[4] = CURR_POS_Y_UPPER;
    send_data[5] = CURR_POS_Y_LOWER;
    /**------------------    ------------------------------*/
    // UART4->CR1 |= USART_CR1_TXEIE;
    BS.sendBusSerial_new(&send_data[0], NUM_TX_DATA);
}

void classSetting()
{
    BS.baud(115200);
    BS_gyro.baud(115200);
    BS.attach(ISR_rx_main, SerialBase::RxIrq);
    // BS.attach(ISR_tx_main, SerialBase::TxIrq);
    BS_gyro.attach(ISR_rx_gyro, SerialBase::RxIrq);
    // eventFlags.set(RX_COMP_F);
    // eventFlags.set(TIMER_FIRES);
    // eventFlags.set(RX_COMP_F_2);
      
    NVIC_SetPriority(UART4_IRQn, 4); 
    NVIC_SetPriority(UART5_IRQn, 5); 
    NVIC_SetPriority(TIM3_IRQn, 6); 
}
/**-------------------------
 
  ------------------------*/
void control(void)
{
    eventFlags.set(TIMER_FIRES);
    // TIMER_FIRES = true;
    // RGB[0] = !RGB[0]; /*  */
    return;
}
/*  */
void ISR_rx_main()
{
    rx_cnt_main = 0;
    char data;
    bool result = false;
    data = BS.getc(); 
    
    if (!RX_COMP_F)
    {
        // eventFlags.wait_all(!RX_COMP_F);
        result = BS.store_data(data, receive_data, HeadNum, foot_num, data_num - 1);
        if (result == true)
        {
            RGB[1] = !RGB[1];
            
            RX_COMP_F = true;
            // BS_gyro.attach(ISR_rx_gyro, SerialBase::RxIrq);
            // eventFlags.set(RX_COMP_F);
        }
    }
    return;
}


void ISR_rx_gyro()
{
    rx_cnt_gyro = 0;
   
    char data1;
    bool res = false;
    RGB[2] = !RGB[2];
    
    data1 = BS_gyro.getc();
    if (!RX_COMP_F_2)
    {
        res = BS_gyro.store_data(data1, receive_data_2, HeadNum, foot_num, data_num_gyro - 1);
        if (res == true)
        {
            /*  */
            RX_COMP_F_2 = true;
            // RGB[2] = !RGB[2]; 
            // BS.attach(ISR_rx_main, SerialBase::RxIrq);
        }
    }
    return;
}

Then the serial monitor raises HardFaultError or stops frequently.
Is my program too heavy?

Any idea would help me…

Thank you, Aoi

After reviewing your code I concluded you don’t need to use interrupts at all. My advice is as follows:

  • Use BufferedSerial for serial communication rather than a custom class. It provides a non-blocking feature to read the recieved data:
...
BufferedSerial  mainSerial(PA_0, PA_1);
BufferedSerial  gyroSerial(PC_12, PD_2);
...
//------------------------
void classSetting()
{
    mainSerial.set_baud(115200);
    gyroSerial.set_baud(115200);
}
...
//------------------------
void input()
{
    while (mainSerial.readable()) {
        readMainSerial();
    }

    while (gyroSerial.readable()) {
        readGyroSerial();
    }
...

//------------------------
void readMainSerial()
{
...
    char    data;

    mainSerial.read(&data, 1);
...
// replace `result = mainSerial.store_data(data, receive_data, HeadNum, foot_num, data_num - 1);`
// with something suitable.
...

//------------------------
void readGyroSerial()
{
    char    data1;

    gyroSerial.read(&data1, 1);
...
// replace `res = gyroSerial.store_data(data1, receive_data_2, HeadNum, foot_num, data_num_gyro - 1);`
// with something suitable.
...
  • Use EventQueue and its method call_every to periodically call a function rather than a Ticker.
...
Thread          thread;
EventQueue      eventQueue;
...
int main()
{
    thread.start(callback(&eventQueue, &EventQueue::dispatch_forever));
    eventQueue.call_every(50ms, control);
...
  • To display debug printf messages on a serial terminal connect the NUCLEO-F446RE_2 board over a USB cable to a PC.

The modified code is below:

#include "mbed.h"
#include "MotorControler.h"
#include "QEI_speed.h"
#include "QEI_angle.h"
#include "calculation.h"
#include "defines.h"
#include "BusSerial_new.h"
#include <stdint.h>
/***************
 *
 *  *
 * *************/
BufferedSerial  mainSerial(PA_0, PA_1);
BufferedSerial  gyroSerial(PC_12, PD_2);
Thread          thread;
EventQueue      eventQueue;

/***************
 *
 *  *
 * *************/
void            classSetting();
void            init();
void            ang_encoder_reset();
void            input();
void            calc_auto();
void            calc_manual();
void            calc_wheel_params(double coordinate);
void            output();
void            debug();
void            control();
void            readMainSerial();
void            readGyroSerial();
void            time_out();
void            ang_encoder_reset();

const int       data_num = 12;
const int       data_num_gyro = 8;
using           comm_data = int;
comm_data       ReceiveData[data_num];
comm_data       ReceiveData_2[data_num_gyro];
uint8_t         receive_data[data_num];
uint8_t         receive_data_2[data_num_gyro];

const int       NUM_TX_DATA = 7;
#define MAIN_ID 252
//uint8_t         send_data[NUM_TX_DATA];
uint8_t     debug_box = 0;
bool        zero_complete = false;
static bool RX_COMP_F = false;
static bool RX_COMP_F_2 = false;

/****************
 *
 *
 *
 * **************/
int main()
{
    thread.start(callback(&eventQueue, &EventQueue::dispatch_forever));
    eventQueue.call_every(50ms, control);

    init();
    while (1) {
        input();

        if (ZERO_ADJ && !zero_complete) {
            printf("adj\n");
            ang_encoder_reset();
        }
        else
            zero_complete = false;
        if (TIMER_FIRES) {
            TIMER_FIRES = false;
            if (OPERATE_MODE == AUTO) {
                calc_auto();
            }
            else {
                RGB[0] = 1;
                calc_manual();
                RGB[0] = 0;
            }
        }

        output();
        debug();
    }
}

/*----------------------------------------------  -------------------------------------------------*/
void init()
{
    printf("boot\n");

    classSetting();
    ang_encoder_reset();
}

/*-----------------------
 *
 * ---------------------*/
void input()
{
    while (mainSerial.readable()) {
        readMainSerial();
    }

    while (gyroSerial.readable()) {
        readGyroSerial();
    }

    /*  */
    if (RX_COMP_F) {
        for (int i = 0; i < data_num; i++) {
            ReceiveData[i].all = receive_data[i];
            printf("%d ", ReceiveData[i].all);
        }

        printf("\n");
        RX_COMP_F = false;
    }

    /* */
    if (RX_COMP_F_2) {
        for (int i = 0; i < data_num_gyro; i++) {
            ReceiveData_2[i].all = receive_data_2[i];
            printf("%d ", receive_data_2[i]);
        }

        RX_COMP_F_2 = false;
    }
}

/**
 */
void calc_auto()
{ }

/**
 */
void calc_manual()
{ }

/**
 */
void calc_wheel_params(double tar_vel_xy, double tar_robo_ang_vel)
{ }

/**
 */
void output()
{ }

/**
 */
void debug()
{
    printf("decel_ratio      = %d\r\n", ANG_ENC[0].getDegree());
    printf("CURR_POS_X_UPPER = %d\r\n", CURR_POS_X_UPPER);
    printf("CURR_POS_X_LOWER = %d\r\n", CURR_POS_X_LOWER);
    printf("CURR_POS_Y_UPPER = %d\r\n", CURR_POS_Y_UPPER);
    printf("CURR_POS_Y_LOWER = %d\r\n", CURR_POS_Y_LOWER);
}

/**
 */
void classSetting()
{
    mainSerial.set_baud(115200);
    gyroSerial.set_baud(115200);
}

/**-------------------------

  ------------------------*/
void control(void)
{
    TIMER_FIRES = true;
    RGB[0] = !RGB[0];   /*  */
}

/*  */
void readMainSerial()
{
    rx_cnt_main = 0;

    bool    result = false;
    char    data;
    mainSerial.read(&data, 1);

    if (!RX_COMP_F) {
        result = mainSerial.store_data(data, receive_data, HeadNum, foot_num, data_num - 1);
        if (result == true) {
            RGB[1] = !RGB[1];
            RX_COMP_F = true;
        }
    }
}

/**
 */
void readGyroSerial()
{
    rx_cnt_gyro = 0;

    bool    res = false;
    RGB[2] = !RGB[2];
    char    data1;
    gyroSerial.read(&data1, 1);

    if (!RX_COMP_F_2) {
        res = gyroSerial.store_data(data1, receive_data_2, HeadNum, foot_num, data_num_gyro - 1);
        if (res == true) {
            /*  */
            RX_COMP_F_2 = true;
            RGB[2] = !RGB[2];
        }
    }
}

/**
 */
void ang_encoder_reset()
{ }

Hello hudakz,

Thank you so much for your kindness.

It has been working fine since I had applied your advice.
That solution was new to me, a beginner of mbed.
I really appreciated it.

But I want to use feedback transmission from f446_2 to f446_1 at all costs.
The reason is that I need to debug wirelessly using the Bluetooth display attached to f446_1.
Did you mean that it was impossible to do it in my project?
Or possible without interrupt like below?

main_serial.write(send_data,NUM_TX_DATA);

Thank you,
Aoi

It was just a practical advise to have simpler debugging. Of course you can use main_serial.write(send_data,NUM_TX_DATA); for diagnostics or other feedback.

1 Like

Thank you for all!
I’ll try it.

Hello,

The system is raising HardFault again since I have added main_serial.write(send_data,NUM_TX_DATA); .
Below is the info:

++ MbedOS Fault Handler ++

FaultType: HardFault

Context:
R   0: 20002A48
R   1: 00000032
R   2: 200022D4
R   3: 00000048
R   4: 20002A48
R   5: 200001CC
R   6: 00000001
R   7: 20006ADB
R   8: 00000004
R   9: 20001640
R  10: 080435C4
R  11: 0000000D
R  12: 00000000
SP   : 2001FF98
LR   : 08001AEF
PC   : 08001A6C
xPSR : 2103000F
PSP  : 20004F50
MSP  : 2001FF78
CPUID: 410FC241
HFSR : 40000000
MMFSR: 00000082
BFSR : 00000000
UFSR : 00000000
DFSR : 00000000
AFSR : 00000000
MMFAR: 00000054
Mode : Handler
Priv : Privileged
Stack: MSP

-- MbedOS Fault Handler --



++ MbedOS Error Info ++
Error Status: 0x80FF013D Code: 317 Module: 255
Error Message: Fault exception
Location: 0x8001A6C
Error Value: 0x20005188
Current Thread: main <handler> Id: 0x20001534 Entry: 0x800229D StackSize: 0x1000 StackMem: 0x20004148 SP: 0x2001FF98 
For more info, visit: https://mbed.com/s/error?error=0x80FF013D&tgt=NUCLEO_F446RE
-- MbedOS Error Info --

I have been struggled in many way for these days (where to call write()function, which part to transfer to thread) but nothing change.
Maybe the addition is not the cause of the Fault, but I have no idea. I want anyone to help me.

At which point of your code did you add such line?

Maybe the addition is not the cause of the Fault, but I have no idea. I want anyone to help me.

You can try to use the crash_log_parser.py tool to work out the point in your code that is causing the fault. The links below could help:

Thank you, I could not find the crash_log_parser without your advice.

I added the line at the following point:

  1. inside the debug() function → stops soon after the main loop started
  2. inside the if(RX_CONP_F_2) of input() function → stops frequently

Now I could get the following message.

Crash Info:
        Crash location = osRtxThreadListRemove [0x08001A6C] (based on PC value)
        Caller location = osRtxThreadDelayTick [0x08001AEF] (based on LR value)
        Stack Pointer at the time of crash = [2001FF98]
        Target and Fault Info:
                Processor Arch: ARM-V7M or above
                Processor Variant: C24
                Forced exception, a fault with configurable priority has been escalated to HardFault
                Data access violation. Faulting address: 00000054

Does it mean that I have made some mistakes in my code (maybe in any of array?) that cause an overflow or illegal access?

P.S. After I modified control() to:

void control(void)
{
    TIMER_FIRES = true;

    calc_auto();

    return;
}

the message changed to:

Crash Info:
        Crash location = read_main_serial() [0x08008E58] (based on PC value)
        Caller location = read_main_serial() [0x08008E55] (based on LR value)
        Stack Pointer at the time of crash = [20005100]
        Target and Fault Info:
                Processor Arch: ARM-V7M or above
                Processor Variant: C24
                Forced exception, a fault with configurable priority has been escalated to HardFault
                Imprecise data access error has occurred

I’ve been suspecting store_data() in read_serial() and I’m working on it.

Try to use non-blocking mode:

int main()
{
    main_serial.set_blocking(false);
...

and in your code replace

    main_serial.write(send_data,NUM_TX_DATA);

with

    while (main_serial.writeable()) {
        main_serial.write(send_data,NUM_TX_DATA);
    }

Thank you for your patience.
I’m really sorry because that was maybe a basic coding fault of mine.
The function store_data() was perhaps the cause.

I felt that the fault message had told me that my code can be accessed to the outside of the array.
I modified the body of the function from

  container[j] = data;

    if (container[0] != head_num) 
        j = 0;
    else
        j++;

    if (j == data_quantity + 1)
    {
        j = 0;
        return true;
    }
    else
        return false; 

to

static int j = 0;

    if (j == 0 && data != head_num)
        return false;
    else if (j == data_quantity && data != foot_num)
    {
        j = 0;
        return false;
    }
    else if (j == data_quantity)
    {
        container[j] = data;
        j = 0;
        return true;
    }
    else
    {
        container[j] = data;
        j++;
        if (j > data_quantity)
            j = 0;
        return false;
    }

After that, I ran the program about 10 times and I have not been run into the issue.

Again thank you so much, I could learn about mbed more deeply and how to get and read fault messages with your advice.
I won’t make such mistakes again and I’ll go for it.

Never mind! We all make mistakes.
I’m glad you solved it :slight_smile:

1 Like