C语言中的串行通信

5
我正在尝试编写一个简单的应用程序,通过串行通信(RS232<->USB)在Linux上从Keithley 6485皮克安计读取当前值。
目前,可以通过执行所有必需的设备初始化并向其发送"READ?"来检索此类值:echo "READ?" > /dev/ttyUSB0。然后,如果cat /dev/ttyUSB0一直监听,我会得到以下输出:-2.250416E-14A,+8.320175E+03,+0.000000E+00,其中第一个数字是所需的值。
为了能够输出该值,我使用以下代码使用termios库:
    /*====================================================================================================*/
    /* Serial Port Programming in C (Serial Port Read)                                                    */
/* Non Cannonical mode                                                                                */
/*----------------------------------------------------------------------------------------------------*/
    /* Program reads a string from the serial port at 9600 bps 8N1 format                                 */
/* Baudrate - 9600                                                                                    */
/* Stop bits -1                                                                                       */
/* No Parity                                                                                          */
    /*----------------------------------------------------------------------------------------------------*/
/* Compiler/IDE  : gcc 4.6.3                                                                          */
/* Library       :                                                                                    */
/* Commands      : gcc -o serialport_read serialport_read.c                                           */
/* OS            : Linux(x86) (Linux Mint 13 Maya)(Linux Kernel 3.x.x)                                */                              
/* Programmer    : Rahul.S                                                                            */
/* Date          : 21-December-2014                                                                   */
/*====================================================================================================*/

/*====================================================================================================*/
/* www.xanthium.in                                            */
/* Copyright (C) 2014 Rahul.S                                                                         */
/*====================================================================================================*/

/*====================================================================================================*/
/* Running the executable                                                                             */
/* ---------------------------------------------------------------------------------------------------*/ 
/* 1) Compile the  serialport_read.c  file using gcc on the terminal (without quotes)                 */
    /*                                                                                                    */
/*  " gcc -o serialport_read serialport_read.c "                                                  */
/*                                                                                                    */
    /* 2) Linux will not allow you to access the serial port from user space,you have to be root.So use   */
    /*    "sudo" command to execute the compiled binary as super user.                                    */
    /*                                                                                                    */
    /*       "sudo ./serialport_read"                                                                     */
/*                                                                                                    */
/*====================================================================================================*/

/*====================================================================================================*/
/* Sellecting the Serial port Number on Linux                                                         */
/* ---------------------------------------------------------------------------------------------------*/ 
/* /dev/ttyUSBx - when using USB to Serial Converter, where x can be 0,1,2...etc                      */
/* /dev/ttySx   - for PC hardware based Serial ports, where x can be 0,1,2...etc                      */
    /*====================================================================================================*/

/*-------------------------------------------------------------*/
    /* termios structure -  /usr/include/asm-generic/termbits.h    */ 
/* use "man termios" to get more info about  termios structure */
/*-------------------------------------------------------------*/

    #include <stdio.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           */

void main(void)
    {
        int fd;/*File Descriptor*/

    printf("\n +----------------------------------+");
    printf("\n |        Serial Port Read          |");
    printf("\n +----------------------------------+");

    /*------------------------------- Opening the Serial Port -------------------------------*/

    /* Change /dev/ttyUSB0 to the one corresponding to your system */

        fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY);    /* ttyUSB0 is the FT232 based USB2SERIAL Converter   */
    //  fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NDELAY); /* ttyUSB0 is the FT232 based USB2SERIAL Converter   */
                            /* O_RDWR   - Read/Write access to serial port       */
                            /* O_NOCTTY - No terminal will control the process   */
                            /* Open in blocking mode,read will wait              */



        if(fd == -1)                        /* Error Checking */
               printf("\n  Error! in Opening ttyUSB0  ");
        else
               printf("\n  ttyUSB0 Opened Successfully ");


    /*---------- Setting the Attributes of the serial port using termios structure --------- */

    struct termios SerialPortSettings;  /* Create the structure                          */

    tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */

    /* Setting the Baud rate */
    cfsetispeed(&SerialPortSettings,B19200); /* Set Read  Speed as 19200                       */
    cfsetospeed(&SerialPortSettings,B19200); /* Set Write Speed as 19200                       */

    /* 8N1 Mode */
    SerialPortSettings.c_cflag &= ~PARENB;   /* Disables the Parity Enable bit(PARENB),So No Parity   */
    SerialPortSettings.c_cflag &= ~CSTOPB;   /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
    SerialPortSettings.c_cflag &= ~CSIZE;    /* Clears the mask for setting the data size             */
    SerialPortSettings.c_cflag |=  CS8;      /* Set the data bits = 8                                 */

    SerialPortSettings.c_cflag &= ~CRTSCTS;       /* No Hardware flow Control                         */
    SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines       */ 


    SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);          /* Disable XON/XOFF flow control both i/p and o/p */
    SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  /* Non Cannonical mode                            */

    SerialPortSettings.c_oflag &= ~OPOST;/*No Output Processing*/

    /* Setting Time outs */
    SerialPortSettings.c_cc[VMIN] = 13; /* Read at least 10 characters */
    SerialPortSettings.c_cc[VTIME] = 0; /* Wait indefinetly   */


    if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) /* Set the attributes to the termios structure*/
        printf("\n  ERROR ! in Setting attributes");
    else
                printf("\n  BaudRate = 19200 \n  StopBits = 1 \n  Parity   = none");

        /*------------------------------- Read data from serial port -----------------------------*/

    char read_buffer[32];   /* Buffer to store the data received              */
    int  bytes_read = 0;    /* Number of bytes read by the read() system call */
    int i = 0;

    tcflush(fd, TCIFLUSH);   /* Discards old data in the rx buffer            */

    bytes_read = read(fd,&read_buffer,32); /* Read the data                   */

    printf("\n\n  Bytes Rxed -%d", bytes_read); /* Print the number of bytes read */
    printf("\n\n  ");
    for(i=0;i<13;i++)    /*printing only the needed bytes*/
        printf("%c",read_buffer[i]);

    printf("\n +----------------------------------+\n\n\n");

    close(fd); /* Close the serial port */

    }

这将输出:

 +----------------------------------+
 |        Serial Port Read          |
 +----------------------------------+
  ttyUSB0 Opened Successfully 
  BaudRate = 19200 
  StopBits = 1 
  Parity   = none

  Bytes Rxed -13

  -2.864104E-14
 +----------------------------------+

但是,为了能够读取值,每次想要知道值时我都需要回显“READ?”命令(或者使用write()函数向设备写入)。

如何以最优雅的方式将写入和读取放在同一个应用程序中(例如,不使用命名管道),因为当前的read()函数会等待来自设备的任何内容,在此期间我不能从同一段C代码发送“READ?”命令?


编辑:显然我的写入过程似乎无法正常工作:

端口设置:

    struct termios SerialPortSettings;  /* Create the structure                          */

    tcgetattr(fd, &SerialPortSettings); /* Get the current attributes of the Serial port */

    cfsetispeed(&SerialPortSettings,(speed_t)B19200); /* Set Read  Speed as 19200                       */
    cfsetospeed(&SerialPortSettings,(speed_t)B19200); /* Set Write Speed as 19200                       */

    SerialPortSettings.c_cflag &= ~PARENB;   /* Disables the Parity Enable bit(PARENB),So No Parity   */
    SerialPortSettings.c_cflag &= ~CSTOPB;   /* CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit */
    SerialPortSettings.c_cflag &= ~CSIZE;    /* Clears the mask for setting the data size             */
    SerialPortSettings.c_cflag |=  CS8;      /* Set the data bits = 8                                 */

    SerialPortSettings.c_cflag = ~CRTSCTS;       /* No Hardware flow Control                         */
    SerialPortSettings.c_cflag |= CREAD | CLOCAL; /* Enable receiver,Ignore Modem Control lines       */ 


cfmakeraw(&SerialPortSettings);

tcflush(fd,TCIFLUSH);

    SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);          // Disable XON/XOFF flow control both i/p and o/p
    SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // Non Cannonical mode                           
    SerialPortSettings.c_oflag &= ~OPOST;//No Output Processing

写作:

    char write_buffer[] = "READ?";  /* Buffer containing characters to write into port       */ 
    int  bytes_written  = 0;    /* Value for storing the number of bytes written to the port */ 

    bytes_written = write(fd,&write_buffer,sizeof(write_buffer) -1);
printf("%s written to ttyUSB0 \n",write_buffer);
    printf("%d Bytes written to ttyUSB0 \n", bytes_written);
    printf("+----------------------------------+\n\n");

    close(fd);/* Close the Serial port */

每次运行时,我都会得到以下结果:
+----------------------------------+ 
|        Serial Port Write         | 
+----------------------------------+ 
ttyUSB0 Opened Successfully 
Attributes set 
READ? written to ttyUSB0 
5 Bytes written to ttyUSB0 
+----------------------------------+

但是,cat /dev/ttyUSB0似乎没有看到从设备传来的任何信息。我已经查看了类似问题的解答:Linux C串行端口读写,并没有在代码上找到太大的区别 - 这是否意味着串口设置错误或我遗漏了些什么?

1
为什么在调用read读取答案之前不能调用write发送查询? - Some programmer dude
1
如果在调用 read 之前调用了 write,那么 read 将不能访问到该值 - 我猜测它是实时按字节读取,而不是从缓冲区中读取。 - vastas
1
你说的“值无法通过read访问”是什么意思?如果在read之前执行write(fd, "READ?", 5),它就无法工作吗?read返回什么(bytes_read的值是多少)?如果失败了,errno是什么?请详细说明。 - Some programmer dude
有人能回答这个问题并将其标记为重复或类似的吗? - Suraj Jain
你在写入后是否关闭了串口,然后再为读取重新打开它?您能否尝试创建一个最小、完整和可验证的示例并向我们展示? - Some programmer dude
显示剩余2条评论
2个回答

3
问题已经解决!
显然,设备正在等待一个结束行终止符,但在char write_buffer[] = "READ?";中没有提供。
因此,当它接收到这样的命令时,总线会“挂起”,为了使其再次工作,必须重新打开端口。
这也解释了为什么echo "READ?" > /dev/ttyUSB0命令能够工作 - 因为它自动输出一个结束行终止符。
我附上了下面的可工作代码 - 感谢大家的评论和帮助!
  int fd;           //device file id
//------------------------------- Opening the Serial Port -------------------------------
    fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY);    // ttyUSB0 is the FT232 based USB2SERIAL Converter 
    if(fd == -1)                        // Error Checking 
    printf("Error while opening the device\n");
//---------- Setting the Attributes of the serial port using termios structure ---------
    struct termios SerialPortSettings;  // Create the structure                          
    tcgetattr(fd, &SerialPortSettings); // Get the current attributes of the Serial port
// Setting the Baud rate
  cfsetispeed(&SerialPortSettings,B19200); // Set Read  Speed as 19200                       
    cfsetospeed(&SerialPortSettings,B19200); // Set Write Speed as 19200                       

    SerialPortSettings.c_cflag &= ~PARENB;   // Disables the Parity Enable bit(PARENB),So No Parity   
    SerialPortSettings.c_cflag &= ~CSTOPB;   // CSTOPB = 2 Stop bits,here it is cleared so 1 Stop bit 
    SerialPortSettings.c_cflag &= ~CSIZE;    // Clears the mask for setting the data size             
    SerialPortSettings.c_cflag |=  CS8;      // Set the data bits = 8                                 
    SerialPortSettings.c_cflag &= ~CRTSCTS;       // No Hardware flow Control                         
    SerialPortSettings.c_cflag |= CREAD | CLOCAL; // Enable receiver,Ignore Modem Control lines        
    SerialPortSettings.c_iflag &= ~(IXON | IXOFF | IXANY);  // Disable XON/XOFF flow control both i/p and o/p 
    SerialPortSettings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);  // Non Cannonical mode 
    SerialPortSettings.c_oflag &= ~OPOST;//No Output Processing
// Setting Time outs 
    SerialPortSettings.c_cc[VMIN] = 13; // Read at least 10 characters 
    SerialPortSettings.c_cc[VTIME] = 0; // Wait indefinetly  

    if((tcsetattr(fd,TCSANOW,&SerialPortSettings)) != 0) // Set the attributes to the termios structure
    printf("Error while setting attributes \n");
    //------------------------------- Read data from serial port -----------------------------

    char read_buffer[32];   // Buffer to store the data received              
    int  bytes_read = 0;    // Number of bytes read by the read() system call 
  int bytes_written = 0;  // Number of bytes written
    int i = 0;

    tcflush(fd, TCIFLUSH);   // Discards old data in the rx buffer            
//Device intialization

  char write_buffer[]="READ? \n ";
  bytes_written=write(fd,&write_buffer,sizeof(write_buffer));


  bytes_read = read(fd,&read_buffer,32); // Read the data                   

    for(i=0;i<13;i++)    //printing only the needed characters
    printf("%c",read_buffer[i]);
    close(fd); // Close the serial port

你的代码仍然存在一个严重的错误。请参见https://stackoverflow.com/questions/51195829/ubuntu-serial-communication-reads-failing-and-then-coming-in-all-at-once。 - sawdust

0
在读取数据之前,请使用select()或poll()检查数据是否可用。如果没有可用的数据,则可以编写“READ?”命令。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接