4

After going through @wallyk answer in how to open, read, and write from serial port in C ,I wrote a program to send data through my usb port. I need to send a 6 byte data of which the 1st byte should be in mark parity and the rest should be in space parity. that is the reason I have declared 2 variables msg1 and msg2

#include<stdio.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include<fcntl.h>// used for opening ttys0
#include<sys/ioctl.h>
#include<sched.h>
#include<string.h> // for memset
#include<time.h>

int set_interface_attribs (int fd, int speed, int parity)
{
        struct termios tty;
        memset (&tty, 0, sizeof tty); // initialize all in struct tty with 0
        if (tcgetattr (fd, &tty) != 0)// gets parameters from fd and stores them in tty struct 
        {
                perror("error from tcgetattr");
                return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars, CSIZE -> character size mask
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // ignore break signal
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;// 1 stop bit    
        tty.c_cflag &= ~CRTSCTS;

        if (tcsetattr (fd, TCSANOW, &tty) != 0) // TCSANOW -> the change takes place immediately
        {
                perror("error from tcsetattr");
                return -1;
        }
        return 0;
}

int main()
{
char *portname = "/dev/ttyUSB0";
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
        perror("error opening");
        return;
}
char msg1[1]={0x01};
char msg2[5]={0x02,0x08,0x00,0xff,0xf5};    
set_interface_attribs (fd, B115200,PARENB|PARODD|CMSPAR);  // set speed to 115200, bps,mark parity
                // set no blocking
write (fd, msg1, sizeof msg1);    
set_interface_attribs (fd, B115200,PARENB|CMSPAR); // set speed to 115200 bps, space parity   
write (fd,msg2,sizeof msg2);
close(fd);
return 0;
}

But now all the data that I send seem to be in space parity and not mark parity. ie if I have configured the 1st byte to be sent in mark parity and the rest in space parity, then all are being sent in space parity. Now if I configure the 1st byte to be sent in space parity and the rest in mark parity, then all are being sent in mark parity.

Community
  • 1
  • 1
Bas
  • 469
  • 8
  • 22
  • 2
    When you call `write` the data may not be written immediately as the kernel may have its own buffering. If you're using e.g. a USB-to-serial dongle it may have its own buffers too. Therefore you may change the attributes the second time before any data is sent. – Some programmer dude May 05 '14 at 06:45
  • @ Joachim Pileborg Well I have even tried using a delay of 15 mille seconds after the 1st byte is sent. Still the output is the same – Bas May 05 '14 at 06:47
  • 2
    Try calling `tcdrain(fd);` after the write of `msg1`. – Michael Burr May 05 '14 at 07:16
  • tcdrain(fd) is also not giving the desired o/p, but if I create a delay of 25ms or above I am getting the o/p correctly. Thanks for the help – Bas May 05 '14 at 08:49
  • Try closing the port after the first byte, then reopen it again and adjust the settings before sending the rest. – Rein Oct 15 '14 at 23:59

1 Answers1

0

I had a similar issue, trying to send the first byte with parity "mark" and all the rest with parity "space" and the USB to Serial driver that I had was ignoring the "Mark/Space" option and I noticed with a protocol analyser that it was using "Odd/Even" instead. So I ended up creating a lookup table with the parity (Odd/Even) that I would need to use before sending each byte, in order to simulate the "Mark/Space" parity and I would change parity accordingly before sending each byte. Obviously this introduces many milliseconds of delay each time the parity swaps, so since this was meant to be only for a single purpose embedded device that would run in a single baud rate, the USB to Serial driver was modified to remove some of the checks that it was performing (like baud rate changes) and make the switch of parity twice as fast. I don't recommend to change the driver if it's for general purpose use, but if you can afford the inter-character delays that are going to be introduced before each parity change, then this may help.

The lookup table was something like this:

static const char parity[256]= {

0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0
};

To send the first byte with parity "mark" I was using this:

if (parity[buff[0]]==1)
    SetParity (fd,EVEN_PARITY);
else
    SetParity (fd,ODD_PARITY);

and this was the "SetParity()" function

int SetParity(int fd, int parity) {

struct termios options,options1;
string strToLog;

bzero (&options, sizeof (options));
bzero (&options1, sizeof (options1));

if (tcgetattr (fd, &options) ==-1) {
    usleep (500);
    int aa=tcgetattr (fd, &options);
    ostringstream osString (" ");
    osString<<std::dec<<fd<<"FD error! Get A";
    if (aa==-1)
        osString<<std::dec<<fd<< "FD error! Get B";
    strToLog.append (osString.str());
      Log(LOG_ALERT,strToLog.c_str());
    if (aa==-1)
        return -1;
}
options.c_cflag |= (CLOCAL | CREAD);

switch (parity) {
    case NO_PARITY:
        options.c_cflag &= ~PARENB;
        options.c_cflag &= ~CSTOPB;
        options.c_cflag &= ~CSIZE;
        options.c_cflag |= CS8;
        break;
    case ODD_PARITY:
        options.c_cflag|=PARODD;
        options.c_cflag|=PARENB;
        options.c_cflag&=~CMSPAR;
        break;

    case EVEN_PARITY:
        options.c_cflag&=~PARODD;
        options.c_cflag|=PARENB;
        options.c_cflag&=~CMSPAR;
        break;

    case MARK_PARITY:
        options.c_cflag|=PARODD;
        options.c_cflag|=PARENB;
        options.c_cflag|=CMSPAR;
        break;

    case SPACE_PARITY:
        options.c_cflag&=~PARODD;
        options.c_cflag|=PARENB;
        options.c_cflag|=CMSPAR;
        break;
    default:
        return (-1);
}

if (-1 == tcsetattr (fd, TCSADRAIN, & (options))) {
    printf ("ERROR--------------------------------setParity\n");
    ostringstream osString (" ");
    osString<<std::dec<<fd<<"FD error! Set A";
    usleep (500);
    int aa= tcsetattr (fd, TCSADRAIN, & (options));
    if (aa==-1)
        osString<<std::dec<<fd<<" FD error! Set B";
    strToLog.append (osString.str());

    printf(strToLog.c_str());
    if (aa==-1)
        return -1;
}

return 1;
}