在C语言串口编程和异步通信领域,XON并不是一个变量名或关键字,而是ASCII码表中定义的一个控制字符,十进制值为17,十六进制为0x11,也被称为DC1(Device Control 1)。它通常和另一个控制字符XOFF(十进制19,0x13,DC3)成对出现,用来实现发送端与接收端之间的软件流控制。当接收方处理速度跟不上发送方时,就可以通过这两个字符通知对方暂停或继续发送。

一、XON与XOFF的基本概念
XON和XOFF属于传统的异步串行通信中的“软件握手”机制。与之相对的是硬件流控制,比如使用RTS和CTS两根物理信号线来指示能否发送数据。软件流控制不占用额外引脚,仅通过在数据流中插入特定字符来达成目的,因此在对引脚数量敏感的老式设备或简单单片机上很常见。
在ASCII定义中,XOFF表示“暂停发送”,XON表示“恢复发送”。这两个字符本身如果出现在正常数据中,可能会被误识别为控制指令,所以很多协议会要求对数据进行透明化处理,或者在启用软件流控制时限定传输内容为不含0x11与0x13的文本。理解这一点,是写好稳健串口程序的前提。
二、C语言中如何配置XON/XOFF流控制
在类Unix系统下,C语言操作串口一般通过termios结构体完成。该结构体中的c_iflag成员包含IXON和IXOFF两个标志位:IXON表示允许在输入时识别XOFF并停止输出,IXOFF表示当输入缓冲区快满时由系统自动发送XOFF。下面是一段开启软件流控制的示例。
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
int setup_xon_xoff(int fd) {
struct termios opts;
if (tcgetattr(fd, &opts) < 0) {
return -1;
}
// 开启输入时识别XON/XOFF,以及由系统自动发送XOFF
opts.c_iflag |= IXON | IXOFF;
// 关闭将CR映射为NL等可能干扰控制字符的处理
opts.c_iflag &= ~(IGNCR | ICRNL);
if (tcsetattr(fd, TCSANOW, &opts) < 0) {
return -1;
}
return 0;
}
上述代码在已有串口描述符fd上修改termios配置,使内核终端驱动参与流控制。开启后,程序员不必手动去write XOFF字符,系统会在接收缓冲达到阈值时自动发出,并在空闲后发出XON。这种方式降低了应用层复杂度,但也要求发送端驱动同样支持识别这两个字符。
若使用Windows平台,C语言可通过SetCommState配合DCB结构体,将fOutX与fInX字段设为TRUE来启用等价功能。虽然API不同,但底层语义一致:用XON和XOFF字符协调收发节奏。
三、手动发送XON与XOFF的场景
有些自定义通信协议不依赖系统驱动,而是在应用层自行处理流控。此时C语言程序需要显式写出控制字符。例如下位机MCU通过串口给上位机发XOFF,告知“我忙不过来了”:
#include <stdio.h>
#include <unistd.h>
void send_xoff(int fd) {
char xoff = 0x13; // XOFF
write(fd, &xoff, 1);
}
void send_xon(int fd) {
char xon = 0x11; // XON
write(fd, &xon, 1);
}
int main(void) {
// 假设fd已打开为串口
// int fd = open("/dev/ttyS0", O_RDWR);
// send_xoff(fd);
// 处理完数据后
// send_xon(fd);
return 0;
}
这种写法把流控逻辑完全掌握在开发者手里,适合裸机程序或需要精细控制缓冲的网关设备。但要注意,如果正常数据包里恰好含有0x11或0x13,对方可能误判为恢复或暂停指令,因此往往要在协议层做转义,比如用0x10作为前导逃逸符。
对比系统驱动自动流控,手动方式的优点是可跨平台、不依赖操作系统termios实现;缺点是占用主线程逻辑,且若发送不及时仍可能丢数据。实际项目中应根据实时性要求取舍。
四、XON/XOFF与硬件流控的对比
为了更直观看出差异,下面用表格列出两者特点:
| 对比项 | 软件流控(XON/XOFF) | 硬件流控(RTS/CTS) |
|---|---|---|
| 所需信号线 | 仅收发数据线 | 额外RTS与CTS线 |
| 性能开销 | 占用数据带宽,需处理转义 | 不占数据带宽 |
| C语言配置 | 设置IXON/IXOFF或DCB字段 | 设置CRTSCTS或fRtsControl |
| 适用场景 | 老设备、简单三线串口 | 高速通信、二进制传输 |
从表中可见,XON/XOFF方案胜在接线简单,但在传输二进制或高吞吐时劣势明显。现代工业总线虽多转向硬件流控,但大量存量设备仍依赖XON/XOFF,因此C语言开发者有必要掌握其原理。
另外,某些终端程序(如minicom、putty)也提供“按Ctrl+S发送XOFF、Ctrl+Q发送XON”的功能,这其实是同一套机制在人机交互上的遗留,了解后能避免误按快捷键导致屏幕“卡住”的困惑。
五、常见误区与调试建议
初学者常把XON误解为某个C语言宏或编译器扩展,实际上它只出现在通信协议层。如果在代码里直接写XON而未定义宏,编译器会报未声明标识符错误。正确做法是用其数值或自行定义宏:
#ifndef XON #define XON 0x11 #endif #ifndef XOFF #define XOFF 0x13 #endif
调试时若发现串口数据间歇性停滞,可用逻辑分析仪抓取线上字符,看是否频繁出现0x13。若系统已开IXOFF却仍丢数据,多半是接收缓冲阈值设置过低或发送端不支持识别XON。此时应检查termios的c_cc数组里VMIN和VTIME,以及对方设备手册。
总结来说,C语言中的XON就是用于流控制恢复的ASCII控制字符,配合XOFF构成经典的软件握手方案。无论在驱动级还是应用级,理解其数值、配置方式与局限,都能帮助你写出更可靠的串口通信程序。