为什么忽略我的缓冲区长度?

时间:2015-06-05 12:53:16

标签: c uart

我正在开发一个小型硬件项目的接收器。我正在使用UART传输数据的小板 接收器代码如下所示,我将在短期内单独解释有问题的位。

#define TTY "/dev/ttys002"

#include <stdio.h>
#include <string.h>
#include <unistd.h> //Unix standard functions
#include <fcntl.h> //File controls
#include <errno.h> //Error numbers
#include <assert.h>
#include <termios.h> //Posix terminal

int open_port(const char * tty) {
    int fd;
    fd = open(tty, (O_RDWR | O_NOCTTY | O_NDELAY));
    assert("__failed to open port__" && fd != -1);
    //Block until read
    fcntl(fd, F_SETFL, 0);
    return fd;
}

void configure_port(int fd) {
    struct termios options;
    //Get current options
    tcgetattr(fd, &options);

    //9600 Baud
    cfsetispeed(&options, B9600);
    cfsetospeed(&options, B9600);

    //Receive & local mode
    options.c_cflag |= (CLOCAL | CREAD);

    //Raw output
    options.c_oflag &= ~OPOST;

    //No hardware flow control
    options.c_cflag &= ~CRTSCTS;

    //No parity, 1 stop bit
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;

    //8 data bits
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;

    //Write options back, set them immediately
    tcsetattr(fd, TCSANOW, &options);
}

int main(int argc, const char * argv[]) {
    int fd = open_port(TTY);
    const size_t count = 8;
    char buf[count + 1];
    ssize_t n;
    configure_port(fd);
    while (1) {
        n = read(fd, buf, count);
        buf[count] = '\0';
        if (n > 0) {
            printf("%s\n", buf);
        }
    }
    return 0;
}

由于我目前没有手头的硬件,我决定在常规tty(#define TTY "/dev/ttys002")上测试我的接收器。为了测试它,我只是编译并运行上面的代码然后打开一个单独的终端并且:

echo "text" >> /dev/ttys002

所有这一切都运行良好而且我得到了所有我正在回复的数据。

但是,当我在tty中输入长消息时会出现问题:

echo "this is a longer test message" >> /dev/ttys002

我在程序输出中将整个消息作为单个字符串接收。为什么会这样?我原本希望文本被分成8个字符(const size_t count = 8;)。

如果它很重要,我使用this guide作为配置的首选。

修改:请参阅评论以进一步讨论该问题。

3 个答案:

答案 0 :(得分:1)

恕我直言,您的消息 被拆分为八个字符块:n = read(fd, buf, count);一次不能提供超过计数字节数。

但由于您未在RAW模式下配置tty线,因此仍处于线缓冲模式。因此,底层驱动程序会阻塞第一个读取,直到它的整行以\n终止(或超过缓冲区容量)。

然后读取返回前8个字节,然后读取立即以8个字节返回,因为数据在驱动程序缓冲区中可用。

如果您想使用原始输入模式,您应该在 man termios 中查看非规范模式:

options.c_cflags &= ~ICANON;
/* optionally if defaults are not appropriates */
options.c_cc[VMIN] = vmin; /* default is 1 */
options.c_cc[VTIME] = vtime; /* default is 0 */

但无论如何,你读取字符并且从不添加终止空值,因此buf没有理由被null终止。您应该只打印实际读取的字符数:

const size_t count = 8;
char buf[count];
ssize_t n;
configure_port(fd);
while (1) {
    n = read(fd, buf, count);
    if (n > 0) {
        printf("%.*s\n", n, buf); /* only print the n characters */
    }
}

答案 1 :(得分:0)

您的问题似乎与此网站的名称相同:)

您在buf变量的堆栈上分配8个字节的数据。然后,在read函数中,您正在编写&#34;这是一个更长的测试消息&#34; string,长度远远超过8个字节。当您使用&{34;%s&#34;时使用printf功能时,printf会在到达空字符(\0)时停止打印,因为它需要您字符串以null结尾。 在您的情况下,您应该检查您的buf变量是否以空值终止。如果不是,则应将buf的最后一个字节设为空字符

答案 2 :(得分:0)

你要找的是伪tty(PTY)。

创建PTY的一种简单方法是使用socat(1),如下所示:

socat pty,link=/tmp/testpty stdio

这将创建一个PTY并将当前终端的stdio附加到其主端。您在此处输入的数据将被发送到PTY的从属端。

现在从程序连接到从属端,它应该按预期工作:

#define TTY "/tmp/testpty"