POSIX操作系統的串行編程指南

POSIX操作系統的串行編程指南
第五版
Michael R.Sweet
Copyright 1994-1999, Allrights Reserved

目錄

序言

第一章 基本的串口通訊
 什麼是串口通訊
 什麼是RS-232
  信號定義
 異步通訊
  什麼是雙工和單工
  流量控制
  什麼是斷開/break
 同步通訊
 訪問串行端口
  串行端口文件
  打開串行端口
  寫入端口
  讀出端口
  關閉端口
  
第二章 配置串行端口
 POSIX終端接口
  控制選項
  本地選項
  輸入選項
  輸出選項
  控制字符

第三章 Modem通訊
 什麼是Modem
 與Modem通訊
  標準Modem命令
  常見的Modem通訊問題

第四章 高級串行端口編程
 串行端口IOCTLs
  獲得控制信號
  設置控制信號
  獲得有效字節數
 從一個串行端口中選擇輸入
  select系統調用
  使用select系統調用
  使用X Intrinsics庫的Select接口
附錄A.引出線
 RS-232 引出線
 RS-422 引出線
 RS-574 (IBM PC/AT)引出線
 SGI 引出線

附錄B.控制符的ASCII編碼
 控制編碼


序言
 這篇POSIX操作系統的串口編程指南將告訴你如何正確,有效,可移植地對PC上的Unix工作站的串行端口進行編程.每一章提供了
 使用POSIX終端控制函數的程序範例,只需要進行少量的更動就可以在IRIX,HP-UX,SunOS,Solaris,DigitalUnix,Linux
 和其它的Unix上運行.並且你將會發現,各個操作系統之間的最大區別僅僅是用於標誌串口設備和鎖文件的文件名不同.

 該指南被按如下的章節和附錄給予說明.

 第一章 基本的串口通訊
 第二章 配置串行端口
 第三章 Modem通訊
 第四章 高級串行端口編程
 附錄A.引出線
 附錄B.控制符的ASCII編碼

 

第一章 基本的串口通訊
 這一章介紹了串行通訊,RS-232和其它用於大多數計算機上的標準,同時給出了一個用於訪問串口的C程序.

1.1 什麼是串口通訊 ?
  
 計算機在某一時間內會傳送一個或多個位,而串口是一次一位地傳送.串口通訊包括大多數網絡設備,鍵盤,鼠標,
 調制解調器和終端設備.
 
 當使用串口通訊時,你發送或收到的每一個字,(比如一個字節或者一個字符),實際上都是一次一位地傳送的.每一
 位或者爲1或者爲0.之後在你將聽到的術語中,mark表示on(1),space表示off(0).

 串行數據的速度經常用每秒位數(bps)或者波特率(baud)來表示.用來表示一秒中有多少1或者0被傳送.在計算機
 發明的初期,300baud被認爲是很快的,而今天計算機的RS232可以達到430,800baud的速度!當波特率超過1,000時,
 你通常會發現速率用千波特率,或kps(例如9,6k,19,2k)來表示.對於超過1,000,000的速率用兆波特率,或Mbps
 (例如1.5Mbps).

 當提到串口設備或者端口時,它們或者被標記爲數據通信設備("DCE")或者數據終端設備("DTE").兩者的不同非常簡單:
 每一個信號對,比如發送和接收,剛好反過來了.當要將兩個DTE或者兩個DCE接口連在一起時,一個串行的可以交換信號
 對的NULL-Modem電纜或適配器可以完成這一任務.

 
1.2 什麼是RS-232
 
 RS-232是一種標準的用於EIA定義的串口通訊的電氣接口.RS-232實際上由三種不同特點組成.每一種定義了不同
 電壓範圍的on和off.常見的是RS-232C,定義mark(1)(on)指-3V到-12V之間的電壓,space(0)指+3V到+12V之間的電壓.
 RS-232C規範中指出這些信號可以發送到25英尺(約8米)遠.如果波特率足夠低的話,這些信號可以發送的更遠些.
 
 除了用於傳輸的線路,還要提供定時,狀態和握手:
 
  表1  RS-232 引腳定義

 引出口號  說明        引出口號   說明            引出口號  說明     引出口號  說明            引出口號  說明   
 ---------+-----------+----------+---------------+---------+--------+---------+---------------+---------+------------
 1        |接地       |6         |DSR數據序列就緒|11       |未定義  |16       |後備RXD        |21       |信號質量檢測 
 ---------+-----------+----------+---------------+---------+--------+---------+---------------+---------+------------
 2        |TXD輸出    |7         |GND邏輯地      |12       |後備DCD |17       |接收者時鐘     |22       |鬧鐘檢測
 ---------+-----------+----------+---------------+---------+--------+---------+---------------+---------+------------
 3        |RXD輸入    |8         |DCD數據負載檢測|13       |後備CTS |18       |未定義         |23       |數據速率選擇
 ---------+-----------+----------+---------------+---------+--------+---------+---------------+---------+------------
  4        |RTS請求發送|9         |保留           |14       |後備TXD |19       |後備RTS        |24       |傳輸時鐘
 ---------+-----------+----------+---------------+---------+--------+---------+---------------+---------+------------
  5        |CTS請求接收|10        |保留           |15       |傳輸時鐘|20       |DTR數據終端就緒|25       |未定義
 ---------+-----------+----------+---------------+---------+--------+---------+---------------+---------+------------


 
 另外還有用於串口的兩個標準,RS-422和RS-574.RS-422使用更低的電壓和微分信號使電纜通訊距離達到將近1000英尺
 (約300米).RS-574則定義了9針的PC串口連接器和電壓.

1.3 信號定義

 RS-232標準定義了18個不同的用於串口通訊的信號.其中,僅有6個在Unix環境中是普遍適用的.

 GND-邏輯地
 
  嚴格地來說,邏輯地並不是信號,但是沒有它,其它的信號無法工作.邏輯地作爲一個參照電壓以告訴電器
  哪個電壓是正哪些是負.
  
 TXD-數據被髮送
 
  TXD信號承載從你的工作站發送到其它的機器或設備上的數據.mark電壓表示1,space電壓表示0.

 RXD-被接收的數據
  RXD信號承載從其它機器或設備上向你的工作站發送數據.類似TXD,mark和space電壓分別表示1和0.

 DCD-數據載波探測
  DCD信號從串口電纜的另一端的機器或設備發送出來.串行線上的space(0)電壓表示機器或設備處於連線狀態.
  DCD並不總是被使用.

 DTR-數據終端就緒
  DTR信號是從你的工作站發送出來,告訴另一端的機器或設備你已經就緒或者尚未就緒,分別用space(0)電壓和
  mark(1)電壓表示.自從你的工作站打開串行接口之後,DTR一直處於自動激活狀態.
 
 CTS-設備接收就緒,等待新的數據被髮送 ( Clear to Send )
  CTS信號來自串行電纜的另一端.space(0)電壓表示你可以從你的工作站上發送更多數據了.(以前接收的已
  經處理完畢)

  CTS常用來調節工作站與串行線另一端的數據傳送流量.
 
 RTS-請求發送
  RTS信號被你的工作站置成space(0)電壓用來表示有更多的數據等待發送.
  與CTS類似,RTS也是用來調節串行電纜兩個終端之間的數據傳送流量.大多數工作站把這個信號一直置爲
  space(0)電壓.
 
1.4 異步通訊
 對於支持串行通訊的機器而言,它需要知道如何判別數據起始和終止.這篇指南將介紹異步串行數據.
 
 在異步模式下,串行數據直到有字符被髮送前一直保持mark(1)狀態.在每個字符之前有一開始位,緊接着是結束位,一
 個可選的奇偶位和若干個停止位.起始位總是space(0),告訴機器串行數據已經準備好了.數據可以在任何時候被髮送
 或接收,因此稱之爲異步的.

  圖1 異步數據傳輸 

 space    +--+     +-----+  +--+  +--+       +12伏
          |開|0  1 |2  3 |4 |5 |6 |7 |
          |始|位 位|位 位|位|位|位|位|
          |  |     |     |  |  |  |  |
 mark ----+  +-----+ +--+  +--+  +------ -12伏
 
 可選的奇偶位簡單地統計數據中包含奇數個1還是偶數個1.如果有偶數個1,則這位爲0,稱偶校驗位.如果是奇數個1,則
 該位爲0,稱奇校驗位.通常還有spaceparity,mark parity和no parity的說法.SpaceParity就是校驗位總是0,markParity
 就是校驗位總是1,NoParity表示不使用或不傳輸校驗位.
 
 接着的那位是終止位.在字符之間可以有1,1.5或2個終止位,它們的值總是1.以前終止位是爲了讓機器有更多的時間去
 處理前一個字符,但現在僅僅是用於在機器接收字符時的同步.

 異步數據格式通常用諸如"8N1","7E1"等等表示.前者表示8個數據位,沒有校驗位,1個終止位.後者表示7個數據位,偶校
 驗位,1個終止位.

 1.4.1 什麼是全雙工和單雙工
 
 全雙工表示機器可以同時既發送數據又可以接收數據,有兩個獨立的數據通道(一進一出).

 單雙工表示機器不能在發送數據的同時又接收數據.通常這意味這僅僅有一個數據通道在使用.但這並不意味不能使用
 RS-232信號,而是通訊聯接僅能使用不支持全雙工操作的其它的標準,而不是RS-232.


 1.4.2 流量控制
 
 當在兩個串行接口之間傳輸數據時通常有需要調節數據流量.這可能需要對之間的串行通訊聯接,某個串行接口或者某個
 存儲介質實行一些限制.對於異步數據通常有兩種方法:

 一是軟件流量控制.使用特殊的字符去標誌數據流的開始(XON,DC1,八進制的021或結束(XOFF,DC3,八進制的023).這些
 字符可以在ASCII中找到定義.然而當傳輸文本信息時,這些字符本身擁有一定的含義,不能被使用.
 
 另一種是硬件流量控制,用RS232的CTS和RTS信號代替特殊字符用於控制.當接收方準備接收更多的數據時,設置CTS爲
 space(0),反之設成mark(1).對應的,當發送方準備發送更多的數據時,設置RTS爲space(0).由於硬件的流量控制使用
 獨立的一套信號,比軟件的實現更快,因爲作同樣的工作,後者需要發送或接收多位信息.另外需要注意的是,並不是所
 有的硬件和操作系統都支持CTS/RTS流量控制.

 1.4.3 什麼是中斷/Break
 
 一般而言,一個接收或者傳輸數據的信號直到一個新的字符被傳送之前一直保持mark(1).信號過了很長時間後才被降
 低到space(1),通常時1/4到1/2秒,那麼我們稱這種狀態爲break.

 Break有時被用來重置通訊線路或者改變諸如Modem的通訊設備的工作模式.第三章的"如何操作Modem"將更深入地談論
 這個問題.
 
1.5 同步通訊

 與異步數據不同的是,同步數據是一種恆定的位流.爲了從通訊線上讀出數據,機器必須發送或者接收一個普通的位作爲
 時鐘以達到發送端和接收端的同步.

 對於同步,機器必須標誌數據的起始位置.實現它的最普通的方法時使用類似串行數據鏈路控制SDLC或者高速數據鏈路控
 制HDLC之類的數據包協議.

 每一個協議定義了固定的位順序以表示數據包的開始和結束,同時也定義了被用來表示沒有數據時使用的位順序.機器
 根據這些位順序可以知道數據包的開始位.
  
 因爲同步協議不使用每個字符的同步位,所以它們一般能比異步通訊提高至少25%的性能,適合遠程網絡和兩個以上的串行
 接口的配置.

 儘管同步協議擁有速度優勢,但大多數RS232硬件仍然由於額外的硬件和必要的軟件而沒有支持它.


1.6 訪問串行端口

 類似於所有設備,UNIX系統通過設備文件提供了對串行端口的訪問.爲了訪問它,僅僅需要打開相應的設備文件.

 1.6.1 串行端口文件
 在UNIX系統的每一個串行端口都有一個或者更多的設備文件與它們相對應(在/dev/目錄下).

   表2 -----串行端口的設備文件名 
             ----------------------------------------+------------------+-------------- --+
  操作系統                  |     端口1       |      端口2      |
                             ---------------------------------------+------------------+-----------------+
  IRIX(R)                      |      /dev/ttyf1   |    /dev/ttyf2    |
                             ---------------------------------------+------------------+-----------------+
  HP-UX                                     |    /dev/tty1p0  |   /dev/tty2p0 |
                             ---------------------------------------+------------------+-----------------+
  Solairis(R)/SunOS(R)          |      /dev/ttya     |  ./dev/ttyb      |
                             ---------------------------------------+------------------+-----------------+
  Linux(R)                                  |  /dev/ttyS0     |  /dev/ttyS1     |
                             ---------------------------------------+------------------+-----------------+
  Digital UNIX(R)                     |  /dev/tty01      | /dev/tty02       |
                             ---------------------------------------+------------------+-----------------+

 1.6.2 打開串行端口

 既然串行端口是一個文件,open(2)函數可以被用來訪問它.UNIX系統的一個特點是一般用戶無法訪問設備文件.所以需要
 更改你所關心的設備文件的訪問權限,以超級用戶來運行程序或者在你的程序中使用setuid,從而擁有設備文件的訪問權.

 這裏,我們假設該文件可以被所有用戶訪問.在運行IRIX的SGI工作站上打開串行端口的代碼如下:

 listing 1 open a serial port
 
 #include <stdio.h> /*標準的輸入輸出*/
 #include <string.h> /*字符串相關函數*/
 #include <unistd.h> /*UNIX標準的函數*/
 #include <fcntl.h> /*文件控制*/
 #iclude <errno.h> /*出錯信息*/
 #include <termios.h> /*POSIX終端控制定義*/

 /* open_port() ----打開串行端口*/
  打開成功則返回文件描述符,否則返回-1
 */

 int open_port(void)
 {
  int fd;
  fd = open("/dev/ttyf1",O_RDWR | O_NOCTTY | O_NDELAY );
  if( fd == -1 ){
   perror("open_port:Unable to open /dev/ttyf1 -");
  }
  else
   fcntl(fd,F_SETFL,0);

  return fd;
 }
 其他的系統只需要更換設備文件名,其它代碼是一樣的.

 1.6.3 端口的打開選項
 
 你會發現當我們打開設備文件後,我們除了用讀寫模式外還用了另外兩個標誌:
  fd = open("/dev/ttyf1",O_RDWR | O_NOCTTY | O_NDELAY );
 
 O_NOCTTY標誌告訴UNIX系統,這個程序不會成爲對應這個端口的控制終端,如果沒有指定這個標誌,那麼任何一個
 輸入,諸如鍵盤中止信號等等,都將會影響你的進程.類似於getty的程序當啓動login進程時都使用了這個特性,
 但是一般的用戶程序不需要這這樣作.

 O_NDELAY標誌告訴UNIX系統這個程序不關心DCD信號線所處的狀態,它用來表示端口的另一端是否激活或者停止.
 如果你想指定這個標誌,你的進程將會一直處在睡眠態,直到DCD信號線是space(0).
 
 1.6.4 寫數據到端口
 
 寫數據到端口很簡單,就是用write(2)系統調用把數據發送出去.
 
 n = write(fd,"ATZ/r",4);
 if( n < 0 )
  fputs("write() of 4 bytes failed!/n",stderr);

 write返回有多少個字節被髮送出去,如果出錯,則返回爲-1.通常你可能遇到的唯一的錯誤是EIO,當MODEM或者
 數據鏈路撤銷數據載波探測(DCD linke)時會出現這種情況,這種情況將一直存在,直到你關閉端口.

 1.6.5 從端口中讀取數據
 
 從端口中讀出數據有些技巧.當你在RAW數據模式下操作時,每一個read(2)系統調用將返回當前串行輸入緩存區中
 存在的字符數.如果沒有字符,這個系統調用將一直阻塞到有字符到達或者間隔時鐘過期,或者錯誤發生.當read函數
 如下設置後將會立即返回.
 
  fcntl(fd,F_SETFL,FNDELAY);
  
 FNDELAY選項將導致當端口上沒有字符可讀時,read函數返回0.爲恢復到一般狀態,可以在調用fcntl時不帶FNDELAY選項.
 
  fcntl(fd,F_SETFL,0);
  
 在用O_NDELAY打開端口之後也可以這樣來恢復.

 1.6.6 關閉串行端口

 爲了關閉串行端口,只需要使用close系統調用
 
  close(fd);
  
 關閉串行端口經常會設置DTR信號爲低,導致大多數的MODEM掛起.


第二章 配置串行端口
 這一章將討論如何用C語言的POSIX終端接口去配置串行端口.

 2. 1 POSIX終端接口
 
 大多數系統都支持POSIX終端接口,用於改變諸如波特率,字符大小等等參數.你所需要作的第一件事情是將termios.h
 include到你的程序中,這個文件中其中定義了POSIX控制函數和終端控制結構.

 兩個重要的POSIX函數是tcgetattr(3)和tcsetattr(3),分別用於獲得和設置終端屬性;你需要提供一個包含所有可用串
 行選項的終端結構:
 
 表3 - Termios結構的成員
 +---------------------------------------------------------------+
 | 成員                | 說明                                               |
 +---------------------------------------------------------------+
 | c_cflag             | 控制選項                                     |
 +---------------------------------------------------------------+
 | c_cflag             | 線路選項                                     |
 +---------------------------------------------------------------+
 | c_iflag              | 輸入選項                                     |
 +---------------------------------------------------------------+
 | c_oflag            | 輸出選項                                      |
 +---------------------------------------------------------------+
 | c_cc                 | 控制特性                                      |
 +---------------------------------------------------------------+
 | c_ispeed         | 輸入波特律(新接口)               |
 +---------------------------------------------------------------+
 | c_ospeed        | 輸出波特律(新接口)               |
 +---------------------------------------------------------------+

 2.2 控制選項

 c_cflag成員用於控制波特率,數據位的數目,奇偶校驗位,終止位和硬件流控制.以下是所支持的配置常數.
 
 表4 -  c_cfalg成員的可設置常量
 +---------------------+-----------------------------------------+
 | 常量                   | 說明                                             |
 +---------------------+-----------------------------------------+
 | CBAND               | 波特律的位掩碼                    |
 +---------------------+-----------------------------------------+
 | B0                        | 0 波特 (放棄DTR)                    |
 +---------------------+-----------------------------------------+
 | B50                      |  50 波特                         |
 +---------------------+-----------------------------------------+
 | B75                     | 75 波特                         |
 +---------------------+-----------------------------------------+
 | B110                   | 110 波特                                      |
 +---------------------+-----------------------------------------+
 | B134                   | 134 波特                                      |
 +---------------------+-----------------------------------------+
 | B150                   | 150 波特                                      |
 +---------------------+-----------------------------------------+
 | B200                   | 200 波特                                      |
 +---------------------+-----------------------------------------+
 | B300                   | 300 波特                                      |
 +---------------------+-----------------------------------------+
 | B600                   | 600 波特                                      |
 +---------------------+-----------------------------------------+
 | B1200                 | 1200 波特                                   |
 +---------------------+-----------------------------------------+
 | 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  | 硬件流控制使能                      |
 | CRTSCTS             | (並非所有硬件都支持)                |
 +---------------------+-----------------------------------------+ 

 c_cflag成員包含了兩個應該一直激活的選項,CLOCAL和 CREAD.These will ensure that your program does not
 become the 'owner' of the port to sporatic job control and hangup signals,and also that the serail
 interface driver will read incomfing data bytes.

 波特率常數(CBAUD,B9600,等)被用於沒有c_ispeed 和 c_ospeed成員的早期接口.下一小節中將使用POSIX函數設置波特率.

 不要直接去初始化c_cflag的(或其它標誌的)成員;你應該總是去使用逐位的AND,OR和NOT操作符去設置或清除成員中的位.
 不同版本的操作系統(甚至補丁)都可以不同方式地使用這些位,所以用逐位操作符將會避免你使用將在更新的串口驅動
 中使用的位標誌.

 2.2.1 設置波特率
 
 不同的系統的波特率儲存在不同的位置.早期的接口用表4中的某一個波特率常數儲存在c_cflag中,然而更新的接口則
 是使用c_ispeed和c_ospeed去儲存實際的波特率值.
 
 cfsetospeed和cfsetispeed函數用來設置termios結構中的波特率,不管是在哪一個系統接口下.一般你可以用以下的代
 碼設置波特率:
 
  listing-2 Setting the baud rate.

  stuct termios options;
  
  /* Get the current optinos for the port ...*/
  tcgetattr(fd,&options);
  
  /* Set the baud rates to 19200 ... */
  cfsetispeed(&options,B19200);
  cfsetospeed(&options,B19200);

  /* Enable the receiver and set local mode ... */
  options.c_cflag |= (CLOCAL | CREAD );

  /* Set the new options for the port ... */
  tcsetattr(fd,TCSANOW,&options);
  
 tcgetattr(3) 函數將當前串行端口的配置填充到options結構中.在我們設置波特率,激活本地模式和串行數據接收後,用
 tcsetattr(3)去設置新的配置.TCSANOW常數指定所有的更改立即生效,而不會去等待輸出的數據發送完畢和輸入的數據接收
 完畢,也可以用其它的常數去等待輸入和輸出的完成或者刷新輸入和輸出緩存.
  
 大多數系統不支持輸入與輸出的速率不等,所以爲了可移植性必須將它們設成相等的值.
 
 表5 -  tcsetattr可設置常量
 +---------------------+-----------------------------------------+
 | 常量                   | 說明                                             |
 +---------------------+-----------------------------------------+
 | TCSANOW        | 不等數據傳輸完全就改變|
 +---------------------+-----------------------------------------+
 | TCSADRAIN     | 等待所有數據傳輸結束     |
 +---------------------+-----------------------------------------+
 | TCSAFLUSH    | 清空輸入輸出緩衝區並設置|
 +---------------------+-----------------------------------------+

 2.2.2 設置字符的大小
 
 不象波特率,設置字符大小沒有可用的函數.你不得不用位掩碼去設置它們.字符大小用位指定:
 
  options.c_cflag &= ~CSIZE; /* mask the character size bits */
  options.c_cflag |= CS8;  /* select 8 data bits */
 
 2.2.3 設置奇偶校驗位

 類似字符大小,你必須手工激活奇偶校驗和設置校驗類型.UNIX串行驅動支持偶,奇和無校驗位.space(0)將用更好的方式去
 表示.

 .NO parity(8N1):
 
 options.c_cflag &= ~PARENB;
 options.c_cflag &= ~CSTOPB;
 options.c_cflag &= ~CSIZE;
 options.c_cflag |= ~CS8;

 .Even parity(7E1):
 
 options.c_cflag |= PARENB;
 options.c_cflag &= ~PARODD;
 options.c_cflag &= ~CSTOPB;
 options.c_cflag &= ~CSIZE;
 options.c_cflag |= CS7;
  
 .Odd Parity(7O1):
 
 options.c_cflag |= PARENB;
 options.c_cflag |= PARODD;
 options.c_cflag &= ~CSTOPB;
 options.c_cflag &= ~CSIZE;
 options.c_cflag |= CS7;

 .Space parity is setup the same as no parity(7S1):
 
 options.c_cflag &= ~PARENB;
 options.c_cflag &= ~CSTOPB;
 options.c_cflag &= ~CSIZE;
 options.c_cflag |= CS8;

 2.2.4 設置硬件流控制
 
 一些版本的UNIX支持使用CTS(Clear to Send)和RTS(Requeut to send)信號線的硬件流量控制.如果你的系統中定義了
 CNEW_RTSCTS或者CRTSCTS常數,那麼就可能支持硬件流量控制.以下可以激活硬件流量控制:
 
  options.c_cflag |= CNEW_RTSCTS; /* also called CRTSCTS */
  
 類似地可以這樣關閉硬件流量控制:
 
  options.c_cflag &= ~CNEW_RTSCTS;
  

 2.3 本地選項
  
 本地模式c_lflags控制驅動程序如何管理輸入字符.一般而言,你可以配置c_lflag爲規範或者原始輸入.
 
 表6 -  c_lflag成員可設置常量
 +---------------------+-----------------------------------------+
 | 常量                   | 說明                                             |
 +---------------------+-----------------------------------------+
 | ISIG                     | 將SIGINTR, SIGSUSP, SIGDSUSP
                 |和  SIGQUIT信號使能            |
 +---------------------+-----------------------------------------+
 | ICANON             | 標準(canonical)輸入模式使能
   (否則是原始[raw]方式)          |
 +---------------------+-----------------------------------------+
 | XCASE               | 映射大寫字母爲 /小寫字母(此常量已過時)  |
 +---------------------+-----------------------------------------+
 | ECHO                | 輸入字符回顯使能                |
 +---------------------+-----------------------------------------+
 | ECHOE               | 刪除字符回顯爲BS-SP-BS使能 |
 +---------------------+-----------------------------------------+
 | ECHOK               | kill字符後回顯NL(新行)字符 |
 +---------------------+-----------------------------------------+
 | ECHONL            | 回顯NL字符                            |
 +---------------------+-----------------------------------------+
 | NOFLUSH         | 取消中斷或退出字符後清空輸入緩衝區|
 +---------------------+-----------------------------------------+
 | IEXTEN              | 擴展功能使能                         |
 +---------------------+-----------------------------------------+
 | ECHOCTL         | 回顯控制字符爲"^字符", 刪除字符爲"~?"|
 +---------------------+-----------------------------------------+
 | ECHOPRT         | 回顯被刪除的字符爲字符刪除|
 +---------------------+-----------------------------------------+
 | ECHOKE              | 在行kill字符時回顯全行BS-SP-BS|
 +---------------------+-----------------------------------------+
 | FLUSHO            | 清空輸出                                   |
 +---------------------+-----------------------------------------+
 | PENDIN              | 在下一個讀入或輸入字符
                                                  時重打入等待輸入 數據   |
 +---------------------+-----------------------------------------+
 | TOSTOP            | 在後臺輸出時發送SIGTTOU信號|
 +---------------------+-----------------------------------------+
 
 2.3.1 選擇規範輸入
 規範輸入是面向行的輸入方式.輸入字符被放進用於和用戶交互可以編輯的緩存區中,直到讀入回車鍵或者換行符才結束.
 當選擇這種模式時,你一般可以使用ICANON,ECHO和ECHOE幾個選項:
 
  options.c_lflag |= ( ICANON | ECHO | ECHOE );

 2.3.2 選擇原始輸入
  原始輸入是未經處理的.當接收時,輸入的字符在它們被收到後立即被傳送.使用原始輸入時,一般你可以取消選擇
  ICANOON,ECHO,ECHOE和ISIG選項:
  options.c_lflag &= ~ ( ICANON | ECHO | ECHOE );

 2.3.3 輸入回顯的注意事項
  輸入模式c_iflag控制端口接收端的字符輸入處理.類似於c_cflag,儲存在c_iflag中的值是所需選項的逐位或.
 
 2.4 輸入選項

  Table 7

 2.4.1 設置輸入奇偶選項

 當你激活c_cflag中的奇偶檢驗後,你應該激活輸入的奇偶檢驗.與之相關的常數有INPCK,IGNPAR,PARMRK和ISTRTP.一般你
 將選擇INPCK和ISTRTP去激活檢驗和移除奇偶位:
 
  options.c_iflag |= (INPCK | ISTRIP );
  
 IGNPAR有些危險,它告訴串行驅動忽略奇歐錯誤,繼續傳送字符就好像沒有錯誤發生一樣.這可能對檢驗通訊鏈路的質量
 有幫助,當一般情況下由於實際原因而不使用.

 PARMRK導致奇偶錯誤在輸入流中用特殊字符被標記.如果IGNPAR被激活,在每一個奇偶錯誤發生的字符之前,NULL字符
 (000八進制)將被髮送給你的程序.否則,DEL(177八進制)和 NUL字符將和錯誤的字符一起被髮送.

 2.4.2 設置軟件流控制
 
 軟件流控制可以用IXON,IXOFF和IXANY常數來激活:
 
  options.c_iflag |= (IXON | IXOFF | OXANY );
  
 取消它僅需將那些位掩住:
 
  options.c_iflag &= ~(IXON | IXOFF | OXANY );

  IXON(起始數據)和XOFF(終止數據)在以下介紹的c_cc數組中被定義

 2.5 輸出選項
 
 c_oflag包含了輸出過濾選項.類似於輸入模式,你可以選擇已預處理或者原始的數據輸出.
 
 Table 8
  
 2.5.1 選擇預處理的輸出
 
 可以通過設置OPOST選項來選擇預處理的輸出:
 
  options.c_oflag |= OPOST;
  
 在所有各種選項中,你也許僅僅可以使用ONLCR選項,它將新行映射成CR和LF兩個符號. 
 其它的輸出選項主要是由於過去的行式打印機和終端的速率不能跟上串行數據流.

 2.5.2 選擇原始輸出
 
 原始輸出可以用c_oflag的OPOST選項
 
  options.c_oflag &= ~OPOST;
  
 當OPOST選項取消後,所有c_oflag的其它選項位都忽略.


 2.6 控制字符
 
 c_cc字符數組包含了過時參數和控制字符的定義.數組中的每一個元素都用常數定義.
 
 Table 9

 2.6.1 設置軟件流控制字符
 
 c_cc數組中的VSTART 和 VSTOP是勇於軟件流控制的字符.一般而言,它們應該被設置成
 DC1(021八進制)和DC3(023八進制),分別表示ASCII碼中的XON和XOFF字符.

 2.6.2 設置讀超時
  
 UNIX串行接口驅動提供了對字符和數據包的讀超時控制.c_cc數組中的VMIN和VTIME就是
 這樣的兩個控制字符.在規範的輸入模式下或者當NDELAY選項在對文件操作的open或fcntl
 被指定時,這兒的超時控制就被忽略而使用無效.

 VMIN指定了最少讀取的字符數.如果被設置爲零,那麼VTIME值就指定了讀取每個字符的等待
 時間.注意的是,這並不意味着讀取N字節的read系統調用將會等待到讀取N個字符爲止.相反,
 這個超時控制僅僅用於第一個字符,read系統調用將立即返回有效的字符數(倚賴於read請求
 的字符數).
 
 /*------這地方沒明白,好像是如果你請求了n個字節,這樣設置的話,當讀到一個字
 節的話,read立即返回,並返回爲n,也許這種情況下,多用於1個字節的超時讀取,n=1.所以這種i
 情況下讀取n個字節的話,應該for(int i = 0;i< n; i++) read(fd,c,1);
 如果是這樣的話,原文這樣寫就好懂多了.
 Rather,the timeout will apply the first charater and the read call will return one
 or zero,at most one byte.So if you want to read n bytes,you should loop again,which
 is up to the number you want to request.
 */
 
 如果VMIN非零,VTIME指定了讀取第一個字符的等待時間,如果在給定時間內讀取了一個字符,那麼
 read系統調用將阻塞直到所有VMIN指定的字符數被讀取.也就是說一旦讀到第一個字符,串行接口
 驅動將期望收到完整的VMIN長度的數據包.如果在給定的時間內沒有沒有讀到任何字符.read調
 用將會返回爲零.這種設置要求串行驅動準確地讀取N字節的字符,read調用要麼返回爲零或者N.
 但是,超時僅僅指定在讀取第一個字符,所以如果由於某種原因,驅動丟失了N字節數據包中的一個
 字符,那麼read調用將會永久性地阻塞在這兒,等待更多的輸入字符.

 VTIME是以1/10秒爲單位指定接收字符的超時時間.如果VTIME設置爲零(缺省情況下),而該端口又沒
 有用open或fcntl設置NDELAY,read將會阻塞不確定的時間.

 
第三章 Modem通訊
 這一章描述了撥號電話調制解調器通訊的基本知識.並給出了一個使用標準AT指令集的MODEM範例.
 
 3.1 什麼是Modem
 
 MODEMs是一個將串行數據調製可以在模擬數據鏈路上傳輸的一定頻率的設備,比如電話線或者有線電
 視線 . 一個標準的電話調製解調將串行數據轉換成可以可以在電話線上傳輸的音調;由於這種轉換
 的速度和複雜,這些音調聽起來更象大聲的尖叫.

 現在的電話MODEMs可以以每秒53,000位,也就是53kbps的速度在電話線上傳輸數據,另外大多數的MODEMs
 使用數據壓縮技術提高傳輸速率,有些類型的數據可以超過100kbps.

 3.2 與MODEM通訊
 
 第一步是打開並配置原始輸入的端口
 Listing 3 配置原始輸入端口
  int fd;
  struct termios options;
  
  /*打開端口*/
  fd = open("/dev/ttyf1",O_RDWR|O_NOCTTY|O_NDELAY);
  
  fcntl(fd,F_SETFL,0);
  
  /*獲取當前選項*/
  tcgetattr(fd,&options);
  
  /*設置原始輸入,1秒的超時*/
  options.c_cflag |= (CLOCAL|CREAD);
  options.c_lflag &= ~(ICANON | ECHO | ECHOE |ISIG);
  optiions.c_oflag &= ~OPOST;
  options.c_cc[VMIN] = 0;
  options.c_cc[VTIME] = 10;

  /*設定選項*/
  tcsetattr(fd,TCSANOW,&options);

 下一步是建立預MODEM的通訊.最好的方法是發送AT命令給MODEM.這也可以讓MODEM去探測你
 將使用的波特率.當MODEM打開併成功地連接上後,它將回應"OK".

  Listing 4 初始化MODEM
  int   /* 返回值 0 = 成功, -1 = 失敗 */
  init_modem(int fd) /* 串行端口文件描述符 */
  {
   char buffer[255]; /* 輸入緩衝區 */
   char *bufptr;  /* 緩存區中的當前字符 */
   int  nbytes;  /* 讀取的字節數 */
   int tries;  /* 目前重試的次數 */
   
   for(tries = 0; tries < 3 ;tries ++)
   {
    /*發送AT命令,後跟一個CR */
    if(write(fd,"AT/r",3) < 3)
     continue;

    /*將字符讀入字符串緩衝中,直到一個新行 */
    bufptr = buffer;
    while( ( nbytes = read(fd,bufptr,buffer+sizeof(buffer) - bufptr -1 )) > 0)
    {
     bufptr += nbytes;
     if( bufptr[-1] == '/n' || bufptr[-1] == '/r' )
      break;
    }

    /*設置 NULL中止字符串,看我們是否得到了一個成功的迴應 */
    *bufptr = '/0';
    if( strncmp(buffer,"OK",2) == 0)
     return (0);
   }
   return (-1);
  }   
    
 3.3 標準的MODEM命令
 
 大多數的MODEM支持AT命令集,所以每個命令前冠以"AT".每個命令按照以下根式被髮送:AT+指定的命令+回車字符
 (CR,015八進制).在處理命令之後,MODEM將會根據命令迴應相應的文本消息.

 3.3.1 ATD ----撥單個數字
 
 ATD命令撥指定的數字.除了數字和長劃符號外,你可以指定音調("T"),脈衝("P"),每秒鐘的暫停(",")和等待撥號音("W"):
 ATDT 555-1212
 ATDT 18009009009W1234,1,1234
 ATD  T555-1212WP1234
 
 MODEM的迴應消息有以下幾種:
 NO DIALTONE
 BUSY
 NO CARRIER
 CONNECT
 CONNECT baud

 3.3.2 ATH---掛起
 
 ATH命令導致MODEM掛起.因爲MODEM必須是在"命令"模式中,你也許在普通的電話呼叫中不使用它.

 如果DTR被dropped,大多數MODEM也會掛起.你可以通過在至少一秒的時間中設置波特率爲0實現這一點.DroppingDTR也會讓MODEM
 回到命令模式中.
 
 在一次成功的掛起之後,MODEM將會迴應"NO CARRIER".如果MODEM仍然是被連接的,"CONNECT"或者"CONNECT baud"消息將會被髮送.

 3.3.3 ATZ---重置MODEM命令
 
 ATZ命令將重置MODEM.MODEM將回應"OK".

 3.4 一般MODEM通訊的問題
 
 首先最重要的是,不要忘記取消輸入回顯.輸入回顯將會造成MODEM和機器之間的一次反饋.
 
 其次,當發送MODEM命令時,你必須以回車CR來結束它,而不是一個新行NL.C語言中CR的字符是'/r'.

 最後,當使用MODEM時,取保你使用的是MODEM所支持的波特率.儘管許多MODEM可以自動波特率探測,但一些仍然有19.2kbps的限制,
 不得不注意.
 

 

發佈了5 篇原創文章 · 獲贊 4 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章