我有一个基于RS-232的实际串行端口,该端口与Linux一起用于与微控制器进行无线通信。 (我在连接的两端使用无线模块,而不是串行电缆)
有一个问题。整个操作必须为半双工,因为无线模块不支持全双工操作。实际上,它们在发送数据时会忽略传入的数据。
因此,在C语言中,我正在寻找最有效率和最快的代码,以便在开始将代码发送回远程之前,何时检测到没有数据来自远程发送器。
此外,远程设备只给我30mS的数据发送给它,数据本身为47字节,速度为56kbps,因此检测线路静默的例程需要快速执行。
我提供了一些想法,但我试图找出最好的。也许有一个我什至不知道的功能会更好。我尝试了不同的VMIN和VTIME值,但发现自己不成功。
这是我的代码提示:
想法1:
char b[2]; //incoming character buffer
int nread=1; //number of bytes read
int fd=open(dev,O_NOCTTY | O_RDONLY | O_SYNC);
while (nread > 0){
nread=read(fd,b,1); //keep reading until no bytes read are reported
}
fclose(fd);
想法2:
int timeout=1; //1 second timeout
int fd=open(dev,O_NOCTTY | O_RDONLY | O_SYNC);
time_t then=time(NULL);
while (time(NULL) - then > timeout){
nread=read(fd,b,1);
if (nread > 0){then=time(NULL);} //reset timer if at least 1 byte is read
}
fclose(fd);
想法3:
char b[2]; //incoming character buffer
int nread=1; //number of bytes read
int numfail=0; //number of failed attempts to read bytes
int fd=open(dev,O_NOCTTY | O_RDONLY | O_SYNC);
while (numfail < 100){
nread=read(fd,b,1); //keep reading until no bytes read are reported 100 consecutive times
if (nread < 1){numfail++;}else{numfail=0;}
}
fclose(fd);
第一个可能行不通的想法(除非我很幸运在这里找到特殊的Linux驱动程序配置?)将一直检查端口是否为单个字节,直到找不到该字节然后退出。
第二个想法设置了字符必须进入的特定时间,如果在给定时间内没有字符到达,则该行被认为是空闲的。否则时钟将重置。
最后一个主意是对第一个主意的扩展,除了程序要连续几次尝试从串行线读取一个字节失败之前,程序不会退出。如果检测到至少一个字节,尝试计数器将复位。我觉得这可能是与更好的VTIME和VMIN设置一起使用的最佳选择,但我不确定。
我只需要一个功能,该功能可以花费很少的时间(最多几个微秒)来检测何时没有任何设备在串行线上发送数据。
我目前唯一的解决方案是创建更多硬件,这些硬件从PC收集串行数据,缓冲数据,然后硬件在将数据发送到远程单元之前检测串行线何时空闲,但是我不想浪费更多的电路板和零件。我宁愿有一个软件解决方案(在Linux中)。
有什么想法吗?
为了清楚起见,远程单元使用基于8052的微控制器(型号为89S52),其UART仅具有1个字节的缓存,而我的计算机使用16550 UART,我认为它具有14或16字节的缓存。
更新
我尝试了另一个更好的主意。似乎可以在2mS的间隔内检测到何时空闲或不空闲的线路发生了变化,但我试图在微秒级上检测到更多。
这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
int main(int argc, char* argv[]){
int fd=open("/dev/ttyS0",O_NOCTTY | O_RDONLY | O_SYNC);
if (fd < 0){
printf("Error\n");
return -1;
}
long v=1;
if (argc > 1){
v=strtol(argv[1],NULL,10); //Custom inter-string timeout
}
struct timeval t={0,5000}; //set initial timeout to 5mS
int gotsz=0; //characters received total
int bpsz=100000; //Most # characters we want
unsigned char b[bpsz]; //characters
unsigned char* bp=b; //Active pointer to characters
bpsz--; //We don't want to wreck memory that isnt ours
fd_set f;
while(1){
FD_ZERO(&f);
FD_SET(fd,&f);
int ret=select(fd+1,&f,NULL,NULL,&t); //check at 5mS intervals for anything
if (ret < 0){
printf("Error\n");
return -1;
}
if (ret != 0){
break; //Stall until anything is received.
}
}
while(1){
FD_ZERO(&f);
FD_SET(fd,&f);
int ret=select(fd+1,&f,NULL,NULL,&t);
if (ret < 0){
printf("Error\n");
return -1;
}
if (ret == 0){
break; //Only exit loop when there are no more characters in a short time
}
int sz=read(fd,bp,bpsz); //fill up buffer advancing start pointer (saves use of strcpy)
gotsz+=sz;
bp+=sz;
bpsz-=sz;
t.tv_usec=v;
}
close(fd);
//print all that we got in hex
bp=b;
printf("Got: ");
int n;
for (n=0;n<gotsz;n++){
printf("%X",*bp);
bp++;
}
printf("\n");
printf("Intermediate Timeout %ld uS (%ld mS)\n",v,v/1000);
return 0;
}
我觉得现在每次我想从串行端口读取数据时,都需要创建某种全局系统锁,以便感测何时读取数据。就是说,我的意思是,例如,如果我将计算机用于多种目的,例如浏览网页和在后台播放音乐或观看视频,并且运行程序,我认为通过使程序冻结上述所有过程来读取串行数据,我认为端口数据将为CPU提供足够的时间来处理串行端口数据。然后,当我的程序结束时,过程可以继续。
有什么主意我能做到吗?