2015-08-13 12 views
3

我是C和驅動編程的新手。目前,我正在編程一個用戶空間驅動程序,使用Debian通過USB與RS232進行通信。在研究過程中,我遇到了下面的代碼。termios庫中的標誌如何表示?

tty.c_cflag  &= ~PARENB;   // No Parity 
tty.c_cflag  &= ~CSTOPB;   // 1 Stop Bit 
tty.c_cflag  &= ~CSIZE; 
tty.c_cflag  |= CS8;    // 8 Bits 

我明白這些線的後果,但是,這些操作將只意義,如果每個控制標誌常數(PARENB,CSTOPB等)是這些標誌的組合具有相同的長度。我似乎無法通過任何文檔(我迄今爲止對C使用的主要抱怨之一,有些難以找到容易理解的文檔)進行驗證,以確認這一點。

我想確保我正確理解程序,因爲它是一種純粹的歸納方法,我不確定爲什麼這些標記會被存儲。有人能證實這些發現,或者指出我可能忽略的一些東西嗎?

Ex。

tty.c_cflag hypothetically is 4-bits long, each of the flags from the 
previous code block corresponding to bits 3, 2, 1, 0. Then I believe the 
following is how these are stored, if we were to say flags PARENB (3) and 
CSTOPB (2) are high, and the other two flags are disabled. 

tty.c_cflag = 1100 
PARENB = 1000 
CSTOPB = 0100 
CSIZE = 0000 
CS8 = 0000 
+1

** C不是C++不是C!** – Olaf

回答

5

在C語言中,你能找到的最好的文檔是源代碼本身,你可以在/usr/include/termios.h找到您的計算機上(實際上是分佈在一個或多個在其中的包括) - 這裏是bsd based termios.h for apples我根據我的回答,值可能會改變,取決於你的Unix的味道。

在那裏,你會發現,你的tty對象是struct termios類型,定義如下:

struct termios { 
    tcflag_t c_iflag; /* input flags */ 
    tcflag_t c_oflag; /* output flags */ 
    tcflag_t c_cflag; /* control flags */ 
    tcflag_t c_lflag; /* local flags */ 
    cc_t  c_cc[NCCS]; /* control chars */ 
    speed_t  c_ispeed; /* input speed */ 
    speed_t  c_ospeed; /* output speed */ 
}; 

所以c_cflagtcflag_t類型,這是由下面這行定義的:

typedef unsigned long tcflag_t; 

unsigned long預計爲8個字節,即32位。

然後,您在示例中使用的所有標誌都被定義如下:使用8倍字節的值:

#define PARENB  0x00001000 /* parity enable */ 
#define CSTOPB  0x00000400 /* send 2 stop bits */ 
#define CSIZE  0x00000300 /* character size mask */ 
#define CS8   0x00000300  /* 8 bits */ 

如此說來,它的工作方式是,c_cflag用作比特陣列,這意味着每個位爲函數顯著。這是一種常用的方法,因爲處理能力中的位操作「便宜」(CPU可以在一個週期內執行一次操作),而在內存空間中「便宜」,而不是使用32個布爾值數組來存儲值一個大小爲1字節的布爾類型來存儲一個二進制值),你可以存儲每個字節8個二進制值。

另一個優點和優化是因爲你的CPU至少有32位,並且在2015年可能是64位,它可以在一個CPU週期中對32個值應用掩碼。

位掩碼的另一種表示是創建一個結構如下所示:

struct tcflag_t { 
    bool cignore; 
    uint8_t csize; 
    bool cstopb; 
    bool cread; 
    bool parenb; 
    bool hupcl; 
    bool clocal; 
    bool ccts_oflow; 
    bool crts_iflow; 
    bool cdtr_iflow; 
    bool ctdr_oflow; 
    bool ccar_oflow; 
}; 

這將是12個字節。要改變它們,你必須做12次操作。

然後你就可以在字節做業務遵循布爾邏輯,這是由真值表定義:

的AND(&),或(|),而不是(~)真值表:

| a | b | & | | a | b | | | | a | ~ | 
| - | - | - | | - | - | - | | 0 | 1 | 
| 0 | 0 | 0 | | 0 | 0 | 0 | | 1 | 0 | 
| 0 | 1 | 0 | | 0 | 1 | 1 | 
| 1 | 0 | 0 | | 1 | 0 | 1 | 
| 1 | 1 | 1 | | 1 | 1 | 1 | 

我們通常綽號操作員「力爲零」和或者運營商作爲「力量1」,因爲 除非兩個值都爲1將導致0,除非這兩個值都是0,將 導致1

所以,如果我們考慮到tty.c_cflag = 0x00000000並要啓用奇偶校驗:

tty.c_cflag |= PARENB; 

然後tty.c_cflag將包含0x00001000,即0b1000000000000

然後,你要設置7位大小:

tty.c_cflag |= CS7; 

and tty.c_cflag將包含0x00001200,即現在0b1001000000000


,讓我們回到你的問題:你的「等價物」的例子是不是真的代表,爲你考慮CSIZECS8不含任何價值。

因此,讓我們通過您從示例獲取的代碼獲得:

tty.c_cflag  &= ~PARENB;   // No Parity 
tty.c_cflag  &= ~CSTOPB;   // 1 Stop Bit 
tty.c_cflag  &= ~CSIZE; 
tty.c_cflag  |= CS8;    // 8 Bits 

這裏,tty.c_cflag包含未知值:

0b???????????????????????????????? 

而且你知道你想要無奇偶校驗,一個停止位,以及8位的數據大小。所以在這裏你 否定了「設置平價」值將其關閉:

~PARENB == 0b0111111111111 

,然後使用運營商,你迫使位爲零:

tty.c_cflag &= ~PARENB —→ 0b???????????????????0???????????? 

然後,你做同樣的CSTOPB

tty.c_cflag &= ~CSTOPB —→ 0b???????????????????0?0?????????? 

終於CSIZE

tty.c_cflag &= ~CSIZE —→ 0b???????????????????0?000???????? 

對於CSIZE,目標是確保重置數據長度的兩位值。 然後你樹立正確的長度,迫使以1值:

tty.c_cflag |= CS8  —→ 0b???????????????????0?011???????? 

其實,重置CSIZE00然後設置CS811是無用的,因爲 直接tty.c_cflag |= CS8這樣做將使11。但是,如果您想要從CS8更改爲CS7,並且 將只設置其中一個位,另一個位置保持原始值,那麼這是很好的做法。

最後,當你打開你的串口時,庫會檢查這些值到 配置端口,並使用默認值爲你沒有強制的所有其他值,你可以使用 你的串口。

我希望我的解釋能夠幫助您更好地瞭解串口上的標誌設置 是怎麼回事,以及完全使用位掩碼。僅供參考,同樣的原理是 用於很多其他的東西,例如IPv4網絡掩碼,文件I/O等。

+0

*「而'unsigned long'預計爲** 8 **字節,即32位。」* - Huh ??? !!! *「你知道你想要...沒有停止位」* - 不,最小規格是一個停止位。 – sawdust

+0

*「並且一個無符號的長度預計爲8個字節,即32位。」*哼,你是對的,我應該重寫數學:-) – zmo

1

宏的實際值取決於平臺(例如,在Linux上CSTOPB是定義爲0100,而在一些BSD上則爲02000)。這就是爲什麼你不應該對他們的確切值做出假設。

舉例來說,這的確是常見的CSIZECS8具有相同的價值,但在某些平臺上,他們可能沒有,因此你首先與,CSIZE面具的補充(其將所有影響字符大小的位歸零),然後對這些位的值進行或運算。如果你認爲CS8與掩碼的模式相同,你可以省略第一步,但是然後代碼會做錯誤的事情,並且在沒有任何警告的情況下以非常模糊的方式,在這個假設沒有被執行的平臺上,持有。

這裏PARENBCSTOPB個體位標記(只有一個1位)可與按位或|進行設置,並且通過按位取與&它們的互補~清除。同時,包括CS8在內的字符大小可以包含任意數量的1位,包括零 - 它們更像是存儲在較大整數的特定位中的小整數。 CSIZE的是,在該表示字符大小的所有地方具有1比特的掩模(任何的CS5CS6CS7CS8) - 該掩模可用於任一提取準確的字符尺寸(例如,以測試if ((tty.c_flag & CSIZE) == CS8)),或在設置之前清除它(這裏是tty.c_flag &= ~CSIZE)。

相關問題