我试图在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.
或类似的东西。每次都不一样。有时它工作正常。这似乎是随机的。
所以我的问题是: 我究竟做错了什么?除了毯子之外我还需要做什么代码?#34;所有这些代码?&#34;我不明白的是什么?我承认我并不真正了解这些图书馆是如何运作的。我假设(我知道,我知道)他们负责流量控制和错误等等。但话说回来,我所复制的例子并没有完全解释,所以我不知道。我只是不知道发生了什么。
答案 0 :(得分:1)
它不断分裂线,我不明白为什么。
如果您想从串口读取线路,则必须对其进行配置才能这样做 相反,您已将其配置为非规范和非阻止模式 该代码根本不符合您声明的意图。
引用Linux termios man 页面:
在规范模式中:
输入逐行提供。一个输入线可用 对行分隔符的类型(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串口配置为读取行。您正在使用真正的操作系统,而不是在微处理器上运行裸机。您所要做的就是激活线路规则来扫描串口接收的字符 编程规范模式的完整详细信息可以在Serial Programming Guide for POSIX Operating Systems和 termios man 页面中找到。
您的代码还有一些问题需要纠正:
memset(&options, 0, sizeof(options))
代码应该调用 tcgetattr()来正确初始化结构。对于规范输入而言,这可能是一个严重的问题,因为现有代码将所有控制代码规范归零,而不是具有适当的定义。 read(fileDescriptor, buf, 1000)
语句以处理可能的错误并处理收到的数据
读取的代码应该是这样的:
rc = read(fileDescriptor, buf, sizeof(buf) - 1);
if (rc < 0) {
/* handle error condition */
} else {
buf[rc] = '\0';
printf("%s", buf);
}
由于 buf [] 分配了1000个字节, read()请求可以返回最多999个字符的行。
答案 1 :(得分:0)
问题是您正在读取任意数量的字节,然后以换行符分隔输出它们:
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;
当然,应该有更多的错误处理。