Serial Comm Ports for ever and ever… (Part 6)

Part 12345678

There is so much to say about Serial Communication ports! Remember, the starting point for these posts was the need for a serial port to be used in a ODBII ECU interface, capable of 5 to 10400 baud, 7 to 8 bits, 1 stop bit, no parity.

The initialization protocol includes a fancy 0x55 byte for UART clock synchronization. So that, in order to make the interface really versatile, I designed a compact function which reads this byte and returns the baud rate.

// Estimate a baud rate based on reading of 0x55 (B01010101 in binary) which turns to: 
// ------___---___---___---___---___-------
//  idle sta 1  2  3  4  5  6  7  8 stp  idle
// The evaluation is in fact based on the 3 first bits duration (data bits are ranging from 5 to 8) + the start bit
// Time out applies to the time needed for the reading of the whole byte 
long estimatedBaudeRate(int bits, int timeOut, byte *ptrErrorCode) {
  byte lcl_errorCode = ERR_NONE;
  int baudRate = 0; // Set variable and default value
  long time_start; // The falling edge of the start bit
  long time_stop; // The falling edge of the 4th bit
  boolean timeoutHasOccured = false;
  long abortTime = (millis() + timeOut);
  for (int i=0; i < 5; i++) {  
    byte expectedLevel = i & 0x01; // Compute the expected level (i modulo 2)
    while ((GetLevel(PINB, RxPin) != expectedLevel) && !timeoutHasOccured) { // Wait for a logical low level
      if (millis() > abortTime) 
        timeoutHasOccured = true;
    }
    long now = micros();
    if (i == 0)
      time_start = now;
    else if (i == 4)
      time_stop = now;
  }
  if (timeoutHasOccured) {
    lcl_errorCode = ERR_TIMEOUT;
  }
  else {
    long interval = (time_stop - time_start) >> 2; // Divide sum of intervals by 4 in order to get the average interval duration
    long time_ByteEnd = (time_start + ((bits + 2) * interval));
    while (micros() < time_ByteEnd); // Flush remaining bits 
    baudRate = long(1000000 / interval); // Compute the baudrate based on intervals duration expressed in µs
  }
  *ptrErrorCode = lcl_errorCode;
  return(baudRate); 
}

The maximal error measured was 1% at 10400 baud which is far enough. On completion of estimatedBaudeRate(), all bits should have gone through. So that a new byte reading could occur just after the execution of this function.

Here is a suggestion for the test loop:

  writeByte(0x55, 10400, 8); // Send Bytes
  long result = estimatedBaudeRate(8, 30, &errorCode);
  if (errorCode == ERR_NONE) {
  Serial.print("Estimated baud rate: ");
  Serial.println(result, DEC);
  }
  else {
    Serial.print("Error code:    ");
    Serial.println(errorCode, DEC); // Print error code
  }

errorCode is a global variable (byte type)
ERR_xxx are custom constants

Next post on same subject

Leave a Reply

You must be logged in to post a comment.