⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 linux serial programming howto - 串口通讯编程.txt

📁 学习(编程技巧_编程知识_程序代码),是学习编程不可多得的学习精验
💻 TXT
📖 第 1 页 / 共 2 页
字号:

如果 MIN > 0 且 TIME = 0, MIN 设定为满足读取功能的最低字元接收个数. 由於 TIME 是 零, 所以计时器将不被使用.  

如果 MIN = 0 且 TIME > 0, TIME 将被当做逾时设定值. 满足读取功能的情况为读取到单一字元, 或者超过 TIME 所定义的时间 (t = TIME *0.1 s). 如果超过 TIME 所定义的时间, 则不会传回任何字元.  

如果 MIN > 0 且 TIME > 0, TIME 将被当做一个分割字元组的计时器. 满足读取功能的条件为 接收到 MIN 个数的字元, 或两个字元的间隔时间超过 TIME 所定义的值. 计时器会在每读到一个字元後重新计时, 且只会在第一个字元收到後才会启动.  

如果 MIN = 0 且 TIME = 0, 读取功能就马上被满足. 目前所存在的字元组个数, 或者 将回传的字元组个数. 根据 Antonino (参考 贡献) 所说, 你可以用 fcntl(fd, F_SETFL, FNDELAY); 在读取前得到相同的结果.  

藉由修改 newtio.c_cc[VTIME] 及 newtio.c_cc[VMIN] 上述的模式就可以测试了.  


#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); 
} 


3.3 非同步式输入  

#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; 
} 


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) 各方面的协助.  
 
 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -