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);
我想用板子上的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);