setting serial port interruption in linux

2019-02-05 07:11发布

问题:

I am trying to set the interruption for a serial port in ubuntu (in program written in C), but it does not work. I have checked that the serial communication works correctly without the interruption, so I may be setting something wrong. The code is the following:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <fcntl.h>
    #include <sys/signal.h>
    #include <errno.h>
    #include <termios.h>

    void signal_handler_IO (int status);   /* definition of signal handler */

    int n;
    int fd;
    int connected;
    struct termios termAttr;
    struct sigaction saio;

    int main(int argc, char *argv[])
    {
         fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
         if (fd == -1)
         {
            perror("open_port: Unable to open /dev/ttyO1\n");
            exit(1);
         }

         saio.sa_handler = signal_handler_IO;
         saio.sa_flags = 0;
         saio.sa_restorer = NULL; 
         sigaction(SIGIO,&saio,NULL);

         fcntl(fd, F_SETFL, FNDELAY);
         fcntl(fd, F_SETOWN, getpid());

         tcgetattr(fd,&termAttr);
         baudRate = B115200; 
         cfsetispeed(&termAttr,B115200);
         cfsetospeed(&termAttr,B115200);
         termAttr.c_cflag &= ~PARENB;
         termAttr.c_cflag &= ~CSTOPB;
         termAttr.c_cflag &= ~CSIZE;
         termAttr.c_cflag |= CS8;
         termAttr.c_cflag |= (CLOCAL | CREAD);
         termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
         termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
         termAttr.c_oflag &= ~OPOST;
         tcsetattr(fd,TCSANOW,&termAttr);
         printf("UART1 configured....\n");

         connected = 1;
         while(connected == 1){
               // some code
         }

         close(fd);
         exit(0);             
    }

    void signal_handler_IO (int status)
    {
         printf("received data from UART.\n");
    }

So anytime time another device send a message through the configured port, the message "received data from UART." is never displayed.

Any suggestion to solve this problem? Also, how does the system relate the interruption with the serial port?, I have read about signal.h but I have not found an answer for that. I got the interruption idea from this page: http://www.faqs.org/docs/Linux-HOWTO/Serial-Programming-HOWTO.html

Thanks in advance for any help. Thanks in advance.

回答1:

The problem is that you're disabling signals from the file descriptor by clearing the FASYNC flag with F_SETFL. You need to set that if you want to get signals:

fcntl(fd, F_SETFL, FNDELAY|FASYNC);

Also, you might want to use the POSIX names for these flags (O_NDELAY and O_ASYNC) rather than the BSD names for more portability, though either will work on Linux.



回答2:

I have found that a piece of code is missing for the original code to work as expected. The code below is working on Linux compiled with gcc. The added line of code is the one marked with /**<<<<<<------This line made it work.**/
One line was also commented out : //baudRate = B115200;.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>

void signal_handler_IO (int status);   /* definition of signal handler */

int n;
int fd;
int connected;
struct termios termAttr;
struct sigaction saio;

int main(int argc, char *argv[])
{
     fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
     if (fd == -1)
     {
        perror("open_port: Unable to open /dev/ttyO1\n");
        exit(1);
     }

     saio.sa_handler = signal_handler_IO;
     saio.sa_flags = 0;
     saio.sa_restorer = NULL; 
     sigaction(SIGIO,&saio,NULL);

     fcntl(fd, F_SETFL, FNDELAY);
     fcntl(fd, F_SETOWN, getpid());
     fcntl(fd, F_SETFL,  O_ASYNC ); /**<<<<<<------This line made it work.**/

     tcgetattr(fd,&termAttr);
     //baudRate = B115200;          /* Not needed */
     cfsetispeed(&termAttr,B115200);
     cfsetospeed(&termAttr,B115200);
     termAttr.c_cflag &= ~PARENB;
     termAttr.c_cflag &= ~CSTOPB;
     termAttr.c_cflag &= ~CSIZE;
     termAttr.c_cflag |= CS8;
     termAttr.c_cflag |= (CLOCAL | CREAD);
     termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
     termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
     termAttr.c_oflag &= ~OPOST;
     tcsetattr(fd,TCSANOW,&termAttr);
     printf("UART1 configured....\n");

     connected = 1;
     while(connected == 1){
           // some code
     }

     close(fd);
     exit(0);             
}

void signal_handler_IO (int status)
{
     printf("received data from UART.\n");
}

The output from the program was:

./a.out 
UART1 configured....
received data from UART.
received data from UART.
received data from UART.
^C

I hope that works for you too.



回答3:

Any suggestion to solve this problem? Also, how does the system relate the interruption with the serial port?

The serial port interrupt is written inside the device part of the serial driver in linux kernel. The interrupt is handled by the driver itself , so you can have not control over it.

What the above program is doing is , notifying you through a signal , when a receive interrupt of the serial part device is triggered.

The above signal handling is not related to interrupts , it is more of even handling. First you register , your program , to get a signal when an io interrupt occours , the signal handler in your program will then display the printf message.

I think the signal handling in your program , is not implemented properly Just correct the signal parts of your program

sigset_t mskvar_1                   //Variable of signal bitfieldtype
struct sigaction sigio_action       //Structure which describes signal handler
void sio_handler(void);             //Signal handler function

int main()
{
  sigfillset(&mskvar_1);                    //set all mask bits of maskbit variable
  sigprocmask(SIG_SETMASK,&mskvar_1,NULL);  //write the mask info present in mskvar_1 to the pd
  sigdelset(&mskvar_1,SIGIO);               //Unmask SIGIO , to register for IO Interrupt Events

  sigio_action.sa_handler = sio_handler;    //Configure Signal Handler
  sigio_action.sa_flags  = 0;
  sigfillset(&sigio_action.sa_mask);
  sigaction(SIGIO,&sigio_action,NULL);      //Install Signal handler

  // Serial port initializtion here
  // Set Serial port parameters , Baud Rate and flags


while(1);
return 0;
}


void sio_handler()
{
  printf("\nSIGIO RECEIVED , I/O interrupt signalled?\n");
  return;
}