从C中的串行端口读取会中断线路 [英] reading from serial port in c breaks up lines

查看:39
本文介绍了从C中的串行端口读取会中断线路的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试用C语言编写一个小程序,该程序将使用select命令从串行端口读取该程序,以使其阻塞并等待输入.它正在工作,除了它会不断中断线路,而且我不知道为什么.该设备被编程为不会中断线路,并且可以与实际的终端程序一起正常工作.我以前从未在C语言中进行过串行通信,而且我在Mac上,所以对我来说这是全新的.我真的不知道该在哪里查找问题所在.

I'm trying to write a little program in C that will read from the serial port using the select command so that it blocks and waits for input. It's working, except it keeps breaking up lines and I have no idea why. The device is programmed not to break up the lines and works fine with actual terminal programs. I've never done serial communication in C before, and I'm on a Mac, so it's all new to me. I really have no idea where to even look for what's going wrong.

我有一些代码可以查找并列出串行端口.为了简单起见,我将其省略,因此,如果存在一个没有意义的变量,那可能就是原因.这是打开端口,设置属性并尝试从中读取的代码,并附有Apple网站复制的注释(对不起):

I have some code that finds and lists serial ports. I'll leave that out for simplicity, so if there's a variable that doesn't make sense, that might be why. Here is the code that opens the port, sets attributes, and tries to read from it, complete with copied comments from Apple's site (sorry):

/* 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.

我在上述程序中实际上得到了什么:

What I actually get in the above program:

    Thi
    s is sam
    ple output.
    Hel
    lo.

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

Or something like that. It's different each time. Sometimes it works fine. It seems to be random.

所以我的问题是:我究竟做错了什么?除了全部"之外,我还需要处理什么代码?我不明白什么?我承认我不太了解这些库的工作原理.我假设(我知道,我知道)他们负责流程控制和错误等等.但是话又说回来,我从中复制的示例并不能完全解释这一点,所以我不知道.我只是真的不知道发生了什么.

So my questions are: What am I doing wrong? What code do I need to work on aside from just a blanket "all of it?" What am I not understanding? I admit I don't really understand how these libraries work, exactly. I am assuming (I know, I know) that they take care of flow control and errors and so on. But then again, the examples I've copied from didn't exactly explain that so I don't know. I just don't really know what's going on.

推荐答案

它一直在分行,我不知道为什么.

it keeps breaking up lines and I have no idea why.

如果要从串行终端读取线路,则必须对其进行配置.
相反,您已将其配置为处于非规范和非阻塞模式.
该代码根本不符合您声明的意图.

If you wants to read lines from the serial terminal, then you have to configure it to do so.
Instead you have configured it to be in non-canonical and non-blocking mode.
The code does not match your stated intentions at all.

在Linux termios man 页面上报价:

Quoting from the Linux termios man page:

在规范模式下:
逐行提供输入.当一个输入线可用时键入行定界符的行号(NL,EOL,EOL2;或行首的EOF).除了EOF之外,行定界符包含在read(2)返回的缓冲区中.

In canonical mode:
Input is made available line by line. An input line is available when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at the start of line). Except in the case of EOF, the line delimiter is included in the buffer returned by read(2).

该代码已明确注释其使用的是非规范模式(即错误的模式):

The code is clearly commented that it is using non-canonical mode (i.e. the wrong mode):

// 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;

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

You need to remove these lines to get canonical mode and read lines instead of raw bytes.

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

If you expect the read() to return complete lines, then the program will have to wait for input. That means that you need blocking 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选项.

The O_NONBLOCK option needs to be removed from the open() syscall.

尽管至少写了三位评论者,但可以将Linux串行终端配置为读取行.您使用的是真实的操作系统,而不是在微处理器上运行裸机.您所要做的就是激活线路规则以扫描串行终端接收到的字符.
可以在 POSIX操作系统串行编程指南 termios man 页面.

In spite of what at least three commenters have written, a Linux serial terminal can be configured to read lines. You are using a real operating system, and not running bare-metal on a microprocessor. All you have to do is activate the line discipline to scan the characters received by the serial terminal.
Full details for programming canonical mode can be found in Serial Programming Guide for POSIX Operating Systems and the termios man page.

您的代码中还有几个问题应予以纠正:

There are also several issues with your code that should be corrected:

  • 该代码应该调用 tcgetattr()来正确初始化该结构,而不是 memset(& options,0,sizeof(options)).对于规范输入而言,这可能是一个严重的问题,因为现有代码将使所有控制代码规范归零,而不是具有适当的定义.
  • 代替直接分配,代码应该执行按位操作(以保留现有设置).请参阅正确设置终端模式.
  • read(fileDescriptor,buf,1000)语句需要扩展以处理可能的错误并处理接收到的数据.
      需要检查
    • 来自 read()系统调用的返回代码是否存在任何错误情况.
    • 如果未检测到错误,则返回码指示缓冲区中返回的字节数.请注意,输入不会以空字节终止,因此在追加空值之前,不应将字符串操作应用于缓冲区.
    • Instead of memset(&options, 0, sizeof(options)) the code should be calling tcgetattr() to properly initialize the structure. This can be a serious issue for canonical input, as the existing code will have zeroed out all of the control code specifications instead of having proper definitions.
    • Instead of direct assignments, the code should be performing bit-wise operations (in order to preserve existing settings). See Setting Terminal Modes Properly.
    • The read(fileDescriptor, buf, 1000) statement needs to be expanded to handle possible errors and to deal with the received data.
      • the return code from the read() syscall needs to be checked for any error conditions.
      • when no error is detected, then the return code indicates the number of bytes returned in the buffer. Note that the input will not be terminated by a null byte, so string operations should not be applied to the buffer until a null is appended.

      读取的代码应类似于:

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

      由于 buf [] 被分配了1000个字节,因此 read()请求可以返回最多999个字符的行.

      Since buf[] is allocated for 1000 bytes, the read() request can return a line up to 999 characters long.

      这篇关于从C中的串行端口读取会中断线路的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆