从c中的串口读取会破坏线路

我正在尝试用C编写一个小程序,它将使用select命令从串口读取,以便它阻塞并等待输入。 它正在工作,除了它不断分裂线,我不知道为什么。 该设备的编程不会破坏线路,并且可以与实际的终端程序一起使用。 我之前从未在C中进行过串口通信,而且我在Mac上,所以这对我来说都是新手。 我真的不知道在哪里可以找到出错的地方。

我有一些代码可以找到并列出串口。 为了简单起见,我会把它留下来,所以如果有一个没有意义的变量,那可能就是原因。 以下是打开端口,设置属性并尝试从中读取的代码,以及来自Apple网站的复制注释(对不起):

/* this is based on a combination of http://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c * and https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/SerialDevices.html */ static int OpenSerialPort(const char *deviceFilePath, int speed) { int fileDescriptor = -1; struct termios options; memset(&options, 0, sizeof(options)); // init it // Open the serial port read/write, with no controlling terminal, // and don't wait for a connection. // The O_NONBLOCK flag also causes subsequent I/O on the device to // be non-blocking. // See open(2) ("man 2 open") for details. fileDescriptor = open(deviceFilePath, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fileDescriptor == -1) { printf("Error opening serial port %s - %s(%d).\n", deviceFilePath, strerror(errno), errno); goto error; } // Note that open() follows POSIX semantics: multiple open() calls to // the same file will succeed unless the TIOCEXCL ioctl is issued. // This will prevent additional opens except by root-owned processes. // See options(4) ("man 4 options") and ioctl(2) ("man 2 ioctl") for details. if (ioctl(fileDescriptor, TIOCEXCL) == kMyErrReturn) { printf("Error setting TIOCEXCL on %s - %s(%d).\n", deviceFilePath, strerror(errno), errno); goto error; } // Set raw input (non-canonical) mode, with reads blocking until either // a single character has been received or a one second timeout expires. // See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios") // for details. cfmakeraw(&options); options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 5; // The baud rate, word length, and handshake options can be set as follows: cfsetspeed(&options, speed); // Set 19200 baud options.c_cflag = (options.c_cflag & ~CSIZE) | CS8; // 8-bit chars // disable IGNBRK for mismatched speed tests; otherwise receive break // as \000 chars options.c_iflag &= ~IGNBRK; // disable break processing options.c_lflag = 0; // no signaling chars, no echo, // no canonical processing options.c_oflag = 0; // no remapping, no delays options.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl options.c_cflag |= (CLOCAL | CREAD);// ignore modem controls, // enable reading options.c_cflag &= ~(PARENB | PARODD); // shut off parity options.c_cflag |= false; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CRTSCTS; // Cause the new options to take effect immediately. if (tcsetattr(fileDescriptor, TCSANOW, &options) == kMyErrReturn) { printf("Error setting options attributes %s - %s(%d).\n", deviceFilePath, strerror(errno), errno); goto error; } // turn on blocking if (fcntl(fileDescriptor, F_SETFL, 0) == kMyErrReturn) { printf("Error clearing O_NONBLOCK %s - %s(%d).\n", deviceFilePath, strerror(errno), errno); goto error; } // Success: return fileDescriptor; // Failure: error: if (fileDescriptor != kMyErrReturn) { close(fileDescriptor); } return -1; } int main(void) { int fileDescriptor; kern_return_t kernResult; // these are Apple-specific io_iterator_t serialPortIterator; // Apple char deviceFilePath[MAXPATHLEN]; fd_set fdset; // make a file descriptor set FD_ZERO (&fdset); // init it char buf[1000]; // some strings are big kernResult = GetDevices(&serialPortIterator); printf("Devices on this system:\n"); kernResult = ListDevicePaths(serialPortIterator, deviceFilePath, sizeof(deviceFilePath)); IOObjectRelease(serialPortIterator); // Release the iterator. // Open the modem port, initialize the modem, then close it. if (!deviceFilePath[0]) { printf("No modem port found.\n"); return EX_UNAVAILABLE; } fileDescriptor = OpenSerialPort("/dev/cu.usbmodem1d1111", B230400); FD_SET (fileDescriptor, &fdset); // add to file descriptor set // now we're going to use select to only read from the file handle when there's data available while (1) { if (select (FD_SETSIZE, &fdset, NULL, NULL, NULL) < 0) // this will block the program until something is on the line { printf("select error\n"); } read(fileDescriptor, buf, 1000); printf("%s\n", buf); memset(buf, '\0', 1000); } // let's try to read from the serial port /* for (int i = 0; i <= 10; i++) { char buf [100]; int n = read(fileDescriptor, buf, sizeof buf); printf("%s\n", buf); //usleep ((7 + 25) * 100); }*/ close(fileDescriptor); printf("Modem port closed.\n"); return EX_OK; } 

预期产量:

  This is sample output. Hello. 

我在上述程序中实际获得的内容:

  Thi s is sam ple output. Hel lo. 

或类似的东西。 每次都不一样。 有时它工作正常。 这似乎是随机的。

所以我的问题是:我做错了什么? 我需要做什么代码,除了一条毯子“所有这些?” 我不明白的是什么? 我承认我并不真正理解这些库是如何工作的。 我假设(我知道,我知道)他们负责流量控制和错误等等。 但话说回来,我复制过的例子并没有完全解释,所以我不知道。 我只是不知道发生了什么。

它不断分裂线,我不明白为什么。

如果要从串行端口读取行,则必须对其进行配置才能执行此操作。
相反,您已将其配置为非规范和非阻止模式。
该代码根本不符合您声明的意图。

从Linux termios 手册页引用:

在规范模式中:
输入逐行提供。 键入其中一个行分隔符(NL,EOL,EOL2;或行首的EOF)时,输入行可用。 除EOF外,行分隔符包含在read(2)返回的缓冲区中。

代码清楚地表明它使用的是非规范模式(即错误的模式):

 // Set raw input (non-canonical) mode, with reads blocking until either // a single character has been received or a one second timeout expires. // See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios") // for details. cfmakeraw(&options); options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 5; 

您需要删除这些行以获取规范模式并读取行而不是原始字节。

如果您希望read()返回完整的行,那么程序将不得不等待输入。 这意味着您需要阻止I / O.

 // The O_NONBLOCK flag also causes subsequent I/O on the device to // be non-blocking. // See open(2) ("man 2 open") for details. fileDescriptor = open(deviceFilePath, O_RDWR | O_NOCTTY | O_NONBLOCK); 

需要从open()系统调用中删除O_NONBLOCK选项。

尽管至少有三个评论者写过,但可以配置Linux串口来读取行。 您正在使用真正的操作系统,而不是在微处理器上运行裸机。 您所要做的就是激活线路规则来扫描串行端口接收的字符。
有关编程规范模式的完整详细信息,请参见POSIX操作系统的串行编程指南和termios 手册页。

您的代码还有几个问题需要纠正:

  • 而不是memset(&options, 0, sizeof(options))代码应该调用tcgetattr()来正确初始化结构。 对于规范输入,这可能是一个严重的问题,因为现有代码将所有控制代码规范归零,而不是具有适当的定义。
  • 代码应该执行逐位操作(以便保留现有设置),而不是直接分配。 请参阅正确设置终端模式 。
  • 需要扩展read(fileDescriptor, buf, 1000)语句以处理可能的错误并处理接收的数据。
    • 需要检查read()系统调用的返回代码是否存在任何错误情况。
    • 当没有检测到错误时,返回码表示缓冲区中返回的字节数。 请注意,输入不会被空字节终止,因此在追加null之前,不应将字符串操作应用于缓冲区。

读取代码应该是这样的:

  rc = read(fileDescriptor, buf, sizeof(buf) - 1); if (rc < 0) { /* handle error condition */ } else { buf[rc] = '\0'; printf("%s", buf); } 

由于buf []被分配为1000个字节,因此read()请求可以返回长达999个字符的行。

问题是您正在读取任意数量的字节,然后以换行符分隔输出它们:

 read(fileDescriptor, buf, 1000); printf("%s\n", buf); 

你打开了描述符O_NONBLOCK ,我不确定你的fcntl调用是否足以清除它。 结果是read拉出但是那个时刻恰好缓冲了许多字符,然后打印它们后跟换行符。

您可能不希望以阻塞模式read ,因为在读取1000个字符之前它可能不会返回。 这可能更接近你想要的:

 amt = read(fileDescriptor, buf, 1000); if (amt > 0) write(1,buff,amt); else break; 

当然,应该有更多的error handling。