Tol*_*ASP 3 c serial-port driver termios
我是C和驱动程序编程的新手.目前,我正在编写用户空间驱动程序,使用Debian通过USB与RS232进行通信.在研究时,我遇到了以下一些代码.
tty.c_cflag &= ~PARENB; // No Parity
tty.c_cflag &= ~CSTOPB; // 1 Stop Bit
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; // 8 Bits
Run Code Online (Sandbox Code Playgroud)
我理解这些行的后果,但是,如果每个控制标志常量(PARENB,CSTOPB等)与这些标志组合的长度相同,则这些操作才有意义.我似乎无法通过任何文件验证这一点(我迄今为止对C的主要不满之一,更难以找到易于理解的文档.)来证实这一点.
我想确保我正确理解程序,因为它是一种纯粹的归纳方法,我不确定为什么这些标志会被存储.有人可以验证这些发现,还是指出我可能会忽略的东西?
防爆.
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
Run Code Online (Sandbox Code Playgroud)
在C中,您可以找到的最佳文档是源代码本身,您可以在计算机上找到它/usr/include/termios.h
(实际上分布在其中的一个或多个包含中) - 这是我基于bsd的termios.h我的答案,价值可能会根据你的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 */
};
Run Code Online (Sandbox Code Playgroud)
c_cflag
类型tcflag_t
也是如此,由以下行定义:
typedef unsigned long tcflag_t;
Run Code Online (Sandbox Code Playgroud)
并且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 */
Run Code Online (Sandbox Code Playgroud)
话虽如此,它的工作方式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;
};
Run Code Online (Sandbox Code Playgroud)
这将是12个字节.要改变它们,你必须做12次操作.
然后,您可以对字节执行的操作遵循布尔逻辑,该逻辑由真值表定义:
And(&
),Or(|
)和Not(~
)真值表:
| 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 |
Run Code Online (Sandbox Code Playgroud)
我们通常的昵称和运营商为"力为零"和或者经营者为"强制为1",因为除非两个值1
,则和将导致0
的,除非两个值0
时,还是会导致1
.
因此,如果我们考虑tty.c_cflag = 0x00000000
并且您想要启用奇偶校验:
tty.c_cflag |= PARENB;
Run Code Online (Sandbox Code Playgroud)
然后tty.c_cflag
将包含0x00001000
,即0b1000000000000
然后你想设置7位大小:
tty.c_cflag |= CS7;
Run Code Online (Sandbox Code Playgroud)
并且tty.c_cflag
将包含0x00001200
,即0b1001000000000
现在,让我们回到你的问题:你的"等效"例子并不具有代表性,因为你正在考虑CSIZE
并且CS8
没有任何价值.
那么让我们来看看你从这个例子中得到的代码:
tty.c_cflag &= ~PARENB; // No Parity
tty.c_cflag &= ~CSTOPB; // 1 Stop Bit
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; // 8 Bits
Run Code Online (Sandbox Code Playgroud)
这里tty.c_cflag
包含一个未知值:
0b????????????????????????????????
Run Code Online (Sandbox Code Playgroud)
而且你知道你不需要奇偶校验,一个停止位和一个8位的数据大小.所以在这里你否定了"设置奇偶校验"值以将其关闭:
~PARENB == 0b0111111111111
Run Code Online (Sandbox Code Playgroud)
然后使用And运算符,您将该位强制为零:
tty.c_cflag &= ~PARENB —? 0b???????????????????0????????????
Run Code Online (Sandbox Code Playgroud)
然后你做同样的事情CSTOPB
:
tty.c_cflag &= ~CSTOPB —? 0b???????????????????0?0??????????
Run Code Online (Sandbox Code Playgroud)
最后CSIZE
:
tty.c_cflag &= ~CSIZE —? 0b???????????????????0?000????????
Run Code Online (Sandbox Code Playgroud)
因为CSIZE
,目标是确保重置数据长度的两位值.然后通过强制1
值来设置正确的长度:
tty.c_cflag |= CS8 —? 0b???????????????????0?011????????
Run Code Online (Sandbox Code Playgroud)
其实,复位CSIZE
到00
再向上设置CS8
到11
是无用的,因为直接做tty.c_cflag |= CS8
将使11
.但这是一个很好的做法,如果你想改变CS8
为CS7
,然后只设置两个位中的一个,另一个保持原始值.
最后,当您打开串口时,库将检查这些值以配置端口,并使用默认值来表示您未强制使用的所有其他值,并且您将能够使用串行端口.
我希望我的解释可以帮助您更好地了解串行端口上的标志设置以及完全使用位掩码的情况.仅供参考,同样的原则被用于许多其他事情,例如IPv4网络掩码,文件I/O等.