Summary:
I am currently working on a program on embedded Linux using termios. If I do not enable even parity, my program works fine. I start to have issues when I set PARENB active and PARODD inactive. My program is designed to be 8E1 at 115200 baud.
What happens is that the first time I run the program on a fresh boot with Even parity enabled, it will transmit a message. The transmitted message does send, but it doesn't send with the parity bit. The second time I run the program it fails at the tcsetattr function with the errno == EINVAL.
I am running this program on Raspberry Pi for debugging and then the program will be ported to a yocto environment.
Current Environment:
Raspberry pi: 4B
Debian version: 10.10
GCC: (Raspbian 8.3.0-6+rpi1) 8.3.0.
My Question:
Does anyone have insight into what piece is missing? I have tried to do this myself as well as tried 2 people's online examples I found on GitHub. All of them have this issue.
The code that I am running is:
// C library headers
#include <stdio.h>
#include <string.h>
// Linux headers
#include <errno.h> // Error integer and strerror() function
#include <fcntl.h> // Contains file controls like O_RDWR
#include <stdlib.h>
#include <sys/types.h>
#include <termios.h> // Contains POSIX terminal control definitions
#include <unistd.h> // write(), read(), close()
#define SERIAL_DEVICE "/dev/ttyS0"
int main() {
struct termios serial_port_settings;
int fd;
int retval;
char buf[] = "hello world \n bye\n";
char ch;
int i;
//SERAIL_DEVICE is "/dev/ttyS0"
fd = open(SERIAL_DEVICE, O_RDWR);
if (fd < 0) {
perror("Failed to open SERIAL_DEVICE");
exit(1);
}
retval = tcgetattr(fd, &serial_port_settings);
if (retval < 0) {
perror("Failed to get termios structure");
exit(2);
}
//setting baud rate to B115200
retval = cfsetospeed(&serial_port_settings, B115200);
if (retval < 0) {
perror("Failed to set 115200 output baud rate");
exit(3);
}
retval = cfsetispeed(&serial_port_settings, B115200);
if (retval < 0) {
perror("Failed to set 115200 input baud rate");
exit(4);
}
//parity and bytes, grabbed from documentation
serial_port_settings.c_cflag |= PARENB; //enable parity
serial_port_settings.c_cflag &= ~PARODD; //disable odd parity, therefore-> even parity
serial_port_settings.c_cflag &= ~CSTOPB; //disable 2 stop bits, therefore-> 1 stop bit
serial_port_settings.c_cflag &= ~CSIZE; //remove size
serial_port_settings.c_cflag |= CS8; //8 data bits
serial_port_settings.c_cflag |= CLOCAL | CREAD; //ignore modem control lines, enable reciever
serial_port_settings.c_lflag |= ICANON; // enable canonical mode
retval = tcsetattr(fd, TCSANOW, &serial_port_settings);
/*************** ERROR HAPPENS HERE ********************/
/* errno is set to EINVAL here */
if (retval < 0) {
perror("Failed to set serial attributes");
exit(5);
}
printf("Successfully set the baud rate\n");
retval = write(fd, buf, 18);
if (retval < 0) {
perror("write on SERIAL_DEVICE failed");
exit(6);
}
retval = tcdrain(fd);
if (retval < 0) {
perror("write on SERIAL_DEVICE failed");
exit(6);
}
close(fd);
return 0;
}
#sawdust and #KamilCuk helped identify my issue. The software was not the problem. It looks like ttyS0 on the Raspberry Pi 4 isn't a fully functional COM port. Because of this trying to add a parity bit would fail.
The code above runs fine on a different Linux machine with a full-featured COM port.
Related
I'm trying to communicate with an external IC via UART/tty in C. I'm using a nanoPI neo2 (allwinner h5) and friendlyARM's own Linux OS (which is a modified Ubuntu core).
I'v tried to get it working, but the TX output of the pi is ?logical inverted?. You can see what I mean in the oscilloscope pictures. There you can see if I select the "Polarity Invert"- Mode the data is ok, but in normal mode it isn't working.
In the past, I got around that by setting the invert Bit of the MCU (e.g. PIC MCU), but the IC I'm using now doesn't support this feature.
http://wiki.friendlyarm.com/wiki/index.php/NanoPi_NEO2
oscilloscope picture 1
oscilloscope picture 2
c source
#include <stdio.h>
//#include <errno.h>
//#include <termios.h>
//#include <unistd.h>
#include <string.h>
#include <fcntl.h> /* File Control Definitions */
#include <termios.h>/* POSIX Terminal Control Definitions*/
#include <unistd.h> /* UNIX Standard Definitions */
#include <errno.h> /* ERROR Number Definitions */
#include <unistd.h>
#include <sys/types.h>
#include <sys/signal.h>
/* source : http://stackoverflow.com/a/6947758 */
int fd;
int set_interface_attribs (int fd, int speed, int parity) {
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0) {
perror ("error from tcgetattr");
return -1;
}
cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // disable break processing
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0) {
perror ("error from tcsetattr");
return -1;
}
return 0;
}
void set_blocking (int fd, int should_block) {
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
perror ("error from tggetattr");
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
perror ("error setting term attributes");
}
int main()
{
char *portname = "/dev/ttyS0";
int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0)
{
//perror ("error %d opening %s: %s", errno, portname, strerror (errno));
return;
}
set_interface_attribs (fd, B115200, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (fd, 0); // set no blocking
write (fd, "hello!\n", 7); // send 7 character greeting
usleep ((7 + 25) * 100); // sleep enough to transmit the 7 plus
// receive 25: approx 100 uS per char transmit
char buf [100];
int n = read (fd, buf, sizeof buf); // read up to 100 characters if ready to read
}
Thanks in advance
Axel
The picture on the scope looks fine - it is an UART signal. The problem here is not programming-related, but failure to understand the difference between UART and RS-232.
You have picked RS-232 encoding on your scope, and RS-232 uses a different, inverted polarity compared to UART.
UART idles high voltage=Vdd, RS-232 idles at negative voltage < -3V.
On UART, a high voltage=Vdd bit means 1 and low voltage=0V means 0.
On RS-232, a high voltage > +3V bit means 0, and negative voltage < -3V means 1.
So when you try to decode a UART signal with RS-232 encoding, it works when you inverse the polarity. Apparently Rigol isn't picky about the actual voltage levels, since it doesn't understand that the signal is not RS-232 at all.
Resolved Bellow: Wrong number of characters in read/write
I am attempting to read and write to a serial port, and I don't have much experience in C/C++.
My port is connected to a motion controller/driver that requires the following settings:
Baudrate: 57600
Data bits: 8
Parity: None
Stop bits: 1
Flow control: Xon/Xoff
Terminator CRLF
Problem: The code will write and read my first command,but it hangs on the second read call. I am currently blocking until I receive at least one character, but each written command should generate a return.
Additional information: I can only run the first write/read if I first unplug the remote and plug it back in. Alternatively, I can open a cool term window, setup my serial port and run a few commands. When I close the cool term window I will be able to write/read once.
Current Code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <time.h>
#include <iostream>
using namespace std;
int open_port(void)
{
int fd;
// open file descriptor
fd = open("/dev/cu.USA19H41P1.1", O_RDWR | O_NOCTTY);
// if unsucessful
if (fd == -1)
{
printf("open_port: unable to open port. \n");
}
else
{
// remains open across executables
fcntl(fd, F_SETFL, 0);
printf("port is open. \n");
}
return (fd);
}
int configure_port(int fd)
{
// store terminal port settings
struct termios port_settings;
memset(&port_settings, 0, sizeof port_settings);
if(tcgetattr(fd, &port_settings) !=0)
{
printf("error tcgetattr \n");
cout << errno;
}
port_settings.c_iflag = 0;
port_settings.c_oflag = 0;
port_settings.c_lflag = 0;
port_settings.c_cflag = 0;
// flush
tcflush(fd, TCIOFLUSH);
// set baud rate
cfsetispeed(&port_settings, B57600);
cfsetospeed(&port_settings, B57600);
// xon/xoff requirment
port_settings.c_iflag |= IXON;
port_settings.c_iflag |= IXOFF;
// no parity requirement
port_settings.c_cflag &= ~PARENB;
// one stop bin requirement
port_settings.c_cflag &= ~CSTOPB;
// turn on read
port_settings.c_cflag |= CREAD;
port_settings.c_cflag |= CLOCAL;
// no character processing and 8 bit input
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;
// one character blocking
port_settings.c_cc[VMIN] = 1;
port_settings.c_cc[VTIME] = 5;
// apply above settings
if(tcsetattr(fd,TCSANOW, &port_settings) != 0)
{
printf("error tcsetattr \n");
cout << errno;
}
// flush buffers one more time
tcflush(fd, TCIOFLUSH);
return(fd);
}
int read_write(int fd)
{
// write to serial port
ssize_t size=write(fd, "1va?\r\n", 8);
// wait until output is transmitted
tcdrain(fd);
// read from serial port
char buf[100];
memset(buf, '\0', sizeof buf);
ssize_t size2=read(fd, &buf, sizeof(buf));
cout << buf << endl;
return(fd);
}
int main(void)
{
// open port
int fd = open_port();
// store old settings
struct termios old_settings;
memset(&old_settings, 0, sizeof old_settings);
tcgetattr(fd,&old_settings);
// configure port
configure_port(fd);
// write/read first command ("1va?\r\n")
read_write(fd);
// write read second command ("1pa?\r\n")
ssize_t size=write(fd, "1pa?\r\n", 8);
tcdrain(fd);
char buf[100];
memset(buf, '\0', sizeof buf);
ssize_t size3=read(fd, &buf, sizeof(buf));
cout << buf;
//close serial port
close(fd);
tcsetattr(fd, TCSANOW, &old_settings);
return 0;
}
Here is some example code on working with Serial ports setting the correct flags and the baud rate:
Provide your code and and more thorough explanation for us to resolve your issue. What do you mean by write and read my first command? Is your device still responsive after this command? Isn't the second command sent?
This should be fairly straightforward for someone in the know, but I have been struggling for a bit and would appreciate some thoughts. I am writing an application that reads from a serial comm port.
My Setup:
I have a windows machine with putty open and connected to my comm port of interest.
I have a linux (ubuntu) machine in which I am writing this software (in C).
I am typing into putty on the windows machine and trying to read it with my application on the linux machine.
What I have:
So far, using the code below I can successfully write to the putty display on the windows machine from my application on the linux computer, but I cannot read the buffer on my linux machine when I type on the windows machine.
Code:
#include <stdio.h> // standard input / output functions
#include <stdlib.h>
#include <string.h> // string function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h>
int openport()
{
int USB = open( "/dev/ttyUSB0", O_RDWR| O_NOCTTY | O_NONBLOCK );
struct termios tty;
struct termios tty_old;
memset(&tty, 0, sizeof tty);
/* Error Handling */
if ( tcgetattr( USB, &tty ) != 0 )
{
printf("Error 1, or something");
}
/* Save old tty parameters */
tty_old = tty;
/* Set Baud Rate */
cfsetospeed (&tty, (speed_t)B9600);
cfsetispeed (&tty, (speed_t)B9600);
/* Setting other Port Stuff */
tty.c_cflag &= ~PARENB; // Make 8n1
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS; // no flow control
tty.c_cc[VMIN] = 1; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
/* Make raw */
cfmakeraw(&tty);
/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );
if ( tcsetattr ( USB, TCSANOW, &tty ) != 0)
{
printf("Error, or something");
}
return USB;
}
int main()
{
int n = 0;
int j = 0;
unsigned char buf[10] = {0,0,0,0,0,0,0,0,0,0};
printf("Hello world!\n");
int USB = openport();
unsigned char cmd[] = "INIT Hey SHanahas \r";
int n_written = 0;
do
{
n_written += write( USB, &cmd[n_written], 1 );
}
while (cmd[n_written-1] != '\r' && n_written > 0);
printf("Here\n\n");
do
{
n = read( USB, &buf, 1 );
printf("%s \n",buf);
printf("%d",n);
}
while( buf != '\r' && n > 0);
printf("Herr2");
return 0;
}
Does anyone have any thoughts on what might be happening? the read() command returns a -1 error.
Thanks!
I am writing simple CLI application for reading data from serial port. I have USB to serial converter with CP2102 and every few seconds, I am receiving 200 B long string.
Edit: I made a mistake. It is not a CP2102, but Profilic PL2303. I also found strange behavior when I use stty -- I cannot change baud rate and it's randomly changing:
$ stty -F /dev/ttyUSB5 57600 # Set baudrate
$ stty -F /dev/ttyUSB5 # Read configuration
speed 1200 baud;
.....
$$ stty -F /dev/ttyUSB5 # Read configuration
speed 9600 baud;
.....
End-of-edit
When I compile my application under Mac OS X, it works without any problem. But when I compile this code for Linux (cheap Android tablet with ARM CPU, I use Android NDK for compilation) I receive some data, then garbage:
~|?>?lq?43??d??2~??O?$?$??c??Hello World, good data.
???5??l??!???????x?????????????fx???~????????????x??????`???????~???f?x???~????`f??f???~?????x??????#ף???????x?????????????fx???~????????????x??????`???????~???f?x???~????`f??f???~??3?
It looks like I set wrong baud rate, but I am 100% sure the baud rate is 57600 bauds.
Is the problem in my code (differences in POSIX, Unix, Linux, etc.) or should I look for problems in hardware (CP2102, etc)?
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // string function definitions
#include <errno.h> // Error number definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <termios.h> // POSIX terminal control definitions
int main(int argc, char *argv[]) {
printf("Opening device... ");
int USB = open("/dev/ttyUSB5", O_RDONLY | O_NOCTTY | O_NONBLOCK);
printf("opened.\n");
struct termios tty;
struct termios tty_old;
memset (&tty, 0, sizeof tty);
/* Error Handling */
if (tcgetattr(USB, &tty) != 0) {
printf("Error %d from tcgetattr: %s!\n", errno, strerror(errno));
}
/* Save old tty parameters */
tty_old = tty;
/* Set Baud Rate */
cfsetospeed (&tty, (speed_t)B57600);
cfsetispeed (&tty, (speed_t)B57600);
/* Setting other Port Stuff */
tty.c_cflag &= ~PARENB; // Make 8n1
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag &= ~CRTSCTS; // no flow control
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 5;
tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
/* Make raw */
cfmakeraw(&tty);
/* Flush Port, then applies attributes */
tcflush(USB, TCIFLUSH);
if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) {
printf("Error %d from tcgetattr: %s!\n", errno, strerror(errno));
}
int n = 0;
char buf [256];
printf("Starting to read data...\n");
do {
n = read( USB, &buf, sizeof buf);
if (n > 0) {
printf("%s", buf);
memset(&buf, '\0', sizeof buf);
}
usleep(100000); /* sleep for 100 milliSeconds */
} while(1);
}
I have problem in sending data serially using RS-232 from linux OS to uc 8051.
8051 setting:
baudrate = 9600;
comport = port1.
parity = none
stop bit = one
// my code for receiving data on 8051 uc
#include <reg51.h>
unsigned char value;
int i,j;
void ini()
{
TMOD=0x20; //Timer1, mode 2, baud rate 9600 bps
TH1=0XFD;
SCON=0x50;
TR1=1;
}
void delay()
{
for(i=0;i<=1000;i++)
for (j=0;j<=300;j++);
}
void recieve()
{
unsigned char value;
while(RI==0);
value=SBUF;
P1=value;
RI=0;
}
void main()
{
while(1)
{
ini();
recieve();
}
}
// and code which run on linux is as following
#include <stdio.h> // standard input / output functions
#include <string.h> // string function definitions
#include <unistd.h> // UNIX standard function definitions
#include <fcntl.h> // File control definitions
#include <errno.h> // Error number definitions
#include <termios.h> // POSIX terminal control definitionss
#include <time.h> // time calls
int open_port(void)
{
int fd; // file description for the serial port
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) // if open is unsucessful
{
//perror("open_port: Unable to open /dev/ttyS0 - ");
printf("open_port: Unable to open /dev/ttyS0. \n");
}
else
{
fcntl(fd, F_SETFL, 0);
printf("port is open.\n");
}
return(fd);
} //open_port
int configure_port(int fd) // configure the port
{
struct termios port_settings; // structure to store the port settings in
cfsetispeed(&port_settings, B9600); // set baud rates
cfsetospeed(&port_settings, B9600);
port_settings.c_cflag &= ~PARENB; // set no parity, stop bits, data bits
port_settings.c_cflag &= ~CSTOPB;
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;
tcsetattr(fd, TCSANOW, &port_settings); // apply the settings to the port
return(fd);
} //configure_port
int query_modem(int fd) // query modem with an AT command
{
char n;
fd_set rdfs;
struct timeval timeout;
// initialise the timeout structure
timeout.tv_sec = 10; // ten second timeout
timeout.tv_usec = 0;
// Create byte array
unsigned char send_bytes[] = { 0x00, 0xff};
write(fd, send_bytes, 2); //Send data
printf("Wrote the bytes. \n");
// do the select
n = select(fd + 1, &rdfs, NULL, NULL, &timeout);
// check if an error has occured
if(n < 0)
{
perror("select failed\n");
}
else if (n == 0)
{
puts("Timeout!");
}
else
{
printf("\nBytes detected on the port!\n");
}
return 0;
} //query_modem
int main(void)
{
int fd = open_port();
configure_port(fd);
query_modem(fd);
return(0);
} // main
But I have problem .. so plz help me out and tell me in which format linux sends data through rs-232. Also receiving format of 8051 micro-controller.
Need sample code in C.
I recomment basic fault finding
make sure you use correct cables (2 & 3 crossed out, no handshake lines to start with)
seperate the devices, i.e. connect your Linux machine to a PC running a terminal
program and try to establish 2-way communication - work on the Linux / C code until you achieve
connect your 8051 to a PC running a terminal program ....
connect Linux and 8051 only if they both work against a known and proven device
If your 8051 code sample represents all code .... how would the 8051 respond to the AT .... it can only receive.
good luck