浅谈linux下的串口通讯开发

2019-10-13 11:20:14王冬梅

3.2 非标准输入程序

在非标准的输入程序模式下, 输入的资料不会被组合成一行而输入后的处理功能 (清除, 杀掉, 删除, 等等.) 都不能使用. 这个模式有两个功能控制参数: c_cc[VTIME] 设定字元输入时间计时器, 及c_cc[VMIN] 设定满足读取功能的最低字元接收个数.

如果 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;
}