在C语言中,你能找到的最好的文档就是源代码本身,你可以在计算机上的
/usr/include/termios.h
找到它(实际上可能分布在其中一个或多个包含文件中)——这里是我所基于的
面向苹果的基于BSD的termios.h,根据你使用的Unix版本不同,值可能会发生变化。
在那里,你将发现你的
tty
对象是
struct termios
类型,定义如下:
struct termios {
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_cc[NCCS];
speed_t c_ispeed;
speed_t c_ospeed;
};
所以c_cflag
的类型是tcflag_t
,它由以下行定义:
typedef unsigned long tcflag_t
unsigned long
被期望是4个字节,即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 */
话虽如此,工作方式是作为一个位数组使用,这意味着每个位对于一个功能都是重要的。这是一种常用的方法,因为位运算在处理能力上是“便宜”的(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(&
)、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 |
我们通常将
And运算符称为“强制为零”,将
Or运算符称为“强制为1”,因为除非两个值都是
1
,否则
And将得到
0
,除非两个值都是
0
,否则
Or将得到
1
。
所以,如果我们考虑
tty.c_cflag = 0x00000000
,并且您想启用奇偶校验:
tty.c_cflag |= PARENB;
然后tty.c_cflag
将包含0x00001000
,即0b1000000000000
接下来您需要设置7位大小:
tty.c_cflag |= CS7
并且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
在这里,
tty.c_cflag
包含一个未知的值:
0b????????????????????????????????
你需要无校验位,一个停止位和8位数据大小。因此,你要否定“设置奇偶校验位”的值以关闭它:
~PARENB == 0b0111111111111
然后使用 And 操作符,您将强制将位设置为零:
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????????
实际上,将 CSIZE
重置为 00
,然后将 CS8
设置为 11
是没有意义的,因为直接执行 tty.c_cflag |= CS8
就会将其设置为 11
。但这是一个好习惯,以防您想从 CS8
更改为 CS7
,这将仅设置其中的一个位,另一个保留原始值。
最后,当您打开串口时,库将检查这些值以配置端口,并对您未强制设置的所有其他值使用默认值,您将能够使用串口。
我希望我的解释有助于您更好地理解串口标志设置以及位掩码的使用。同样的原理也被用于许多其他事情,比如IPv4网络掩码、文件I/O等。
unsigned long
应该是 8 字节,即32位。" -- 嗯???!!! "而且你知道你想要...没有停止位" -- 不,最小规格是一个停止位。 - sawdust