天天看點

Linux下序列槽通信詳解(上)打開序列槽和序列槽初始化詳解1.打開序列槽2.序列槽的初始化

轉載位址:http://blog.csdn.net/specialshoot/article/details/50707965

linux下序列槽通信主要有下面幾個步驟

Linux下序列槽通信詳解(上)打開序列槽和序列槽初始化詳解1.打開序列槽2.序列槽的初始化

序列槽通信流程圖

下面我會一一介紹這幾個步驟。

1.打開序列槽

代碼(序列槽為ttyUSB0)

[java] view plain copy

  1. //打開序列槽  
  2. int open_port(void)  
  3. {  
  4.     int fd;  
  5.     fd=open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NONBLOCK);//O_NONBLOCK設定為非阻塞模式,在read時不會阻塞住,在讀的時候将read放在while循環中,下一節篇文檔将詳細講解阻塞和非阻塞  
  6. //  printf("fd=%d\n",fd);  
  7.     if(fd==-1)  
  8.     {  
  9.         perror("Can't Open SerialPort");  
  10.     }  
  11.     return fd;  
  12. }  

打開序列槽時也可以多加一些内容,比如判斷序列槽為阻塞狀态、測試是否為終端裝置等,這些是必要的,是以較上面的基本的打開序列槽的代碼,更加完整健壯一些的代碼流程如下所示:

Linux下序列槽通信詳解(上)打開序列槽和序列槽初始化詳解1.打開序列槽2.序列槽的初始化

打開序列槽較完整流程圖

代碼:

[cpp] view plain copy

  1. int open_port(int fd,int comport)   
  2. {   
  3.     char *dev[]={"/dev/ttyUSB0","/dev/ttyS1","/dev/ttyS2"};  
  4.     if (comport==1)//序列槽1   
  5.     {  
  6.         fd = open( "/dev/ttyUSB0", O_RDWR|O_NOCTTY|O_NDELAY);   
  7.         if (-1 == fd)  
  8.         {   
  9.             perror("Can't Open Serial Port");   
  10.             return(-1);   
  11.         }   
  12.      }   
  13.      else if(comport==2)//序列槽2   
  14.      {       
  15.         fd = open( "/dev/ttyS1", O_RDWR|O_NOCTTY|O_NDELAY); //沒有設定<span style="font-family: Arial, Helvetica, sans-serif;">O_NONBLOCK非阻塞模式,也可以設定為非阻塞模式,兩個模式在下一篇部落格中具體說明</span>  
  16.         if (-1 == fd)  
  17.         {   
  18.             perror("Can't Open Serial Port");   
  19.             return(-1);   
  20.         }   
  21.      }   
  22.      else if (comport==3)//序列槽3   
  23.      {   
  24.         fd = open( "/dev/ttyS2", O_RDWR|O_NOCTTY|O_NDELAY);   
  25.         if (-1 == fd)  
  26.         {   
  27.             perror("Can't Open Serial Port");   
  28.             return(-1);   
  29.         }   
  30.      }   
  31.      if(fcntl(fd, F_SETFL, 0)<0)   
  32.             printf("fcntl failed!\n");   
  33.      else   
  34.         printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));   
  35.      if(isatty(STDIN_FILENO)==0)   
  36.         printf("standard input is not a terminal device\n");   
  37.      else   
  38.         printf("isatty success!\n");   
  39.      printf("fd-open=%d\n",fd);   
  40.      return fd;   
  41. }  

關鍵函數解釋:

open

功能描述:用于打開或建立檔案,成功則傳回檔案描述符,否則傳回-1,open傳回的檔案描述符一定是最小的未被使用的描述符

[cpp] view plain copy

  1. #include<fcntl.h>  
  2. 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代表出錯

[cpp] view plain copy

  1. #include<unistd.h>  
  2. #include<fcntl.h>  
  3. int fcntl(int fd,int cmd);  
  4. int fcntl(int fd,int cmd,long arg);  
  5. 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.序列槽的初始化

序列槽初始化工作需要做以下工作:

  1. 設定波特率
  2. 設定資料流控制
  3. 設定幀的格式(即資料位個數,停止位,校驗位)
Linux下序列槽通信詳解(上)打開序列槽和序列槽初始化詳解1.打開序列槽2.序列槽的初始化

序列槽初始化 代碼: [cpp] view plain copy

  1. int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)   
  2. {   
  3.      struct termios newtio,oldtio;   
  4.      if  ( tcgetattr( fd,&oldtio)  !=  0) {    
  5.       perror("SetupSerial 1");  
  6.     printf("tcgetattr( fd,&oldtio) -> %d\n",tcgetattr( fd,&oldtio));   
  7.       return -1;   
  8.      }   
  9.      bzero( &newtio, sizeof( newtio ) );   
  10.      newtio.c_cflag  |=  CLOCAL | CREAD;    
  11.      newtio.c_cflag &= ~CSIZE;    
  12.      switch( nBits )   
  13.      {   
  14.      case 7:   
  15.       newtio.c_cflag |= CS7;   
  16.       break;   
  17.      case 8:   
  18.       newtio.c_cflag |= CS8;   
  19.       break;   
  20.      }   
  21.      switch( nEvent )   
  22.      {   
  23.      case 'o':  
  24.      case 'O': //奇數   
  25.       newtio.c_cflag |= PARENB;   
  26.       newtio.c_cflag |= PARODD;   
  27.       newtio.c_iflag |= (INPCK | ISTRIP);   
  28.       break;   
  29.      case 'e':  
  30.      case 'E': //偶數   
  31.       newtio.c_iflag |= (INPCK | ISTRIP);   
  32.       newtio.c_cflag |= PARENB;   
  33.       newtio.c_cflag &= ~PARODD;   
  34.       break;  
  35.      case 'n':  
  36.      case 'N':  //無奇偶校驗位   
  37.       newtio.c_cflag &= ~PARENB;   
  38.       break;  
  39.      default:  
  40.       break;  
  41.      }   
  42. switch( nSpeed )   
  43.      {   
  44.      case 2400:   
  45.       cfsetispeed(&newtio, B2400);   
  46.       cfsetospeed(&newtio, B2400);   
  47.       break;   
  48.      case 4800:   
  49.       cfsetispeed(&newtio, B4800);   
  50.       cfsetospeed(&newtio, B4800);   
  51.       break;   
  52.      case 9600:   
  53.       cfsetispeed(&newtio, B9600);   
  54.       cfsetospeed(&newtio, B9600);   
  55.       break;   
  56.      case 115200:   
  57.       cfsetispeed(&newtio, B115200);   
  58.       cfsetospeed(&newtio, B115200);   
  59.       break;   
  60.      case 460800:   
  61.       cfsetispeed(&newtio, B460800);   
  62.       cfsetospeed(&newtio, B460800);   
  63.       break;   
  64.      default:   
  65.       cfsetispeed(&newtio, B9600);   
  66.       cfsetospeed(&newtio, B9600);   
  67.      break;   
  68.      }   
  69.      if( nStop == 1 )   
  70.       newtio.c_cflag &=  ~CSTOPB;   
  71.      else if ( nStop == 2 )   
  72.       newtio.c_cflag |=  CSTOPB;   
  73.      newtio.c_cc[VTIME]  = 0;   
  74.      newtio.c_cc[VMIN] = 0;   
  75.      tcflush(fd,TCIFLUSH);   
  76. if((tcsetattr(fd,TCSANOW,&newtio))!=0)   
  77.      {   
  78.       perror("com set error");   
  79.       return -1;   
  80.      }   
  81.      printf("set done!\n");   
  82.      return 0;   
  83. }   

講解這片代碼之前,我們要先研究一下termios的資料結構。最小的termios結構的典型定義如下: [cpp] view plain copy

  1. struct termios  
  2. {  
  3.            tcflag_t c_iflag;  
  4.            tcflag_t c_oflag;  
  5.            tcflag_t c_cflag;  
  6.            tcflag_t c_lflag;  
  7.            cc_t           c_cc[NCCS];  
  8. };  

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

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

五種模式的參數說明見部落格 http://blog.csdn.net/querdaizhi/article/details/7436722

tcgetattr可以初始化一個終端對應的termios結構,tcgetattr函數原型如下: [cpp] view plain copy

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

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

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

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

  1. TCSANOW:立刻對值進行修改
  2. TCSADRAIN:等目前的輸出完成後再對值進行修改
  3. TCSAFLUSH:等目前的輸出完成之後,再對值進行修改,但丢棄還未從read調用傳回的目前的可用的任何輸入。

在我們的代碼中,我們設定為NOW立即對值進行修改。 tcflush用于清空中端為完成的輸入/輸出請求及資料,它的函數原型如下: [cpp] view plain copy

  1. int tcflush(int fd, int queue_selector);  

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

  1. newtio.c_cflag  |=  CLOCAL | CREAD;    
  2. 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用來設定輸入輸出的波特率,函數模型如下:

[cpp] view plain copy

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

參數說明:

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

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

下一篇文章我會記錄序列槽的讀寫及關閉操作的詳細步驟。并且會把源代碼連結給出供大家參考!