Serial Comm Ports for ever and ever… (Part 6)
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