藉由修改 newtio.c_cc[VTIME] 及 newtio.c_cc[VMIN] 上述的模式就可以测试了.
[code]
#include
#include
#include
#include
#include
#define BAUDRATE B38400
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* POSIX 系统相容 */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
main()
{
int fd,c, res;
struct termios oldtio,newtio;
char buf[255];
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
tcgetattr(fd,&oldtio); /* 储存目前的序列埠设定 */
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
/* 设定输入模式 (非标准型, 不回应,...) */
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0; /* 不使用分割字元组计时器 */
newtio.c_cc[VMIN] = 5; /* 在读取到 5 个字元前先停止 */
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
while (STOP==FALSE) { /* 输入回圈 */
res = read(fd,buf,255); /* 在输入 5 个字元後即返回 */
buf[res]=0; /* 所以我们能用 printf... */
printf(":%s:%d\n", buf, res);
if (buf[0]=='z') STOP=TRUE;
}
tcsetattr(fd,TCSANOW,&oldtio);
}
[/code]
3.3 非同步式输入
[code]
#include
#include
#include
#include
#include
#include
#define BAUDRATE B38400
#define MODEMDEVICE "/dev/ttyS1"
#define _POSIX_SOURCE 1 /* POSIX 系统相容 */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
void signal_handler_IO (int status); /* 定义讯号处理程序 */
int wait_flag=TRUE; /* 没收到讯号的话就会是 TRUE */
main()
{
int fd,c, res;
struct termios oldtio,newtio;
struct sigaction saio; /* definition of signal action */
char buf[255];
/* 开启装置为 non-blocking (读取功能会马上结束返回) */
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
/* 在使装置非同步化前, 安装讯号处理程序 */
saio.sa_handler = signal_handler_IO;
saio.sa_mask = 0;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
/* 允许行程去接收 SIGIO 讯号*/
fcntl(fd, F_SETOWN, getpid());
/* 使档案ake the file descriptor 非同步 (使用手册上说只有 O_APPEND 及
O_NONBLOCK, 而 F_SETFL 也可以用...) */
fcntl(fd, F_SETFL, FASYNC);
tcgetattr(fd,&oldtio); /* 储存目前的序列埠设定值 */
/* 设定新的序列埠为标准输入程序 */
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]=1;
newtio.c_cc[VTIME]=0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
/* 等待输入讯号的回圈. 很多有用的事我们将在这做 */
while (STOP==FALSE) {
printf(".\n");usleep(100000);
/* 在收到 SIGIO 後, wait_flag = FALSE, 输入讯号存在则可以被读取 */
if (wait_flag==FALSE) {
res = read(fd,buf,255);
buf[res]=0;
printf(":%s:%d\n", buf, res);
if (res==1) STOP=TRUE; /* 如果只输入 CR 则停止回圈 */
wait_flag = TRUE; /* 等待新的输入讯号 */
}
}
/* 回存旧的序列埠设定值 */
tcsetattr(fd,TCSANOW,&oldtio);
}
/***************************************************************************
* 讯号处理程序. 设定 wait_flag 为 FALSE, 以使上述的回圈能接收字元 *
***************************************************************************/
void signal_handler_IO (int status)
{
printf("received SIGIO signal.\n");
wait_flag = FALSE;
}
[/code]
3.4 等待来自多个讯号来源的输入
这一段很短. 它只能被拿来当成写程式时的提示, 故□例程式也很简短. 但这个□例不只能用在序列埠上, 还可以用在被当成档案来使用的装置上.
select 呼叫及伴随它所引发的巨集共用 fd_set. fd_set 则是一个位元阵列, 而其中每一个位元代表一个有效的档案叙述结构. select 呼叫接受一个有效的档案叙述结构并传回 fd_set 位元阵列, 而该位元阵列中若有某一个位元为 1, 就表示相对映的档案叙述结构的档案发生了输入, 输出或有例外事件. 而这些巨集提供了所有处理 fd_set 的功能. 亦可参考手册 select(2).
以下内容为程序代码:
#include
#include
#include
main()
{
int fd1, fd2; /* 输入源 1 及 2 */
fd_set readfs; /* 档案叙述结构设定 */
int maxfd; /* 最大可用的档案叙述结构 */
int loop=1; /* 回圈在 TRUE 时成立 */
/* open_input_source 开启一个装置, 正确的设定好序列埠,
并回传回此档案叙述结构体 */
fd1 = open_input_source("/dev/ttyS1"); /* COM2 */
if (fd1<0) exit(0);
fd2 = open_input_source("/dev/ttyS2"); /* COM3 */
if (fd2<0) exit(0);
maxfd = MAX (fd1, fd2)+1; /* 测试最大位元输入 (fd) */
/* 输入回圈 */
while (loop) {
FD_SET(fd1, &readfs); /* 测试输入源 1 */
FD_SET(fd2, &readfs); /* 测试输入源 2 */
/* block until input becomes available */
select(maxfd, &readfs, NULL, NULL, NULL);
if (FD_ISSET(fd1)) /* 如果输入源 1 有讯号 */
handle_input_from_source1();
if (FD_ISSET(fd2)) /* 如果输入源 2 有讯号 */
handle_input_from_source2();
}
}
这个□例程式在等待输入讯号出现前, 不能确定它会停顿下来. 如果你需要在输入时加入逾时功能, 只需把 select 呼叫换成:
int res;
struct timeval Timeout;
/* 设定输入回圈的逾时值 */
Timeout.tv_usec = 0; /* 毫秒 */
Timeout.tv_sec = 1; /* 秒 */
res = select(maxfd, &readfs, NULL, NULL, &Timeout);
if (res==0)
/* 档案叙述结构数在 input = 0 时, 会发生输入逾时. */
这个程式会在 1 秒钟後逾时. 如果超过时间, select 会传回 0, 但是应该留意 Timeout 的时间递减是由 select 所等待输入讯号的时间为基准. 如果逾时的值是 0, select 会马上结束返回.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
4. 其它资源
Linux Serial-HOWTO 叙述如何设定序列埠及所有相关的硬体资讯.
由 Michael Sweet 所写的 Serial Programming Guide for POSIX Compliant Operating Systems. 这个连结已经荒废了但我找不到它的新位址. 有人知道能在哪找到它吗? 它是很棒的文件!
termios(3) 的使用手册. 叙述所有有关 termios 结构体的旗标.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
5. 贡献
就跟简介所说的一样, 我并非在这领域有所专精, 但我自己遇到问题, 并透过他人的帮助找到答案. 感谢来自 European Transonic Windtunnel 的 Strudthoff 先生, Cologne, Michael Carter (mcarter@rocke.electro.swri.edu), 及 Peter Waltenberg (p.waltenberg@karaka.chch.cri.nz)
与我同时准备这份文件的 Antonino Ianella (antonino@usa.net 所篆写的 Serial-Port-Programming Mini HOWTO. Greg Hankins 要求我把 Antonino's Mini-HOWTO 一并放入这份文件.
这份文件的结构及 SGML 的格式是源自 Greg Hankins 的 Serial-HOWTO. 感谢 Dave Pfaltzgraff (Dave_Pfaltzgraff@patapsco.com), Sean Lincolne (slincol@tpgi.com.au), Michael Wiedmann (mw@miwie.in-berlin.de), 及 Adrey Bonar (andy@tipas.lt) 各方面的协助.