串口相關操作信息

這篇文章是轉載別人的。

轉載地址是:https://blog.csdn.net/specialshoot/article/details/50707965

linux下串口通信主要有下面幾個步驟
Linux串口通信流程

串口通信流程圖

 

下面我會一一介紹這幾個步驟。
1.打開串口

代碼(串口爲ttyUSB0)

    //打開串口
    int open_port(void)
    {
        int fd;
            
        fd=open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NONBLOCK);//O_NONBLOCK設置爲非阻塞模式,在read時不會阻塞住,在讀的時候將read放在while循環中,下一節篇文檔將詳細講解阻塞和非阻塞
    //    printf("fd=%d\n",fd);
        
        if(fd==-1)
        {
            perror("Can't Open SerialPort");
        }
        
        return fd;
    }

打開串口時也可以多加一些內容,比如判斷串口爲阻塞狀態、測試是否爲終端設備等,這些是必要的,所以較上面的基本的打開串口的代碼,更加完整健壯一些的代碼流程如下所示:


打開串口較完整流程圖

代碼:

    /**
     * open port
     * @param  fd
     * @param  comport 想要打開的串口號
     * @return  返回-1爲打開失敗
     */
    int open_port(int fd,int comport)
    {
        char *dev[]={"/dev/ttyUSB0","/dev/ttyS1","/dev/ttyS2"};
     
        if (comport==1)//串口1
        {
            fd = open( "/dev/ttyUSB0", O_RDWR|O_NOCTTY|O_NDELAY);
            if (-1 == fd)
            {
                perror("Can't Open Serial Port");
                return(-1);
            }
         }
         else if(comport==2)//串口2
         {     
            fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY); //沒有設置<span style="font-family: Arial, Helvetica, sans-serif;">O_NONBLOCK非阻塞模式,也可以設置爲非阻塞模式,兩個模式在下一篇博客中具體說明</span>
     
            if (-1 == fd)
            {
                perror("Can't Open Serial Port");
                return(-1);
            }
         }
         else if (comport==3)//串口3
         {
            fd = open( "/dev/ttyS2", O_RDWR|O_NOCTTY|O_NDELAY);
            if (-1 == fd)
            {
                perror("Can't Open Serial Port");
                return(-1);
            }
         }
         /*恢復串口爲阻塞狀態*/
         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;
    }

關鍵函數解釋:

open

功能描述:用於打開或創建文件,成功則返回文件描述符,否則返回-1,open返回的文件描述符一定是最小的未被使用的描述符

    #include<fcntl.h>
    int open(const char *pathname, int oflag, ... );

參數解釋:

pathname:文件路徑名,串口在Linux中被看做是一個文件

oflag:一些文件模式選擇,有如下幾個參數可以設置

    O_RDONLY只讀模式
    O_WRONLY只寫模式
    O_RDWR讀寫模式

上面三個參數在設置的時候必須選擇其中一個!!!下面的是可選的

    O_APPEND每次寫操作都寫入文件的末尾
    O_CREAT如果指定文件不存在,則創建這個文件
    O_EXCL如果要創建的文件已存在,則返回 -1,並且修改 errno 的值
    O_TRUNC如果文件存在,並且以只寫/讀寫方式打開,則清空文件全部內容
    O_NOCTTY如果路徑名指向終端設備,不要把這個設備用作控制終端。
    O_NONBLOCK如果路徑名指向 FIFO/塊文件/字符文件,則把文件的打開和後繼 I/O設置爲非阻塞模式(nonblocking mode)

下面三個常量同樣是選用的,他們用於同步輸入輸出

    O_DSYNC等待物理 I/O 結束後再 write。在不影響讀取新寫入的數據的前提下,不等待文件屬性更新。
    O_RSYNC讀(read)等待所有寫入同一區域的寫操作完成後再進行
    O_SYNC等待物理 I/O 結束後再 write,包括更新文件屬性的 I/O

對於串口的打開操作,必須使用O_NOCTTY參數,它表示打開的是一個終端設備,程序不會成爲該端口的控制終端。如果不使用此標誌,任務的一個輸入(比如鍵盤終止信號等)都會影響進程。

O_NDELAY表示不關心DCD信號所處的狀態(端口的另一端是否激活或者停止)。

fcntl

功能描述:根據文件描述詞來操作文件的特性,返回-1代表出錯

    #include<unistd.h>
    #include<fcntl.h>
    int fcntl(int fd,int cmd);
    int fcntl(int fd,int cmd,long arg);
    int fcntl(int fd,int cmd,struct flock *lock);

參數說明:

    fd:文件描述符
    cmd:命令參數

fcntl函數有5種功能:
1. 複製一個現有的描述符(cmd=F_DUPFD).
2. 獲得/設置文件描述符標記(cmd=F_GETFD或F_SETFD).
3. 獲得/設置文件狀態標記(cmd=F_GETFL或F_SETFL).
4. 獲得/設置異步I/O所有權(cmd=F_GETOWN或F_SETOWN).
5. 獲得/設置記錄鎖(cmd=F_GETLK , F_SETLK或F_SETLKW).
具體使用見http://www.cnblogs.com/lonelycatcher/archive/2011/12/22/2297349.html
isatty
函數功能,實現只使用了一個終端專用的函數tcgetattr(如果成功之星,它不改變任何東西),並取其返回值。若爲終端設備返回1,否則返回0。詳情見http://blog.csdn.net/wangjingyu00711/article/details/41693155
2.串口的初始化
串口初始化工作需要做以下工作:

    設置波特率
    設置數據流控制
    設置幀的格式(即數據位個數,停止位,校驗位)

串口初始化
串口初始化
代碼:

    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");
        printf("tcgetattr( fd,&oldtio) -> %d\n",tcgetattr( fd,&oldtio));
          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':
         case 'O': //奇數
          newtio.c_cflag |= PARENB;
          newtio.c_cflag |= PARODD;
          newtio.c_iflag |= (INPCK | ISTRIP);
          break;
         case 'e':
         case 'E': //偶數
          newtio.c_iflag |= (INPCK | ISTRIP);
          newtio.c_cflag |= PARENB;
          newtio.c_cflag &= ~PARODD;
          break;
         case 'n':
         case 'N':  //無奇偶校驗位
          newtio.c_cflag &= ~PARENB;
          break;
         default:
          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;
    }

講解這片代碼之前,我們要先研究一下termios的數據結構。最小的termios結構的典型定義如下:

    struct termios
    {
               tcflag_t c_iflag;
               tcflag_t c_oflag;
               tcflag_t c_cflag;
               tcflag_t c_lflag;
               cc_t           c_cc[NCCS];
    };

上面五個結構成員名稱分別代表:

    c_iflag:輸入模式
    c_oflag:輸出模式
    c_cflag:控制模式
    c_lflag:本地模式
    c_cc[NCCS]:特殊控制模式

五種模式的參數說明見博客http://blog.csdn.net/querdaizhi/article/details/7436722
tcgetattr可以初始化一個終端對應的termios結構,tcgetattr函數原型如下:

    #include<termios.h>  
    int tcgetattr(int fd, struct termios *termios_p);

這個函數調用把低昂前終端接口變量的值寫入termios_p參數指向的結構。如果這些值其後被修改了,可以通過調用函數tcsetattr來重新配置。
tcsetattr函數原型如下:

    #include<termios.h>  
    int tcsetattr(int fd , int actions , const struct termios *termios_h);  

參數actions控制修改方式,共有三種修改方式,如下所示:

    TCSANOW:立刻對值進行修改
    TCSADRAIN:等當前的輸出完成後再對值進行修改
    TCSAFLUSH:等當前的輸出完成之後,再對值進行修改,但丟棄還未從read調用返回的當前的可用的任何輸入。

在我們的代碼中,我們設置爲NOW立即對值進行修改。
tcflush用於清空中端爲完成的輸入/輸出請求及數據,它的函數原型如下:

int tcflush(int fd, int queue_selector);

其中queue_selector時控制tcflush的操作,取值可以爲如下參數中的一個:TCIFLUSH清楚正收到的數據,且不會讀出來;TCOFLUSH清楚正寫入的數據,且不會發送至終端;TCIOFLUSH清除所有正在發送的I/O數據。
再看我們的代碼,我們修改字符大小的代碼爲

    newtio.c_cflag  |=  CLOCAL | CREAD;  
    newtio.c_cflag &= ~CSIZE;  

c_cflag代表控制模式

    CLOCAL含義爲忽略所有調制解調器的狀態行,這個目的是爲了保證程序不會佔用串口。
    CREAD代表啓用字符接收器,目的是是的能夠從串口中讀取輸入的數據。
    CS5/6/7/8表示發送或接收字符時使用5/6/7/8比特。
    CSTOPB表示每個字符使用兩位停止位。
    HUPCL表示關閉時掛斷調制解調器。
    PARENB:啓用奇偶校驗碼的生成和檢測功能。
    PARODD:只使用奇校驗而不使用偶校驗。

c_iflag代表輸入模式

    BRKINT:當在輸入行中檢測到一個終止狀態時,產生一箇中斷。
    TGNBRK:忽略輸入行中的終止狀態。
    TCRNL:將接受到的回車符轉換爲新行符。
    TGNCR:忽略接受到的新行符。
    INLCR:將接受到的新行符轉換爲回車符。
    IGNPAR:忽略奇偶校檢錯誤的字符。
    INPCK:對接收到的字符執行奇偶校檢。
    PARMRK:對奇偶校檢錯誤作出標記。
    ISTRIP:將所有接收的字符裁減爲7比特。
    IXOFF:對輸入啓用軟件流控。
    IXON:對輸出啓用軟件流控。

c_cc特殊的控制字符

標準模式和非標準模式下,c_cc數組的下標有不同的值:

標準模式:

    VEOF:EOF字符
    VEOL:EOF字符
    VERASE:ERASE字符
    VINTR:INTR字符
    VKILL:KILL字符
    VQUIT:QUIT字符
    VSTART:START字符
    VSTOP:STOP字符

非標準模式:

    VINTR:INTR字符
    VMIN:MIN值
    VQUIT:QUIT字符
    VSUSP:SUSP字符
    VTIME:TIME值
    VSTART:START字符
    VSTOP:STOP字符

cfsetispeed和cfsetospeed用來設置輸入輸出的波特率,函數模型如下:

    int cfsetispeed(struct termios *termptr, speed_t speed);
    int cfsetospeed(struct termios *termptr, speed_t speed);

參數說明:

    struct termios *termptr:指向termios結構的指針
    speed_t speed:需要設置的波特率
    返回值:成功返回0,否則返回-1

這樣,所有的初始化操作我們就完成了。

 
---------------------  
作者:HAN_UESTC  
來源:CSDN  
原文:https://blog.csdn.net/specialshoot/article/details/50707965  
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章