ARMmbed / mbed-os

Arm Mbed OS is a platform operating system designed for the internet of things
https://mbed.com
Other
4.68k stars 2.98k forks source link

Async methods in RawSerial non accessible in UnbufferedSerial #15305

Open mcesar-rlacruz opened 2 years ago

mcesar-rlacruz commented 2 years ago

Description of defect

Some asynchronous methods in the now deprecated class RawSerial are no longer accessible from the new class UnbufferedSerial, as now it inherits privately from SerialBase, whereas previously RawSerial inherited publicly. Such methods are read() (the callback and char matching ones), write() (the callback one) and others.

Target(s) affected by this defect ?

Detected on DISCO_L072CZ_LRWAN1, but apparently universal.

Toolchain(s) (name and version) displaying this defect ?

ARMC6 6.16

What version of Mbed-os are you using (tag or sha) ?

6.16.0-rc1

What version(s) of tools are you using. List all that apply (E.g. mbed-cli)

mbed Studio 1.4.4, mbed-cli, Keil uVision 5, apparently universal

How is this defect reproduced ?

UnbufferedSerial gps;
gps.SerialBase::read(...);
g_gps.set_break();

Will report that read() and set_break() are private members of SerialBase.

0xc0170 commented 2 years ago

Unbufferedserial provides read/write, exposing some parts of SerialBase but not all. You can describe why it should expose read/write.

What's the use case? Is this about asynch methods read/write from SerialBase?

mcesar-rlacruz commented 2 years ago

Hi,

My particular use case is receiving NMEA messages inside a system with various talkers and, yes, it is about (but not only) the now hidden async read/write methods from SerialBase. RawSerial::read() allowed launching an async RX transfer to be terminated after receiving a fixed amount of data or when some magic char is received, but this last capability is now lost (but the code is there, inside SerialBase!). Maybe I can circumvent that by registering a callback with UnbufferedSerial::sigio() that checks all arriving chars to be equal to the magic one, but this will led to my callback being executed on each char RX, whereas previously all that was managed transparently by RawSerial, that called the callback I supplied and, even more, told me if the amount of data was exhausted, or the magic char was found, or there was a frame error in the port or...

Also, I see no way now to:

These are the reasons I think that SerialBase::read/write (and also xxx_break() and abort_xxx()) should be user accessible.

Finally, I had many problems with this system when migrating from mbed 5 to mbed 6 until I decided to change from private SerialBase to public SerialBase in the declaration of class UnbufferedSerial inside file UnbufferedSerial.h. C'mon, the code is there, let us use it ;-)

Thanks & regards.

EDIT: now in my local mbed 6 I switched back to private SerialBase, but added more using declarations to re-gain visibility for the methods I do need.

mcesar-rlacruz commented 2 years ago

Hi again,

I tried using sigio() to register a callback to be called at each character RX. Maybe it is that I do not understand it, but I cannot get the callback called.

I see no way to solve this communication problem with the new UnbufferedSerial class, a problem that was solved previously with RawSerial.

I have also found a previous issue about this: #13388

And a thread in the forum describing another possible scenario.

Thanks & regards.

mcesar-rlacruz commented 2 years ago

Still looking into this. Table below shows the methods inside UnbufferedSerial and their true meaning:

Method Does
read reads 1 char using _base_getc(), blocking
write writes many chars using _base_putc(), blocking
set_flow_control calls SerialBase::set_flow_control()
seek nothing, returns -ESPIPE
close nothing, returns 0
sync nothing, returns 0
isatty nothing, returns true
rewind nothing, returns -ESPIPE
tell nothing, returns -ESPIPE
size nothing, returns -EINVAL
truncate nothing, returns -EINVAL
set_blocking nothing, returns 0 or -ENOTTY
is_blocking nothing, returns true
enable_input calls SerialBase::enable_input(enabled);
enable_output calls SerialBase::enable_output(enabled);
poll calls SerialBase::readable()/writable();
writable calls SerialBase::readable()
readable calls SerialBase::writable()
sigio nothing

So, basically, UnbufferedSerial is an envelope for getc()/putc(), but definitely not a replacement for the deprecated RawSerial.

Knowing this I tried to re-phrase my problem with BufferedSerial. The results are slightly better (at least sigio() works, but it is not even comparable to the old attach() that at least distinguished between RX and TX IRQs), but lacking all of the previous functionality inside RawSerial I described above.

My perception is that now on mbed6, simply, there is no direct way to gain some low level control over serial ports.

Fortunately SerialBase is documented and one can inherit his/her own UART class from it to cope with this. But I wonder if, seeing the current state of affairs, the low level methods on it will remain inside mbed in the future.

0xc0170 commented 2 years ago

I have also found a previous issue about this: https://github.com/ARMmbed/mbed-os/issues/13388

Good found. I recall seeing this before but no agreement.

You can use sgio and handle it by yourself (I found few pointers quickly, there should be however the functionality provided somewhere already): https://github.com/ARMmbed/mbed-os/issues/5035

I don't have anything at hand now unfortunately.

mcesar-rlacruz commented 2 years ago

Thanks. Unfortunately the problem described on #5035 was finally solved using a read() method resembling the now hidden read() method inside SerialBase.