Linux下的串口编程(二)

    技术2025-04-06  38

    Linxu下的串口编程(二)

     

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

    Author             :tiger-johnWebSite            :blog.csdn.net/tigerjb

    Email               jibo.tiger@gmail.com

    Update-Time   : 2011214日星期一

     

    Tiger声明:本人鄙视直接复制本人文章而不加出处的个人或团体,

    但不排斥别人转载tiger-john的文章,只是请您注明出处并和本人

    联系或留言给我3Q

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

     

     

    前面已经提到过Linux下皆为文件,这当然也包括我们今天的主角àUART0串口。因此对他的一切操作都和文件的操作一样(涉及到了open,read,write,close等文件的基本操作)。

    一.Linux下的串口编程又那几部分组成

     

     

    1.    打开串口

    2.    串口初始化

    3.    读串口或写串口

    4.    关闭串口

    二.串口的打开

    既然串口在linux中被看作了文件,那么在对文件进行操作前先要对其进行打开操作

    1.Linxu中,串口设备是通过串口终端设备文件来访问的,即通过访问/dev/ttyS0,/dev/ttyS1,/dev/ttyS2这些设备文件实现对串口的访问。

    2.调用open()函数来代开串口设备,对于串口的打开操作,必须使用O_NOCTTY参数。

    l  O_NOCTTY:表示打开的是一个终端设备,程序不会成为该端口的控制终端。如果不使用此标志,任务一个输入(eg:键盘中止信号等)都将影响进程。

    l  O_NDELAY:表示不关心DCD信号线所处的状态(端口的另一端是否激活或者停止)。

    3.打开串口模块有那及部分组成

    1>调用open()函数打开串口,获取串口设备文件描述符

    2>获取串口状态,判断是否阻塞

    3>测试打开的文件描述符是否为终端设备

     

     

     

     

     

     

    4程序:

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

    * 名称:                    UART0_Open

    * 功能:                    打开串口并返回串口设备文件描述

    * 入口参数:            fd    :文件描述符     port :串口号(ttyS0,ttyS1,ttyS2)

    * 出口参数:            正确返回为1,错误返回为0

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

    int UART0_Open(int fd,char* port)

    {

     

          fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);

          if (FALSE == fd)

                 {

                        perror("Can't Open Serial Port");

                        return(FASLE);

                 }

      //判断串口的状态是否为阻塞状态                            

      if(fcntl(fd, F_SETFL, 0) < 0)

          {

                 printf("fcntl failed!/n");

               return(FALSE);

          }     

      else

      {

           printf("fcntl=%d/n",fcntl(fd, F_SETFL,0));

      }

      //测试是否为终端设备    

      if(0 == isatty(STDIN_FILENO))

          {

                 printf("standard input is not a terminal device/n");

            return(FALSE);

          }

      else

          {

               printf("isatty success!/n");

          }       

      printf("fd->open=%d/n",fd);

      return fd;

    }

     

     

     

    三.串口的初始化

     

    1.    linux中的串口初始化和前面的串口初始化一样。需要设置串口波特率,数据流控制,帧的格式(即数据位个数,停止位,校验位,数据流控制)

    2.    串口初始化模块有那几部分组成:

    1>.设置波特率

    2>设置数据流控制

    2>设置帧的格式(即数据位个数,停止位,校验位)

    John哥说明:

    1>设置串口参数时要用到termios结构体,因此先要通过函数

    tcgettattr(fd,&options)获得串口指向termios结构的指针。

    2>通过cfsetispeed函数和cfsetospeed函数用来设置串口的输入/输出波特率。一般情况下,输入和输出波特率相等的。

    3>设置数据位可以通过修改termios机构体中c_flag来实现。其中CS5,CS6,CS7,CS8对应数据位的5678。在设置数据位时,必须要用CSIZE做位屏蔽。

    4>数据流控制是使用何种方法来标志数据传输的开始和结束。

    5>在设置完波特率,数据流控制,数据位,校验位,停止位,停止位后,还要设置最小等待时间和最小接收字符。

    6>在完成配置后要通过tcsetattr()函数来激活配置。

    3.程序:

     

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

    * 名称:                UART0_Set

    * 功能:                设置串口数据位,停止位和效验位

    * 入口参数:        fd         串口文件描述符

    *                              speed      串口速度

    *                              flow_ctrl  数据流控制

    *                           databits   数据位   取值为 7 或者8

    *                           stopbits   停止位   取值为 1 或者2

    *                           parity     效验类型 取值为N,E,O,,S

    *出口参数:              正确返回为1,错误返回为0

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

    int UART0_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity)

    {

       

          int   i;

             int   status;

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

             

        struct termios options;

       

        /*tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将它们保存于options,该函数,还可以测试配置是否正确,该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1.

        */

        if  ( tcgetattr( fd,&options)  !=  0)

           {

              perror("SetupSerial 1");    

              return(FALSE); 

           }

      

        //设置串口输入波特率和输出波特率

        for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)

                    {

                  if  (speed == name_arr[i])

                  {       

                              cfsetispeed(&Options, speed_arr[i]); 

                              cfsetospeed(&Options, speed_arr[i]);  

                  }

           }     

       

        //修改控制模式,保证程序不会占用串口

        options.c_cflag |= CLOCAL;

        //修改控制模式,使得能够从串口中读取输入数据

        options.c_cflag |= CREAD;

      

        //设置数据流控制

        switch(flow_ctrl)

        {

          

           case 0 ://不使用流控制

                  options.c_cflag &= ~CRTSCTS;

                  break;   

          

           case 1 ://使用硬件流控制

                  options.c_cflag |= CRTSCTS;

                  break;

           case 2 ://使用软件流控制

                  options.c_cflag |= IXON | IXOFF | IXANY;

                  break;

        }

        //设置数据位

        options.c_cflag &= ~CSIZE; //屏蔽其他标志位

        switch (databits)

        {  

           case 5    :

                         options.c_cflag |= CS5;

                         break;

           case 6    :

                         options.c_cflag |= CS6;

                         break;

           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; 

                     options.c_iflag &= ~INPCK;    

                     break; 

           case 'o':  

           case 'O'://设置为奇校验    

                     options.c_cflag |= (PARODD | PARENB); 

                     options.c_iflag |= INPCK;             

                     break; 

           case 'e': 

           case 'E'://设置为偶校验  

                     options.c_cflag |= PARENB;       

                     options.c_cflag &= ~PARODD;       

                     options.c_iflag |= INPCK;       

                     break;

           case 's':

           case 'S': //设置为空格 

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

        }

       

        //修改输出模式,原始数据输出

        options.c_oflag &= ~OPOST;

       

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

        options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */  

        options.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */

       

        //如果发生数据溢出,接收数据,但是不再读取

        tcflush(fd,TCIFLUSH);

       

        //激活配置 (将修改后的termios数据设置到串口中)

        if (tcsetattr(fd,TCSANOW,&options) != 0)  

        {

                   perror("com set error!/n");  

           return (FALSE); 

        }

        return (TRUE); 

    }

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

    * 名称:                  UART0_Init()

    * 功能:                串口初始化

    * 入口参数:        fd           文件描述符   

    *               speed     串口速度

    *                              flow_ctrl   数据流控制

    *               databits    数据位   取值为 7 或者8

    *                           stopbits   停止位   取值为 1 或者2

    *                           parity     效验类型 取值为N,E,O,,S                    

    * 出口参数:        正确返回为1,错误返回为0

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

    int UART0_Init(int fd, int speed,int flow_ctrlint databits,int stopbits,int parity)

    {

        int err;

        //设置串口数据帧格式

        if (UART0_Set(fd,115200,0,8,1,'N') == FALSE)

        {                                                     

            return FALSE;

        }

        else

        {

                   return  TRUE;

            }

    }

     

     

     

     

    注:

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

     

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

    options.c_oflag  &= ~OPOST;   /*Output*/

     

     

     

    四.    串口的读写函数:

    1.     读写串口是通过使用read函数和write函数来实现的。

    2.     程序

     

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

    * 名称:                  UART0_Recv

    * 功能:                接收串口数据

    * 入口参数:        fd                  :文件描述符    

    *                              rcv_buf     :接收串口中数据存入rcv_buf缓冲区中

    *                              data_len    :一帧数据的长度

    * 出口参数:        正确返回为1,错误返回为0

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

    int UART0_Recv(int fd, char *rcv_buf,int data_len)

    {

        int len,fs_sel;

        fd_set fs_read;

       

        struct timeval time;

       

        FD_ZERO(&fs_read);

        FD_SET(fd,&fs_read);

       

        time.tv_sec = 10;

        time.tv_usec = 0;

       

        //使用select实现串口的多路通信

        fs_sel = select(fd+1,&fs_read,NULL,NULL,&time);

        if(fs_sel)

           {

                  len = read(fd,data,data_len);

                  return len;

           }

        else

           {

                  return FALSE;

           }     

    }

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

    * 名称:                UART0_Send

    * 功能:                发送数据

    * 入口参数:        fd                  :文件描述符    

    *                              send_buf    :存放串口发送数据

    *                              data_len    :一帧数据的个数

    * 出口参数:        正确返回为1,错误返回为0

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

    int UART0_Send(int fd, char *send_buf,int data_len)

    {

        int len = 0;

       

        len = write(fd,send_buf,data_len);

        if (len == data_len )

           {

                  return len;

           }     

        else   

            {

                   

                    tcflush(fd,TCOFLUSH);

                    return FALSE;

            }

        }

     

    五.    关闭串口

    在完成对串口设备的操作后,要调用close函数关闭该文件描述符。

    程序:

     

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

    * 名称:                UART0_Close

    * 功能:                关闭串口并返回串口设备文件描述

    * 入口参数:        fd    :文件描述符   

    * 出口参数:        void

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

     

    void UART0_Close(int fd)

    {

        close(fd);

    }

    一.    一个完整程序

    /****************************************Copyright (c)************************************************** **                                      xi an you dian xue yuan **                                      graduate school ** XNMS **                                      WebSite :blog.csdn.net/tigerjb **--------------File Info------------------------------------------------------------------------------- ** File name: main.c ** Last modified Date:  2011-01-31 ** Last Version: 1.0 ** Descriptions: ** **------------------------------------------------------------------------------------------------------ ** Created by: jibo ** Created date: 2011-06-5 ** Version: 1.0 ** Descriptions: The original version ** **------------------------------------------------------------------------------------------------------ ** Modified by: ** Modified date: ** Version: ** Descriptions: ** ********************************************************************************************************/ #include<stdio.h>       #include<stdlib.h>      #include<unistd.h>      #include<sys/types.h>   #include<sys/stat.h>    #include<fcntl.h>       #include<termios.h>     #include<errno.h>       #include<string.h> #define FALSE  -1 #define TRUE   0 int UART_Open(int fd,char* port); void UART_Close(int fd); int UART_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity); int UART_Init(int fd, int speed,int flow_ctrlint ,int databits,int stopbits,char parity); int UART_Recv(int fd, char *rcv_buf,int data_len); int UART_Send(int fd, char *send_buf,int data_len); int UART_Open(int fd,char* port) {   fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);   if (FALSE == fd){ perror("Can't Open Serial Port");   return(FALSE);   }   if(fcntl(fd, F_SETFL, 0) < 0){ printf("fcntl failed!\n");     return(FALSE);   } else {         // printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));   }   if(0 == isatty(STDIN_FILENO)){   printf("standard input is not a terminal device\n");         return(FALSE);   }   return fd; } void UART_Close(int fd) { close(fd); } int UART_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity) {           int   i;    // int   status;    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    };   struct termios options;  if(tcgetattr( fd,&options)  !=  0){      perror("SetupSerial 1");         return(FALSE);       } for(i= 0;i < sizeof(speed_arr) / sizeof(int);i++) { if  (speed == name_arr[i]) {               cfsetispeed(&options, speed_arr[i]);         cfsetospeed(&options, speed_arr[i]);    }     }   options.c_cflag |= CLOCAL; options.c_cflag |= CREAD; switch(flow_ctrl){ case 0 : options.c_cflag &= ~CRTSCTS; break;     case 1 :     options.c_cflag |= CRTSCTS;     break;     case 2 :     options.c_cflag |= IXON | IXOFF | IXANY;     break; }      options.c_cflag &= ~CSIZE;  switch (databits){    case 5 :     options.c_cflag |= CS5;     break;     case 6 :     options.c_cflag |= CS6;     break;     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;           options.c_iflag &= ~INPCK;              break;       case 'o':        case 'O':             options.c_cflag |= (PARODD | PARENB);           options.c_iflag |= INPCK;                       break;       case 'e':       case 'E':            options.c_cflag |= PARENB;                 options.c_cflag &= ~PARODD;                 options.c_iflag |= INPCK;                break;     case 's':      case 'S':          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);  }           options.c_oflag &= ~OPOST;  options.c_cc[VTIME] = 1;     options.c_cc[VMIN] = 1;  tcflush(fd,TCIFLUSH); if(tcsetattr(fd,TCSANOW,&options) != 0){  perror("com set error!\n");        return (FALSE);   return (TRUE);   } int UART_Init(int fd, int speed,int flow_ctrlint ,int databits,int stopbits,char parity) { if (FALSE == UART_Set(fd,speed,flow_ctrlint,databits,stopbits,parity)) {     return FALSE;     } else {     return  TRUE;     } } int UART_Recv(int fd, char *rcv_buf,int data_len) {     int len,fs_sel;     fd_set fs_read;          struct timeval time;          FD_ZERO(&fs_read);     FD_SET(fd,&fs_read);          time.tv_sec = 10;     time.tv_usec = 0;          fs_sel = select(fd+1,&fs_read,NULL,NULL,&time);     if(fs_sel){    len = read(fd,rcv_buf,data_len);    return len;     } else { return FALSE; } } int UART_Send(int fd, char *send_buf,int data_len) {     int ret;          ret = write(fd,send_buf,data_len);     if (data_len == ret ){    return ret;     } else {        tcflush(fd,TCOFLUSH);        return FALSE;              }      } int main(int argc, char **argv) {     int fd = FALSE;            int ret;              char rcv_buf[512];     int i;     if(argc != 2){    printf("Usage: %s /dev/ttySn \n",argv[0]);    return FALSE;     }     fd = UART_Open(fd,argv[1]);      if(FALSE == fd){    printf("open error\n");    exit(1);     }     ret  = UART_Init(fd,9600,0,8,1,'N');     if (FALSE == fd){    printf("Set Port Error\n");    exit(1);      }     ret  = UART_Send(fd,"*IDN?\n",6);     if(FALSE == ret){    printf("write error!\n");    exit(1);     }     printf("command: %s\n","*IDN?");     memset(rcv_buf,0,sizeof(rcv_buf));     for(i=0;;i++)     {    ret = UART_Recv(fd, rcv_buf,512);        if( ret > 0){     rcv_buf[ret]='\0';     printf("%s",rcv_buf);    } else {    printf("cannot receive data1\n");             break;    }  if('\n' == rcv_buf[ret-1])  break;     }     UART_Close(fd);       return 0; }     /******************************************************************************************************* **                            End Of File ********************************************************************************************************/

     

    UART0串口编程目录:

    串口编程之前奏篇

    UART0串口编程系列(一)裸机下的轮训方式的串口编程

    UART0串口编程系列(二)裸机下的中断方式的串口编程

    UART0串口编程系列(三)UC/OS下的串口编程注意的问题

    UART0串口编程系列(四)UC/OS下的串口发送任务编程

    UART0串口编程系列(五) UC/OS下的串口接收任务编程

    UART0串口编程系列(六)Linux下串口编程要知道的那些事

    UART0串口编程系列(七)Linux下的串口编程

     

     

     

     

     

     

    最新回复(0)