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

Part 12345678

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.

Leave a Reply

You must be logged in to post a comment.