Trying to understand documentation for set_flow_control

I need to use hardware flow control but I am struggling to get it to work. Never done this before with MBED OS.

I’m using an LPC1768 board and MBED OS 6.3 and coding using Mbed Studio
I’m looking to send just 1 byte at a predefined interval.

My code compiles and can flash onto the board but it is causing a MBED OS runtime crash.

++ MbedOS Error Info ++
Error Status: 0x80010133 Code: 307 Module: 1
Error Message: Mutex: 0x100014AC, Not allowed in ISR context
Location: 0x635B
Error Value: 0x100014AC
Current Thread: rtx_idle Id: 0x10001934 Entry: 0x3D6D StackSize: 0x200 StackMem: 0x10001A00 SP: 0x10007EC4

Documentation states (but do not understand the syntax, i.e. what’s it saying exactly, and no example is given):

void set_flow_control ( Flow type ,
PinName flow1 = NC ,
PinName flow2 = NC
)

Parameters are:

type the flow control type (Disabled, RTS, CTS, RTSCTS)
flow1 the first flow control pin (RTS for RTS or RTSCTS, CTS for CTS)
flow2 the second flow control pin (CTS for RTSCTS)

Decided to use unbufferedSerial as timing critical.

Set up code as per example given in documentation and just added in flow control: https://os.mbed.com/docs/mbed-os/v6.5/apis/unbufferedserial.html

#include "mbed.h"

// Create a DigitalOutput object to toggle an LED whenever data is received.
static DigitalOut led(LED1);

// Create a UnbufferedSerial object with a default baud rate.
static UnbufferedSerial serial_port(p13, p14);

void on_rx_interrupt()
{
    char c;

    // Toggle the LED.
    led = !led;

    // Read the data to clear the receive interrupt.
    if (serial_port.read(&c, 1)) {
        // Echo the input back to the terminal.
        serial_port.write(&c, 1);
    }
}

int main(void)
{
    // Set desired properties (9600-8-N-1).
    serial_port.baud(9600);
    serial_port.format(
        /* bits */ 8,
        /* parity */ SerialBase::None,
        /* stop bit */ 1
    );

    // Set up flow control -----------------------------------------------------------
   serial_port.set_flow_control(mbed::UnbufferedSerial::RTSCTS, p12, p11);

    // Register a callback to process a Rx (receive) interrupt.
    serial_port.attach(&on_rx_interrupt, SerialBase::RxIrq);
}

Apologies… The code actually works!

I had inserted a printf statement where I shouldn’t have (you can guess where)…

Hello Gerriko,

Are your sure that it work? Even if you remove a printf from the ISR, the UnbufferedSerial's read method calls Mutex'es lock and unlock methods:

ssize_t UnbufferedSerial::read(void *buffer, size_t size)
{
    unsigned char *buf = static_cast<unsigned char *>(buffer);

    if (size == 0) {
        return 0;
    }

    lock();

    buf[0] = _base_getc();

    unlock();

    return 1;
}

And that is not allowed in ISR context, as it’s explained in the related Mbed documentation. Although you say the timing is critical, I think UART is a “slow” device so you can try to defer the execution of receive handler to a different thread by using an EventQueue. For example:

#include "mbed.h"

// Create a DigitalOutput object to toggle an LED whenever data is received.
DigitalOut          led(LED1);

// Create a UnbufferedSerial object with a default baud rate.
UnbufferedSerial    serial_port(p13, p14);
Thread              thread;
EventQueue          eventQueue;

void onSerialReceived(void)
{
    char    c;

    // Toggle the LED.
    led = !led;

    // Read the data to clear the receive interrupt.
    if (serial_port.read(&c, 1)) {
        // Echo the input back to the terminal.
        serial_port.write(&c, 1);
    }
}

void onRxInterrupt()
{
    // Defer execution of receive handler to the context of thread "thread" by EventQueue
    eventQueue.call(onSerialReceived);
}

int main(void)
{
    // Set desired properties (9600-8-N-1).
    serial_port.baud(9600);
    serial_port.format( /* bits */ 8, /* parity */ SerialBase::None, /* stop bit */ 1);

    // Set up flow control -----------------------------------------------------------
    serial_port.set_flow_control(mbed::UnbufferedSerial::RTSCTS, p12, p11);

    // Register a callback to process a Rx (receive) interrupt.
    serial_port.attach(&onRxInterrupt, SerialBase::RxIrq);
    
    // Start thread and dispach event queue forever.
    thread.start(callback(&eventQueue, &EventQueue::dispatch_forever));
}

Best regards, Zoltan

Yes the original code is working. The LED toggles with each byte sent and I then receive back the byte as an echo.

Thank you. You are right and I was wrong! It’s because the lock() and unlock() functions do not use/call the Mutex. Actually, they do nothing:

void SerialBase::lock()
{
    // Stub
}

void SerialBase:: unlock()
{
    // Stub
}
1 Like

I still prefer the old RawSerial + circularbuffer when needed.