Mister Mystère
Mister Mystère

Reputation: 1002

Windows serial port reading is extremely slow compared to theory

I am trying to make a simple program which sends 9 bytes of data over a COM port to a microcontroller, then waits for 11 bytes of data from the microcontroller. The entire process has to occur within 500µs, which is feasible at 115200 bauds (edit: sorry, feasible at 500kbauds, but this test is done at 115.2k).

However, I have measured the duration of an iteration (which yields correct results) using QueryPerformanceCounter and the total duration (dominated by reading) varies wildly between 4 and 6ms instead of 1.7ms - even at 500kbauds the duration stays around 4-5ms! How is this possible? How can I remedy to this?

I have tested ALL libraries I could find out there, they all yield similar results (or do not work at all when reading), but here is an example with Boost on MSVC. I am all ears if you have tested anything on your side which works.

PC:

#include "stdafx.h"
#include <iostream>
#include <boost/asio/serial_port.hpp> 
#include <boost/asio.hpp> 

using namespace std;

int main()
{
    try 
    {
        boost::asio::io_service io;
        boost::asio::serial_port arduino(io, "COM3");
        arduino.set_option(boost::asio::serial_port_base::baud_rate(115200));

        LARGE_INTEGER frequency;
        QueryPerformanceFrequency(&frequency);
        LARGE_INTEGER start;
        QueryPerformanceCounter(&start);

        //Send first set of duty cycles first
        char dc[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        boost::asio::write(arduino, boost::asio::buffer(dc, 9));
        char buf[11] = { 0 };
        boost::asio::read(arduino, boost::asio::buffer(buf, 11));

        for (unsigned int i = 0; i < 11; ++i) {
            printf("%d ", buf[i]);
        }

        LARGE_INTEGER end;
        QueryPerformanceCounter(&end);
        double interval = static_cast<double>(end.QuadPart - start.QuadPart) / frequency.QuadPart * 1000000; // in seconds
        printf("(%f us)\n", interval);

    }
    catch (boost::system::system_error& e)
    {
        cout << "Error: " << e.what() << endl;
        return 1;
    }

    return 0;
}

/* Output:
0 1 2 3 4 5 6 7 8 -1 7 (6234.500000 us)
*/

Arduino:

#include <Arduino.h>

#define BAUDRATE 115200
#define N_BYTES_IN 9
#define PERIOD_US 1200

unsigned int ssiDummyVal = 2047;

void setup() {
  Serial.begin(BAUDRATE);
  while(Serial.available()); //Flush buffer (need to reset the arduino after connection and before transmission of bytes).
  pinMode(LED_BUILTIN, OUTPUT); //Dummy output
}

unsigned long t;

void loop() {
  t = micros();
  
  while(Serial.available() < N_BYTES_IN); //Wait for duty cycles

  //Receive duty cycles
  unsigned char dc[N_BYTES_IN];
  for(unsigned int i = 0 ; i < N_BYTES_IN ; ++i)
  {
    dc[i] = Serial.read();
    analogWrite(LED_BUILTIN, dc[i]);
  }

  //Send dummy current values
  for(unsigned int i = 0 ; i < N_BYTES_IN ; ++i)
  {
    unsigned int val = float(analogRead(A0))/1023*255;
    Serial.write(i);
  }

  //Bit-bang SSI interface TODO

  //Finally send SSI value as 2 bytes
  unsigned char ssiValLSB = ssiDummyVal & 255;
  unsigned char ssiValMSB = (ssiDummyVal >> 8) & 255;
  Serial.write(ssiValLSB); //LSByte first
  Serial.write(ssiValMSB); //MSByte second

  unsigned long dt;
  //Only for the 10 first samples to avoid clogging the screen
  static unsigned int sample = 0;
  while((dt = abs(micros()-t)) < PERIOD_US) //Estimate how much time margin we have
  {
    if(sample < 10) { 
      //Serial.write(int(float(dt)/PERIOD_US*255)); //Gives the fraction of the period_us used to do the iteration
    }
  }
  sample++;
}

Upvotes: 0

Views: 1090

Answers (1)

Ben Voigt
Ben Voigt

Reputation: 283803

Your math is wrong.

200 serial symbols (110 out, 90 back, because your 8-N-1 configuration uses 10 timeslices per byte... 1 start, 8 data, 1 stop) at 115200 baud will take over 1.7 milliseconds just for the data transfer. That allows no time for interrupt handling and thread scheduling or for the microcontroller to receive its message and compose a response.

Upvotes: 2

Related Questions