Break condition on serial

Hi everyone

I have the following two problems with a break condition on the serial device:

  1. Despite I set IGNBRK, I receive a break condition (3 times 0x00). (The break condition is caused by a baud rate mismatch, 4800 instead of 115200 baud)

  2. After I received this break condition, the serial device is somehow unavailable. I have to close and reopen the device in order to be able to receive further data. The serial device seems to be still open and the file descriptor is still associated with the device, but if I rearm the resource manager notifier (ionotify, see below) I do not get any pulse anymore.

Here follows my source code. I tried to reduce the code to its essential elements, therefore I eliminated all the error detection and handling.

Int32 openSerial(void)
{
	Int32 fd;
	struct termios ctrlSettings;

	fd = open("/dev/ser1", O_RDWR | O_NONBLOCK);

	tcgetattr(fd, &ctrlSettings);
	cfmakeraw(&ctrlSettings);
	
	ctrlSettings->c_iflag |= IGNBRK;   /* ignore break signals */
	ctrlSettings->c_iflag &= ~BRKINT;  /* disable break signal interrupt */

	ctrlSettings->c_cflag |= CREAD;	 /* enable read */
	ctrlSettings->c_cflag &= ~PARENB;  /* disable parity bit */
	ctrlSettings->c_cflag &= ~CSTOPB;  /* only one stop bit */
	ctrlSettings->c_cflag &= ~CSIZE;
	ctrlSettings->c_cflag |= CS8	    /* 8 data bits */

	/* set speed */
	cfsetospeed(&ctrlSettings, (speed_t)(serialComSettings->baudrate))
	cfsetispeed(&ctrlSettings, (speed_t)(serialComSettings->baudrate))
	
	tcsetattr(fd, TCSANOW, &ctrlSettings)
	
	tcflush(fd, TCIOFLUSH);
	
	return fd;
}

After opening the serial device, I arm the resource manager:

ionotify(fd, _NOTIFY_ACTION_POLLARM, _NOTIFY_COND_INPUT, event)

and when I get notified, I read the bytes:

Int32 getSerial(Int32 fd, Uint8* data, Uint16 bytesToRead)
{
	Int32 bytesRead = 0;

    bytesRead = (Int32)read(fd, data, bytesToRead);

	if(isBreakCondition(data, bytesReceived) == true){
		printf("break condition detected\n\r");    /* debug info */
		printf("is a tty = %d \n\r", isatty(fd));  /* console output: true */
	}

  return bytesRead; 
}

Has anyone an idea what could cause these problems?

I’m not sure why a baud rate error would appear as a break. I don’t know how QNX 6 handles framing errors, which is what I think this is. It is plausible that this misinterpretation might occur.

As to the stuck condition, I’d look into whether the device is paused such as when one receives XOFF. If you stty the device, there’s some bit turned on when this happens, pended or some such.

Thanks for the answer, maschoen!

Problem 1)
A break condition is interpreted if the line is put on space for longer than a frame duration. A frame duration on 115200 (8N1) is approx 86.8us, which is less than a single “space” on 4800 baud (208us). Well, of course this is also a framing error.

Problem 2)
I will investigate this tomorrow.

Have a good day!

if(isBreakCondition(data, bytesReceived) == true){
What does this function do?

This function returns true if bytesReceived == 3 and all bytes contain 0x00.

This is what stty gives me after the “break condition” was received.

# stty < /dev/ser1 
Name:  /dev/ser1
Type:  serial
Opens: 2
+raw +clocal +ignbrk +imaxbel +onlcr
+ihflow +ohflow
 intr=^C  quit=^\ erase=^?  kill=^U   eof=^D start=^Q  stop=^S  susp=^Z 
lnext=^V   min=01  time=00   pr1=^[   pr2=5B  left=44 right=43    up=41 
 down=42   ins=40   del=50  home=48   end=59 
par=none bits=8 stopb=1 baud=115200 rows=0,0

I don’t know why +onlcr +ihflow +ohflow are set, anyway, I set them to - but the behaviour stays the same.

Looks like a mystery to me. Have you tried +ignpar (Ignore parity errors)?

If its any consolation, Linux also returns null’s with IGNBRK and ~BRKINT.

To the uart, a break at 115200 baud is as short as 0.008 ms (10-bit times). A single character at 4800 baud is 24x longer (about 2ms). So obviously, most any character sent at 4800 is going to cause a “break” condition on the uart.

However, since the 115200 uart is sampling so much faster, it may also detect a variable number of VALID null characters when the 4800 baud signal transitions from a 0->1 or 1->0.

If you test characters received at 115200 against various characters sent at 4800, you will see that a different number of chars are framed up - depending on the bits in the character. For example produces two, ‘j’ gives four and ‘h’ gives three- the most common.

Bottom line, NULLs are framed up and since you disabled BRK processing in the uart, there is no way to know how they were created - so they are returned as characters.

I looked at this issue a little bit deeper in detail and it seems like your statement is right, denkelly.

Every transiton from 1 to 0 is interpreted as 0x00. If I send 0x10 I receive 2 times 0x00, once for the start bit (during idle the line is high) and once for the transition within the byte.

According to that, I receive 5 times 0x00 if I send 0x55 (0b01010101, LSB is sent first). I tried this with a couple other values.

It does not matter if I enable or disable IGNBRK, I receive the exact same values for the same bytes.

Well I will handle problem #1 as follows: Enable IGNBRK and ignore incoming data if it equals 0x00.

Unfortunately I’m still stuck with problem #2.
I could simply close and reopen the connection every time I receive a 0x00 byte, but I do not feel very confortable with this workaround. Does anyone else have an idea what I could try or can I give you further information?

Yes I tried it, but it makes no difference.

I did get IGNBRK to work. Try this file: dl.dropbox.com/u/13676760/temp/readcond.c

Hi

I found the error. The following facts led to the discussed behavior:

  1. The resource manager can only be armed if the input buffer is empty
  2. The application that is using my serialCom reads byte by byte and not the whole buffer at once
  3. If the application reads 0x00, it decides to not further read data from the serialCom and calls MsgReceive()

This is what I found in the documentation:

What I will do now is something like that:


Uint16 getData(Uint8* data, Uint16 bytesToRead)
{
  Uint16 bytesRead = 0;

  bytesRead = getSerial(fd, data, bytesToRead);

  if (isInputBufferEmpty() == true){
    armComChannel();
  }

  return bytesRead;
}

readFromComChannel(){

  if (isInputBufferEmpty() == false){
    getData();
  } else {
    MsgReceive();
  }

}

Thanks a lot guys for helping me! I really appreciate it!

Regards