c - termios 库中的标志是如何表示的?

标签 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

我理解这些行的后果,但是,只有当每个控制标志常量(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

最佳答案

在 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 预计为 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 */

话虽如此,它的工作方式是将 c_cflag 用作位数组,这意味着每个位对于函数都很重要。这是一种常用的方法,因为位操作在处理能力上“便宜”(您的 CPU 可以在一个周期内完成一个位操作),并且在内存空间上“便宜”,因为它不是使用 32 个 bool 值的数组来存储值(一个大小为 1 字节的 bool 类型来存储一个二进制值),每个字节可以存储 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 次操作。

然后您可以对字节执行的操作遵循由真值表定义的 bool 逻辑:

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
现在,让我们回到您的问题:您的“等效”示例并没有真正的代表性,因为您正在考虑 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

然后使用 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 等。

关于c - termios 库中的标志是如何表示的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31999358/

相关文章:

delphi - 串口连接超过6个设备有限制吗?

c - C 中的算术级数的结果不正确

c - 如何避免 C 中的全局变量?

c# - 从 URI 创建流对象(TCP、串行)的工厂方法

ios - iOS 设备的 RS232

MOUSE_INPUT_DATA 的 Java 等价物

android - 如何重写硬件 linux 驱动程序以供 android 使用

使用 SHA-2 交叉签名证书使用 signtool 签名和验证后 Windows 驱动程序安装失败

c - "i%0xff"的结果是什么?

c - 无法在不丢失预处理器的情况下在 C 中包含 ASM 头文件