3

I'm trying to learn how to program the ttyS0 serial port in Linux using C. I have another machine connected to my serial port sending alternating hex values of 5f and 6f about every two seconds. I've verified with other port monitoring apps that these values are appearing at the port. In my code I'm using a blocking read() into a 10 char length buffer. Even though my other machine is still sending data, read() blocks forever. If I include the line fcntl(fd, F_SETFL, FNDELAY); which sets read() to non-blocking read() always returns with a value of -1, meaning no data was in the UART buffer, and my for loop code just prints out random values that are in the buffer. So in short my assumption is that my code is not reading ttyS0 and I have no idea why. Below is my code, hopefully someone will see what's causing my problem and set me straight. By the way, I'm using Scientific Linux, I believe ttyS0 is com port 1, as it is in RedHat and Fedora. Aslo below is the output when i run the code. It seems to be writing to the COM port with no problems, but for a read it says its unavailable. Also it's clear that the buffer I'm printing out is just random values not data that's been read in. Thanks

console output

hello world
hi again
write error: : Success
 wrote 4 bytes
number of bytes read is -1
read error:: Resource temporarily unavailable
4  8  120  -99  -73  -65  41  -120  4  8  
should of put something out

Code

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>

int main()
{
    printf("hello world\n");
    int n;
    int fd;
    char c;
    int bytes;

    char buffer[10];
    char *bufptr;
    int nbytes;
    int tries;
    int x;
    struct termios options;


    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
    if(fd == -1)
    {
        perror("open_port: Unable to open:");
    }
    else
    {
        fcntl(fd, F_SETFL, 0);
        printf("hi again\n");
    }

    tcgetattr(fd, &options);

    cfsetispeed(&options, B115200);
    cfsetospeed(&options, B115200);
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    options.c_cflag &= ~( ICANON | ECHO | ECHOE |ISIG );
    options.c_iflag &= ~(IXON | IXOFF | IXANY );
    options.c_oflag &= ~OPOST;

    tcsetattr(fd, TCSANOW, &options);


    write(fd, "ATZ\r",4);
    printf(" wrote\n");
    bufptr = buffer;


    fcntl(fd, F_SETFL, FNDELAY);
     bytes = read(fd, bufptr, sizeof(buffer));
    printf("number of bytes read is %d\n", bytes);
    perror ("read error:");

    for (x = 0; x < 10 ; x++)
    {
        c = buffer[x];
        printf("%d  ",c);
    }
    close(fd);

    //puts(buffer[0]);
    printf("\nshould of put something out \n");

    return (0);
}
Frank Dejay
  • 689
  • 5
  • 13
  • 17
  • 1
    If you make the filedescriptor nonblocking, `read` returning `-1` might mean other things as well. You have to check `errno` to see what it really means. – Some programmer dude Apr 20 '12 at 08:26
  • @JoachimPileborg OK. I added **perrer()** lines after the **read()** and **write()**. Looks like its writing fine, but unable to access the port for reading. – Frank Dejay Apr 20 '12 at 18:00
  • 1
    @FrankDejay In the `options.c_cflag &= ~( ICANON | ECHO | ECHOE |ISIG );` line, did you mean to set `options.c_lflag` rather than `options.c_cflag`? – Vilhelm Gray Sep 26 '13 at 19:07
  • The three primary issues with your code are: (1) Your program uses non-blocking mode for input. You would be much better off using blocking mode. (2) Your program uses non-canonical mode. Since the output and input are lines of text, you could instead configure the terminal for canonical mode. See https://stackoverflow.com/questions/25996171/linux-blocking-vs-non-blocking-serial-read/26006680#26006680 (3) The termios configuration is botched, as mentioned by @VilhelmGray – sawdust Jan 18 '18 at 02:01

1 Answers1

4

The following line will cause problems:

options.c_cflag &= CSTOPB;

It will reset all other bits of the c_cflag.

If you want to use 1 stop bit, then use:

options.c_cflag &= ~CSTOPB;

If you want to use 2 stop bits, then use:

options.c_cflag |= CSTOPB;

EDIT:

Also the following line cause problems:

fcntl(fd, F_SETFL, 0);

It will reset several important flags.

SKi
  • 8,007
  • 2
  • 26
  • 57
  • Thanks I made those changes. But it's still not working. I edited my code in the post to represent the changes. I also added **perror()** lines. Looks like it writing fine, but it can't access the port for reading. Why do you think that is? – Frank Dejay Apr 20 '12 at 17:58
  • @Frank Dejay There is also some strange in flag setting. I updated my answer. – SKi Apr 23 '12 at 09:02
  • *"Also the following line cause problems:* `fcntl(fd, F_SETFL, 0);` *It will reset several important flags."* -- Actually that will not cause problems; the syscall can only modify a documented & restricted set of file-descriptor options. In this case it will only clear the O_NDELAY flag (but it's later re-enabled and causes problems for the OP). – sawdust Jan 18 '18 at 01:56