linux串口编程

串行口是计算机一种常用的接口,具有连接线少,通讯简单,得到广泛的使用。常用的串口是RS-232-C接口(又称EIA RS-232-C)它是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。它的全名是"数据终端设备(DTE)和数据通讯设备(DCE)之间串行二进制数据交换接口技术标准"该标准规定采用一个25个脚的DB25连接器,对连接器的每个引脚的信号内容加以规定,还对各种信号的电平加以规定。传输距离在码元畸变小于4%的情况下,传输电缆长度应为50英尺。

linux文件中,所有的设备文件一般都位于/dev下,其中串口一、串口二分别对应的设备名依次为“/dev/ttyS0”、“/dev/ttyS1”,可以查看在/dev下的文件以确认。在linux下面对于串口的读写就可以通过简单的readwrite函数来完成,所不同的是只是需要对串口的其他参数另坐配置。

1.   串口编程需要用到的头文件

#include <stdio.h> // 标准输入输出定义

#include <stdlib.h>

#include <fcntl.h> // 文件控制定义,主要完成串口通信中对文件的读写操作

#include <unistd.h> // linux标准函数定义

#include <sys/ioctl.h>

#include <termios.h>   // POSIX终端控制定义                        

#include <sys/time.h> 

#include <sys/types.h> 

2.   串口终端函数

2.1      打开串口设备

     int fd;

       char *device = "/dev/tts/0";                   // 设备路径,初始使用UART0

      

       for(t=1;t<argc;t++)                                // 获取程序入口时输入的参数

       {

              if(!strcmp(argv[t],"-d") && (argc > (t+1)))

              {

                     device = argv[t+1];

              }

       }

       if(!strcmp(device,"/dev/tts/1"))               // 不允许使用UART1,因为它已和PC相连。

       {

              printf("can not use /dev/tts/1\n");

              return -1;     

       }

       fd = open(device, O_RDWR);                         // 打开设备

       if (fd < 0)                                          // 设备打开失败

       {

              printf("open device error\n");

              return -1;

       }

2.2      设置串口

最基本的串口设置包括波特率设置,校验位和停止位设置。实际上串口的设置只要是设置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 */

};

2.2.1  波特率设置

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

              } 

       }

}

2.2.2  校验位和停止位设置

/**

*@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 */

        break; 

case 'o':  

case 'O':    

        options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/ 

        options.c_iflag |= INPCK;  // INPCK:奇偶校验使能

        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':    // Space 校验

case 's':  /*as no parity*/  

     options.c_cflag &= ~PARENB;

        options.c_cflag &= ~CSTOPB;

        options.c_iflag |= INPCK;

break; 

default:  

        fprintf(stderr,"Unsupported parity\n");   

        return (FALSE); 

 

// 设置停止位 

switch (stopbits)

{  

  case 1:   

         options.c_cflag &= ~CSTOPB;  // 1个停止位

         break; 

  case 2:   

         options.c_cflag |= CSTOPB;  // 2个停止位

     break;

  default:   

          fprintf(stderr,"Unsupported stop bits\n"); 

          return (FALSE);

}

 

// 处理未接收的字符

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

}

2.2.3  模式设置

需要注意的是,如果不是开发终端之类的,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode)方式来通信。

       newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始数据输入

       newtio.c_oflag &= ~(OPOST); // 原始数据输出

2.2.4  串口配置实例

void init_ttyS(int fd)

{

       struct termios newtio;

 

       bzero(&newtio, sizeof(newtio));

// 得到当前串口的参数

       tcgetattr(fd, &newtio);             

                    

// 将输入波特率设为19200

// 将输出波特率设为19200

       cfsetispeed(&newtio, B19200);                           

       cfsetospeed(&newtio, B19200);            

             

// 使能接收并使能本地状态

       newtio.c_cflag |= (CLOCAL | CREAD);                  

 

// 无校验 8位数据位1位停止位

       newtio.c_cflag &= ~PARENB;                        

       newtio.c_cflag &= ~CSTOPB;

       newtio.c_cflag &= ~CSIZE;

 

// 8个数据位

       newtio.c_cflag |= CS8;

 

// 原始数据输入

       newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

       newtio.c_oflag &= ~(OPOST);

 

// 设置等待时间和最小接收字符数

       newtio.c_cc[VTIME]    = 0;                         

       newtio.c_cc[VMIN]     = 0;  

 

// 处理未接收的字符

       tcflush(fd, TCIFLUSH);                            

 

// 激活新配置

       tcsetattr(fd,TCSANOW,&newtio);                        

}

 

2.1      读串口

读取串口数据使用文件操作read函数读取,如果设置为原始模式(Raw Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。

char  buff[1024];

int    Len;

int  readByte = read(fd,buff,Len);

可以使用操作文件的函数来实现异步读取,如fcntl,或者select等来操作。

void SERIAL_RX(void)

{

      //  read(fd, RXBUF , RX_len);

#if 1

       int ret,n,pos,retval;

       fd_set rfds;

       struct timeval tv ;

       pos = 0;//指向接收缓冲

 

       for(n = 0; n < RX_len; n++)

       {

              RXBUF[n] = 0xFF;

       }

 

       FD_ZERO(&rfds);// 清空串口接收端口集

       FD_SET(fd,&rfds);// 设置串口接收端口集   

       tv.tv_sec = 2;

       tv.tv_usec = 0;

 

       while(FD_ISSET(fd,& rfds)) // 检测串口是否有读写动作

       {    

// 每次循环都要清空,否则不会检测到有变化

              FD_ZERO(&rfds);// 清空串口接收端口集

              FD_SET(fd,&rfds);// 设置串口接收端口集   

 

              retval = select(fd+1,&rfds,NULL,NULL,&tv);  

              if(retval == -1)

              {

                     perror("select()");

                     break;

              }                             

              else if(retval)

              {  //判断是否还有数据

                     //sleep(2);                             

                     ret = read(fd, RXBUF, RX_len);

                     pos += ret;

                     //printf("ret = %d \n",ret);

                     if((RXBUF[pos-2] == '\r') & (RXBUF[pos-1] == '\n')) // 确实接收到了数据,并打印出来

                     {

                            FD_ZERO(&rfds);

                            FD_SET(fd,&rfds);      

                            retval = select(fd+1,&rfds,NULL,NULL,&tv);  

                            if(!retval)//no datas

                            {

                                   break;

                            }      

                     }

              }

              else

              {

                     break;

              }

       }

}

Linux下直接用read串口可能会造成堵塞,或数据读出错误。然而用select先查询com口,再用read去读就可以避免,并且当com口延时时,程序可以退出,这样就不至于由于com口堵塞,程序就死了。

Select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明)

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

先说明两个结构体:

l        struct fd_set

可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如:

?        FD_ZERO(fd_set *set):清除一个文件描述符集;

?        FD_SET(int fd, fd_set *set):将一个文件描述符加入文件描述符集中;

?        FD_CLR(int fd, fd_set *set):将一个文件描述符从文件描述符集中清除;

?        FD_ISSET(int fd, fd_set *set): 检查集合中指定的文件描述符是否可以读写。

一会儿举例说明。

l        struct timeval

是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。

struct timeval{

              long tv_sec;

             long tv_unsec;

}

具体解释select的参数:

l        int maxfdp

是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。

l        fd_set *readfds

是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

l        fd_set *writefds

是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。

l        fd_set *errorfds

同上面两个参数的意图,用来监视文件错误异常。

l        struct timeval* timeout

select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为00毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即selecttimeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

l        返回值:

负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件

在有了select后可以写出像样的网络程序来!举个简单的例子,就是从网络上接受数据写入一个文件中。

一般来说,在使用select函数之前,首先使用FD_ZEROFD_SET来初始化文件描述符集,在使用了select函数时,可循环使用FD_ISSET测试描述符集,在执行完对相关的文件描述符后,使用FD_CLR来清除描述符集。

2.2      写串口

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

char  buffer[1024];

int    Length;

int    nByte;

nByte = write(fd, buffer ,Length);

2.3      关闭串口

close(fd);

3.   具体应用实例

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <fcntl.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include<time.h>

#include <termios.h>                            

#include <sys/select.h>  

#include <sys/types.h> 

//#include "COMM.h"

 

 

#define ACK_SUCCESS             0x00      // 操作成功

#define  ACK_FAIL             0x01     // 操作失败

#define  ACK_FULL            0x04     //  Full

#define ACK_NOUSER        0x05      // 无此用户

#define  ACK_USER_EXIST 0x07      // 用户已存在

#define  ACK_TIMEOUT             0x08      // 采集超时

#define ACK_COUNT          0x3   //发生错误时候,重试次数

 

extern unsigned char TXBUF[9900];

extern unsigned char RXBUF[9900];

extern unsigned char rev_ok;

extern unsigned int  TX_len;

extern unsigned int  RX_len;

extern unsigned char one_onecontrast(unsigned char user_number_h,unsigned char user_number_l);

extern unsigned char one_morecontrast(void);

extern unsigned char Get_UserNumber_Right(void);

extern unsigned char set_addmode(unsigned char yn_repeat);

extern unsigned char add_Fingerprint(unsigned char time_number,unsigned char user_number_h,

                                                        unsigned char user_number_l,unsigned char user_right);

extern unsigned char del_alluser(void);

extern unsigned char del_oneuser(unsigned char user_number_h,unsigned char user_number_l);

extern unsigned char read_usernumber(void);

 

 

int fd;

 

unsigned char USER_NUMBER_H;    // 用户号高8位

unsigned char USER_NUMBER_L;     // 用户号低8位

unsigned char USER_RIGHT;            // 用户权限

 

unsigned char FEATURE_BUFFER[512];   // 要下传的指纹特征值数据

unsigned char FEATURE_LEN;                 // 要下传的指纹特征值的长度

 

 

unsigned char CharToHex(unsigned char ch);

unsigned char select_one_onecontrast(void);

void select_Get_Usernumber_right(void);

 

 

/*********************************************************************************************

* name:        SERIAL_TX

* func:          发生数据到指纹模块串口

* para:          none

* ret:            none

* modify:

* comment:         

*********************************************************************************************/

int SERIAL_TX(void)

{

       int ret;

 

       ret = write(fd, TXBUF, TX_len);        // 试图从串口发送数 据                              

       if(ret == -1)                        // 确实接收到了数据,并打印出来

       {

       //     *(rcv_buf+ret)='\0';

              printf(" Write device error!\n");

              return -1;

       //     ret = 0;

       }

 

       return 0;

}

 

/*********************************************************************************************

* name:        SERIAL_RX

* func:          从指纹模块串口接收数据

* para:          none

* ret:            none

* modify:

* comment:         

*********************************************************************************************/

void SERIAL_RX(void)

{

      //  read(fd, RXBUF , RX_len);

#if 1

       int ret,n,pos,retval;

       fd_set rfds;

       struct timeval tv ;

       pos = 0;//指向接收缓冲

       tv.tv_sec = 2;

       tv.tv_usec = 0;

       for(n = 0; n < RX_len; n++)

       {

              RXBUF[n] = 0xFF;

       }

      

       //while(FD_ISSET(fd,&uart_r)||FD_ISSET(fd,&uart_w));      // 检测串口是否有读写动作

       while(1) // 检测串口是否有读写动作

       {    

              FD_ZERO(&rfds);// 清空串口接收端口集

              FD_SET(fd,&rfds);// 设置串口接收端口集   

              retval = select(fd+1,&rfds,NULL,NULL,&tv);  

              if(retval == -1)

              {

                     perror("select()");

                     break;

              }                             

              else if(retval)

              {  //判断是否还有数据

                     //sleep(2);                             

                     ret = read(fd, RXBUF, RX_len);

                     pos += ret;

                     //printf("ret = %d \n",ret);

                     if((RXBUF[pos-2] == '\r') & (RXBUF[pos-1] == '\n'))                           // 确实接收到了数据,并打印出来

                     {

                            FD_ZERO(&rfds);

                            FD_SET(fd,&rfds);      

                            retval = select(fd+1,&rfds,NULL,NULL,&tv);  

                            if(!retval)//no datas

                            {

                                   break;

                            }      

                     }

              }

              else

              {

                     break;

              }

       }

}

 

void init_ttyS(int fd)

{

       struct termios newtio;

 

       bzero(&newtio, sizeof(newtio));

       tcgetattr(fd, &newtio);                                   // 得到当前串口的参数

       cfsetispeed(&newtio, B19200);                            // 将输入波特率设为19200

       cfsetospeed(&newtio, B19200);                           // 将输出波特率设为19200

       newtio.c_cflag |= (CLOCAL | CREAD);                   // 使能接收并使能本地状态

       newtio.c_cflag &= ~PARENB;                         // 无校验 8位数据位1位停止位

       newtio.c_cflag &= ~CSTOPB;

       newtio.c_cflag &= ~CSIZE;

       newtio.c_cflag |= CS8;

       newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始数据输入

       newtio.c_oflag &= ~(OPOST);

 

       newtio.c_cc[VTIME]    = 0;                          // 设置等待时间和最小接收字符数

       newtio.c_cc[VMIN]     = 0;  

 

       tcflush(fd, TCIFLUSH);                             // 处理未接收的字符

       tcsetattr(fd,TCSANOW,&newtio);                         // 激活新配置

}

 

unsigned char set_addmode(unsigned char yn_repeat)

{

       unsigned char check, i;

       TXBUF[0] = 0xF5;

       TXBUF[1] = 0x2D;

       TXBUF[2] = 0x00;

       TXBUF[3] = yn_repeat;

       TXBUF[4] = 0x00;

       TXBUF[5] = 0x00;

       check = TXBUF[1];

       for (i = 2; i < 6; i++)

       {

              check ^= TXBUF[i];

       }

       TXBUF[6] = check;

       TXBUF[7] = 0xF5;

      

       rev_ok = 1;

       TX_len = 8;

       RX_len = 8;

 

       SERIAL_TX();

       sleep(delaytime);

       SERIAL_RX();

       rev_ok = RXBUF[4];

 

       return (rev_ok);

}

 

int main(int argc, char ** argv)

{

       int t,ret;

       char mode;

 

       char *device = "/dev/tts/0";                          // 设备路径,初始使用UART0

      

       for(t=1;t<argc;t++)                                // 获取程序入口时输入的参数

       {

              if(!strcmp(argv[t],"-d") && (argc > (t+1)))

              {

                     device = argv[t+1];

              }

       }

       if(!strcmp(device,"/dev/tts/1"))               // 不允许使用UART1,因为它已和PC相连。

       {

              printf("can not use /dev/tts/1\n");

              return -1;     

       }

       fd = open(device, O_RDWR);                         // 打开设备

      

       if (fd < 0)                                          // 设备打开失败

       {

              printf("open device error\n");

              return -1;

       }

      

       init_ttyS(fd);                                   // 初始化设备

 

       while(1)

       {    

              set_addmode(1);                                                  // 设置指纹添加模式,禁止重复

       }    

       close(fd);                                                // 关闭打开的设备

       return 0;                                                 // 正常返回

}



-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------2-----------------------------------------------------------------------------------------------------------------------------------

据通信的基本方式可分为并行通信与串行通信两种。
· 并行通信是指利用多条数据传输线将一个资料的各位同时传送。它的特点是传输速度
快,适用于短距离通信,但要求传输速度较高的应用场合。
· 串行通信是指利用一条传输线将资料一位位地顺序传送。特点是通信线路简单,利用
简单的线缆就可实现通信,降低成本,适用于远距离通信,但传输速度慢的应用场合。
串口设置详解
本节主要讲解设置串口的主要方法。
如前所述,设置串口中最基本的包括波特率设置,校验位和停止位设置。串口的设置主
要是设置struct termios结构体的各成员值,如下所示:
#include
struct termio
{      
      unsigned short c_iflag; /* 输入模式标志 */
      unsigned short c_oflag; /* 输出模式标志 */
      unsigned short c_cflag; /* 控制模式标志*/
      unsigned short c_lflag; /*本地模式标志 */
      unsigned char c_line; /* line discipline */
      unsigned char c_cc[NCC]; /* control characters */
};
在这个结构中最为重要的是c_cflag,通过对它的赋值,用户可以设置波特率、字符大小、
数据位、停止位、奇偶校验位和硬件流控等。另外c_iflag 和c_cc 也是比较常用的标志。在
此主要对这3 个成员进行详细说明。
                 c_cflag支持的常量名称
CBAUD        波特率的位掩码
B0           0波特率(放弃DTR)
B1800        1800波特率
B2400        2400波特率
B4800        4800波特率
B9600        9600波特率
B19200       19200波特率
B38400       38400波特率
B57600       57600波特率
B115200      115200波特率
EXTA         外部时钟率
EXTB         外部时钟率
CSIZE        数据位的位掩码
CS5          5个数据位
CS6          6个数据位
CS7          7个数据位
CS8          8个数据位
CSTOPB       2个停止位(不设则是1个停止位)
CREAD        接收使能
PARENB       校验位使能
PARODD       使用奇校验而不使用偶校验
HUPCL        最后关闭时挂线(放弃DTR)
CLOCAL       本地连接(不改变端口所有者)
LOBLK        块作业控制输出
CNET_CTSRTS  硬件流控制使能

      c_iflag支持的常量名称
INPCK        奇偶校验使能
IGNPAR       忽略奇偶校验错误
PARMRK       奇偶校验错误掩码
ISTRIP       除去奇偶校验位
IXON         启动出口硬件流控
IXOFF        启动入口软件流控
IXANY        允许字符重新启动流控
IGNBRK       忽略中断情况
BRKINT       当发生中断时发送SIGINT信号
INLCR        将NL映射到CR
IGNCR        忽略CR
ICRNL        将CR映射到NL
IUCLC        将高位情况映射到低位情况
IMAXBEL      当输入太长时回复ECHO
      c_cc 支持的常量名称
VINTR     中断控制,对应键为CTRL+C
VQUIT     退出操作,对应键为CRTL+Z
VERASE    删除操作,对应键为Backspace(BS)
VKILL     删除行,对应键为CTRL+U
VEOF      位于文件结尾,对应键为CTRL+D
VEOL      位于行尾,对应键为Carriage return(CR)
VEOL2     位于第二行尾,对应键为Line feed(LF)
VMIN      指定了最少读取的字符数
VTIME     指定了读取每个字符的等待时间

串口控制函数
Tcgetattr         取属性(termios结构)
Tcsetattr         设置属性(termios结构)
cfgetispeed     得到输入速度
Cfgetospeed           得到输出速度
Cfsetispeed            设置输入速度
Cfsetospeed           设置输出速度
Tcdrain           等待所有输出都被传输
tcflow           挂起传输或接收
tcflush           刷清未决输入和/或输出
Tcsendbreak           送BREAK字符
tcgetpgrp              得到前台进程组ID
tcsetpgrp               设置前台进程组ID

      [color=#ff0000]完整的串口配置模板,实用!把常用的选项在函数里面列出,可大大方便用户的调试使用[/color]

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
    struct termios newtio,oldtio;
    /*保存测试现有串口参数设置,在这里如果串口号等出错,会有相关的出错信息*/
    if ( tcgetattr( fd,&oldtio) != 0)
    {
        perror("SetupSerial 1");
         return -1;
     }
    bzero( &newtio, sizeof( newtio ) );
    /*步骤一,设置字符大小*/
    newtio.c_cflag |= CLOCAL | CREAD;
    newtio.c_cflag &= ~CSIZE;
    /*设置停止位*/
    switch( nBits )
    {
        case 7:
            newtio.c_cflag |= CS7;
            break;
        case 8:
            newtio.c_cflag |= CS8;
            break;
    }
/*设置奇偶校验位*/
    switch( nEvent )
    {
        case 'O': //奇数
            newtio.c_cflag |= PARENB;
            newtio.c_cflag |= PARODD;
            newtio.c_iflag |= (INPCK | ISTRIP);
        break;
        case 'E': //偶数
            newtio.c_iflag |= (INPCK | ISTRIP);
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
        break;
            case 'N': //无奇偶校验位
            newtio.c_cflag &= ~PARENB;
        break;
    }
    /*设置波特率*/
    switch( nSpeed )
    {
        case 2400:
            cfsetispeed(&newtio, B2400);
            cfsetospeed(&newtio, B2400);
        break;
        case 4800:
            cfsetispeed(&newtio, B4800);
            cfsetospeed(&newtio, B4800);
        break;
        case 9600:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
        break;
        case 115200:
            cfsetispeed(&newtio, B115200);
            cfsetospeed(&newtio, B115200);
        break;
        case 460800:
            cfsetispeed(&newtio, B460800);
            cfsetospeed(&newtio, B460800);
        break;
        default:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
        break;
    }
    /*设置停止位*/
    if( nStop == 1 )
        newtio.c_cflag &= ~CSTOPB;
    else if ( nStop == 2 )
        newtio.c_cflag |= CSTOPB;
    /*设置等待时间和最小接收字符*/ 
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 0;
    /*处理未接收字符*/
    tcflush(fd,TCIFLUSH);
    /*激活新配置*/
    if((tcsetattr(fd,TCSANOW,&newtio))!=0)
    {
        perror("com set error");
        return -1;
    }
    printf("set done!\n");
    return 0;

            
            
            
            
           

[b]串口使用详解[/b]
在配置完串口的相关属性后,就可对串口进行打开,读写操作了。其使用方式与文件操作一样,区别在于串口是一个终端设备。

[b]打开串口[/b]
fd = open( "/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY);

   Open函数中除普通参数外,另有两个参数O_NOCTTY和O_NDELAY。
   O_NOCTTY: 通知linix系统,这个程序不会成为这个端口的控制终端。
   O_NDELAY: 通知linux系统不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)。
然后,恢复串口的状态为阻塞状态,用于等待串口数据的读入。用fcntl函数:
       fcntl(fd, F_SETFL, 0);

   接着,测试打开的文件描述府是否引用一个终端设备,以进一步确认串口是否正确打开。
       isatty(STDIN_FILENO);
串口的读写与普通文件一样,使用read,write函数。
       read(fd,buff,8);
       write(fd,buff,8);
实例


#i nclude stdio.h>
#i nclude string.h>
#i nclude sys/types.h>
#i nclude errno.h>
#i nclude sys/stat.h>
#i nclude fcntl.h>
#i nclude unistd.h>
#i nclude termios.h>
#i nclude stdlib.h>
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
    struct termios newtio,oldtio;
    if ( tcgetattr( fd,&oldtio) != 0) { 
        perror("SetupSerial 1");
        return -1;
    }
    bzero( &newtio, sizeof( newtio ) );
    newtio.c_cflag |= CLOCAL | CREAD; 
    newtio.c_cflag &= ~CSIZE; 
    switch( nBits )
    {
    case 7:
        newtio.c_cflag |= CS7;
        break;
    case 8:
        newtio.c_cflag |= CS8;
        break;
    }
    switch( nEvent )
    {
    case 'O':
        newtio.c_cflag |= PARENB;
        newtio.c_cflag |= PARODD;
        newtio.c_iflag |= (INPCK | ISTRIP);
        break;
    case 'E': 
        newtio.c_iflag |= (INPCK | ISTRIP);
        newtio.c_cflag |= PARENB;
        newtio.c_cflag &= ~PARODD;
        break;
    case 'N': 
        newtio.c_cflag &= ~PARENB;
        break;
    }
switch( nSpeed )
    {
    case 2400:
        cfsetispeed(&newtio, B2400);
        cfsetospeed(&newtio, B2400);
        break;
    case 4800:
        cfsetispeed(&newtio, B4800);
        cfsetospeed(&newtio, B4800);
        break;
    case 9600:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
    case 115200:
        cfsetispeed(&newtio, B115200);
        cfsetospeed(&newtio, B115200);
        break;
    default:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
    }
    if( nStop == 1 )
        newtio.c_cflag &= ~CSTOPB;
    else if ( nStop == 2 )
    newtio.c_cflag |= CSTOPB;
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 0;
    tcflush(fd,TCIFLUSH);
    if((tcsetattr(fd,TCSANOW,&newtio))!=0)
    {
        perror("com set error");
        return -1;
    }
    printf("set done!\n");
    return 0;
}
int open_port(int fd,int comport)
{
    char *dev[]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};
    long vdisable;
    if (comport==1)
    {    fd = open( "/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY);
        if (-1 == fd){
            perror("Can't Open Serial Port");
            return(-1);
        }
        else 
            printf("open ttyS0 .....\n");
    }
    else if(comport==2)
    {    fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY);
        if (-1 == fd){
            perror("Can't Open Serial Port");
            return(-1);
        }
        else 
            printf("open ttyS1 .....\n");
    }
    else if (comport==3)
    {
        fd = open( "/dev/ttyS2", O_RDWR|O_NOCTTY|O_NDELAY);
        if (-1 == fd){
            perror("Can't Open Serial Port");
            return(-1);
        }
        else 
            printf("open ttyS2 .....\n");
    }
    if(fcntl(fd, F_SETFL, 0)0)
        printf("fcntl failed!\n");
    else
        printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));
    if(isatty(STDIN_FILENO)==0)
        printf("standard input is not a terminal device\n");
    else
        printf("isatty success!\n");
    printf("fd-open=%d\n",fd);
    return fd;
}
int main(void)
{
    int fd;
    int nread,i;
    char buff[]="Hello\n";
    if((fd=open_port(fd,1))0){
        perror("open_port error");
        return;
    }
    if((i=set_opt(fd,115200,8,'N',1))0){
        perror("set_opt error");
        return;
    }
    printf("fd=%d\n",fd);
//    fd=3;
    nread=read(fd,buff,8);
    printf("nread=%d,%s\n",nread,buff);
    close(fd);
    return;
 
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值