Serial Comm Ports for ever and ever… (Part 7)
Oh nooooo. I missed that. Eventually, my serial comm port requires parity checking! This unexpetecd requirement forced me to revise the whole concept of bytes and bits reading in order to take into account the time necessary to compute parity and check it.
Here is a little reminder on parity
Parity helps insuring that the data bits received have not been altered during transmission. An extra bit is added next to data bits, which will be checked at arrival and compared to what it would be. The parity may be one of the following options
- ‘N’: none, no parity bit, no checking
- ‘M’: mark, always add a ‘1’ bit
- ‘S’: space, always add a ‘0’ bit
- ‘E’ or ‘O’: even or odd. Means that the number of data bits (1s) AND the parity bit must be an even or an odd number.
Example:
Transmit our good ol’ 0x55 which is: [lsb] 1010101 [msb] in binary. You can count four 1s, the parity of data bits is even. Now, if we decide to transmit in ‘E’ (Remember, even) mode, we do not need to add any other 1 to keep the parity even. So that the parity bit shall be ‘0’ and the data bits combined with the parity bit will look like: [lsb] 10101010 [msb]. If we decide to transmit in ‘O’ (Remember, odd) mode, we DO need an other 1 to make the parity odd. So that the parity bit shall be ‘1’ and the data bits combined with the parity bit will look like: [lsb] 10101011 [msb]
Computing parity is as easy as XORing bits, just like:
byteParity = b0 ^ b1 ^ … bn;
Setting the parity bit is also very easy:
// even mode parityBitValue = (byteParity & 0x01); // odd mode parityBitValue = (~byteParity & 0x01);
These are the new functions and their overlaods.
The writing functions:
void writeBytes(byte *ptrData, int bytes, int bytesOffset, long baudRate, int bits, char parity, int stopBits) { // Write bytes to the Tx line of a custom serial port int parityBitOffset = 0; int startBitOffset = 1; for (int j=0; j < bytes; j++) { int byteValue = ptrData[bytesOffset+j]; byte bitValue; int compositeBits = byteValue; // Initialize composite bits with data bits the LSB is 0 as start bit compositeBits <> i) & 0x01); } // Parity bit 'E': Even, 'M': Mark (1), 'N': None, 'O': Odd, 'S': Space (0) switch (parity) { case PAR_EVEN: // Make the whole set of bits even bitValue = (parityValue & 0x01); break; case PAR_MARK: // Fixed level bitValue = 0x01; break; case PAR_ODD: // Make the whole set of bits odd bitValue = (~parityValue & 0x01); break; case PAR_SPACE: // Fixed level bitValue = 0x00; break; } compositeBits |= (bitValue << (bits + startBitOffset)); parityBitOffset = 1; } // Append stop bit(s) for (int i=0; i < stopBits; i++) { compositeBits |= (1 << (startBitOffset + bits + parityBitOffset + i)); } // Transfer bits long time_interval = long(1000000 / baudRate); // in µs long time_endOfBit; long time_startOfByte = micros(); // Set reference time for (int i=0; i < (startBitOffset + bits + parityBitOffset + stopBits); i++) { bitValue = ((compositeBits >> i) & 0x01); // Extract bit value SetLevel(PORTB, TxPin, bitValue); // Set bit on Tx Line time_endOfBit = time_startOfByte + (time_interval * (i + 1)); while (micros() < time_endOfBit); // Wait until interval time has elapsed } } } // Overloaded functions of writeBytes void writeByte(byte byteValue, long baudRate, int bits, char parity, int stopBits) { // Overlaoded function of writeBytes: Write one byte to the Tx line of a custom serial port byte value = byteValue; writeBytes(&value, 1, 0, baudRate, bits, parity, stopBits); } void writeBytes(byte *pdata, int bytes) { // Write one or more byte(s) Simplified function writeBytes(pdata, bytes, 0, def_baudRate, def_bits, def_parity, def_stopBits); }
and the reading functions:
int readBytes(byte *ptrData, int bytes, int bytesOffset, long baudRate, int bits, char parity, int stopBits, int timeOut, byte *ptrErrorCode) { // Read one or more incoming bytes // Timout applies to each incoming byte // Returns number of received bytes byte lclErrorCode = ERR_NONE; // Set error code byte parityBitValue; int nbrReadBytes = 0; // Number of received bytes (may be # from the expected number of bytes) int parityBits = 0; // Number of parity bits if (parity != PAR_NONE) parityBits = 1; long time_startOfByte; long interval = long(1000000 / baudRate); // Calculate sensing time intervals in µs for (int j=0; j < bytes; j++) { long abortTime = (millis() + timeOut); // Compute time above which timeout is triggered while (GetLevel(PINB, RxPin) && (lclErrorCode == ERR_NONE)) { // Loop while logical level is high and no error occured if (millis() > abortTime){ // Monitor timeout lclErrorCode = ERR_TIMEOUT; } } time_startOfByte = micros() + (interval >> 1); // now + half interval if (lclErrorCode == ERR_NONE) { byte parityValue = 0x00; // ParityValue = 1 if odd, 0 if even byte stopBitValue = 0x01; // Yes one! byte byteValue = 0x00; // Reset value for (int i=0; i < (bits + parityBits + stopBits); i++) { // Read all bits long time_nextBit = (time_startOfByte + (interval * (i + 1))); // Compute next half bit interval while (micros() < time_nextBit); // Wait bit interval byte bitValue = GetLevel(PINB, RxPin); // Sense bit level if (i < bits) { // Count data bits byteValue |= (bitValue << i); // Compute byte value (LSBs first) parityValue ^= bitValue; // Compute parity } else if (i < (bits + parityBits)) { // compute parity bit parityBitValue = bitValue; // Record parity bit value } else { stopBitValue &= bitValue; // Check stop bit(s); one or more 0 set stopBitValue to 0 } } nbrReadBytes++; // Increment number of read bytes ptrData[bytesOffset + nbrReadBytes - 1] = byteValue; // Record byte value in vector of data // Detect errors if (stopBitValue != 0x01) { lclErrorCode = ERR_FRAME; } else { switch (parity) { // Decode parity errors case PAR_EVEN: if (parityBitValue != (parityValue & 0x01)) lclErrorCode = ERR_PARITY; break; case PAR_MARK: if (parityBitValue != 0x01) lclErrorCode = ERR_PARITY; break; case PAR_ODD: if (parityBitValue != (~parityValue & 0x01)) lclErrorCode = ERR_PARITY; break; case PAR_SPACE: if (parityBitValue != 0x00) lclErrorCode = ERR_PARITY; break; } } } } *ptrErrorCode = lclErrorCode; // Update errorCode value return(nbrReadBytes); // Return received bytes } // Overloaded functions of readBytes int readByte(long baudRate, int bits, char parity, int stopBits, int timeOut, byte *ptrErrorCode) { byte result = 0x00; readBytes(&result, 1, 0, baudRate, bits, parity, stopBits, timeOut, ptrErrorCode); return(int(result)); }
Did you notice? The indent has changed… It is because I decided to use an external code editor which is much more convenient for writing complex code with plenty variables, pointers etc. I will get back with this option in a later post.