I have writen some Linux program to comunicate my device. I have "same" my program for Windows ("same" because of it's same logic).
I'm using 8N2 data frame format # 9600 bps, with neither software (XOn/XOff) nor hardware (RTS/CTS) flow control. I don't use DTR, DCD, RI, DSR pins of RS-232 9-pin D-sub. I use only RX and TX pins to communicate with my device.
In Linux I have this part of code:
struct termios PortOpts, result;
int fd; /* File descriptor for the port */
/* Configure Port */
tcgetattr(fd, &PortOpts);
// BaudRate - 9600
cfsetispeed(&PortOpts, B9600);
cfsetospeed(&PortOpts, B9600);
// enable reciever and set local mode, frame format - 8N2, no H/W flow control
PortOpts.c_cflag &= (~(CLOCAL | CREAD | CSIZE | CSTOPB));
PortOpts.c_cflag |= ((CLOCAL | CREAD | CS8 | CSTOPB) & (~PARENB));
PortOpts.c_cflag &= (~CRTSCTS);
// PortOpts.c_cflag &= ~PARENB
// PortOpts.c_cflag |= CSTOPB
// PortOpts.c_cflag &= ~CSIZE;
// PortOpts.c_cflag |= CS8;
// no parity check, no software flow control on input
PortOpts.c_iflag |= IGNPAR;
PortOpts.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY | INPCK);
// raw data input mode
PortOpts.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
// raw data output
PortOpts.c_oflag &= ~OPOST;
// setting timeouts
PortOpts.c_cc[VMIN] = 1; // minimum number of chars to read in noncanonical (raw mode)
PortOpts.c_cc[VTIME] = 5; // time in deciseconds to wait for data in noncanonical mode (raw mode)
if (tcsetattr(fd, TCSANOW, &PortOpts) == 0) {
tcgetattr(fd, &result);
if ( (result.c_cflag != PortOpts.c_cflag) ||
(result.c_oflag != PortOpts.c_oflag) ||
(result.c_iflag != PortOpts.c_iflag) ||
(result.c_cc[VMIN] != PortOpts.c_cc[VMIN]) ||
(result.c_cc[VTIME] != PortOpts.c_cc[VTIME]) ) {
perror("While configuring port1");
close(fd);
return 1;
}
} else {
perror("While configuring port2");
close(fd);
return 1;
};
File descriptor 'fd' is for '/dev/ttyS0' device.
I get that message: While configuring port2: Input/output error
I have a laptop, though I don't have any serial ports except for USB. Is this a reason?
I run program as 'root'.
Sorry for my broken English.
Can you run strace on the program; that will give more specifics of just where the IO error is occurring.
One thing to keep in mind - errno doesn't get reset, so the actual error could be from any system call before the perror.
Related
My application is able to communicate with baud rates like 4800, 9600 and 115200 but can't with 14400 or 38400. I have to include asm/termios because I need struct termios2 since I'm going to use c_ispeed and c_ospeed members for any buad rate.
Also the second problem I encounter is that read function doesn't return afterVTIME. Do you know why this happens? Any help is appreciated. Thanks.
#include <asm/termios.h>
int serialDevice = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_SYNC);
serialSettings.baudRate = 4800;
serialSettings.dataBits = 8;
serialSettings.hardwareFlowControl = 0;
serialSettings.parity = 0;
serialSettings.parityOdd = 0;
serialSettings.stopBits = 1;
serialSettings.xonxoff = 1;
setSerialSettings(serialDevice, &serialSettings);
//-------------------------------------------------------
int8_t setSerialSettings(int serialDevice, Serial_Params_t *settings)
{
struct termios2 tty;
memset(&tty, 0, sizeof tty);
// get current serial settings
if (ioctl(serialDevice, TCGETS2, &tty) == -1)
{
sendLog("Can't get serial attributes | setSerialSettings", LOG_TYPE_ERROR);
return FALSE;
}
// baudrate
tty.c_cflag &= ~CBAUD;
tty.c_cflag |= BOTHER;
tty.c_ispeed = MAX(110, MIN(settings->baudRate, MAX_BAUDRATE));
tty.c_ospeed = MAX(110, MIN(settings->baudRate, MAX_BAUDRATE));
// enable input parity check
tty.c_iflag |= INPCK;
// data bits: CS5, CS6, CS7, CS8
tty.c_cflag &= ~CSIZE;
switch (settings->dataBits)
{
case 5:
tty.c_cflag |= CS5;
break;
case 6:
tty.c_cflag |= CS6;
break;
case 7:
tty.c_cflag |= CS7;
break;
case 8:
default:
tty.c_cflag |= CS8;
break;
}
// stop bit
switch (settings->stopBits)
{
case 1:
default:
tty.c_cflag &= ~CSTOPB;
break;
case 2:
tty.c_cflag |= CSTOPB;
}
// parity
if (settings->parity == 1)
tty.c_cflag |= PARENB;
else
tty.c_cflag &= ~PARENB;
// odd/even parity
if (settings->parityOdd == 1)
tty.c_cflag |= PARODD;
else
tty.c_cflag &= ~PARODD;
// flow control
// XON/XOFF
if (settings->xonxoff == 1)
tty.c_iflag |= (IXON | IXOFF | IXANY);
else
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
// enable RTS/CTS
if (settings->hardwareFlowControl == 1)
tty.c_cflag |= CRTSCTS;
else
tty.c_cflag &= ~CRTSCTS;
tty.c_cc[VMIN] = 1; // return read function when receive 1 byte
tty.c_cc[VTIME] = 10; // 1 seconds read timeout (deciseconds)
tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
// non-canonical mode
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
// flush port & apply attributes
tcflush(serialDevice, TCIFLUSH);
if (ioctl(serialDevice, TCSETS2, &tty) == -1)
{
sendLog("Can't set serial attributes | setSerialSettings", LOG_TYPE_ERROR);
return FALSE;
}
return TRUE;
}
My application is able to communicate with baud rates like 4800, 9600 and 115200 but can't with 14400 or 38400.
There is a pretty nice writeup for how custom serial speed setting works here: https://github.com/npat-efault/picocom/blob/master/termios2.txt.
In brief, given a struct termios2 identified by tty, to set both input and output speed to custom values, you must
ensure that tty.c_cflag & CBAUD == BOTHER. You appear to do this correctly.
set the desired output speed in tty.c_ospeed. You do this, too.
either
ensure that (tty.c_cflag >> IBSHIFT) & CBAUD == B0, in which case the output speed will also be used as the input speed, or
ensure that (tty.c_cflag >> IBSHIFT) & CBAUD == BOTHER, in which case tty.c_ispeed will be used as the input speed.
You do not do either of those. I'm uncertain why this would cause incorrect communication for some speeds and not others, but the driver is reputed to play some interesting games with speed settings, and maybe you've stumbled across one.
As for
read function doesn't return after VTIME
I think you have incorrect expectations. You are setting VMIN and VTIME both to nonzero values. In this case, VTIME is the maximum inter-character time, not an overall read timeout. With these settings, a blocking read will wait indefinitely for the first character, then will keep reading subsequent characters, up to the requested number, as long as each one arrives within VTIME deciseconds of the previous one.
If you want an overall timeout on every read() call, then set VMIN to 0, and be prepared for some read() calls to read 0 bytes. As always, read() may also read a positive number of bytes but fewer than requested. That may be more likely to happen in this configuration than in the one you're presently using, depending on your choice of VTIME and the behavior of the peer.
I have a serial app on C that receives data and writes it onto a binary file. The problem is that all the data is the same but when i have 0A on sending side, i have 0D on receiving side. I have set the serial port on raw mode and opened the file with wb option. Any clue how to avoid this? If some code is needed, i'll post it.
thanks
EDIT--------------------------
File opening:
FILE *fout;
fout = fopen(file,"wb");
Serial options:
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
/* To disable software flow control simply mask those bits: */
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);
tcsetattr(fd, TCSANOW, &options);
success = 1;
return success;
Writting on the file:
fwrite(buffer,1,n,fout);
----------FIX----------------------
Setting this option fixes the problem :
options.c_oflag &= ~OPOST;
You need to mask off the ICRNL mode which translates the enter (carriage return) key to the newline character. This is on the receiving end, not in your program. There's also a corresponding output mode that might be set on your end, but less likely.
I am writing a Linux driver for my chinese arduino. At one moment I need to change the baud rate. I looked for examples and found that listing:
Listing 2 - Setting the baud rate.
struct termios options;
/*
* Get the current options for the port...
*/
tcgetattr(fd, &options);
/*
* Set the baud rates to 19200...
*/
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
/*
* Enable the receiver and set local mode...
*/
options.c_cflag |= (CLOCAL | CREAD);
/*
* Set the new options for the port...
*/
tcsetattr(fd, TCSANOW, &options);
The next to last line of code has the |= operator. What does it do? I've never seen it before.
options.c_cflag |= (CLOCAL | CREAD);
is generally equivalent to
options.c_cflag = options.c_cflag | (CLOCAL | CREAD);
except options.c_cflag is evaluated only once, which doesn't matter in the above expression, but it would matter if options.c_cflag had any side effects (for example, if it were *options.c_cflag++)
I'm trying to read raw bytes from a serial port sent by a IEC 870-5-101 win32 protocol simulator with a program written in C running on Linux 32bit.
It's working fine for byte values like 0x00 - 0x7F. But for values beginning from 0x80 to 0xAF the high bit is wrong, e.g.:
0x7F -> 0x7F //correct
0x18 -> 0x18 //correct
0x79 -> 0x79 //correct
0x80 -> 0x00 //wrong
0xAF -> 0x2F //wrong
0xFF -> 0x7F //wrong
After digging around for two days now, I have no idea, what's causing this.
This is my config of the serial port:
cfsetispeed(&config, B9600);
cfsetospeed(&config, B9600);
config.c_cflag |= (CLOCAL | CREAD);
config.c_cflag &= ~CSIZE; /* Mask the character size bits */
config.c_cflag |= (PARENB | CS8); /* Parity bit Select 8 data bits */
config.c_cflag &= ~(PARODD | CSTOPB); /* even parity, 1 stop bit */
config.c_cflag |= CRTSCTS; /*enable RTS/CTS flow control - linux only supports rts/cts*/
config.c_iflag &= ~(IXON | IXOFF | IXANY); /*disable software flow control*/
config.c_oflag &= ~OPOST; /* enable raw output */
config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* enable raw input */
config.c_iflag &= ~(INPCK | PARMRK); /* DANGEROUS no parity check*/
config.c_iflag |= ISTRIP; /* strip parity bits */
config.c_iflag |= IGNPAR; /* DANGEROUS ignore parity errors*/
config.c_cc[VTIME] = 1; /*timeout to read a character in tenth of a second*/
I'm reading from the serial port with:
*bytesread = read((int) fd, in_buf, BytesToRead);
Right after this operation "in_buf" contains the wrong byte, so I guess there's something wrong with my config, which is a port from a win32 DCB structure.
Thanks for any ideas!
Based on your examples, only the 8th bit (the high bit) is wrong, and it's wrong by being always 0. You are setting ISTRIP in your line discipline on the Linux side, and that would cause this. ISTRIP does not, as the comment in the C code claims, strip parity bits. It strips the 8th data bit.
If ISTRIP is set, valid input bytes shall first be stripped to seven bits; otherwise, all eight bits shall be processed. IEEE Std 1003.1, 2004 Edition, chapter 11, General Terminal Interface
I've got an Arduino board and want to read the data it's spitting off using USB at a custom baud rate. Hacking up some of the code Arduino suggest, I get this C code:
int serialport_init(const char* serialport, int baud)
{
struct termios toptions;
int fd;
printf("init_serialport: opening port %s # %d bps\n", serialport,baud);
fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
serialPortPointer = fd;
if (fd == -1)
{
printf("Unable to open port when initialising hardware'n");
return -1;
}
if (tcgetattr(fd, &toptions) < 0)
{
printf("Couldn't get term attributes when initialising hardware\n");
return -1;
}
speed_t brate = baud; // let you override switch below if needed
switch(baud) {
case 4800: brate=B4800; break;
case 9600: brate=B9600; break;
case 14400: brate=B14400; break;
case 19200: brate=B19200; break;
case 28800: brate=B28800; break;
case 38400: brate=B38400; break;
case 57600: brate=B57600; break;
case 115200: brate=B115200; break;
}
cfsetispeed(&toptions, EXTA);
cfsetospeed(&toptions, EXTA);
// 8N1
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
// no flow control
toptions.c_cflag &= ~CRTSCTS;
toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
toptions.c_oflag &= ~OPOST; // make raw
// see: http://unixwiz.net/techtips/termios-vmin-vtime.html
toptions.c_cc[VMIN] = 0;
toptions.c_cc[VTIME] = 20;
if(tcsetattr(fd, TCSANOW, &toptions) < 0)
{
printf("Couldn't set term attributes when initialising hardware\n");
return -1;
}
return fd;
}
The problem is that the termios.h file doesn't support 31250 (MIDI) baud rate... If I try entering 31250 as the baud rate, this function returns -1 and says "Couldn't set term attributes when initialising hardware" (it fails right at the end).
So - how can I write a program, in C, or any other language, that reads the data off at the baud rate I want? Does termios.h support custom baud rates?
I literally just want to read the data on the serial port - nothing else.
This is a library that enables MIDI I/O communications on the Arduino serial ports. You need an Arduino capable of minimum 2 serial ports (like this one). One serial will be used for communication to MIDI devices (31250bps), and the other one to PC (for example 115200bps). If you have only one serial port on your Arduino board then you can also experiment with a software serial library like this.
The termios.h API simply does not have a way to express user-defined baud rates, though depending on your operating system there may be an extension to do so. Can you try setting up the Arduino to use the more standard 38400 instead?