[返回]
计算机世界1999年第7期

UNIX系统中的串行通信

山东省机械工业学校 张 鹏

  近年来Unix系统越来越受到人们的重视,特别是在计算机应用的关键领域中,Unix系统扮演着重要角色。笔者在实际工作中摸索出一套Unix系统中串行通信的方法,该方法避免了对底层硬件的直接操作,因而简单易行,可广泛用于Unix主机之间、Unix主机与下位机(如单片机)之间的串行通信。

  该方法的基本思想是利用Unix的设备重定向。串行口在Unix中被当作标准终端设备对待,对串行口的读写和对终端的读写是一样的。在实际工作中,可单独编写一个程序(以下简称通信程序/进程)负责与串行口通信,然后在主程序中将通信程序当作进程调用即可。基本步骤如下:

1.将通信进程的输入、输出重定向到串行口上;2.在通信进程中初始化串行口;3.在通信进程中用read()、write()调用对串行口读写。

  下面分别详细介绍。

  1.重定向通信进程的输入、输出。

  假设通信程序名称为bsc.o,对COM2口读写。COM2口在Unix系统中的设备文件名为/dev/tty2a。若为COM1口,则对应的设备文件名为/dev/ttyla。系统中所有设备均在/dev目录中有其对应的设备文件名。若串行口在用户自己加入到计算机中的串行板卡上,则可用mkdev serial命令建立与之对应的设备文件名。在主程序中以进程方式调用通信程序、并重定向其输入输出的代码如下:

 int start_bsc(){

  int pid,fd[2];

  switch(pid=fork()){

  case -1:

  fprintf(stderr,"fork() 调 用 失 败.\n");

  return -1;

  case 0: / * child */

  break;

  default: / * parent */

  return pid;

  }


  / * 进 程 调 用 成 功 */

  / * 重 定 向 输 入 到/dev/tty2a */

 fd[0]=open("/dev/tty2a",0_RDO
NLY);

 close(0);

 dup2(fd[0],0);

 

/ * 重 定 向 输 出 到/dev/tty2a */

 fd[1]=open("/dev/tty2a",0_WRONLY |0_CREAT,0644);

 close(1);

 dup2(fd[1],1);



/ * 执 行bsc.o */

 execlp("bsc.o","bsc.o",(char *)0);

 / * 除 非execlp() 调 用 遇 到 错 误,否 则 下 面 的 代 码 不 会 被 执 行 */

 fprinf(stderr,"execlp() 调 用 失 败.\n");

 return -1;

}

  在 主 程 序 中 结 束 通 信 进 程 的 方 法 如 下:

 kill (pid_t)pid_bsc.SIGTERM);

  其 中pid_bsc 为 调 用start_bsc() 函 数 时
得 到 的 通 信 进 程 标 识 号。

  2.在 通 信 进 程 中 初 始 化 串 行 口 的 代 码 如 下:

 struct termio save,term;/ * 定 义 终 端 设 备 的 结 构 */

 if (ioct1 (0,TCGETA, &term)== -1){

 fprintf (stderr," 标 准 输 入 不 是tty 设 备.\n");

 exit(1);

}

 save=term; / * 保 存 原tty 设 备 状 态 */

 / * 重 新 设 置 设 备 状 态 */

 term.c_lflag &=ICANON; / * 关 闭 正 则 处 理 模 式 */

 term.c_cc[VMIN]=0;

 term.c_cc[VTIME]=100;/ * 设 置read() 调 用 读 取

终 端 设 备 的 时 间 */

 term.c_cflag &=CBAUD; / * 关 闭 原 波 特 率 设 置 */

 term.c_cflag |=B1200; / * 重 新 设 置 波 特 率 为1200bps */

 ioct1 (0,TCSETA, &term);

在 通 信 进 程 结 束 时

应 用 如 下 代 码 恢 复 原 设 备 状 态:

 ioct1(0,TCSETA, &save);

  以上代码中用到的structtermio、ioct1()、ICANON、VMIN、VTIME等在头文件termio.h中有定义。

  3.在通信进程用read()、write()调用对串行口读写。

  经过以上设置后,在通信进程中即可直接用read()、write()调用对串行口进行读写了。read()、write()调用原本为在标准输入输出上进行读写,由于主程序将通信进程的输入输出定向到COM2口上,因此read()、write()调用也就变为从COM2读、向COM2写。

  从串行口读取一个(多个)字节的语句为:

  read(0,buf,1);/*buf为字符指针,1为要读的字节数*/

  向串行口写一个(多个)字节的语句为:

  write(1,buf,1);/*buf为字符指针,1为要写的字节数*/

  在上述方法中,主程序只是将通信进程的输入输出定向到串行口上,而主程序本身的输入输出并没有改变,即仍对应于显示器、键盘。