linux下串口编程入门

时间:2021-10-10 16:15:37

linux下串口编程入门

(精讲解-含示例程序-好)

简介:
  Linux操作系统从一开始就对串行口提供了很好的支持,本文就Linux下的串行口通讯编程进行简单的介绍。

串口简介
  串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用。常用的串口是RS-232-C接口(又称EIA RS-232-C)它是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。它的全名是"数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准"该标准规定采用一个25个脚的DB25连接器,对连接器的每个引脚的信号内容加以规定,还对各种信号的电平加以规定。传输距离在码元畸变小于4%的情况下,传输电缆长度应为50英尺。
  Linux操作系统从一开始就对串行口提供了很好的支持,本文就Linux下的串行口通讯编程进行简单的介绍,如果要非常深入了解,建议看看本文所参考的《Serial Programming Guide for POSIX Operating Systems》

串口操作

  串口操作需要的头文件
#include     <stdio.h>      /*标准输入输出定义*/
#include     <stdlib.h>     /*标准函数库定义*/
#include     <unistd.h>     /*Unix 标准函数定义*/
#include     <sys/types.h> 
#include     <sys/stat.h>  
#include     <fcntl.h>      /*文件控制定义*/
#include     <termios.h>    /*PPSIX 终端控制定义*/
#include     <errno.h>      /*错误号定义*/

打开串口

在 Linux 下串口文件是位于 /dev 下的。串口一 为 /dev/ttyS0,串口二 为 /dev/ttyS1。打开串口是通过使用标准的文件打开函数操作:
int fd;
/*以读写方式打开串口*/
fd = open( "/dev/ttyS0", O_RDWR);
if (-1 == fd){
/* 不能打开串口一*/
perror(" 提示错误!");
}

设置串口

最基本的设置串口包括波特率设置,效验位和停止位设置。串口的设置主要是设置 struct termios 结构体的各成员值。
struct termio
{    unsigned short  c_iflag;    /* 输入模式标志 */   
    unsigned short  c_oflag;        /* 输出模式标志 */   
    unsigned short  c_cflag;        /* 控制模式标志*/   
    unsigned short  c_lflag;        /* local mode flags */   
    unsigned char  c_line;            /* line discipline */   
    unsigned char  c_cc[NCC];    /* control characters */
};
设置这个结构体很复杂,我这里就只说说常见的一些设置:
波特率设置 下面是修改波特率的代码:
struct  termios Opt;
tcgetattr(fd, &Opt);
cfsetispeed(&Opt,B19200);     /*设置为19200Bps*/
cfsetospeed(&Opt,B19200);
tcsetattr(fd,TCANOW,&Opt);

设置波特率的例子函数:
/**
*@brief  设置串口通信速率
*@param  fd     类型 int  打开串口的文件句柄
*@param  speed  类型 int  串口速度
*@return  void
*/
int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
        B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300, 38400, 
            19200,  9600, 4800, 2400, 1200,  300, };
void set_speed(int fd, int speed){
    int   i;
    int   status;
    struct termios   Opt;
    tcgetattr(fd, &Opt);
    for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++) {
        if  (speed == name_arr[i]) {    
            tcflush(fd, TCIOFLUSH);    
            cfsetispeed(&Opt, speed_arr[i]); 
            cfsetospeed(&Opt, speed_arr[i]);  
            status = tcsetattr(fd1, TCSANOW, &Opt); 
            if  (status != 0) {       
                perror("tcsetattr fd1"); 
                return;    
            }   
            tcflush(fd,TCIOFLUSH);  
        } 
    }
}

 

设置效验的函数:
/**
*@brief   设置串口数据位,停止位和效验位
*@param  fd     类型  int  打开的串口文件句柄
*@param  databits 类型  int 数据位   取值 为 7 或者8
*@param  stopbits 类型  int 停止位   取值为 1 或者2
*@param  parity  类型  int  效验类型 取值为N,E,O,,S
*/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
    struct termios options;
    if  ( tcgetattr( fd,&options)  !=  0) {
        perror("SetupSerial 1");    
        return(FALSE); 
    }
    options.c_cflag &= ~CSIZE;
    switch (databits) /*设置数据位数*/
    {  
    case 7:       
        options.c_cflag |= CS7;
        break;
    case 8:    
        options.c_cflag |= CS8;
        break;  
    default:   
        fprintf(stderr,"Unsupported data size\n"); return (FALSE); 
    }
switch (parity)
{  
    case 'n':
    case 'N':   
        options.c_cflag &= ~PARENB;   /* Clear parity enable */
        options.c_iflag &= ~INPCK;     /* Enable parity checking */
        break; 
    case 'o':  
    case 'O':    
        options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/ 
        options.c_iflag |= INPCK;             /* Disnable parity checking */
        break; 
    case 'e': 
    case 'E':  
        options.c_cflag |= PARENB;     /* Enable parity */   
        options.c_cflag &= ~PARODD;   /* 转换为偶效验*/    
        options.c_iflag |= INPCK;       /* Disnable parity checking */
        break;
    case 'S':
    case 's':  /*as no parity*/  
        options.c_cflag &= ~PARENB;
        options.c_cflag &= ~CSTOPB;break; 
    default:  
        fprintf(stderr,"Unsupported parity\n");   
        return (FALSE); 
    } 
/* 设置停止位*/ 
switch (stopbits)
{  
    case 1:   
        options.c_cflag &= ~CSTOPB; 
        break; 
    case 2:   
        options.c_cflag |= CSTOPB; 
       break;
    default:   
         fprintf(stderr,"Unsupported stop bits\n"); 
         return (FALSE);
}
/* Set input parity option */
if (parity != 'n')  
    options.c_iflag |= INPCK;
tcflush(fd,TCIFLUSH);
options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/  
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
if (tcsetattr(fd,TCSANOW,&options) != 0)  
{
    perror("SetupSerial 3");  
    return (FALSE); 
}
return (TRUE); 
}

需要注意的是: 如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode)方式来通讯,设置方式如下:
options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
options.c_oflag  &= ~OPOST;   /*Output*/

读写串口
设置好串口之后,读写串口就很容易了,把串口当作文件读写就是。

·发送数据

char  buffer[1024];int    Length;int    nByte;nByte = write(fd, buffer ,Length)


·读取串口数据

使用文件操作read函数读取,如果设置为原始模式(Raw Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。可以使用操作文件的函数来实现异步读取,如fcntl,或者select等来操作。

char  buff[1024];int    Len;int  readByte = read(fd,buff,Len);

关闭串口

关闭串口就是关闭文件。

close(fd);

例子
下面是一个简单的读取串口数据的例子,使用了上面定义的一些函数和头文件
/**********************************************************************
代码说明:使用串口二测试的,发送的数据是字符,
但是没有发送字符串结束符号,所以接收到后,后面加上了结束符号。
我测试使用的是单片机发送数据到第二个串口,测试通过。
**********************************************************************/
#define FALSE  -1
#define TRUE   0
/*********************************************************************/
int OpenDev(char *Dev)
{
    int    fd = open( Dev, O_RDWR );
        //| O_NOCTTY | O_NDELAY   
    if (-1 == fd)   
    {            
        perror("Can't Open Serial Port");
        return -1;       
    }   
    else   
        return fd;
}
int main(int argc, char **argv){
    int fd;
    int nread;
    char buff[512];
    char *dev  = "/dev/ttyS1"; //串口二
    fd = OpenDev(dev);
    set_speed(fd,19200);
    if (set_Parity(fd,8,1,'N') == FALSE)  {
        printf("Set Parity Error\n");
        exit (0);
    }
while (1) //循环读取数据
{  
    while((nread = read(fd, buff, 512))>0)
    {
        printf("\nLen %d\n",nread);
        buff[nread+1] = '\0';  
        printf( "\n%s", buff);  
    }
}
    //close(fd); 
    // exit (0);
}

---------------------------------------------------------------------------

 
Linux串口编程分析

这个话题,大家可能再熟悉不过了,网上资料很多,因为这是linux下编程比较重要的一个方面,懂这方面的人很多;这里我只是想给初学者简单的介绍下这方面的知识:

串口编程其实说白了, 是拿根串口线把电脑和所要控制的机器连接起来,然后在通过编程的方式对下位机进行发送指定的数据或进行控制,或进行传输,然后在接受下位机反馈回来的信息提示是否已经正确。是不是好俗!

串口是计算机上一种非常通用设备通信的协议,常用PC机上包含的是RS232规格的串口,当然,除了RS232 ,还有RS485和RS422两种规格,用于不同的设备通信;这里主要是介绍RS232串口编程。

在串口编程中,比较重要的是串口的设置,我们要设置的部分包括波特率,数据位,停止位,奇偶校验位;要注意的是,每台机器的串口默认设置可能是不同的,如果你没设置这些,仅仅按照默认设置进行发送数据,很可能出现n 多异想不到而又查不出来的情况;所以,在真正通讯前,我们必须设置这些:

下面就开始介绍这些基本设置的函数,(其实都是些固定框架,程序中稍微改改就行)~o~

1.设置波特率

注意每台机器都有输出和输入接受信息的速度 ,所以用cfsetispeed 和cfsetospeed来分别设置;注意到struct termios 这样一个结构,它包括了串口端所有的设置,下面还要用到。它在termios.h中被定义。。还有一个地方比较难以理解,为什么设置了speed_arr 和name_arr两个数组,这是因为在linuxe下,系统为波特率专门准备了一张表用B38400,B19200......代替,而我们实际上传进去的只能是38400,19200这些值,所以我们拿我们传进去的和name_arr进行比较,如果相等则从系统对照表中取出相应值进行设置,如果不等证明传的值在系统对照表中没有,则不进行设置。

int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,//
B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {38400,19200,9600,4800,2400,1200,300,
38400,19200,9600, 4800, 2400, 1200,300, };
void set_speed(int fd, int speed)
{
int i;
int status;
struct termios Opt; //定义了这样一个结构
tcgetattr(fd, &Opt); //用来得到机器原端口的默认设置
for ( i= 0;i < sizeof(speed_arr) / sizeof(int);i++)
{
if(speed == name_arr[i]) //判断传进来是否相等
{
tcflush(fd, TCIOFLUSH); //刷新输入输出缓冲
cfsetispeed(&Opt, speed_arr[i]); //这里分别设置
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(fd, TCSANOW, &Opt); //这是立刻把bote rates设置真正写到串口中去
if(status != 0)
perror("tcsetattr fd1"); //设置错误
return;
}
tcflush(fd,TCIOFLUSH); //同上
}
}

2。设置奇偶校验,数据,停止位

这三个参数通常放在一起设置,databits是数据位,stopbits是停止位,parity是校验位。
串口的这些设置是很复杂很复杂的,Termios成员*定义c_cflag 控制项 c_lflag 线路项 c_iflag 输入项 c_oflag 输出项 c_cc 控制字符c_ispeed 输入波特 c_ospeed 输出波特 那么多项,对于每一项都有很多的设置,这里我们不讲的那么复杂,就一个通用的串口框架进行解释,主要进行奇偶校验,数据,停止位的设置。而其他的一些控制项,在程序中用到时穿插讲解:

int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options; //定义一个结构
if( tcgetattr( fd,&options)!=0) //首先读取系统默认设置options中,必须
{
perror("SetupSerial 1");
return(FALSE);
}
options.c_cflag &= ~CSIZE; //这是设置c_cflag选项不按位数据位掩码
switch (databits) /*设置数据位数*/
{
case 7:
options.c_cflag |= CS7; //设置c_cflag选项数据位为7位
break;
case 8:
options.c_cflag |= CS8; //设置c_cflag选项数据位为8位
break;
default:
fprintf(stderr,"Unsupported data size\n"); //其他的都不支持
return (FALSE);
}
switch (parity) //设置奇偶校验,c_cflag和c_iflag有效
{
case 'n':
case 'N': //无校验当然都不选
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o': //奇校验 其中PARENB校验位有效;PARODD奇校验
INPCK检查校验
case 'O': options.c_cflag |= (PARODD | PARENB);/* 设置为奇效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E': //偶校验,奇校验不选就是偶校验了
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* 转换为偶效验*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
default:
fprintf(stderr,"Unsupported parity\n");
return (FALSE);
}
/* 设置停止位*/
switch (stopbits) //这是设置停止位数,影响的标志是c_cflag
{
case 1:
options.c_cflag &= ~CSTOPB; //不指明表示一位停止位
break;
case 2:
options.c_cflag |= CSTOPB; //指明CSTOPB表示两位,只有两种可能
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return (FALSE);
}
/* Set input parity option */
if (parity != 'n') //这是设置输入是否进行校验
options.c_iflag |= INPCK;

// 这个地方是用来设置控制字符和超时参数的,一般默认即可。稍微要注意的是c_cc数组的VSTART 和 VSTOP 元素被设定成DC1 和 DC3,代表ASCII 标准的XON和XOFF字符。所以如果在传输这两个字符的时候就传不过去,这时需要把软件流控制屏蔽 options.c_iflag &= ~(IXON | IXOFF | IXANY);


options.c_cc[VTIME] = 150; // 15 seconds
options.c_cc[VMIN] = 0;

tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */ //刷新和立刻写进去
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
//串口设置框架到这里就大概结束了,对于设置了数据位校验位停止位和波特率的端口已经可以传输大多数信息。在实际中的情况往往是很多特例,比如,
在用write发送数据时没有键入回车,信息就将发送不出去的情况,这主要是因为我们在输出输入时是按照 规范模式接受到回车或者换行才发送,而很多情况我们是不需要回车和换行的,这时,应当切换到行方式输入,设置options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);不经处理直接发送。
又比如
在我们发送字符0x0d的时候,往往接受端得到的字符是0x0a 这是怎么回事,原因是在串口设置中c_iflag和c_oflag中存在 从NL-CR 和CR-NL的映射,也就是说,串口可以把回车和换行看成一个字符,所以,此时我们应该屏蔽掉这些,用options.c_oflag &=~(INLCR|IGNCR|ICRNL|);和options.c_oflag &=~(ONLCR|OCRNL);进行设置。

总之,串口的设置是很复杂也很麻烦的东西,具体情况要具体分析,找到相应的办法,如果发现数据不能传送,不妨耐点心在串口设置上找答案吧

言归正传,后面的东西就很简单了,接下来是打开串口:

int OpenDev(char *Dev)
{
int fd = open( Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY这种方式看open函数
if (-1 == fd)
{ /*设置数据位数*/
perror("Can't Open Serial Port");
return -1;
}
else
return fd;

}

然后是数据的接受和发送,把通用的主函数贴下来,很容易的。

int main(int argc, char **argv)
{
int fd;
int nread;
char buff[512];
char *dev ="/dev/ttyS0"; //linux下的端口就是通过打开设备文件操作的
fd = OpenDev(dev); //打开
if (fd>0)
set_speed(fd,19200); //打开后设置波特率19200
else
{
printf("Can't Open Serial Port!\n");
exit(0);
}
if (set_Parity(fd,8,1,'N')== FALSE) //设置8,1,n 注意,这里和上面要和下位机相符才可能通信
{
printf("Set Parity Error\n");
exit(1);
}

// 一般读的时候一般都用read ,写的时候一般都用write,read要注意阻塞后程序停止不动,所以要用select 进行控制,注意tv每次循环都要设置;write 不用考虑阻塞,但要用循环写方式保证一定写完,其实读最好也用循环读方式保证一定能读到所有东西并且能拼接在一起,然后在进行其他操作。最后 while(1) 是串口通讯中常用的循环就是一直执行,直到碰到break;这些东西挺烦琐,不过其实也没什么。这里就不详细说了,下面是个最最简单的。。
while(1)
{
while((nread = read(fd,buff,512))>0)
{
printf("\nLen %d\n",nread);
buff[nread+1]='\0';
printf("\n%s",buff);
}
}
//close(fd);
//exit(0);
}

完了,是不是不难,其实除了串口设置是新知识,,事实上linux都是文件,串口是设备文件,设置好后,其他的东西就当成文件进行操作吧。
--------------------------------------------------------------------------
Linux串口编程有一些关键信息点:
1. Linux下串口设备是保存在一定位置的,比方说:“/dev/ttyS0, /dev/ttyS1”,首先要用open函数打开这个设备,调用函数:
Open(“/dev/ttyS1”, O_RDWR);
这一点和在Windows下有一些区别,在Windows下是用CreateFile来创建串口的设备,如:     
CreateFile("COM1",...)
--------------------------------------------------------------------------
需要注意的是: 如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode)方式来通讯,设置方式如下:

options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
options.c_oflag  &= ~OPOST;   /*Output*/

读写串口

设置好串口之后,读写串口就很容易了,把串口当作文件读写就是。

·发送数据

char  buffer[1024];int    Length;int    nByte;nByte = write(fd, buffer ,Length)

·读取串口数据

使用文件操作read函数读取,如果设置为原始模式(Raw Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。可以使用操作文件的函数来实现异步读取,如fcntl,或者select等来操作。
char  buff[1024];int    Len;int  readByte = read(fd,buff,Len);

关闭串口

关闭串口就是关闭文件。

close(fd);

例子

下面是一个简单的读取串口数据的例子,使用了上面定义的一些函数和头文件

/**********************************************************************
代码说明:使用串口二测试的,发送的数据是字符,
但是没有发送字符串结束符号,所以接收到后,后面加上了结束符号。
我测试使用的是单片机发送数据到第二个串口,测试通过。
**********************************************************************/
#define FALSE  -1
#define TRUE   0
/*********************************************************************/
int OpenDev(char *Dev)
{
int fd = open( Dev, O_RDWR );
        //| O_NOCTTY | O_NDELAY
if (-1 == fd)
{  
  perror("Can't Open Serial Port");
  return -1;
}
else
  return fd;
}
int main(int argc, char **argv){
int fd;
int nread;
char buff[512];
char *dev  = "/dev/ttyS1"; //串口二
fd = OpenDev(dev);
漀?朦??????????set_speed(fd,19200);
if (set_Parity(fd,8,1,'N') == FALSE)  {
  printf("Set Parity Error\n");
  exit (0);
}
while (1) //循环读取数据

while((nread = read(fd, buff, 512))>0)
{
  printf("\nLen %d\n",nread);
  buff[nread+1] = '\0'; 
  printf( "\n%s", buff); 
}
}
//close(fd);
// exit (0);
}
------------------------------------------------------------------------------------------
刚刚接触linux和串口编程的菜鸟,看到一个源程序实例,却不知道如何执行,请教各位大侠。
以下是在网上找到的例子。
请问一下,那4个文件中的 SEND和 RECV是干什么用的?用gcc编译了recv.c和 send.c,之后就不知道该如何进行下去了。只知道linux下要用minicom去进行串口程序的信息交换,可是实在不知道实际怎么操作,查了很久也没查到,也许我的问题实在太白痴,大家不要笑我阿。
我的任务,是要做一个串口程序,在linux系统下的2个串口之间进行信息传递,星期一就要交作业了,大侠们帮帮忙啊。。。

共四个文件:RECV recv.c SEND send.c
用串口线连接两台PC,第二根与第三根交叉
该程序我一直用于两台计算机交换文件,从未出错

----------------------SEND-------------------------
cat $1 |send /dev/tty2a 9600
----------------------send.c-------------------------

/* this program read from stdin and write to serial(specify in command parg) */
/* usage: send device baud, for example cat <file>|send /dev/ttyi01 9600 */

#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<termio.h>
#include<time.h>
#include<signal.h>

#define C_SND 1
#define C_RCV 2
#define C_ACK 7
#define C_END 9
#define C_BEG 11
#define C_ERR 63

struct PACKHEAD
{
short cmd;
int len;
}pkhead;

main(int argc, char *argv[])
{
int fd;
int baud;
int len, cou;
char buf[2050];

if(argc != 3)
{
fprintf(stderr,"usage: send device baud\n");
return(1);
}
baud=atoi(argv[2]);
if(baud<0||baud>38400)
{
fprintf(stderr,"baud error\n");
return(1);
}
fd=open(argv[1],O_RDWR,0);
if(fd<0)
{
fprintf(stderr,"open <%s> error %s\n",argv[1],strerror(errno));
return(1);
}
if(setTerm(fd))
{
fprintf(stderr,"setterm error %s\n",strerror(errno));
return(1);
}

cou=0;

/* i am begin to send */
pkhead.cmd=C_BEG;
pkhead.len=0;
write_n(fd,&pkhead,sizeof(pkhead));

/* is server ready */
read_n(fd,&pkhead,sizeof(pkhead));
if(pkhead.cmd!=C_RCV)
{
fprintf(stderr,"recvive not ready");
return(1);
}

fprintf(stderr,"begin to send\n");

while( (len=read(0,buf,2048)) >0 )
{
/* send a pack */
pkhead.cmd=C_SND;
pkhead.len=len;
write_n(fd,&pkhead,sizeof(pkhead));
write_n(fd,buf,len);

/* has peer receive the pack */
read_n(fd,&pkhead,sizeof(pkhead));
if(pkhead.cmd!=C_ACK)
{
fprintf(stderr,"recvive not recv this pack");
return(1);
}

cou+=len;
fprintf(stderr,"send %d byte\n",len);
}
pkhead.cmd=C_END;
pkhead.len=0;
write_n(fd,&pkhead,sizeof(pkhead));
fprintf(stderr,"all send %d byte\n",cou);
}

/* 时间中断处理函数 */
void sig_alarm(int sig)
{
}

/* 设超时时间 */
static void (*old_sig)();
static int sig_flag=0;

int set_time_out(int sec)
{
if(sec<1)
return(1);
sig_flag=1;
old_sig=signal(SIGALRM,sig_alarm);
alarm(sec);
return(0);
}

/* 设超时时间 */
int reset_time()
{
if(sig_flag==1)
{
signal(SIGALRM,old_sig);
alarm(0);
}
return(0);
}

static struct termio oterm_attr;
int setTerm(int fd)
{
struct termio term_attr;
if(ioctl(fd,TCGETA,&oterm_attr)<0) return -1;
if(ioctl(fd,TCGETA,&term_attr)<0) return -1;

term_attr.c_iflag &=~(IXON|IXOFF|IXANY|INLCR|IGNCR|ICRNL|ISTRIP);
term_attr.c_lflag &=~(ISIG|ECHO|ICANON|NOFLSH|XCLUDE);
term_attr.c_cflag &=~CBAUD;
term_attr.c_cflag |=B19200;
term_attr.c_oflag &=~(OPOST|ONLCR|OCRNL);

term_attr.c_cc[VMIN]=1;
term_attr.c_cc[VTIME]=0;

if(ioctl(fd,TCSETAW,&term_attr)<0) return(-1);
if(ioctl(fd,TCFLSH,2)<0) return(-1);

return 0;
}

int resetTerm(int fd)
{

if(ioctl(fd,TCSETAW,&oterm_attr)<0) return -1;
return 0;
}

read_n(int fd, void *buf, int len)
{
int cou, ret;
ret=0;cou=0;
while(len>0)
{
ret=read(fd,(char*)buf+cou,len);
if(ret<1)
{
fprintf(stderr,"read device err %s\n",strerror(errno));
break;
}
cou+=ret;
len=len-ret;
}
}
write_n(int fd, void *buf, int len)
{
int cou, ret;
ret=0;cou=0;
while(len>0)
{
ret=write(fd,(char*)buf+cou,len);
if(ret<1)
{
fprintf(stderr,"write device err %s\n",strerror(errno));
break;
}
cou+=ret;
len=len-ret;
}
}



----------------------RECV-------------------------
recv /dev/tty2a 9600 >$1
----------------------recv.c-------------------------


#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<time.h>
#include<signal.h>
#include<termios.h>

#define C_SND 1
#define C_RCV 2
#define C_ACK 7
#define C_END 9
#define C_BEG 11
#define C_ERR 63

struct PACKHEAD
{
short cmd;
int len;
}pkhead;

main(int argc, char *argv[])
{
int fd;
int baud;
int len, cou;
char buf[2050];

if(argc != 3)
{
fprintf(stderr,"usage: send device baud\n");
return(1);
}
baud=atoi(argv[2]);
if(baud<0||baud>38400)
{
fprintf(stderr,"baud error\n");
return(1);
}
fd=open(argv[1],O_RDWR,0);
if(fd<0)
{
fprintf(stderr,"open <%s> error %s\n",argv[1],strerror(errno));
return(1);
}
if(setTerm(fd))
{
fprintf(stderr,"setterm error %s\n",strerror(errno));
return(1);
}

cou=0;

/* are you ready */
read_n(fd,&pkhead,sizeof(pkhead));
if(pkhead.cmd!=C_BEG)
{
fprintf(stderr,"send not ready\n");
return(1);
}

/* i am ready to recv */
pkhead.cmd=C_RCV;
pkhead.len=0;
write_n(fd,&pkhead,sizeof(pkhead));

fprintf(stderr,"begin to receive\n");

while( 1 )
{
/* read a pack */
read_n(fd,&pkhead,sizeof(pkhead));
if(pkhead.cmd!=C_SND) break;
len=pkhead.len;
read_n(fd,buf,len);
write(1,buf,len);

/* i receive a pack */
pkhead.cmd=C_ACK;
pkhead.len=0;
write_n(fd,&pkhead,sizeof(pkhead));

cou+=len;
fprintf(stderr,"recv %d byte\n",len);
}
fprintf(stderr,"all recv %d byte\n",cou);
}

/* 时间中断处理函数 */
void sig_alarm(int sig)
{
}

/* 设超时时间 */
static void (*old_sig)();
static int sig_flag=0;

int set_time_out(int sec)
{
if(sec<1)
return(1);
sig_flag=1;
old_sig=signal(SIGALRM,sig_alarm);
alarm(sec);
return(0);
}

/* 设超时时间 */
int reset_time()
{
if(sig_flag==1)
{
signal(SIGALRM,old_sig);
alarm(0);
}
return(0);
}

static struct termio oterm_attr;
int setTerm(int fd)
{
struct termio term_attr;
if(ioctl(fd,TCGETA,&oterm_attr)<0) return -1;
if(ioctl(fd,TCGETA,&term_attr)<0) return -1;

term_attr.c_iflag &=~(IXON|IXOFF|IXANY|INLCR|IGNCR|ICRNL|ISTRIP);
term_attr.c_lflag &=~(ISIG|ECHO|ICANON|NOFLSH|XCLUDE);
term_attr.c_cflag &=~CBAUD;
term_attr.c_cflag |=B19200;
term_attr.c_oflag &=~(OPOST|ONLCR|OCRNL);

term_attr.c_cc[VMIN]=1;
term_attr.c_cc[VTIME]=0;

if(ioctl(fd,TCSETAW,&term_attr)<0) return(-1);
if(ioctl(fd,TCFLSH,2)<0) return(-1);

return 0;
}

int resetTerm(int fd)
{

if(ioctl(fd,TCSETAW,&oterm_attr)<0) return -1;
return 0;
}

read_n(int fd, void *buf, int len)
{
int cou, ret;
ret=0;cou=0;
while(len>0)
{
ret=read(fd,(char*)buf+cou,len);
if(ret<1)
{
fprintf(stderr,"read device err %s\n",strerror(errno));
break;
}
cou+=ret;
len=len-ret;
}
}
write_n(int fd, void *buf, int len)
{
int cou, ret;
ret=0;cou=0;
while(len>0)
{
ret=write(fd,(char*)buf+cou,len);
if(ret<1)
{
fprintf(stderr,"write device err %s\n",strerror(errno));
break;
}
cou+=ret;
len=len-ret;
}
}