Linux 串口驅動程序開發

讀取串口數據使用文件操作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置於阻塞狀態,一定等到監視文件描述符集合中某個文件描述符發生變化爲止;第二,若將時間值設爲0秒0毫秒,就變成一個純粹的非阻塞函數,不管文件描述符是否有變化,都立刻返回繼續執行,文件無變化返回0,有變化返回一個正值;第三,timeout的值大於0,這就是等待的超時時間,即select在timeout時間內阻塞,超時時間之內有事件到來就返回了,否則在超時後不管怎樣一定返回,返回值同上述。

l        返回值:

負值:select錯誤 正值:某些文件可讀寫或出錯 0:等待超時,沒有可讀寫或錯誤的文件

在有了select後可以寫出像樣的網絡程序來!舉個簡單的例子,就是從網絡上接受數據寫入一個文件中。

一般來說,在使用select函數之前,首先使用FD_ZERO和FD_SET來初始化文件描述符集,在使用了select函數時,可循環使用FD_ISSET測試描述符集,在執行完對相關的文件描述符後,使用FD_CLR來清除描述符集。

2.2      寫串口

讀寫串口設置好串口之後,讀寫串口就很容易了,把串口當作文件讀寫就是。發送數據:

char  buffer[1024];

int    Length;

int    nByte;

nByte = write(fd, buffer ,Length);

2.3      關閉串口

close(fd);

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