Note that there are some explanatory texts on larger screens.

plurals
  1. POAsynchronous IO on serial port
    primarykey
    data
    text
    <p>I need your advices in designing a fully asynchronous IO on a half-duplex serial port. Currently I have a reader thread and many writer threads controlled by semaphores and a mutex. Now I want to simplify synchronization by eliminating threads. The main problem is that serial port IO has a strange behavior.</p> <p>All I need is to make sure that <code>read</code> and <code>write</code> system calls block only calling thread until IO operation is actually done. I'm assuming <code>read</code> is a blocking system call by default. Though I'm getting <code>-1</code> as return of <code>read</code>. There is a strange <code>EBUSY</code> error which I have no description for. Current code:</p> <pre><code>bool SerialManager::initialize(const PortType&amp; portType, const size_t&amp; number) { // Open Serial port (/dev/ttyS2 in this case) fd = open(portName.str().c_str(), O_RDWR ); //O_NOCTTY if (fd &lt; 0) // if open is not successful { cerr &lt;&lt; ERROR &lt;&lt; "Unable to open `" &lt;&lt; portName &lt;&lt; "'." &lt;&lt; endl; return false; } else { cout &lt;&lt; INFO &lt;&lt; "Port " &lt;&lt; portName.str() &lt;&lt; " successfully opened." &lt;&lt; endl; cout &lt;&lt; INFO &lt;&lt; "Configuring port..." &lt;&lt; endl; fcntl(fd, F_SETFL,~O_NONBLOCK); struct termios port_settings; // structure to store the port settings in cfsetispeed(&amp;port_settings, B38400); // set baud rate cfsetospeed(&amp;port_settings, B38400); // set baud rate port_settings.c_cflag |= CLOCAL | CREAD; port_settings.c_cflag &amp;= ~CRTSCTS; // disable H/W flow control port_settings.c_lflag &amp;= ~( ISIG | // disable SIGxxxx signals IEXTEN | // disable extended functions ECHO | ECHOE); // disable all auto-echo functions port_settings.c_lflag &amp;= ~ICANON ; // raw mode port_settings.c_oflag &amp;= ~OPOST; // raw output port_settings.c_iflag &amp;= ~(IXON | IXOFF | IXANY); // disable S/W flow control; port_settings.c_cc[VTIME] = 20; // wait 0.1 second to get data port_settings.c_cc[VMIN] = 0; port_settings.c_cflag = (port_settings.c_cflag &amp;= ~CSIZE) | CS8; // set data byte size port_settings.c_cflag &amp;= ~CSTOPB; // set stop bit 1 port_settings.c_cflag &amp;= ~PARENB; // set no parity port_settings.c_iflag |= IGNPAR; // ignore parity port_settings.c_iflag &amp;= ~(INPCK | ISTRIP | PARMRK); // Set if (tcsetattr(fd, TCSANOW, &amp;port_settings) &lt; 0) { cerr &lt;&lt; ERROR &lt;&lt; "Unable to configure serial port." &lt;&lt; endl; return false; } else { cout &lt;&lt; INFO &lt;&lt; "Port `" &lt;&lt; portName.str() &lt;&lt; "' configuration was successful." &lt;&lt; endl; return true; } } } </code></pre> <p>To write data:</p> <pre><code>int SerialManager::asyncWriteData(const byte* data, const size_t&amp; size) { int writeSize = write(fd, data, size); return writeSize; } </code></pre> <p>For read:</p> <pre><code>void SerialManager::asyncRead(byte* buffer, const size_t&amp; size, bool&amp; ok) { byte temp[256]; ssize_t packetSize = read(fd, temp, 256); if (packetSize &gt; 0) { for (size_t i = 0; i &lt; size; ++i) buffer[i] = temp[i]; ok = true; } cout &lt;&lt; errno &lt;&lt; endl; perror("Error occured: "); // &lt;=== Here I'm getting EBUSY (code 16) ok = false; } </code></pre> <p>Using <code>SerialManager</code> class outside:</p> <pre><code>.... word checksum = this-&gt;id + 0x2C; checksum = ~checksum; // Send read command byte command[] = { 0xff, // heading 0xff, // ~ this-&gt;id, // id of actuator 0x04, // length 0x02, // instruction: read 0x24, // start address: present position 0x02, // data length static_cast&lt;byte&gt;(checksum) //checksum }; SerialManager::lockPort(); // lock a mutex to avoid collitions int numbytes = SerialManager::asyncWriteData(command, 8); if (numbytes &lt; 0) { cerr &lt;&lt; ERROR &lt;&lt; "Could not write to serial port." &lt;&lt; endl; return 0; } cout &lt;&lt; INFO &lt;&lt; numbytes &lt;&lt; " bytes has been written." &lt;&lt; endl; for (size_t i = 0; i &lt; 8; ++i) { cout &lt;&lt; hex &lt;&lt; setfill('0') &lt;&lt; setw(2) &lt;&lt; (int) command[i] &lt;&lt; ' '; } cout &lt;&lt; endl; byte* data = new byte[8]; bool ok; // Here I need to make sure data write is completed before start reading SerialManager::asyncRead(data, 8, ok); if (ok) { word position = data[5] + (static_cast&lt;word&gt;(data[6]) &lt;&lt; 8); return position; } else { cerr &lt;&lt; ERROR &lt;&lt; "Unable to read data from serial port..." &lt;&lt; endl; return -1; } SerialManager::unlockPort(); // Unlock previously locked mutex .... </code></pre> <p><strong>Update:</strong></p> <p>I removed reader thread which makes no sense. Because we have a half-duplex line with no control over transmission. There are two problems with synchronous IO:</p> <ol> <li><p>Sending a very long data from controller to actuators, first actuator responds unresponsively while data is on port: <img src="https://i.stack.imgur.com/P7jPx.png" alt="First"></p></li> <li><p>An actuators may respond while another one's data is not transmitted completely <img src="https://i.stack.imgur.com/EvDFu.png" alt="Second"></p></li> </ol> <p>Also problem with <code>EBUSY</code> is solved by adding a <code>fsync</code> after <code>write</code>. Which is somehow what I need. (A blocking <code>write</code>):</p> <pre><code>int SerialManager::asyncWriteData(const byte* data, const size_t&amp; size) { ssize_t packetSize = write(fd, data, size); if (packetSize &gt; 0) { fsync(fd); } return packetSize; } </code></pre> <p>from man fsync:</p> <blockquote> <p>fsync() transfers ("flushes") all modified in-core data of (i.e., modified buffer cache pages for) the file referred to by the file descriptor fd to the disk device (or other permanent storage device) where that file resides. The call blocks until the device reports that the transfer has completed. It also flushes metadata information associated with the file</p> </blockquote>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload