serial data transfer using device node

75 Views Asked by At

I need to communicate over my serial Port. I have written a c code to open the devices for tx & rx and configure the settings, write and read the data. First time when I execute the code, I'm able to read and write data. But when I try it second time, I'm unable to receive or transmit data. Can sometime help me to know what the reason for that will be. I have closed the files that are opened.

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

#define buff_size 256

 int main()
{
        int tx_fd,rx_fd;
        struct termios option1,option2;
        int byte_write,byte_read;
        char buffer[buff_size];

        printf("test\n");

        tx_fd = open("/dev/ttyUSB2",O_RDWR);
        printf("tx port is opened\n");
        if(tx_fd < 0)
        {
                perror("error opening transmitter uart port");
                return 1;
        }

        rx_fd = open("/dev/ttyUSB0",O_RDWR);
        printf("rx port opened\n");
        if(rx_fd < 0)
        {
                perror("error opening receiver uart port");
                close(tx_fd);
                return 1;
        }

        //configuration setting
        ////baud rate oof input and output is set to 9600;
        cfsetospeed(&option2,B9600);
        cfsetispeed(&option1,B9600);

        ////
        option1.c_cflag = CS8;
        option2.c_cflag = CS8;


        option1.c_cflag &= ~PARENB;
        option2.c_cflag &= ~PARENB;
        option1.c_cflag &= ~CSTOPB;
 tcflush(tx_fd,TCIFLUSH);
        tcflush(rx_fd,TCOFLUSH);

        // Apply receiver (RX) settings
    if (tcsetattr(rx_fd, TCSANOW, &option2) < 0) {
        perror("Error setting receiver attributes");
        close(tx_fd);
        close(rx_fd);
        return 1; // Handle the error and exit
    }
    
tcsetattr(tx_fd,TCSANOW,&option1);
    tcsetattr(rx_fd,TCSANOW,&option2);

        char data[] = "hello! \n";
        byte_write = write(tx_fd,data,sizeof(data));
        printf("write %d bytes to receiver\n",byte_write);


        if(byte_write < 0)
        {
                perror("Error in writing data\n");
                close(tx_fd);
                close(rx_fd);
                return 1;
        }

        usleep(10000);

        byte_read = read(rx_fd,buffer,sizeof(buffer));
        if(byte_read < 0)
        {
                perror("error in reading data\n");
                close(tx_fd);
                close(rx_fd);
                return 1;
        }

        buffer[byte_read] = '\0';
        printf("read %d bytes from transmitter\n",byte_read);
        printf("Received: %s \n",buffer);

        if( close(rx_fd) < 0)
        {  perror("there is a error in closing rx");
        }
        if( close(tx_fd) < 0)
        {
                perror("there is a error in closing tx");
        }

        return 0;
}

this is the code that I have tried. I found that I'm having an issue with a particular serial port to open the file while executing for the second time. But still can't understand what the issue is. The code stands in a place where it can't be open, but the error message is also not executed.

1

There are 1 best solutions below

0
Ian Abbott On

If the open() call is blocked when the executable is run the second time, it is probably because the CLOCAL flag was cleared in the struct termios c_cflag member when the executable was run the first time. That causes a blocking open() call to wait for the Carrier Detect (a.k.a. CD, Data Carrier Detect, DCD) signal to be asserted on the serial line.

open() can be called in nonblocking mode by setting the O_NONBLOCK flag in the second parameter. That will make open() return immediately without waiting for the CD signal, allowing you to change the termios settings. However, the read and write operations will also be in nonblocking mode. To restore blocking mode, you can use the fcntl() F_SETFL command to clear the O_NONBLOCK flag after opening:

        rx_fd = open("/dev/ttyUSB0",O_RDWR | O_NONBLOCK);
        if(rx_fd < 0)
        {
                perror("error opening receiver uart port");
                close(tx_fd);
                return 1;
        }
        // get file control flags for receiver
        int rx_flg = fcntl(rx_fd, F_GETFL);
        if (rx_flg == -1)
        {
                perror("error getting receiver file flags");
                close(rx_fd);
                close(tx_fd);
                return 1;
        }
        // disable nonblocking mode for receiver file
        rx_flg &= ~O_NONBLOCK;
        if (fcntl(rx_fd, F_SETFL, rx_flg) == -1)
        {
                perror("error disabling nonblocking mode on receiver");
                close(rx_fd);
                close(tx_fd);
                return 1;
        }

The nonblocking open with blocking restored after the open could be factored out into a function that can also be used to open the other TTY:

#include <errno.h>
#include <fcntl.h>

int tty_open(const char *pathname, int flags)
{
    int fd;
    int save_errno;

    /* Open in nonblocking mode. */
    fd = open(pathname, flags | O_NONBLOCK);
    save_errno = errno;
    if (fd != -1 && (flags & O_NONBLOCK) == 0)
    {
        /* Restore originally requested blocking mode. */
        if (fcntl(fd, F_SETFL, flags) == -1)
        {
            save_errno = errno;
            close(fd);
            fd = -1;
        }
    }
    errno = save_errno;
    return fd;
}

That could be called like this:

        rx_fd = tty_open("/dev/ttyUSB0",O_RDWR);

You should also either fully initialize the struct termios objects passed to tcsetattr(), or use tcgetattr() to read the existing settings into the struct termios objects before modifying the settings. Your current code only initializes the c_cflag members of option1 and option2. The other members contain indeterminate values, which will either cause the tcsetattr() calls to return an error, or indeterminate termios settings to be applied to the TTY, or some other undefined behavior to occur.