Set stty parameters
try
stty -F /dev/ttyS0 cs7 cstopb -ixon raw speed 1200
stty serial port settings for parity not persistent
The stty command is simply a method from the shell to utilize the termios API.
Application programs are expected to use the termios API to configure the serial terminal to the exact requirements of the situation (rather than rely on an expected configuration on startup).
If the app environment that you're using does not permit access to the termios API, then you may be using an inappropriate method.
Do you know any linux app that could explicitly react somehow to a parity error?
The following C program reads lines (i.e. canonical mode) from a serial terminal, and is configured to detect a Mark (or 1) as the parity bit with an 8-bit character frame.
#define SERIALTERMINAL "/dev/ttyS0"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
int set_interface_attribs(int fd, int speed)
{
struct termios tty;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&tty, (speed_t)speed);
cfsetispeed(&tty, (speed_t)speed);
tty.c_cflag |= CLOCAL | CREAD;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; /* 8-bit characters */
tty.c_cflag |= PARENB; /* enable parity */
tty.c_cflag &= ~PARODD; /* Even parity */
tty.c_cflag |= CMSPAR; /* force Even parity to SPACE */
tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */
tty.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */
tty.c_lflag |= ICANON | ISIG; /* canonical input */
tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);
tty.c_iflag &= ~IGNCR; /* preserve carriage return */
tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
tty.c_iflag &= ~(IXON | IXOFF | IXANY); /* no SW flowcontrol */
tty.c_iflag |= IGNBRK; /* ignore breaks */
tty.c_iflag &= ~ISTRIP;
tty.c_iflag &= ~IGNPAR; /* report error */
tty.c_iflag |= INPCK; /* test parity */
tty.c_iflag |= PARMRK; /* verbose parity err */
tty.c_oflag &= ~OPOST;
tty.c_cc[VEOL] = 0;
tty.c_cc[VEOL2] = 0;
tty.c_cc[VEOF] = 0x04;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
return 0;
}
int main(void)
{
char *portname = SERIALTERMINAL;
int fd;
int wlen;
fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("Error opening %s: %s\n", portname, strerror(errno));
return -1;
}
/*baudrate 115200, 8 bits, Space for parity, 1 stop bit */
set_interface_attribs(fd, B115200);
/* simple output */
wlen = write(fd, "Hello!\n", 7);
if (wlen != 7) {
printf("Error from write: %d, %d\n", wlen, errno);
}
tcdrain(fd); /* delay for output */
/* simple canonical input, read lines */
do {
unsigned char buf[81];
unsigned char *p;
int rdlen;
rdlen = read(fd, buf, sizeof(buf) - 1);
if (rdlen > 0) {
buf[rdlen] = 0;
printf("Read %d:", rdlen);
/* first display as hex numbers then ASCII */
for (p = buf; rdlen-- > 0; p++) {
printf(" 0x%x", *p);
if (*p < ' ')
*p = '.'; /* replace any control chars */
}
printf("\n \"%s\"\n\n", buf);
} else if (rdlen < 0) {
printf("Error from read: %d: %s\n", rdlen, strerror(errno));
} else { /* rdlen == 0 */
printf("Nothing read. EOF?\n");
}
/* repeat read */
} while (1);
}
The program was executed on an old Linux (Ubuntu 14.04.2 LTS) PC that has a built-in 16550A serial port.
[ 2.656593] 00:08: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A
This serial port does not seem capable of transmitting 8-bits with parity (an 11-bit frame), but does seem capable of reading 8-bits with parity.
The serial data is generated from a SBC that has a 9-bit capable UART. An oscilloscope was used to capture frames to confirm that the 8S1 and 8E1 frames were 11 bits long.
(An FTDI USB-to-RS232 converter was not reliable in generating all parity configurations with 8-bit characters.)
When the sender is configured for 8-bits and Space for parity (which matches the program), the PC program reads "ABCDEFG\n" as:
Read 8: 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0xa
"ABCDEFG."
The data is read correctly.
When the sender is configured for 8-bits and Even parity, the PC program reads "ABCDEFG\n" as:
Read 14: 0x41 0x42 0xff 0x0 0x43 0x44 0xff 0x0 0x45 0xff 0x0 0x46 0x47 0xa
"AB�.CD�.E�.FG."
The read (correctly) identifies three characters that have Mark instead of Space as the parity bit.
Each character with "parity error" is preceded by bytes of 0xFF 0x00
(i.e. a total of three bytes).
Note that when the actual received datum is 0xFF
(with no parity error), termios will expand that datum to two bytes of 0xFF 0xFF
. So beware that when the next datum is 0x00
, then this is not the error indication. IOW reading 0xFF 0xFF 0x00
converts to actual data 0xFF 0x00
.
But when the actual received datum is 0xFF
with a parity error, then read returns 0xFF 0x00 0xFF
(i.e. there is no expansion combined with the error indication).
Persistent stty Settings?
Credits go to PHS. Symlinking .bashrc to .profile actually worked. Wow, I thought bash would read .profile by default.
Thanks Phs.
setting serial port parameters
The stty
settings appear to be for raw mode, whereas your tcgetattr()
/tcsetattr()
code tries to use non-canonical mode but is incomplete for output processing (OPOST is not cleared).
The result is that the serial port is in a half-raw mode for output.
Try using cfmakeraw()
to setup non-canonical mode.
cfmakeraw() sets the terminal to something like the "raw" mode of the old Version 7 terminal driver: input is available character by character, echoing is disabled, and all special processing of terminal input and output characters is disabled. The terminal attributes are set as follows:
termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
termios_p->c_cflag &= ~(CSIZE | PARENB);
termios_p->c_cflag |= CS8;
For working sample code see this.
Note that the return codes from system calls during initialization are checked.
Linux set higher baud rate and associated settings
You may use setserial console utility:
1) Get baud_base value from setserial
output
setserial -a /dev/<...>
2) Calculate divisor = baud_base / desired_baud_rate
For example if baud_base = 3000000 (3MHz):
baud_rate = 115200 -> divisor = 26.04 (approximately 26)
baud_rate = 230400 -> divisor = 13.02 (approximately 13)
baud_rate = 921600 -> divisor = 3.26
3.26 is too much to set divisor=3 and too little to set divisor=4.
So, in this case you can't use baud_rate=921600 because of hardware limitations.
You may choose divisor=3 (baud_rate=1000000) or divisor=4 (baud_rate=750000). These baud rates are not standart, but possible.
stty -F /dev/<...> 9600 -icrnl -ixon -crtscts -parenb # desired UART settings
setserial /dev/<...> spd_cust # use custom value for divisor
setserial /dev/<...> divisor 3 # set custom value for divisor
stty -F /dev/<...> 38400 # activate setserial settings
# now baud_rate is (baud_base / divisor)
Related Topics
Write to a File After Piping Output from Tail -F Through to Grep
Switching Users Using Winscp Between Different Accounts
How to Grep While Avoiding 'Too Many Arguments'
Does 'Dash' Support 'Bash' Style Arrays
Loop Over File Names from 'Find'
Getmodulehandle(Null) on Linux
Split Delimited File into Smaller Files by Column
Setting the Thread /Proc/Pid/Cmdline
How to Import Environment Settings into My Perl Program
Pkill Returns 255 in Combination with Another Command via Remote Ssh
Difference Between Linux Kernel and Unix Kernel(Such as Freebsd) from Programmer's Point of View
Getting Meteor 0.9.2 Build to Work Osx -> Linux
What's the Max File Mapping Size in 64Bits MAChine
Certificate with Extended Key Usage Only Works in Firefox