Receiving with parity

I’ve never had to serially receive data without the use of the SoftwareSerial library, but I’m now running into interfacing with a device using an 8E1 transmission. From what I’ve read, the read() function only works for 8N1.

Can anyone point me to a good tutorial on receiving in C? I’m only partially familiar with C++ so I’d rather something in C that I can understand more readily.

I also have a question about the parity bit. I get the concept. I looked into the datasheet to see what registers I need to set, where the parity error flag is located, etc. My question is more fundamental. Where does this parity bit go upon receiving? Does it go into the buffer, and if so, does that change my data into a 9-bit structure that I have to separate?

I had it in my head that the parity bit would be a separate piece of data that wouldn’t interfere with the byte-by-byte reading from Serial.read() initially, but it seems considerably more complicated than that. I spent about 4 hours looking for answers to these question but I feel more confused now than when I started.

Parity generation / checking is typically implemented in the UART hardware. So, it would probably be a good idea to tell us what processor you're using.

a USART would typically provide in indication in some register that there is a parity bit error in the received byte. I haven't found any mechanism for determining a parity error when reading a character.

Normally when running with parity, you mainly want the parity bit automatically generated for you.
On receive, the parity is bit is checked, and if it's wrong the byte is either dropped in the driver, , or the error is simply ignored and the byte is read() as if there was no error, or an error condition is passed to the read() (somehow)
That last is least common - the arduino read() functions don't have any mechanism for reporting an error.
Ignoring character and ignoring the error are about equally common, in practice. Most uart protocols that actually need to do error checking usually have some higher level check, like a checlsum or CRC.

If you have some protocol that requires that you recognize bytes with the wrong parity, you'll probably need to modify the low-level drivers code (Serial or SoftwareSerial)

Thanks for the responses. Sorry, I thought I had thrown the chip in but I must've deleted that bit before posting. I'll add a bit more info to fill out the picture for you.

I'm running on an ATMega 2560 currently and I'm trying to interface with the 8E1 device via Serial2. The 8E1 device is transmitting small 3-byte packets of data, but I have no way to edit firmware on this device to add additional error detection such as CRC or checksums. The data coming in is slow - one byte every 10ms, with a 20ms gap between packets.

To be clear - while the parity check is something I would like to use to have some form of error detection, however incomplete that may be, I'm more interested in reading the data.

From what I've read in the datasheet for the 2560, this is what (I think) would need to be done for the parity check:

  • Set Serial2 to even parity by setting UPM21 = 1 and UPM20 = 0
  • As each byte is about to be read out of the buffer, check UPE2 to see whether there is parity
  • When sending return data, the parity generator automatically adds the parity bit as long as UPM21 = 1

The confusion for me comes from this line (direct quote) from the datasheet:

"The result of the check is stored in the receive buffer together with the received data and stop bits."

If that's the case, then my immediate thought as to why Serial2.read() wouldn't work is the size of the data in the buffer. Is the extra bit not being handled by the SoftwareSerial library?

This gets to the point of why I was interested in a guide to receiving from a buffer in C - as westftw mentioned, I would likely need to rewrite the libraries to handle this and I would like a better understanding of how to pull from the buffer. I would think anything that pulls an entire byte at a time from the buffer would either not be usable, or only be usable with additional filtering of the other bits in the buffer.

I'm not sure why I had such a hard time finding relevant information about this online; maybe I'm not searching for quite the right keywords or something. It seems like this would be a relatively common implementation.

Anyway, thanks for the help so far and I'll update if I make any sort of breakthrough with it. If I'm completely out of line with my thinking on any of this, please let me know.

I haven’t traced the code through in detail yet, but the following from HardwareSerial_private.h seems relevant:

void HardwareSerial::_rx_complete_irq(void)
{
  if (bit_is_clear(*_ucsra, UPE0)) {
    // No Parity error, read byte and store it in the buffer if there is
    // room
    unsigned char c = *_udr;
    rx_buffer_index_t i = (unsigned int)(_rx_buffer_head + 1) % SERIAL_RX_BUFFER_SIZE;

    // if we should be storing the received character into the location
    // just before the tail (meaning that the head would advance to the
    // current location of the tail), we're about to overflow the buffer
    // and so we don't write the character or advance the head.
    if (i != _rx_buffer_tail) {
      _rx_buffer[_rx_buffer_head] = c;
      _rx_buffer_head = i;
    }
  } else {
    // Parity error, read byte but discard it
    *_udr;
  };
}

So, characters with parity errors are dropped and never enter HardwareSerial’s read buffer. Seems to me you can just ensure that you get a 3-byte packet within the time allotted by the protocol without an intervening 20ms gap. If so, it’s a valid packet. If not, discard it and start looking for the next one. Doesn’t require any rewriting of the existing library.

I think that mostly answers my question. What you sent in conjunction with the datasheet info makes considerably more sense to me than my initial look.

As far as the receiving code based around Serial.read() goes, as well as a bunch of other SoftwareSerial library functions in general, I took a slightly deeper dive into the libraries and I think I have a better understanding of what's going on.

Digging through the Stream.cpp library helped a great deal; that seems like where more of the action is happening.

Thanks for the push in the right direction.