termios
termios是用于终端I/O的较新(现在已经有几十年历史)Unix API。在termios的帮助下执行串行I/O的程序剖析如下:
- 通过系统调用
open
打开串行端口 - 通过特定的
termios
函数和数据结构配置通信参数和其他接口属性 - 通过如下系统调用
write
,read
,readv
,writev
,select
,poll
进行读写、轮询。 - 通过
close
关闭设备
termios 的必要声明和常量可以在头文件 <termios.h> 中找到。
#include <termios.h>
termios I/O API 支持两种不同的模式:
- 规范模式。
对终端输入以行为单位进行处理。对于每个读请求,终端驱动程序最多返回一行。 - 非规范模式。
输入字符不装配成行。
如果没有特殊处理,默认的模式是规范模式
termios.h
串口所有的配置都是通过使用在 termios.h 标头中定义的 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 __ispeed, __ospeed;//输入输出波特率
};
- 输入标志被终端设备驱动控制输入特性(例如输入字节宽,使能输入校验检查)。
- 输出标志控制着驱动输出(例如,执行输出进程,映射新行到CR/LF)
- 控制标志影响RS-232串行线(例如忽略调制解调器状态行,每个字符一个或两个停止位)。
- 本地标志影响驱动和用户间的接口(例如回显是否打开,擦除字符是否可见,使能中断产生的信号,工作为后台输出控制停止信号)
- POSIX.1在输入上定义了11个特殊字符(例如文件结束符EOF),可以选择是否需要特殊处理这些字符。
c_iflag
c_iflag
配置串口输入属性。
/*
忽略中断条件
在异步串行数据传输的上下文中,中断条件被定义为比单个字节长的一系列零值位(低电平)。
*/
#define IGNBRK (1 << 0)
/*
如果设置了该位而未设置 IGNBRK,则中断条件会清除终端输入和输出队列,并为与终端关联的前台进程组发出 SIGINT 信号。
如果 BRKINT 和 IGNBRK 均未设置,则在未设置 PARMRK 时将中断条件作为单个 '\0' 字符传递给应用程序,否则作为三字符序列 '\377'、'\0'、'\ 0'。
*/
#define BRKINT (1 << 1) /* Signal interrupt on break. */
/*
当设置了INPCK
如果设置了该位,则忽略任何帧或奇偶校验错误的字节。
*/
#define IGNPAR (1 << 2) /* Ignore characters with parity errors. */
/*
当设置了INPCK 且未设置 IGNPAR 时
如果设置了该位,则在传递给程序时会标记具有奇偶校验或帧错误的输入字节。
*/
#define PARMRK (1 << 3) /* Mark parity and framing errors. */
/*
如果设置了该位,则启用输入奇偶校验。 如果未设置,则不检查输入的奇偶校验错误; 字符被简单地传递给应用程序。
输入处理的奇偶校验与是否启用底层终端硬件上的奇偶校验检测和生成无关;
例如,您可以清除 INPCK 输入模式标志并设置 PARENB 控制模式标志以忽略输入的奇偶校验错误,但仍生成输出奇偶校验。
如果设置了该位,则检测到奇偶校验错误时会发生什么取决于是设置了 IGNPAR 位还是 PARMRK 位。 如果这些位均未设置,则将带有奇偶校验错误的字节作为“\0”字符传递给应用程序。
*/
#define INPCK (1 << 4) /* Enable input parity check. */
/*
如果设置了该位,则有效输入字节将被剥离为 7 位; 否则,所有八位都可供程序读取。
*/
#define ISTRIP (1 << 5) /* Strip 8th bit off characters. */
/*
如果设置了该位,则作为输入接收到的换行符 ('\n') 将作为回车符 ('\r') 传递给应用程序。
*/
#define INLCR (1 << 6) /* Map NL to CR on input. */
/*
如果设置了该位,则在输入时会丢弃回车字符 ('\r')。 当您键入 RET 键时,放弃回车可能对发送回车和换行的终端很有用。
*/
#define IGNCR (1 << 7) /* Ignore CR. */
/*
如果设置了该位但未设置 IGNCR,则作为输入接收到的回车字符 ('\r') 将作为换行符 ('\n') 传递给应用程序。
*/
#define ICRNL (1 << 8) /* Map CR to NL on input. */
/*
如果设置了该位,则启用输出的启动/停止控制。 换句话说,如果计算机收到一个 STOP 字符,它会暂停输出,直到收到一个 START 字符。 在这种情况下,STOP 和 START 字符永远不会传递给应用程序。 如果未设置该位,则 START 和 STOP 可以作为普通字符读取。
*/
#define IXON (1 << 9) /* Enable start/stop output control. */
/*
如果设置了该位,则启用对输入的启动/停止控制。 换句话说,计算机会根据需要发送 STOP 和 START 字符,以防止输入比程序读取它的速度更快。 这个想法是生成输入数据的实际终端硬件通过暂停传输来响应 STOP 字符,并通过恢复传输来响应 START 字符。
*/
#define IXOFF (1 << 10) /* Enable start/stop input control. */
#if defined __USE_MISC || defined __USE_XOPEN || defined __USE_XOPEN2K8
/*
如果设置了该位,则当输出已被 STOP 字符挂起时,任何输入字符将重新开始输出。 否则,只有 START 字符重新启动输出。
这是一个 BSD 扩展; 它只存在于 BSD 系统和 GNU/Linux 和 GNU/Hurd 系统上。
*/
# define IXANY (1 << 11) /* Any character will restart after stop. */
#endif
#ifdef __USE_MISC
/*
如果设置了该位,则填充终端输入缓冲区会向终端发送 BEL 字符(代码 007)以响铃。
*/
# define IMAXBEL (1 << 13) /* Ring bell when input queue is full. */
#endif
#if defined __USE_GNU || (defined __USE_XOPEN && !defined __USE_XOPEN2K)
/*
(POSIX 不支持)在输入时将大写字符映射为小写。
*/
# define IUCLC (1 << 14) /* Translate upper case input to lower case. */
#endif
c_oflag
c_oflag
配置串口输出属性。
/*
如果设置了该位,则输出数据将以某种未指定的方式进行处理,以便在终端设备上正确显示。 这通常包括将换行符 ('\n') 映射到回车和换行对。
如果未设置该位,则字符按原样传输。
*/
#define OPOST (1 << 0) /* Perform output processing. */
#if defined __USE_MISC || defined __USE_XOPEN
/*
当设置了 OPOST时
如果设置了此位,则将输出中的换行符转换为一对字符,回车后跟换行符。
*/
# define ONLCR (1 << 1) /* Map NL to CR-NL on output. */
#endif
#ifdef __USE_MISC
/*
当设置了 OPOST时
如果设置了该位,则将输出中的制表符转换为适当数量的空格,以每八列模拟一个制表位。 该位仅存在于 BSD 系统和 GNU/Hurd 系统上; 在 GNU/Linux 系统上,它可以作为 XTABS 使用。
*/
# define OXTABS TAB3 /* Expand tabs to spaces. */
/*
当设置了 OPOST时
如果设置了该位,则丢弃输出中的 C-d 字符(代码 004)。 这些字符会导致许多拨号终端断开连接。 该位仅存在于 BSD 系统和 GNU/Hurd 系统上。
*/
# define ONOEOT (1 << 3) /* Discard EOT (^D) on output. */
#endif
#if defined __USE_MISC || defined __USE_XOPEN
# define OCRNL (1 << 4) /* Map CR to NL. */
# define ONOCR (1 << 5) /* Discard CR's when on column 0. */
# define ONLRET (1 << 6) /* Move to column 0 on NL. */
#endif
#if defined __USE_MISC || defined __USE_XOPEN
# define NLDLY (3 << 8) /* NL delay. */
# define NL0 (0 << 8) /* NL type 0. */
# define NL1 (1 << 8) /* NL type 1. */
# define TABDLY (3 << 10 | 1 << 2) /* TAB delay. */
# define TAB0 (0 << 10) /* TAB delay type 0. */
# define TAB1 (1 << 10) /* TAB delay type 1. */
# define TAB2 (2 << 10) /* TAB delay type 2. */
# define TAB3 (1 << 2) /* Expand tabs to spaces. */
# define CRDLY (3 << 12) /* CR delay. */
# define CR0 (0 << 12) /* CR delay type 0. */
# define CR1 (1 << 12) /* CR delay type 1. */
# define CR2 (2 << 12) /* CR delay type 2. */
# define CR3 (3 << 12) /* CR delay type 3. */
# define FFDLY (1 << 14) /* FF delay. */
# define FF0 (0 << 14) /* FF delay type 0. */
# define FF1 (1 << 14) /* FF delay type 1. */
# define BSDLY (1 << 15) /* BS delay. */
# define BS0 (0 << 15) /* BS delay type 0. */
# define BS1 (1 << 15) /* BS delay type 1. */
# define VTDLY (1 << 16) /* VT delay. */
# define VT0 (0 << 16) /* VT delay type 0. */
# define VT1 (1 << 16) /* VT delay type 1. */
#endif /* __USE_MISC || __USE_XOPEN */
#if defined __USE_GNU || (defined __USE_XOPEN && !defined __USE_XOPEN2K)
# define OLCUC (1 << 17) /* Translate lower case output to upper case */
#endif
#ifdef __USE_XOPEN
# define OFILL (1 << 18) /* Send fill characters for delays. */
# define OFDEL (1 << 19) /* Fill is DEL. */
#endif
c_cflag
c_cflag
配置串口通信控制属性
#ifdef __USE_MISC
/*
如果设置了该位,则表示完全忽略控制模式和线速度值。 这仅在调用 tcsetattr 时才有意义。
c_cflag 成员和 cfgetispeed 和 cfgetospeed 返回的线速度值将不受调用影响。 如果您想设置其他成员中的所有软件模式,但保留 c_cflag 中的硬件详细信息不变,则 CIGNORE 很有用。 (这是 tcsettattr 的 TCSASOFT 标志的工作方式。)
该位从未在 tcgetattr 填充的结构中设置。
*/
# define CIGNORE (1 << 0) /* Ignore these control flags. */
#endif
/*
以下是串口通信数据位的定义
*/
#define CSIZE (CS5|CS6|CS7|CS8) /* Number of bits per byte (mask). */
#define CS5 0 /* 5 bits per byte. */
#define CS6 (1 << 8) /* 6 bits per byte. */
#define CS7 (1 << 9) /* 7 bits per byte. */
#define CS8 (CS6|CS7) /* 8 bits per byte. */
/*
以下是串口通信停止位的定义
*/
#define CSTOPB (1 << 10) /* Two stop bits instead of one. */
/*
如果设置了该位,则可以从终端读取输入。 否则,输入到达时将被丢弃。
*/
#define CREAD (1 << 11) /* Enable receiver. */
/*
如果设置了该位,则启用奇偶校验位的生成和检测。
如果未设置该位,则不会向输出字符添加奇偶校验位,并且不检查输入字符的正确奇偶校验。
*/
#define PARENB (1 << 12) /* Parity enable. */
/*
以下是串口通信奇偶校验的定义
*/
#define PARODD (1 << 13) /* Odd parity instead of even. */
/*
如果设置了此位,则当所有打开终端设备的进程都关闭文件或退出时,将生成调制解调器断开连接。
*/
#define HUPCL (1 << 14) /* Hang up on last close. */
/*
如果该位被设置,则表明终端是“本地”连接的,调制解调器状态线(例如载波检测)应该被忽略。
在许多系统上,如果未设置此位并且您在未设置 O_NONBLOCK 标志的情况下调用 open,则 open 会阻塞,直到建立调制解调器连接。
如果该位未设置并且检测到调制解调器断开连接,则会向终端的控制进程组(如果有)发送一个 SIGHUP 信号。 通常,这会导致进程退出; 断开连接后从终端读取会导致文件结束条件,写入会导致返回 EIO 错误。 终端设备必须关闭并重新打开才能清除该状态。
*/
#define CLOCAL (1 << 15) /* Ignore modem status lines. */
#ifdef __USE_MISC
/*
(POSIX 不支持)启用 RTS/CTS(硬件)流控制
*/
# define CRTSCTS (1 << 16) /* RTS/CTS flow control. */
/*
如果设置了该位,则启用基于 RTS 线(RS232 协议)的输入流控制。
*/
# define CRTS_IFLOW CRTSCTS /* Compatibility. */
/*
如果设置了该位,则启用基于 RTS 线(RS232 协议)的输出流控制。
*/
# define CCTS_OFLOW CRTSCTS /* Compatibility. */
# define CDTRCTS (1 << 17) /* DTR/CTS flow control. */
/*
如果设置了该位,则启用输出的基于载波的流量控制。
*/
# define MDMBUF (1 << 20) /* DTR/DCD flow control. */
# define CHWFLOW (MDMBUF|CRTSCTS|CDTRCTS) /* All types of flow control. */
#endif
c_lflag
c_lflag
配置本地模式相关属性
#ifdef __USE_MISC
/*
该位类似于 ECHOK。 它通过在屏幕上擦除已被杀死的整行来实现 KILL 字符的特殊显示。 这是一个 BSD 扩展,仅存在于 BSD 系统和 GNU/Linux 和 GNU/Hurd 系统中。
*/
# define ECHOKE (1 << 0) /* Visual erase for KILL. */
#endif
#define _ECHOE (1 << 1) /* Visual erase for ERASE. */
/*
如果设置了该位,则回显通过从屏幕上擦除当前行中的最后一个字符来指示擦除输入的 ERASE 字符。 否则,擦除的字符将重新回显以显示发生了什么(适用于打印终端)。
该位仅控制显示行为; ICANON 位本身控制对 ERASE 字符的实际识别和输入的擦除,没有它 ECHOE 是无关紧要的。
*/
#define ECHOE _ECHOE
#define _ECHOK (1 << 2) /* Echo NL after KILL. */
/*
该位通过在正常回显 KILL 字符后移动到新行来启用 KILL 字符的特殊显示。 ECHOKE 的行为更便于查看。
如果未设置该位,则 KILL 字符会回显,就像它不是 KILL 字符时一样。 然后由用户记住 KILL 字符已经删除了前面的输入; 屏幕上没有任何的指示。
该位仅控制显示行为; ICANON 位本身控制 KILL 字符的实际识别和输入的擦除,没有它 ECHOK 根本不相关。
*/
#define ECHOK _ECHOK
#define _ECHO (1 << 3) /* Enable echo. */
/*
如果设置了该位,输入字符会回显到终端。
*/
#define ECHO _ECHO
#define _ECHONL (1 << 4) /* Echo NL even if ECHO is off. */
/*
如果该位被设置并且 ICANON 位也被设置,那么即使 ECHO 位没有被设置,换行符 ('\n') 也会被回显。
*/
#define ECHONL _ECHONL
#ifdef __USE_MISC
/*
该位与 ECHOE 一样,能够以适合硬拷贝终端的方式显示 ERASE 字符。 当您键入 ERASE 字符时,将打印一个“\”字符,然后是第一个擦除的字符。 再次键入 ERASE 字符只会打印下一个擦除的字符。 然后,下次键入普通字符时,会在字符回显之前打印“/”字符。
这是一个 BSD 扩展,仅存在于 BSD 系统和 GNU/Linux 和 GNU/Hurd 系统中。
*/
# define ECHOPRT (1 << 5) /* Hardcopy visual erase. */
/*
如果设置了该位并且 ECHO 位也设置了,则回显带有“^”后跟相应文本字符的控制字符。 因此,control-A 回显为‘^A’。 这通常是交互式输入的首选模式,因为将控制字符回显到终端可能会对终端产生一些不良影响。
这是一个 BSD 扩展,仅存在于 BSD 系统和 GNU/Linux 和 GNU/Hurd 系统中。
*/
# define ECHOCTL (1 << 6) /* Echo control characters as ^X. */
#endif
#define _ISIG (1 << 7) /* Enable signals. */
/*
该位控制是否识别 INTR、QUIT 和 SUSP 字符。 当且仅当该位被设置时,才执行与这些字符相关的功能。 处于规范或非规范输入模式对这些字符的解释没有影响。
禁用对这些字符的识别时应谨慎。 不能交互中断的程序对用户非常不友好。 如果您清除该位,您的程序应该提供一些替代接口,允许用户以交互方式发送与这些字符相关的信号,或从程序中转义。
*/
#define ISIG _ISIG
#define _ICANON (1 << 8) /* Do erase and kill processing. */
/*
如果设置该位,则启用规范输入处理模式。 否则,以非规范模式处理输入。
*/
#define ICANON _ICANON
#ifdef __USE_MISC
/*
该位决定 WERASE 字符应擦除多远。 WERASE 字符擦除回单词的开头; 问题是,文字从哪里开始?
如果该位清零,则单词的开头是空白字符之后的非空白字符。 如果设置了该位,则单词的开头是字母数字字符或下划线跟在其中一个字符之后的字符。
*/
# define ALTWERASE (1 << 9) /* Alternate WERASE algorithm. */
#endif
#define _IEXTEN (1 << 10) /* Enable DISCARD and LNEXT. */
#define IEXTEN _IEXTEN
#ifdef __USE_MISC
# define EXTPROC (1 << 11) /* External processing. */
#endif
#define _TOSTOP (1 << 22) /* Send SIGTTOU for background output. */
/*
如果设置了该位并且系统支持作业控制,则尝试写入终端的后台进程会生成 SIGTTOU 信号。
*/
#define TOSTOP _TOSTOP
#ifdef __USE_MISC
/*
这是当用户键入 DISCARD 字符时切换的位。 当该位被设置时,所有的输出都会被丢弃。
*/
# define FLUSHO (1 << 23) /* Output being flushed (state). */
#endif
#if defined __USE_XOPEN && !defined __USE_XOPEN2K
# define XCASE (1 << 24) /* Canonical upper/lower case. */
#endif
#ifdef __USE_MISC
/*
设置该位禁用处理 STATUS 字符
*/
# define NOKERNINFO (1 << 25) /* Disable VSTATUS. */
/*
如果设置了该位,则表示有一行输入需要重新打印。 键入 REPRINT 字符设置此位; 该位保持设置直到重新打印完成。
*/
# define PENDIN (1 << 29) /* Retype pending input (state). */
#endif
#define _NOFLSH (1 << 31) /* Disable flush after interrupt. */
/*
通常,INTR、QUIT 和 SUSP 字符会导致终端的输入和输出队列被清除。 如果设置了该位,则不会清除队列。
*/
#define NOFLSH _NOFLSH
c_cc
c_cc
定义了一些特殊字符,仅在规范模式下生效。
在规范输入中,终端驱动程序识别许多执行各种控制功能的特殊字符。
#define VEOF 0 /* End-of-file character [ICANON]. */
#define VEOL 1 /* End-of-line character [ICANON]. */
#ifdef __USE_MISC
# define VEOL2 2 /* Second EOL character [ICANON]. */
#endif
#define VERASE 3 /* Erase character [ICANON]. */
#ifdef __USE_MISC
# define VWERASE 4 /* Word-erase character [ICANON]. */
#endif
#define VKILL 5 /* Kill-line character [ICANON]. */
#ifdef __USE_MISC
# define VREPRINT 6 /* Reprint-line character [ICANON]. */
#endif
#define VINTR 8 /* Interrupt character [ISIG]. */
#define VQUIT 9 /* Quit character [ISIG]. */
#define VSUSP 10 /* Suspend character [ISIG]. */
#ifdef __USE_MISC
# define VDSUSP 11 /* Delayed suspend character [ISIG]. */
#endif
#define VSTART 12 /* Start (X-ON) character [IXON, IXOFF]. */
#define VSTOP 13 /* Stop (X-OFF) character [IXON, IXOFF]. */
#ifdef __USE_MISC
# define VLNEXT 14 /* Literal-next character [IEXTEN]. */
# define VDISCARD 15 /* Discard character [IEXTEN]. */
#endif
#define VMIN 16 /* Minimum number of bytes read at once [!ICANON]. */
#define VTIME 17 /* Time-out value (tenths of a second) [!ICANON]. */
#ifdef __USE_MISC
# define VSTATUS 18 /* Status character [ICANON]. */
#endif
#define NCCS 20 /* Value duplicated in <hurd/tioctl.defs>. */
speed_t (截取部分)
#define B1200 1200 /* 1200 baud. */
#define B1800 1800 /* 1800 baud. */
#define B2400 2400 /* 2400 baud. */
#define B4800 4800 /* 4800 baud. */
#define B9600 9600 /* 9600 baud. */
#define B7200 7200 /* 7200 baud. */
#define B14400 14400 /* 14400 baud. */
#define B19200 19200 /* 19200 baud. */
#define B28800 28800 /* 28800 baud. */
#define B38400 38400 /* 38400 baud. */
终端IO函数
termios source code
termios
提供了如下终端IO函数供用户使用:
#include <termios.h>
#include <unistd.h>
int tcgetattr(int fd, struct termios *termios_p); /*获取属性*/
int tcsetattr(int fd, int optional_actions,
const struct termios *termios_p);/*设置属性*/
int tcsendbreak(int fd, int duration);/*发送Break字符*/
int tcdrain(int fd);/*等待所有输出被传输*/
int tcflush(int fd, int queue_selector);/*冲洗输入和/或输出缓冲区*/
int tcflow(int fd, int action);/*挂起传输或者接收*/
void cfmakeraw(struct termios *termios_p);/*返回将终端设置为原始模式的配置*/
speed_t cfgetispeed(const struct termios *termios_p);/*获取输入速度*/
speed_t cfgetospeed(const struct termios *termios_p);/*获取输出速度*/
int cfsetispeed(struct termios *termios_p, speed_t speed);/*设置输入速度*/
int cfsetospeed(struct termios *termios_p, speed_t speed);/*设置输出速度*/
int cfsetspeed(struct termios *termios_p, speed_t speed);/*同时设置输入输出速度*/
pid_t tcgetpgrp (int fd);/*获取前台进程组ID*/
int tcsetpgrp (int fd, pid_t pgrp_id);/*设置前台进程组ID*/
pid_t tcgetsid (int fd);/*得到控制TTY的会话首进程的进程组ID*/
上述函数间的关系如下所示
tcsetattr
用于设置串口配置
int tcsetattr(int fd, int optional_actions,const struct termios *termios_p);
第二个参数optional_actions
指示新的配置参数生效的时机,它可以有如下几种情况:
- TCSANOW
立即进行更改 - TCSADRAIN
等待输出排队传输完成后进行更改。 在更改影响输出的参数时,通常应使用此选项。 - TCSAFLUSH
清空输入输出缓冲区才进行更改,并且在进行更改之前,所有接收到但未读取的输入都将被丢弃。 - TCSASOFT
这是一个标志位,您可以将其添加到上述任何替代方案中。 它的意思是禁止改变终端硬件的状态。 它是一个 BSD 扩展; 它仅在 BSD 系统和 GNU/Hurd 系统上受支持。
tcgetattr
和tcsetattr
这两个函数能处理大约70种不同的标志,已经涵盖了目前绝大部分关于串口的操作。
cfmakeraw()
需要特别注意下,这个函数实际上是输出原始模式的默认配置,而不是直接将串口配置为原始模式。其源码如下:
void cfmakeraw (struct termios *t)
{
t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
t->c_oflag &= ~OPOST;
t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
t->c_cflag &= ~(CSIZE|PARENB);
t->c_cflag |= CS8;
t->c_cc[VMIN] = 1; /* read returns when one char is available. */
t->c_cc[VTIME] = 0;
}
在 struct termios
的帮助下,可以设置(通过 tcsetattr()
)或获取(通过 tcgetattr()
)超过 70 个不同的标志。 大量的标志,以及它们有时深奥和病态的含义和行为,是在 Unix 下进行串行编程很难的原因之一。 在设备配置中,一定要注意不要出错。
串口编程
1.打开串口
可以通过open
系统调用直接打开串口设备
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
int open_port(void)
{
int fd; /* File descriptor for the port */
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("open_port: Unable to open /dev/ttyf1 - ");
}
return (fd);
}
O_RDWR : 读写方式
O_NOCTTY :该参数不会使打开的文件成为该进程的控制终端。如果没有指定这个标志,那么任何一个 输入都将会影响用户的进程
O_NDELAY:这个程序不关心DCD信号线所处的状态,端口的另一端是否激活或者停止。如果用户不指定这个标志,则进程将会一直处在睡眠状态,直到DCD信号线被激活。
2. 设置波特率
如下代码是设置串口输入和输出波特率为115200
struct termios options;
tcgetattr(fd, &options);
//设置输入和输出都为 115200bps
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
//Enable the receiver and set local mode...
options.c_cflag |= (CLOCAL | CREAD);
CREAD:使能接收
CLOCAL:设置本地连接,忽略调制解调器状态线 。在许多系统上,如果未设置此位并且未设置 O_NONBLOCK
标志的情况下调用 open
,则 open 会阻塞,直到建立调制解调器连接。如果该位未设置并且检测到调制解调器断开连接,则会向终端的控制进程组(如果有)发送一个 SIGHUP 信号。 通常,这会导致进程退出;
3. 设置数据位
如下是设置8位数据位
options.c_cflag &= ~CSIZE; /* Mask the character size bits */
options.c_cflag |= CS8; /* Select 8 data bits */
CSIZE:(CS5|CS6|CS7|CS8)
4.设置校验位
PARENB:设置此位启用奇偶校验位的生成和检测
PARODD:该位仅在 PARENB 置位时有用。 如果设置了 PARODD,则使用奇校验,否则使用偶校验。
//No parity 无校验
options.c_cflag &= ~PARENB
options.c_cflag &= ~CSTOPB
//Even parity 偶校验
options.c_cflag |= PARENB
options.c_cflag &= ~PARODD
//Odd parity 奇校验
options.c_cflag |= PARENB
options.c_cflag |= PARODD
5.设置停止位
CSTOPB:设置此位表示2个停止位,否则是1个停止位
//1位停止位
options.c_cflag &= ~CSTOPB
//2位停止位
options.c_cflag |= CSTOPB
6.设置硬件流控
某些版本的 UNIX 支持使用 CTS(清除发送)和 RTS(请求发送)信号线的硬件流控制。 如果您的系统上定义了 CNEW_RTSCTS
或 CRTSCTS
常量,则可能支持硬件流控制。
//使能硬件流控
options.c_cflag |= CNEW_RTSCTS; /* Also called CRTSCTS */
//禁止硬件流控
options.c_cflag &= ~CNEW_RTSCTS;
在实际串口编程中,以上标志位已经足够进行基本串口通信,如果对串口通信还有其他需求(如格式、回显等等),则还需要配置其他标志位。
参考文献
1.UNIX环境高级编程(18章 终端IO)
2. Serial Programming Guide for POSIX Operating Systems
3. glibc-Terminal Modes