本文是學(xué)習(xí)linux串口編程必讀文章,網(wǎng)上的linux串口程序大多參照此文寫成。 原文地址是:http://digilander./robang/rubrica/serial.htm 有興趣的可以看看原文。
posix 操作系統(tǒng)串口指引(Serial Programming Guide for POSIX Operating Systems) 5th Edition Michael R. Sweet Copyright 1994-1999, All Rights Reserved.
簡介
posix 操作系統(tǒng)串口編程指南將教你如何在您的UNIX ?工作站或PC機(jī)成功,高效, 便攜的編寫串口程序。每章提供的編程示例使用的POSIX (可移植的UNIX標(biāo)準(zhǔn))終端控制功能,并只要經(jīng)過極少數(shù)的修改就可以運(yùn)行在IRIX ? , HP - UX, SunOS ? ,以及Solaris ? ,Digital UNIX ? , Linux? ,和大多數(shù)其他的類UNIX操作系統(tǒng)。操作系統(tǒng)的最大的不同是用于串口設(shè)備和鎖定的文件的文件名。
這個指南有下面幾章和附錄組成
第一章 串口編程基礎(chǔ)
第二章 配置串口
第三章 調(diào)制解調(diào)器通信
第四章 高級串口編程
附錄A RS-232引腳
附錄B ASCII控制流
----------------------------------------------------------------------
第一章 串口編程基礎(chǔ)
本章介紹串口通信,RS232和用語大多數(shù)計(jì)算機(jī)的其他標(biāo)準(zhǔn)以及如何用c程序訪問串行口。
什么是串口通信?
計(jì)算機(jī)可以每次傳送1bit或者nbit信息(數(shù)據(jù)),串口指每次只傳送1bit數(shù)據(jù)。串口通信包括多種網(wǎng)絡(luò)設(shè)備:鍵盤,鼠標(biāo),貓,和終端。
當(dāng)用串口傳送一個字(即字符或字節(jié))數(shù)據(jù),你接收和發(fā)送時每次傳送一個bit.每個bit是1或者0.
串口的數(shù)據(jù)傳輸速率大都是以bit/s(bps)或者波特率(baud)表示1和0在每秒內(nèi)傳輸?shù)膫€數(shù)?;氐接?jì)算機(jī)開始的時代,300baud被認(rèn)為是很快的,但現(xiàn)今電腦應(yīng)用RS232速率可以達(dá)到43800baud(波特)。當(dāng)波特率超過1000,你將經(jīng)??匆娝俾蕿榍oud,或者kbps(例如9.6k,19.2k等).當(dāng)速度在1,000,000以上表示為megabaud或者M(jìn)bps(例如1.5Mbps)。
當(dāng)談到串口設(shè)備和端口,他們被稱為數(shù)據(jù)通信設(shè)備(DCE)或者數(shù)據(jù)終端設(shè)備(DTE)。他們之間的區(qū)別很簡單-每對傳輸信號和接收,是交換。當(dāng)兩個DTE或者兩個DCE設(shè)備連接在一起,一個無貓串口電纜或者適配器被用作交換信號對。
什么是 RS-232?
RS-232是電氣工業(yè)組織(EIA)制定的為串口通信的電氣化接口標(biāo)準(zhǔn)。RS-232實(shí)際上分三個等級(A,B,C),他們每個有不同的高低電壓標(biāo)準(zhǔn)。填充應(yīng)用最廣泛的是RS-232C,他定義邏輯1的電壓在-3V到-12V,邏輯0的電壓在+3V到+12V。RS-232規(guī)格上講這些信號在25英尺后就無法使用。你通??梢酝ㄟ^降低波特率將數(shù)據(jù)傳得更遠(yuǎn)。
除了電纜進(jìn)出數(shù)據(jù),還有其他時間,狀態(tài),握手:
表1 - RS-232 引腳 管腳 描述
1 地
2 TXD-傳輸數(shù)據(jù)
3 RXD-接收數(shù)據(jù)
4 RTS-要求發(fā)送
5 CTS-清除待發(fā)
6 DSR-數(shù)據(jù)設(shè)置完成
7 GND-邏輯地
8 DCD-數(shù)據(jù)載波檢測
9 預(yù)留
10 預(yù)留
11 未分配
12 備用DCD
13 備用CTS
14 備用 TXD
15 傳輸時鐘
16 備用 RXD
17 接收時鐘
18 未分配
19 備用 RTS
20 DTR-數(shù)據(jù)終端準(zhǔn)備
21 信號質(zhì)量檢查
22 環(huán)路檢查
23 數(shù)據(jù)速率選擇
24 發(fā)送時鐘
25 未分配
有兩種接口標(biāo)準(zhǔn)RS-422和RS-422。RS-422用更低的電壓和不同信號允許線纜長度上限為1000英尺(300米)。RS-574定義9引腳的串口連接和電壓。
信號定義
RS-232標(biāo)準(zhǔn)定義串口中18中不同的信號。在這些中僅有6個是在UNIX環(huán)境中可用。
GND - 邏輯地
邏輯地不是信號,辦事沒有它卻沒有辦法操作信號。基本上,邏輯地是一個參考電壓已讓電子管知道那些事正的和負(fù)的。
TXD- 傳輸數(shù)據(jù)
TXD信號載波數(shù)據(jù)從你的工作站到另一端的計(jì)算機(jī)或者設(shè)備(如貓)。一個高電平表示1,一個空電平表示0.
RXD- 接收數(shù)據(jù)
RXD載波信號將電腦或設(shè)備傳輸?shù)焦ぷ髡?。像TXD,高低電平分別表示1和0.
DCD-數(shù)據(jù)載波檢測
DCD信號接收來自電腦或者其他串口電纜。一個低電平在信號線上表示設(shè)備和電腦正在連接。DCD通常沒有用。
DTR- 數(shù)據(jù)終端準(zhǔn)備
DTR信號是工作站產(chǎn)生兵告訴計(jì)算機(jī)或者另一端的設(shè)備你正在準(zhǔn)備(空電平)或者沒有準(zhǔn)備(高電平)。DTR能夠自動運(yùn)行當(dāng)你打開在工作站上的串口。
CTS- 清除發(fā)送
CTS信號從其他串口電纜接收數(shù)據(jù)。
空電平表示已經(jīng)準(zhǔn)備從工作站發(fā)送串口數(shù)據(jù)。CTS通常用來控制從工作站到終端的串行數(shù)據(jù)流。
RTS- 請求發(fā)送
TRS信號設(shè)置低電平被工作站用來表示更多的數(shù)據(jù)準(zhǔn)備發(fā)送。
像CTS,RTS輔助控制從工作站到計(jì)算機(jī)和另一端的串口設(shè)備的數(shù)據(jù)流。大部分工作站始終設(shè)置這種信號為低電平。
異步通信
對于計(jì)算機(jī)要知道什么時候串行數(shù)據(jù)傳送進(jìn)來,他需要一些方法決定從那個字符開始從哪里結(jié)束和下一個起始點(diǎn)。本指南專門處理異步串行數(shù)據(jù)。
在異步模式下串口數(shù)據(jù)線保持高電平直到出現(xiàn)一個字符被傳送。一個開始位之前每個字符和后邊緊跟的位的字符性質(zhì),一個奇偶校驗(yàn)位,和一個或者多個停止位。開始位通常是一個空電平然后告訴電腦新的串行數(shù)據(jù)有用。數(shù)據(jù)然后被發(fā)送或者任何時間接收,因此稱為異步。
Figure 1 - Asynchronous Data Transmission
奇偶校驗(yàn)位是這個數(shù)據(jù)位的和表示這些數(shù)據(jù)包含一個偶數(shù)或奇數(shù)個1。偶校驗(yàn),奇偶校驗(yàn)位是0如果在字符里1的數(shù)目里是偶數(shù)0;奇校驗(yàn),奇偶校驗(yàn)位是0如果數(shù)據(jù)中1的數(shù)目是1.你也許聽過低電平校驗(yàn),高電平校驗(yàn),無奇偶校驗(yàn)。低校驗(yàn)是指奇偶校驗(yàn)位是全0,高校驗(yàn)指得是這些位全為1。無校驗(yàn)是指無校驗(yàn)位表示或者傳輸。 剩余的位被稱為停止位。
在連個字符間可以為1,1.5,或者2停止bits并且他們的值為1.停止位以前被用來給計(jì)算機(jī)時間處理前邊的字符,但是現(xiàn)在只是為異步解釋計(jì)算機(jī)接收字符服務(wù)。
異步數(shù)據(jù)格式通常表示為"8N1", "7E1",和往前。這些分別代表“8位數(shù)據(jù)位,無奇偶校驗(yàn),1bit停止位”和“7位數(shù)據(jù)位,偶校驗(yàn),1bit停止位”。
什么是全雙工和半單工?
全雙工是指計(jì)算機(jī)能夠同時接收和傳輸數(shù)據(jù)-有兩個單獨(dú)的數(shù)據(jù)信道(一進(jìn)一出)。
半雙工是指計(jì)算機(jī)不能同時接收和傳輸數(shù)據(jù)。通常這意味著只有單一的信道通信。但是并不是所有的RS232信號都不使用。然而,他通常指得是通信鏈接用其他標(biāo)準(zhǔn)而不是RS232,RS232并不支持全雙工操作。
流控
當(dāng)在倆個串口間傳輸數(shù)據(jù)控制數(shù)據(jù)流是必須的。這是因?yàn)橄拗浦虚g串口通信連接,一個是串口,或者另外一些是存儲媒體。兩種方法是常用的異步通信數(shù)據(jù)。
第一個方法是通常被稱為“軟件”流控和應(yīng)用專用字符開始(XON or DC1, 021 octal)或者停止(XOFF or DC3, 023 octal)數(shù)據(jù)流。這些字符定義在美國交換信息標(biāo)準(zhǔn)編碼(ASCII).雖然這些代碼傳輸文本信息很有用,但是他們在沒有特殊程序轉(zhuǎn)換的時候不能使用。
第二種方法被稱為“硬件”控制流,應(yīng)用RS-232CTS和RTS信號代替特殊字符。接受者當(dāng)它準(zhǔn)備接受更多數(shù)據(jù)的時候設(shè)置CTS為低電壓,同樣的,還沒有準(zhǔn)備傳輸別的數(shù)據(jù)的時候,傳送者設(shè)置RTS為低電平。因?yàn)橛布骺赜靡唤M分離的信號,他比軟流控更快,同樣需要發(fā)送和接收多位信心做同樣的事情。CTS/RTS流控并非被所有的硬件和操作系統(tǒng)支持。
什么是中斷?
通常一個接收或者傳輸數(shù)據(jù)信號保持在高電平知道一個新的字符傳輸。如果信號下降到低電平很長時間,常常是1/4,1/2秒,然后可以說產(chǎn)生中斷。
一個中斷有時候用來重啟一個通信線路或者改變操作系統(tǒng)的通信模式例如貓。第三章,調(diào)制解調(diào)器涵蓋這些更加高級的應(yīng)用。
同步通信
不像異步通信,同步通信表現(xiàn)為一個恒定的數(shù)據(jù)流。讀取線上的數(shù)據(jù),電腦必須提供或者接收一個同步時鐘以至發(fā)送和接收同步。
甚至這樣的同步,電腦必須用某種方法標(biāo)記開始數(shù)據(jù)。最常見的做法是用一個數(shù)據(jù)協(xié)議包例如串行數(shù)據(jù)鏈路控制(SDLC)或者高速數(shù)據(jù)鏈路控制(HDLC).
每個協(xié)議定義確定的位序列來表示數(shù)據(jù)包的開始和結(jié)束。每個協(xié)議也定義一個bit序列,用在沒有數(shù)據(jù)的時候;這些序列允許電腦開始查看開始的數(shù)據(jù)包。
因?yàn)橥絽f(xié)議沒有用每個字符同步位,他們通常提供至少25%的改進(jìn)比異步通信,并且更加適合遠(yuǎn)程網(wǎng)絡(luò)和配置更多的串行口。
盡管同步通信的高速率優(yōu)點(diǎn),但是大多數(shù)RS-232硬件并不支持,因?yàn)檫€需要額外的硬件和軟件。
接入串行口
像所有的設(shè)備一樣,UNIX提供接入串口的設(shè)備文件。要接入串口只需有打開這些相應(yīng)的設(shè)備文件。 串口文件 每個串口在UNIX系統(tǒng)上有一個或多個設(shè)備文件(文件在 /dev 目錄)與它有關(guān):
表 2 - 串口和設(shè)備文件
system port1 prot2
IRIX? /dev/ttyf1 /dev/ttyf2
HP-UX /dev/tty1p0 /dev/tty2p0
Solaris?/SunOS? /dev/ttya /dev/ttyb
Linux? /dev/ttyS0 /dev/ttyS1
Digital UNIX? /dev/tty01 /dev/tty02
打開一個串口
因?yàn)橐粋€串口是一個文件,所以open()函數(shù)被用來接入它。一個問題是UNIX設(shè)備文件普通用戶通常不能接入。工作區(qū)包括接口的權(quán)限到文件的問題,運(yùn)行你的程序作為超級用戶(root),或者設(shè)置程序的用戶名以讓普通用戶也能運(yùn)行設(shè)備文件。
現(xiàn)在我們假設(shè)文件已經(jīng)可以讓所有用戶使用。這個代碼打開串口1工作區(qū)在IRIX操作系統(tǒng)上:
Listing 1 - 打開串口
#include /* 標(biāo)準(zhǔn)輸入/輸出定義 */
#include /* 串函數(shù)定義 */
#include /* UNIX 標(biāo)準(zhǔn)函數(shù)定義*/
#include /* 文件控制定義 */
#include /* 錯誤數(shù)定義 */
#include /* POSIX 終端控制定義 */
/* * 'open_port()' - 打開串口 1. * * 返回文件描述成功或者 -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);
}
其他的操作系統(tǒng)將需要相應(yīng)的設(shè)備文件名,但是其他的代碼是一樣的。
開放操作
你將發(fā)現(xiàn)當(dāng)你打開設(shè)備文件,我們通常使用兩個標(biāo)記除了read+write模式:
fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);
O_NOCTTY標(biāo)志告訴UNIX這個程序不想成為“控制終端”控制的端口,如果你不能明確說明這個,那么任何輸入(像鍵盤中斷信號等)將會影響你的程序。程序例如getty(1M/8)應(yīng)用這個特征當(dāng)開始登陸程序,但是通常一個用戶程序不希望這個問題。
O_NDELAY標(biāo)志告訴UNIX這個程序不關(guān)心DCD信號線狀態(tài)-是否其他的端口是否運(yùn)行。如果不指定這個標(biāo)號,你的程序?qū)贒CD信號線是低電平時停止。
寫數(shù)據(jù)到端口
寫入數(shù)據(jù)到端口很簡單-只要應(yīng)用write() 系統(tǒng)回叫發(fā)送數(shù)據(jù)給它:
n = write(fd, "ATZ\r", 4);
if (n < 0)
fputs("write() of 4 bytes failed!\n", stderr);
write函數(shù)返回位的數(shù)值或者當(dāng)錯誤時為-1。通常這個錯誤只有當(dāng)一個調(diào)制解調(diào)器或者數(shù)據(jù)連接丟數(shù)據(jù)載波檢測行時,你將運(yùn)行到EIO 。這個情況將會維持直到你釋放端口。
讀端口數(shù)據(jù)
讀端口數(shù)據(jù)有點(diǎn)麻煩。當(dāng)你操作這個端口在原始數(shù)據(jù)模式,每個read()系統(tǒng)回叫將返回串口輸入緩存實(shí)際提供的無論多少字符。如果沒有字符可用,回叫將會阻塞(等待)直到字符進(jìn)入,一個間隔計(jì)時器失效,或者錯誤發(fā)送。read函數(shù)將會立即返回通過下面這樣做:
fcntl(fd, F_SETFL, FNDELAY);
FNDELAY選項(xiàng)使read函數(shù)返回0,當(dāng)沒有字符在端口上。存儲正常(阻塞)動作,沒有FNDELAY選項(xiàng)則回叫fcntl():
fcntl(fd, F_SETFL, 0);
這個同樣應(yīng)用在操作 O_NDELAY選項(xiàng)打開串口。
關(guān)閉串口
關(guān)閉串口,使用close系統(tǒng)調(diào)用:
close(fd);
關(guān)閉一個串口將會也設(shè)置DTR信號低電平,這會把調(diào)制解調(diào)掛起。
---------------------------------------------------------------------------------------第一章完(待續(xù))
第二章 配置串口
這章討論如何配置串口從C用POSIX終端接口。
POSIX終端接口
大多數(shù)系統(tǒng)支持POSIX終端(串)接口改變參數(shù)例如波特率,字符大小,等。首先你需要做的是包含頭文件 <termios.h>; 這個文件定義終端控制結(jié)構(gòu)和POSIX控制函數(shù)。
最重要的POSIX函數(shù)是tcgetattr(3) 和 tcsetattr(3),這兩個分別表示獲取和設(shè)置終端屬性。你提供一個指針到包含可用的所有串口選項(xiàng)控制臺結(jié)構(gòu):
表3-控制臺結(jié)構(gòu)成員
Member Description
c_cflag 控制選項(xiàng)
c_lflag 現(xiàn)行選項(xiàng)
c_iflag 輸入選項(xiàng)
c_oflag 輸出選項(xiàng)
c_cc 控制字符
c_ispeed 輸入波特率 (new interface)
c_ospeed 輸出波特率(new interface)
控制選項(xiàng)
c_cflag成員控制波特率,一些數(shù)據(jù)位,奇偶校驗(yàn),停止位和硬件控制流。有常數(shù)為所有的支持配置。
Table 4 - c_cflag 成員常數(shù)描述
常數(shù) 描述
CBAUD Bit mask for baud rate
B0 0 baud (drop DTR)
B50 50 baud
B75 75 baud
B110 110 baud
B134 134.5 baud
B150 150 baud
B200 200 baud
B300 300 baud
B600 600 baud
B1200 1200 baud
B1800 1800 baud
B2400 2400 baud
B4800 4800 baud
B9600 9600 baud
B19200 19200 baud
B38400 38400 baud
B57600 57,600 baud
B76800 76,800 baud
B115200 115,200 baud
EXTA External rate clock
EXTB External rate clock
CSIZE Bit mask for data bits
CS5 5 data bits
CS6 6 data bits
CS7 7 data bits
CS8 8 data bits
CSTOPB 2 stop bits (1 otherwise)
CREAD Enable receiver
PARENB Enable parity bit
PARODD Use odd parity instead of even
HUPCL Hangup (drop DTR) on last close
CLOCAL Local line - do not change "owner" of port
LOBLK Block job control output
CNEW_ RTSCTS
CRTSCTS Enable hardware flow control (not supported on all platforms)
c_cflag成員包含兩個選項(xiàng)應(yīng)該常常應(yīng)用,CLOCAL和CREAD.這將確保你的程序不被其他端口控制和掛起信號干擾,同時串口接口驅(qū)動將讀取進(jìn)入的數(shù)據(jù)。
波特率常量 (CBAUD, B9600, etc.) 是用來為更老的接口,他們沒有c_ispeed和c_ospeed 成員。參考下一節(jié)關(guān)于POSIX函數(shù)信息來設(shè)置波特率。
沒有直接初始化 c_cflag (或者其他flag)成員;你應(yīng)該常常使用逐位與,或,非操作來設(shè)置或者清空成員位。不同的操作系統(tǒng)版本(和甚至補(bǔ)?。?yīng)用各不相同,因此用位操作將會防止你使用錯誤的位標(biāo)志,這些位標(biāo)志需要更新一個串口驅(qū)動。
設(shè)置波特率
不同的操作系統(tǒng)波特率保存在不同的地方。老的接口依靠表4中的波特率常數(shù)存儲波特率c_cflag成員,然而更新的應(yīng)用提供c_ispeed和c_ospeed 成員,他們包含實(shí)際的波特率數(shù)值。
cfsetospeed(3) 和cfsetispeed(3)函數(shù)不論在何種操作系統(tǒng)接口提供設(shè)置到控制臺的波特率。典型的你最好應(yīng)用下邊的代碼設(shè)置波特率:Listing 2 - 設(shè)置波特率
struct termios options;
/*
* 得到現(xiàn)在端口的選項(xiàng)。。。。。。
*/
tcgetattr(fd, &options);
/*
*設(shè)置波特率為19200。。。。
*/
cfsetispeed(&options, B19200);
cfsetospeed(&options, B19200);
/*
* 啟動接收者和設(shè)置本地模式。。。。。。
*/
options.c_cflag |= (CLOCAL | CREAD);
/*
* 設(shè)置新的端口選項(xiàng)。。。。。。。
*/
tcsetattr(fd, TCSANOW, &options);
tcgetattr(3) 函數(shù)裝滿你提供的串口配置到控制臺。然后我們設(shè)置波特率和讓本地模式運(yùn)行和串口數(shù)據(jù)接收,我們用tcsetattr(3).選擇新的配置。TCSANOW 常量規(guī)定所以的改變應(yīng)該立即執(zhí)行而不等待數(shù)據(jù)傳送和接收結(jié)束。有其他的常量來等待輸入和輸出完成或者刷新輸入輸出緩存。
大多數(shù)系統(tǒng)不支持不同的輸入輸出速率,所以要保證設(shè)置相同的值保證最大的可移植性。
Table 5 - Constants for tcsetattr
Constant Description
TCSANOW Make changes now without waiting for data to complete
TCSADRAIN Wait until everything has been transmitted
TCSAFLUSH Flush input and output buffers and make the change
設(shè)置字符大小
不像波特率,沒有方便的函數(shù)來設(shè)置字符大小。你必須做一個小的位掩碼設(shè)置。字符的尺寸在位中指定:
options.c_cflag &= ~CSIZE; /* 字符大小的位掩碼*/
options.c_cflag |= CS8; /* 選中8位數(shù)據(jù)位*/
設(shè)置奇偶校驗(yàn)
Like the character size you must manually set the parity enable and parity type bits. UNIX serial drivers support even, odd, and no parity bit generation. Space parity can be simulated with clever coding.
像字節(jié)大小一樣你必須手動設(shè)置奇偶校驗(yàn)使能和校驗(yàn)類型。UNIX串口驅(qū)動支持奇數(shù),偶數(shù),和無校驗(yàn)位系列??招r?yàn)?zāi)苡么a仿真。
無校驗(yàn)(8N1):
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
奇校驗(yàn)(7E1):
options.c_cflag |= PARENB
options.c_cflag &= ~PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
偶校驗(yàn)(7O1):
options.c_cflag |= PARENB
options.c_cflag |= PARODD
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
空校驗(yàn)設(shè)置和無校驗(yàn)相同(7S1):
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
設(shè)置硬件流控
某些版本的UNIX支持使用CTS和RTS信號線的硬件流控。如果 CNEW_RTSCTS or CRTSCTS 常數(shù)被系統(tǒng)定義然后然后硬件流控才能被支持。按照下邊能讓硬件流控:
options.c_cflag |= CNEW_RTSCTS; /* Also called CRTSCTS */
同樣, 停止硬件流控l:
options.c_cflag &= ~CNEW_RTSCTS;
本地選項(xiàng)
本地選項(xiàng)成員c——cflag控制串口驅(qū)動管理如何輸入字符。通常你能配置c_lflag 成員對標(biāo)準(zhǔn)或者原始輸入。