C 无法在ubuntu上使用调制解调器读取任何数据

C 无法在ubuntu上使用调制解调器读取任何数据,c,linux,serial-port,C,Linux,Serial Port,我正在尝试从串行端口读取数据,并从这里获取了示例代码: 代码示例为: #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h> /* baudrate settings are defined in <asm/termbits.h>, which is included b

我正在尝试从串行端口读取数据,并从这里获取了示例代码:

代码示例为:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

/* baudrate settings are defined in <asm/termbits.h>, which is
included by <termios.h> */
#define BAUDRATE B9600     
//B38400            
/* change this definition for the correct port */
#define MODEMDEVICE "/dev/ttyACM0"
#define _POSIX_SOURCE 1 /* POSIX compliant source */

#define FALSE 0
#define TRUE 1

volatile int STOP=FALSE; 

main()
{
  printf("starting program\n");

          int fd,c, res;
          struct termios oldtio,newtio;
          char buf[255];
        /* 
          Open modem device for reading and writing and not as controlling tty
          because we don't want to get killed if linenoise sends CTRL-C.
        */
         fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); 
         if (fd <0) {perror(MODEMDEVICE); exit(-1); }

         printf("fd=%d\n", fd);

         tcgetattr(fd,&oldtio); /* save current serial port settings */
         bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */

        /* 
          BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
          CRTSCTS : output hardware flow control (only used if the cable has
                    all necessary lines. See sect. 7 of Serial-HOWTO)
          CS8     : 8n1 (8bit,no parity,1 stopbit)
          CLOCAL  : local connection, no modem contol
          CREAD   : enable receiving characters
        */
         newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

        /*
          IGNPAR  : ignore bytes with parity errors
          ICRNL   : map CR to NL (otherwise a CR input on the other computer
                    will not terminate input)
          otherwise make device raw (no other input processing)
        */
         newtio.c_iflag = IGNPAR | ICRNL;

        /*
         Raw output.
        */
         newtio.c_oflag = 0;

        /*
          ICANON  : enable canonical input
          disable all echo functionality, and don't send signals to calling program
        */
         newtio.c_lflag = ICANON;

        /* 
          initialize all control characters 
          default values can be found in /usr/include/termios.h, and are given
          in the comments, but we don't need them here
        */
         newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */ 
         newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
         newtio.c_cc[VERASE]   = 0;     /* del */
         newtio.c_cc[VKILL]    = 0;     /* @ */
         newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
         newtio.c_cc[VTIME]    = 0;     /* inter-character timer unused */
         newtio.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */
         newtio.c_cc[VSWTC]    = 0;     /* '\0' */
         newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */ 
         newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
         newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
         newtio.c_cc[VEOL]     = 0;     /* '\0' */
         newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
         newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
         newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
         newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
         newtio.c_cc[VEOL2]    = 0;     /* '\0' */

        /* 
          now clean the modem line and activate the settings for the port
        */
         tcflush(fd, TCIFLUSH);
         tcsetattr(fd,TCSANOW,&newtio);
         printf("tcsetattr returned %d\n", res);
         printf("just before while STOP loop\n");        
        /*
          terminal settings done, now handle input
          In this example, inputting a 'z' at the beginning of a line will 
          exit the program.
        */
         while (STOP==FALSE) {     /* loop until we have a terminating condition */
         /* read blocks program execution until a line terminating character is 
            input, even if more than 255 chars are input. If the number
            of characters read is smaller than the number of chars available,
            subsequent reads will return the remaining chars. res will be set
            to the actual number of characters actually read */
             printf("just before read\n");
             res = read(fd,buf,255); 
            printf("just after read\n");

            buf[res]=0;             /* set end of string, so we can printf */
            printf(":%s:%d\n", buf, res);
            if (buf[0]=='z') STOP=TRUE;
         }
         /* restore the old port settings */
         tcsetattr(fd,TCSANOW,&oldtio);
        }
我试着用手机拨调制解调器,但什么也没拨

如果我使用ls/dev/ttyA*我会看到/dev/ttyACM0

我是否需要以某种方式配置端口

调制解调器是Conexant 93010语音调制解调器。它确实有效。我在VMWare虚拟机中运行ubuntu有一点复杂。但是我确实在虚拟机中连接,当我连接设备时,我确实看到了ttyACM0

更新 我在测试中发现,我确实得到了输出,但它非常延迟。例如,如果我打电话进来,我最终会显示戒指。但我必须不断地通过调制解调器打电话,切断线路,然后再打电话。15圈后,我看到了输出。我将read改为一次只读取5个字符。为什么拖延

如果我将read调用更改为一次读取10个字符,这是相同的行为。在我看到屏幕上打印的任何数据(所有这些戒指)之前,需要15-16个戒指


我在Windows上运行串行代码,没有延迟。因此,这不是硬件。

由于历史原因,串行驱动程序层中有许多设置,它们将执行不同的预处理功能。您的问题似乎出现在这里:

newtio.c_lflag = ICANON;
...
newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
...
newtio.c_cc[VEOL]     = 0;     /* '\0' */
....
newtio.c_cc[VEOL2]    = 0;     /* '\0' */
根据以下文件:

规范模式与非规范模式

c_lflag中ICANON佳能标志的设置决定是否 终端在规范模式(ICANON set)或非规范模式下运行 ical模式(ical未设置)。默认情况下,设置了ICANON

在规范模式下:

  • 逐行提供输入。输入行可用 键入其中一个行分隔符时(NL、EOL、EOL2;或EOF at 行的开始)。除EOF外,行分隔符 包含在读取(2)返回的缓冲区中
因此,听起来这里发生的事情是内核缓冲区最终被填满并向您发送信息,或者您得到一个控制字符(可能是错误的)

无论如何,请尝试将
newio.clflag
设置为0;这将清除规范输入模式。也可能有帮助


我通常会清除所有termios设置,如下所示,然后根据需要进行设置:

struct termios newio;
if( tcgetattr(fd, &newio) < 0 ){
   /* Error checking */
}
newio.c_iflag |= IGNBRK;
newio.c_iflag &= ~BRKINT;
newio.c_iflag &= ~ICRNL;
newio.c_oflag = 0;
newio.c_lflag = 0;
newio.c_cc[VTIME] = 0;
newio.c_cc[VMIN] = 1;
if( tcsetattr( fd, TCSANOW, &newio ) < 0 ) {
    /* Error checking */
}
struct termios newio;
if(tcgetattr(fd和newio)<0){
/*错误检查*/
}
newio.c|u iflag |=IGNBRK;
newio.c_iflag&=~BRKINT;
newio.c_iflag&=~ICRNL;
newio.c_of lag=0;
newio.c_lflag=0;
newio.c_cc[VTIME]=0;
newio.c_cc[VMIN]=1;
如果(tcsetattr(fd、TCSANOW和newio)<0){
/*错误检查*/
}

如果您不想自己进行串行端口设置,则上述设置与我在和中使用的设置相同。

检查诸如read和/或TceAttr等功能的返回值,顺便问一下,您是否安装/激活了selinux?我想我没有安装selinux。它是标准的ubuntu。就在前面几个街区read@OznOg编辑以显示TsetAttr的返回,如果res为255@buf[res]=0,则关闭1;更糟糕的是,如果read返回-1ca,则您可以使用minicom之类的软件与调制解调器“交谈”,并对建议的不符合POSIX的代码投反对票。请参阅@sawdust我从来没有说过任何关于POSIX合规性的内容,但为了澄清,我编辑了答案的后半部分。您的代码仍然不可移植,尽管它没有OP复制的代码那么糟糕。还有其他用户评论说,符合POSIX的代码比您的示例中的硬赋值“工作得更好”。请参阅,我曾经在公共域程序中替换了非POSIX termios代码,如OP,然后有了显著的改进和可靠的传输。
struct termios newio;
if( tcgetattr(fd, &newio) < 0 ){
   /* Error checking */
}
newio.c_iflag |= IGNBRK;
newio.c_iflag &= ~BRKINT;
newio.c_iflag &= ~ICRNL;
newio.c_oflag = 0;
newio.c_lflag = 0;
newio.c_cc[VTIME] = 0;
newio.c_cc[VMIN] = 1;
if( tcsetattr( fd, TCSANOW, &newio ) < 0 ) {
    /* Error checking */
}