我想读取串行 COM 端口并将数据写入 LINUX 中的文件。实际上我是从其他电脑的超级终端发送数据。
问题是没有 while 循环我只能写一行。
但是使用 while(1) 循环我无法向文件写入任何内容。
否则我必须发送大文件,然后应用程序退出/终止并写入该文件。
我的应用程序应该写入数据(可能是 2 行或任何东西);之后它必须等待下一个数据。
所以请帮帮我......
这是我的代码
=========================================
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#define BAUDRATE B115200
#define MODEMDEVICE "/dev/ttyS0"
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
void signal_handler_IO (int status); /* definition of signal handler */
int wait_flag=TRUE; /* TRUE while no signal received */
struct timeval timeout;
char n;
fd_set rdfs;
/*Some variables*/
int num, offset = 0, bytes_expected = 255;
main()
{
int fd,c, res,i;
char In1;
struct termios oldtio,newtio;
struct sigaction saio; /* definition of signal action */
char buf[255];
FILE *fp;
/* open the device to be non-blocking (read will return immediatly) */
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyS0 - ");
return 1;
}
else
{
fcntl(fd, F_SETFL, 0);
n = select(fd + 1, &rdfs, NULL, NULL, &timeout);
}
fp = fopen("/root/Desktop/gccAkash/OutputLOG.txt","a+");
assert(fp != NULL);
/* install the signal handler before making the device asynchronous */
saio.sa_handler = signal_handler_IO;
sigemptyset(&saio.sa_mask);
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
/* allow the process to receive SIGIO */
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, FASYNC);
tcgetattr(fd,&oldtio); /* save current port settings */
/* set new port settings for canonical input processing */
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
newtio.c_cc[VMIN]=0; //it will wait for one byte at a time.
newtio.c_cc[VTIME]=10; // it will wait for 0.1s at a time.
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
while (STOP==FALSE)
{
if (wait_flag==FALSE) //if input is available
{
do {
res = read(fd,buf+offset,255);
offset += res;
} while (offset < bytes_expected);
if (offset!=0)
{
for (i=0; i<offset; i++) //for all chars in string
{
In1 = buf[i];
fputc ((int) In1, fp);
printf("charecter:%c\n\r",In1);
} //EOFor
}//EOIf
buf[res]=0;
printf("Received Data is:%s\n\r",buf);
if (res==0)
STOP=TRUE; //stop loop if only a CR was input
wait_flag = TRUE; // wait for new input
}
} //while stop==FALSE
while(readchar)
/* restore old port settings */
tcsetattr(fd,TCSANOW,&oldtio);
close(fd);
}
/************************************************** *************************
* signal handler. sets wait_flag to FALSE, to indicate above loop that *
* characters have been received. *
************************************************** *************************/
void signal_handler_IO (int status)
{
printf("received SIGIO signal.\n");
wait_flag = FALSE;
}
Run Code Online (Sandbox Code Playgroud)
你不应该使用 SIGIO。我从来没有找到任何好的文档来使用它来做任何事情,并且有更好的方法。poll 、 select和epoll系列函数可以更好地实现此目的。在我看来,您的程序正忙于等待数据可用,这意味着它正在占用 CPU 时间。替代方案让您的程序在无事可做时进入睡眠状态。不过,您可以使用暂停来获得这种效果,并且程序将进入睡眠状态,直到收到信号(如 SIGIO)。您也可以使用以阻塞模式打开 tty 文件,并让 read 阻塞您,直到有内容被读取。
您不应该printf同时使用信号处理程序和常规代码。通常,您根本不应该使用信号处理程序中的stdio操作,因为它可以休眠,但您通常可以在测试期间摆脱它。从信号处理程序和常规代码(或者在多个信号处理程序中,如果一个信号不阻塞其他信号)使用它是不好的,因为这样函数将访问相同的数据。本质上,printf在您的常规代码中可能会被 SIGIO 中断,然后printf调用 in ,并且两个printfs 都希望访问 的FILE stdout内部数据 - 要么是该数据,要么是锁(旨在停止阻止不同的线程,而不是同一个线程)第二次调用)将阻止SIGIO处理程序对 的调用printf,这将阻止整个程序。这里的问题称为竞争条件,可能发生也可能不发生,具体取决于时间。
在代码中:
_
do {
res = read(fd,buf+offset,255);
offset += res;
} while (offset < bytes_expected);
Run Code Online (Sandbox Code Playgroud)
_
即使您可能已经填充了部分缓冲区,您仍会不断传递 255 作为所需的长度。如果您第一次通过循环获得 254 字节的数据,然后再次调用并再次请求 255 字节并获得超过 1 字节的数据,则说明您已超出缓冲区。另外,你永远不会回offset退到它0,所以它只会不断增长,所以如果你的程序读取超过 255 个字节,就会发生缓冲区溢出。
您也没有检查read返回值是否不是-1。如果它是 -1 那么你的程序就会做非常糟糕的事情。
另外,在写入已经读回的数据之前,没有真正的理由(据我所知)尝试累积 255 字节的数据。即使您只想处理串行端口中传入的前 255 个字节,您也可以继续在最后一个字节传入时写入第一个字节,然后在达到 255 个限制后退出循环。
struct sigaction saio,但它还有其他字段。这些应该设置为某些内容,或者您只是将随机位作为标志和掩码放入。_
struct sigaction saio = {0};
saio.sa_handler = signal_handler_IO;
/* Now you don't have to worry about the flags and mask b/c those both are
zeroed out and I'm pretty sure those are decent values for your purposes,
but you could still set them explicitly. */
Run Code Online (Sandbox Code Playgroud)
_
newtio给tcsetattr,但您可能尚未完全初始化它。你应该这样做:_
tcgetattr(fd,&oldtio); /* save current port settings */
memcpy(&newtio, oldtio, sizeof(struct termios) ); /* !! <-- This line !! */
Run Code Online (Sandbox Code Playgroud)
在设置其他标志之前。这样,您可以只使用未明确设置的任何字段中已有的值。
此外,您似乎正在设置所有BAUDRATE标志。
/* set new port settings for canonical input processing */
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
Run Code Online (Sandbox Code Playgroud)
这很可能是不正确的。 BAUDRATE用于屏蔽波特码,如下所示:tcflag_t baud_code = tio.c_cflag & BAUDRATE;
现在baud_code只有与波特率集相对应的位,没有CRTSCTS和CLOCAL东西集。您应该使用类似于B9600和B115200的波特码之一,而不是 BAUDRATE,除非您想:
tcflag_t non_baud_flags = tio.c_cflag | ~BAUDRATE;
tcflag_t new_flags = non_baud_flags | B9600;
Run Code Online (Sandbox Code Playgroud)
这只会改变波特率,而不会影响其余标志。这就是int cfsetspeed(struct termios *termios_p, speed_t speed);目的。
_
/* I swapped these two lines */
tcsetattr(fd,TCSANOW,&newtio);
tcflush(fd, TCIFLUSH); /* Now the settings are correct before you flush, so no junk */
/* And moved these from above */
sigaction(SIGIO,&saio,NULL); /* There was no real reason for this to be up there,
but no real harm, either. It just goes better here,
but does need to be set before you set the file as
async. */
/* allow the process to receive SIGIO */
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, FASYNC); /* Now SIGIO won't be called on behalf of this file until after
the serial port has be set up and flushed. */
Run Code Online (Sandbox Code Playgroud)