Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
5 years, 12 months ago.
running a thread from inside a class (mutex lock error) ?
I am trying to have a class for controlling some communications, using MBED OS 5. It compiles successfully, however when running it on our device, a NUCLEO-L432KC we encounter a mutex error:
MbedOS_Error
-- MbedOS Error Info -- ++ MbedOS Error Info ++ Error Status: 0x80020115 Code: 277 Module: 2 Error Message: Mutex lock failed Location: 0x8007C1B Error Value: 0xFFFFFFFC Current Thread: main Id: 0x20002188 Entry: 0x8008041 StackSize: 0x1000 StackMem: 0x20001188 SP: 0x20001EC0 For more info, visit: https://armmbed.github.io/mbedos-error/?error=0x80020115 -- MbedOS Error Info --
Our debugging has lead us to the believe that the fact of 'starting' the thread with the function causes the mutex error. In the simplest working example below, we have highlighted where the code can be commented out to prevent the mutex error.
minimal working example
#include "mbed.h" #include "rtos.h" class Comm{ public: Thread *p_comm_in; bool _RUN; RawSerial pc; Queue<void, 8> inCharQ; // Input Character Queue void serialISR(){ uint8_t newChar = pc.getc(); inCharQ.put((void*)newChar); } void commInFn() { // should put everything you write onto the terminal while (_RUN) { osEvent newEvent = inCharQ.get(); uint8_t newChar = ((uint8_t)(&newEvent.value.p)); pc.putc(newChar); } } Comm(): pc(SERIAL_TX, SERIAL_RX){ // inherit from the RawSerial constructor Thread comm_in(osPriorityAboveNormal, 1024); p_comm_in = &comm_in; pc.printf("%s\n\r", "Welcome, this won't work, but that's okay" ); } void start_comm(){ _RUN = true; pc.attach(callback(this, &Comm::serialISR)); // COMMENT THIS OUT TO PREVENT MUTEX ERROR p_comm_in->start(callback(this, &Comm::commInFn)); } }; //Main int main() { Comm comm_plz; comm_plz.start_comm(); while (1) { } }
It would be nice to know how to " try building a non-release version with MBED_CONF_PLATFORM_ERROR_FILENAME_CAPTURE_ENABLED configuration enabled" using the online IDE, as it seems it might be able to tell us what line of the code is causing this error.
2 Answers
5 years, 12 months ago.
I'm going to use a mailbox which includes the storage. You're passing pointer to an automatic stack variable into the queue. That memory location is invalid after ISR exits. Other problem with having only one global char variable is that you can't put pointer into the queue multiple times and expect to get different chars out. The mbed examples tend to use heap memory with queues.
I've also had bad luck using serial.attach outside of ctor, I don't know why.
Usually queuing up each char is not what you want to do. Usually in ISR you stuff chars into a buffer looking for end of message char like '\n', then put the whole string into the mailbox to parse. And you want to clean out all chars from serial while in the ISR in case there has been more than one.
This seems to work for me on an STM32L4.
#include "mbed.h" class Comm{ public: Comm() : thread_(osPriorityNormal, 1024), pc_(SERIAL_TX, SERIAL_RX) { pc_.attach(callback(this, &Comm::receiveISR)); pc_.printf("\n\n\nStart...\n"); } void start() { thread_.start(callback(this, &Comm::commThread)); } private: void receiveISR() { while (pc_.readable()) { char new_char = pc_.getc(); putMail(new_char); } } void commThread() { while (true) { osEvent evt = mailbox_.get(); if (evt.status == osEventMail) { mail_t *mail = (mail_t *)evt.value.p; pc_.putc(mail->new_char); // Add extra char to confirm we are seeing output and not terminal echo print pc_.putc('.'); mailbox_.free(mail); } } } void putMail(char new_char) { mail_t *mail = mailbox_.alloc(); if (mail == NULL) { // I belieive RawSerial printf is ISR safe pc_.printf("Mailbox Full\n"); } else { mail->new_char = new_char; mailbox_.put(mail); } } /* ----- Objects ----- */ Thread thread_; RawSerial pc_; // Define mailbox data type typedef struct { char new_char; } mail_t; Mail<mail_t, 20>mailbox_; }; int main() { Comm comm_plz; comm_plz.start(); while (1) { // do nothing Thread:wait(10000); } }
5 years, 12 months ago.
Hello Adel,
Great answer from Graham! The Mail
class seems be very useful but as he said you can do it without queuing too:
#include "mbed.h" class Comm : public Thread { RawSerial _pc; bool _RUN; volatile bool _commIn; void serialISR() { _pc.attach(NULL); // detaching ISR prevents being called again and again _commIn = true; } void commInFn() { while (_RUN) { if (_commIn) { _commIn = false; while (_pc.readable()) _pc.putc(_pc.getc()); _pc.attach(callback(this, &Comm::serialISR)); // re-attach ISR } } } public: Comm() : _pc(USBTX, USBRX), _RUN(0), _commIn(false), Thread(osPriorityAboveNormal, 1024) { _pc.printf("Welcome.\r\n"); } void start_comm() { _RUN = true; _pc.attach(callback(this, &Comm::serialISR)); // attach ISR start(callback(this, &Comm::commInFn)); } }; //Main int main() { Comm comm_plz; comm_plz.start_comm(); while (1) { } }