Linux 中的串口编程

Serial port programming in Linux

我想用板子上的RS232端口与PC通讯。我知道我可以为此目的使用 "dev/ttyS0"

我可以使用 write() 函数打开数据并将数据写入 PC。但问题是 我无法从 "dev/ttyS0" 读取。 每次我使用读取函数时,我都会得到 "Resource temporarily unavailable"。你们能告诉我如何解决这个问题吗?

这是我的代码:

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

int main()
{
    int n = 0, fd = 0, bytes = 0;
    char ch = 0;

    char buffer[10], *bufPtr;
    int nBytes = 0, tries = 0, x = 0;

    struct termios term;

    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);

    if (fd == -1)
    {
        perror("open");
        return;
    }
    else
    {
        fcntl(fd, F_SETFL, 0);
        perror("Port");
    }

    if (n = tcgetattr(fd, &term) == -1)
    {
        perror("tcgetattr");
        return;
    }

    if (n = cfsetispeed(&term, B115200) == -1)
    {
        perror("cfsetispeed");
        return;
    }

    if (n = cfsetospeed(&term, B115200) == -1)
    {
        perror("cfsetospeed");
        return;
    }

    term.c_cflag |= (CLOCAL | CREAD);
    term.c_cflag &= ~PARENB;
    term.c_cflag &= ~CSTOPB;
    term.c_cflag &= ~CSIZE;
    term.c_cflag |= CS8;
    term.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    term.c_iflag &= ~(IXON | IXOFF | IXANY);
    term.c_cflag &= ~CRTSCTS;
    term.c_oflag &= ~OPOST; 

    if (n = tcsetattr(fd, TCSANOW, &term) == -1)
    {
        perror("tcsetattr");
        return;
    }

    write(fd,"LINUX",5);
    perror("write");

    fcntl(fd, F_SETFL, FNDELAY);    
    perror("fcntl");
    bytes = read(fd, buffer, sizeof(buffer));
    perror("read");
    printf("Bytes : %d and data: %s\n", bytes, buffer);
}

你的考试是什么?您是否将 RS232 DB9 连接器的针脚 2 和针脚 3 短路了?

否则read总是return-1如果没有数据可读

这就是您的代码使用 O_NDELAY 打开串行线的标志所要做的。

为了避免在没有数据可读的情况下读取,可以使用select 在你的情况下:

struct timeval tv;
fd_set rset;
int retV;
int timeout = 5000; // 5 seconds

tv.tv_usec = (timeout * 1000) % 1000000;
tv.tv_sec = timeout / 1000;

FD_ZERO ( &rset );
FD_SET (  fd, &rset );
retV = select ( fd+1, &rset, NULL, NULL, &tv );

if( retV == 0 )
{
   // timeout stuff
}
else if( retV < 0 )
{
    // Error stuff. Read errno to see what happened
}
else
{
   // read data
}

编辑

删除fcntl(fd, F_SETFL, FNDELAY); 如果您想要正常的阻塞读取,请取消设置该标志。

在您的代码中,您还假设 read 将 return 所有发送的字符,但事实并非如此。 read 将 return 个可供读取的字符。这意味着如果您发送 "LINUX" 可能会请求读取 5 次以读取发送的所有 5 个字符。

最后一件事

printf("Bytes : %d and data: %s\n", bytes, buffer);

Undefined Behavior 因为 buffer 不是 NULL 终止的字符串。您可以解决它在接收到的字符上循环并以 %c 格式打印它,或者 NULL 终止您的缓冲区:

if (bytes > 0)
   buffer[bytes] = '[=12=]';

char stringToSend[] = "LINUX";
size_t len = strlen(stringToSend) +1 ;

write(fd,"LINUX", len);
perror("write");

size_t receivedBytes = 0;
bytes = 0;
while (receivedBytes<len)
{
   bytes = read(fd, &buffer[receivedBytes], sizeof(buffer)-1);
   perror("read");

   if (bytes > 0)
       receivedBytes += bytes;
}

printf("Bytes : %d and data: %s\n", receivedBytes, buffer);