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为要写的字节数*/
在上述方法中,主程序只是将通信进程的输入输出定向到串行口上,而主程序本身的输入输出并没有改变,即仍对应于显示器、键盘。