2

Device description (for context, skip it if you don't feel comfortable with electronic):

For a simple device, the communication is done in half-duplex UART (TX and RX are on the same wire), in the following way:

  • One pin (write-mode) indicate if the UART is sending or receiving (1: TX, 0:RX)
  • One pin write to the wire (TX).
  • One pin read from the wire (RX).

When the write-mode is in TX (writing), the RX pin is in high-impedance and TX in transmitting. While the write-mode is in RX (reading), the TXpin is in high-impedance and RXreceiving.

This is just for context, I do not expect electronic question/answers here.

WiringPI sample:

For this to happens, I have the following sample:

#include <wiringPi.h>
#include <wiringSerial.h>

int main()
{
    wiringPiSetup ();
    auto fd = serialOpen ("/dev/ttyAMA0", 115200);
    
    pinMode(0, OUTPUT);
    
    for(size_t i=0; i<10; ++i)
    {
        digitalWrite(0, HIGH);
        serialPutchar(fd, '\x55');
        digitalWrite(0, LOW);
        delay(1000);
    }

    serialClose(fd);
}

Using an oscilloscope, I can clearly see that the write-mode pin is reset before the UART end to send the data.

Obviously, I tried to add some "delay" or empty-loop to adjust, but this is not reliable for μs times (due to usual precision in timers on OS).

The question:

How to synchronize, so the write-mode pin is reset just after the UART byte is sent? (No more than approximately 150μs later).

Adrian Maire
  • 14,354
  • 9
  • 45
  • 85
  • I cannot see a reason why this is tagged c++? Looks like plain c code for me. – πάντα ῥεῖ Sep 08 '20 at 09:25
  • 1
    Actually it is not standard C (auto is not used in it C semantic with implicit int, but as C++11 keyword).. The important is that both languages would match the question. – Adrian Maire Sep 08 '20 at 09:44
  • Using C++11 `auto` in embedded systems is plain dangerous. It's questionable to use it in pretty much any context. – Lundin Sep 08 '20 at 10:04
  • Wth is "write mode" anyway? Use RTS/CTS hardware handshaking, these things have been standardized for 60 something years... And what about signal ground? Connecting a battery driven Rasp to a stationary PC ain't gonna work without signal ground. – Lundin Sep 08 '20 at 10:06
  • @AdrianMaire Ooops, don't worry. I've been overlooking the auto` keyword anyways. But that would invalidate the c tag IMO. Language tags shouldn't be abused to address a bigger audience. – πάντα ῥεῖ Sep 08 '20 at 10:41
  • @πάνταῥεῖ Language tags is accepted when used to specify the language used for a specific problem or library (https://meta.stackoverflow.com/a/364198/903651). Aka, wiringPi is also available in e.g. Python, but that answer would not fit, however both `C` or `C++` in any of their versions (e.g. c++20) would fit. – Adrian Maire Sep 08 '20 at 11:05
  • @πάνταῥεῖ It is not C, `int main()` is not valid in C but in C++. – 12431234123412341234123 Sep 08 '20 at 12:43
  • @AdrianMaire `auto` is C and C++, but they have different meanings. In C auto is a storage class specifier and basically means on stack. On C++ it means the the compiler has to decide which type is correct. In this case, it would be `int` in C and C++ – 12431234123412341234123 Sep 08 '20 at 12:44
  • @12431234123412341234123 Whatever, it's a minor concern regarding the question. I'll upvote it, and I am OK with the tagging as is. – πάντα ῥεῖ Sep 08 '20 at 13:48
  • To be honest, I expected(probably wrongly) wiringPi to be doing more optimal (simpler and closer to electronic) access to BCM2835 than standard buffered posix write()/read(). My first try was to implement bare-metal access to registers, but that is a pain because it requires deep learning of the hardware. It has been possible up to some degree, but for UART access, I wanted to give a try to this lib. – Adrian Maire Sep 08 '20 at 14:22
  • Hello, Adrian Maire did you get your problem solved? if yes, Can you please show me working example? – mastermind.pk Jul 26 '21 at 07:23
  • Hi @mastermind.pk: No, I could not find a solution for this. I end-up designing a physical layer (electronic circuit) to convert RX/TX to a single wire half-duplex. You can find the project: https://github.com/Escain/HiwonderRPI and question about electronic here: https://electronics.stackexchange.com/questions/518865/how-to-interface-uart-servo-lx-224hv – Adrian Maire Jul 26 '21 at 08:32

2 Answers2

2

I see 2 ways to implement this:

1. I can't test this right now, but it seems you could use the

 void serialFlush (int fd) ;

According to the docs "This discards all data received, or waiting to be send down the given device" see at http://wiringpi.com/reference/serial-library/

(Edit: after re-reading that sentence, it's clear that it would indeed flush also the data to be written, so this option is out...)

  1. Using tcdrain() (https://linux.die.net/man/3/tcdrain), where you pass the fd given back by serialOpen()
zgyarmati
  • 1,135
  • 8
  • 15
  • Indeed, serialFlush does not block, but just "clear" the buffer. – Adrian Maire Sep 08 '20 at 09:50
  • `tcdrain` looks promising (I will test it and accept/comment) – Adrian Maire Sep 08 '20 at 09:52
  • This works, but definitively don't match timing requirements, it is about the same jiter than using timers/scheduling. – Adrian Maire Sep 09 '20 at 08:00
  • That's unfortunate... Would you be able to use the RTS (GPIO17) / CTS (GPIO16) pins of the UART port, or you have to stick to a normal GPIO? If the RTS/CTS pins are available, then you could also try to enable hw hadshaking (after all, it's exactly for this kind of usecases...) – zgyarmati Sep 10 '20 at 19:58
  • Good idea, I will give it a try. – Adrian Maire Sep 11 '20 at 07:45
  • RTS/CTS are more related to the status of the UART (e.g. busy/available), than the transmission/reception state. I finally implemented this behaviour by hardware with a 555 monostate retriggerable timer of 0.1ms (but this is off-topic for SO so I don't put the answer here). – Adrian Maire Oct 05 '20 at 09:50
1

Set blocking on the file descriptor.

How to restore file descriptor operation to blocking mode can be found in many places. You may use code from this answer and do:

set_blocking_mode(fd, 1);
serialPutchar(fd, '\x55');

After that write() inside serialPutchar will be blocking. Note that fd is opened with O_NONBLOCK in serialOpen().

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Could you put a working example? I am not able to find any `set_blocking_mode`, neither blocking argument to serialOpen – Adrian Maire Sep 08 '20 at 17:30
  • The linked answer has the whole implementation of `set_blocking_mode` function. `neither blocking argument to serialOpen` there is no blocking argument, seralOpen doesn't let you specify the mode. `Could you put a working example?` https://stackoverflow.com/questions/914463/how-to-make-a-file-descriptor-blocking – KamilCuk Sep 08 '20 at 17:36