Linux串口编程示例

termios

termios是用于终端I/O的较新(现在已经有几十年历史)Unix API。在termios的帮助下执行串行I/O的程序剖析如下:

  1. 通过系统调用open打开串行端口
  2. 通过特定的termios函数和数据结构配置通信参数和其他接口属性
  3. 通过如下系统调用write,read,readv,writev,select,poll进行读写、轮询。
  4. 通过close关闭设备

termios 的必要声明和常量可以在头文件 <termios.h> 中找到。

#include <termios.h>

termios I/O API 支持两种不同的模式:

  1. 规范模式。
    对终端输入以行为单位进行处理。对于每个读请求,终端驱动程序最多返回一行。
  2. 非规范模式。
    输入字符不装配成行。

如果没有特殊处理,默认的模式是规范模式

termios.h

串口所有的配置都是通过使用在 termios.h 标头中定义的 struct termios 数据结构完成的。 此结构对于串行设备的配置和查询其设置都至关重要。 它至少包含以下字段:

glibc-2.34 termios.h

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 系统上受支持。

tcgetattrtcsetattr这两个函数能处理大约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 信号。 通常,这会导致进程退出;

Control Modes

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_RTSCTSCRTSCTS 常量,则可能支持硬件流控制。

//使能硬件流控
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

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值