目录
1、终端
1.1、终端分类
1.1.1、本地终端和远程终端
1.1.2、物理终端和伪终端
1.2、查看连接终端
1.3、终端参数
1.4、终端的工作模式
1.4.1、规范模式
1.4.2、非规范模式
1.4.3、原始模式
2、termios API
2.1、struct termios结构体
2.1.1、c_iflag
2.1.2、c_oflag
2.1.3、c_cflag
2.1.4、c_lflag
2.1.5、c_cc
2.2、函数
2.2.1、cfmakeraw()函数
2.2.2、cfsetispeed()函数
2.2.3、cfsetospeed()函数
2.2.4、cfsetspeed()函数
2.2.5、tcdrain()函数
2.2.6、tcflush()函数
2.2.6、tcflow()函数
2.2.7、tcgetattr()函数
2.2.8、tcsetattr()函数
3、串口应用编程流程
本地终端 | 鼠标、键盘、显示器 | |
远程终端 | 串口远程终端 | 通过串口登录 |
网络远程终端 | 通过 ssh、 Telnet 等协议登录到一个远程主机 |
注:/dev/ttyX是本地终端对应的设备节点, 包括/dev/tty1~/dev/tty63 一共63 个本地终端。
物理终端 | 本地直接关联物理设备 |
伪终端 | 没有关联任何物理设备 |
注:/dev/pts/X是伪终端对应的设备节点。
使用who 命令可以查看系统当前连接了哪些终端。
输入模式 | 输入模式控制输入数据(如终端驱动程序从串口或键盘接收到的字符数据)在被传递给应用程序之前的处理方式。 |
输出模式 | 输出模式控制输出字符的处理方式,即由应用程序发送出去的字符数据在传递到串口或屏幕之前是如何处理的。 |
控制模式 | 控制模式控制终端设备的硬件特性,譬如对于串口来说,该字段比较重要,可设置串口波特率、数据位、校验位、停止位等硬件特性。 |
本地模式 | 本地模式用于控制终端的本地数据处理和工作模式。 |
特殊控制字符 | 特殊控制字符是一些字符组合,如 Ctrl+C、 Ctrl+Z 等, 当用户键入这样的组合键,终端会采取特殊处理方式。 |
输入速率 | 终端的输入速率 |
输出速率 | 终端的输出速率 |
在规范模式下,所有的输入是基于行进行处理的。在用户输入一个行结束符(回车符、 EOF 等)之前,系统调用 read()函数是读不到用户输入的任何字符的。除了 EOF 之外的行结束符(回车符等)与普通字符一样会被 read()函数读取到缓冲区中。在规范模式中,行编辑是可行的,而且一次 read()调用最多只能读取一行数据。如果在 read()函数中被请求读取的数据字节数小于当前行可读取的字节数,则 read()函数只会读取被请求的字节数,剩下的字节下次再被读取。
在非规范模式下,所有的输入是即时有效的,不需要用户另外输入行结束符,而且不可进行行编辑。
TIME 和 MIN 的值只能用于非规范模式,两者结合起来可以控制对输入数据的读取方式。 | |
MIN = 0 和 TIME = 0 | 在这种情况下, read()调用总是会立即返回。若有可读数据,则读取数据并返回被读取的字节数; 否则读取不到任何数据并返回 0。 |
MIN > 0 和 TIME = 0 | 在这种情况下, read()函数会被阻塞, 直到有 MIN 个字符可以读取时才返回。返回值是读取的字符数量。到达文件尾时返回 0。 |
MIN = 0 和 TIME > 0 | 在这种情况下, 只要有数据可读或者经过 TIME 个十分之一秒的时间, read()函数则立即返回,返回值为被读取的字节数。如果超时并且未读到数据,则 read()函数返回 0。 |
MIN > 0 和 TIME > 0 |
在这种情况下, 当有 MIN 个字节可读或者两个输入字符之间的时间间隔超过 TIME 个十分之一秒时, read()函数才返回。因为在输入第一个字符后系统才会启动定时器,所以,在这种情况下, read()函数至少读取一个字节后才返回。 |
原始模式是一种特殊的非规范模式。在原始模式下,所有的输入数据以字节为单位被处理。在这个模式下,终端是不可回显的, 并且禁用终端输入和输出字符的所有特殊处理。
串口的应用编程可以通过 ioctl()对串口进行配置,调用 read()读取串口的数据、调用 write()向串口写入数据。但是通常不这么做!因为 Linux 为上层用户做了一层封装,将这些 ioctl()操作封装成了一套标准的 API。可以直接使用这套标准 API 进行串口应用编程。
注:这一套接口并不是针对串口开发的,而是针对所有的终端设备。
struct termios
{
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
};
注:对于这些变量尽量不要直接对其初始化,而要将其通过“按位与”、“按位或” 等操作添加标志或清除某个标志。
注:不同的终端设备,本身硬件上就存在很大的区别,所以配置参数不是对所有终端设备都是有效的。
输入模式标志。
宏 | 说明 |
IGNBRK | 忽略输入终止条件 |
BRKINT | 当检测到输入终止条件时发送 SIGINT 信号 |
IGNPAR | 忽略帧错误和奇偶校验错误 |
PARMRK | 对奇偶校验错误做出标记 |
INPCK | 对接收到的数据执行奇偶校验 |
ISTRIP | 将所有接收到的数据裁剪为 7 比特位、也就是去除第八位 |
INLCR | 将接收到的 NL(换行符)转换为 CR(回车符) |
IGNCR | 忽略接收到的 CR(回车符) |
ICRNL | 将接收到的 CR(回车符)转换为 NL(换行符) |
IUCLC | 将接收到的大写字符映射为小写字符 |
IXON | 启动输出软件流控 |
IXOFF | 启动输入软件流控 |
IXANY | 允许任意字节启动流控 |
输出模式标志。
宏 | 说明 |
OPOST | 启用输出处理功能,如果不设置该标志则下面其他标志都被忽略 |
OLCUC | 将输出字符中的大写字符转换成小写字符 |
ONLCR | 将输出中的换行符(NL '\n')转换成回车符(CR '\r') |
OCRNL | 将输出中的回车符(CR '\r')转换成换行符(NL '\n') |
ONOCR | 在第 0 列不输出回车符(CR) |
ONLRET | 不输出回车符 |
OFILL | 发送填充字符以提供延时 |
OFDEL | 如果设置该标志,则表示填充字符为 DEL 字符,否则为 NULL 字符 |
控制模式控制终端设备的硬件特性(串口波特率、数据位、校验位、停止位等)。
CBAUD | 波特率的位掩码 |
B0 | 波特率为 0 |
B300 | 300 波特率 |
B1200 | 1200 波特率 |
B1800 | 1800 波特率 |
B2400 | 2400 波特率 |
B4800 | 4800 波特率 |
B9600 | 9600 波特率 |
B19200 | 19200 波特率 |
B38400 | 38400 波特率 |
B57600 | 57600 波特率 |
B115200 | 115200 波特率 |
B230400 | 230400 波特率 |
B460800 | 460800 波特率 |
B500000 | 500000 波特率 |
B576000 | 576000 波特率 |
B921600 | 921600 波特率 |
B1000000 | 1000000 波特率 |
B1152000 | 1152000 波特率 |
B1500000 | 1500000 波特率 |
B2000000 | 2000000 波特率 |
B2500000 | 2500000 波特率 |
B3000000 | 3000000 波特率 |
注:在 Linux 系统下, 使用 CBAUD 位掩码所选择的位来指定串口波特率;在其它一些系统中,可能使用c_ispeed 成员变量和 c_ospeed 成员变量来指定串口的波特率。
CSIZE | 数据位的位掩码 |
CS5 | 5 个数据位 |
CS6 | 6 个数据位 |
CS7 | 7 个数据位 |
CS8 | 8 个数据位 |
CSTOPB | 2 个停止位,如果不设置该标志则默认是一个停止位 |
CREAD | 接收使能 |
PARENB | 使能奇偶校验 |
PARODD | 使用奇校验、而不是偶校验 |
HUPCL | 关闭时挂断调制解调器 |
CLOCAL | 忽略调制解调器控制线 |
CRTSCTS | 使能硬件流控 |
本地模式用于控制终端的本地数据处理和工作模式。
ISIG | 若收到信号字符(INTR、 QUIT 等),则会产生相应的信号 |
ICANON | 启用规范模式 |
ECHO | 启用输入字符的本地回显功能。 |
ECHOE | 若设置 ICANON,则允许退格操作 |
ECHOK | 若设置 ICANON,则 KILL 字符会删除当前行 |
ECHONL | 若设置 ICANON,则允许回显换行符 |
ECHOCTL | 若设置 ECHO,则控制字符(制表符、换行符等)会显示成“^X”,其中 X 的 ASCII 码等于给相应控制字符的 ASCII 码加上 0x40。例如,退格字符(0x08)会显示为“^H”('H'的 ASCII 码为 0x48) |
ECHOPRT | 若设置 ICANON 和 IECHO,则删除字符(退格符等)和被删除的字符都会被显示 |
ECHOKE | 若设置 ICANON,则允许回显在 ECHOE 和 ECHOPRT 中设定的 KILL字符 |
NOFLSH | 在通常情况下,当接收到 INTR、 QUIT 和 SUSP 控制字符时,会清空 输入和输出队列。如果设置该标志,则所有的队列不会被清空 |
TOSTOP | 若一个后台进程试图向它的控制终端进行写操作,则系统向该后台进程的进程组发送 SIGTTOU 信号。该信号通常终止进程的执行 |
IEXTEN | 启用输入处理功能 |
特殊控制字符是一些字符组合,如 Ctrl+C、 Ctrl+Z 等, 当用户键入这样的组合键,终端会采取特殊处理方式。
VEOF | 文件结尾符 EOF,对应键为 Ctrl+D; 该字符使终端驱动程序将输入行中的全部字符传递给正在读取输入的应用程序。如果文件结尾符是该行的第一个字符,则用户程序中的 read 返回 0,表示文件结束。 |
VEOL | 附加行结尾符 EOL,对应键为 Carriage return(CR) ; 作用类似于行结束符。 |
VEOL2 | 第二行结尾符 EOL2,对应键为 Line feed(LF) ; |
VERASE | 删除操作符 ERASE,对应键为 Backspace(BS) ; 该字符使终端驱动程序删除输入行中的最后一个字符; |
VINTR | 中断控制字符 INTR,对应键为 Ctrl+C; 该字符使终端驱动程序向与终端相连的进程发送SIGINT 信号; |
VKILL | 删除行符 KILL,对应键为 Ctrl+U, 该字符使终端驱动程序删除整个输入行; |
VMIN | 在非规范模式下,指定最少读取的字符数 MIN; |
VQUIT | 退出操作符 QUIT,对应键为 Ctrl+Z; 该字符使终端驱动程序向与终端相连的进程发送 SIGQUIT 信号。 |
VSTART | 开始字符 START,对应键为 Ctrl+Q; 重新启动被 STOP 暂停的输出。 |
VSTOP | 停止字符 STOP,对应键为 Ctrl+S; 字符作用“截流”,即阻止向终端的进一步输出。用于支持 XON/XOFF 流控。 |
VSUSP | 挂起字符 SUSP,对应键为 Ctrl+Z; 该字符使终端驱动程序向与终端相连的进程发送SIGSUSP 信号,用于挂起当前应用程序。 |
VTIME | 非规范模式下, 指定读取的每个字符之间的超时时间(以分秒为单位) TIME。 |
将终端配置为原始模式。
#include
#include
void cfmakeraw(struct termios *termios_p);
设置输入波特率。
注:设置波特率有专门的函数,用户不能直接通过位掩码来操作。
#include
#include
int cfsetispeed(struct termios *termios_p, speed_t speed);
设置输出波特率。
注:设置波特率有专门的函数,用户不能直接通过位掩码来操作。
#include
#include
int cfsetospeed(struct termios *termios_p, speed_t speed);
设置波特率。
注:设置波特率有专门的函数,用户不能直接通过位掩码来操作。
#include
#include
int cfsetspeed(struct termios *termios_p, speed_t speed);
调用 tcdrain()函数后会使得应用程序阻塞, 直到串口输出缓冲区中的数据全部发送完毕为止!
#include
#include
int tcdrain(int fd);
参数 fd:文件描述符。
返回值:调用成功时返回 0;失败将返回-1,并设置 errno。
调用该函数会清空输入/输出缓冲区中的数据。
#include
#include
int tcflush(int fd, int queue_selector);
参数 fd:文件描述符。
参数 queue_selector:
TCIFLUSH | 对接收到而未被读取的数据进行清空处理 |
TCOFLUSH | 对尚未传输成功的输出数据进行清空处理 |
TCIOFLUSH | 对尚未处理的输入/输出数据进行清空处理 |
返回值:调用成功时返回 0;失败将返回-1,并设置 errno。
调用 tcflow()函数会暂停数据传输或接收工作。
#include
#include
int tcflow(int fd, int action);
参数 fd:文件描述符
参数 action:
TCOOFF | 暂停数据输出(输出传输) |
TCOON | 重新启动暂停的输出 |
TCIOFF | 发送 STOP 字符,停止终端设备向系统发送数据 |
TCION | 发送一个 START 字符,启动终端设备向系统发送数据; |
返回值:调用成功时返回 0;失败将返回-1,并设置 errno。
获取到终端当前的配置参数。
#include
#include
int tcgetattr(int fd, struct termios *termios_p);
参数 fd:文件描述符
参数 termios_p:保存获取的配置参数。
返回值:调用成功时返回 0;失败将返回-1,并设置 errno。
将配置参数写入到终端设备,使其生效。
#include
#include
int tcsetattr(int fd, int optional_actions,const struct termios *termios_p);
参数 fd:文件描述符
参数 optional_actions:指定更改何时生效
TCSANOW | 配置立即生效 |
TCSADRAIN | 配置在所有写入 fd 的输出都传输完毕之后生效 |
TCSAFLUSH | 所有已接收但未读取的输入都将在配置生效之前被丢弃 |
参数 termios_p:将 struct termios 对象中的配置参数写入到终端设备中。
返回值:调用成功时返回 0;失败将返回-1,并设置 errno。
1)调用open()打开串口设备文件
注:调用 open()函数时,使用 O_NOCTTY 标志,该标志用于告知系统它不会成为进程的控制终端。
2)调用cfmakeraw()将终端配置为原始模式
3)接收使能
4)调用tcflush()刷新缓冲区
5)设置输入输出波特率
6)设置数据位
7)设置停止位
8)设置奇偶校验位
9)设置 MIN 和 TIME 的值
10) 调用tcsetattr()更新配置
11)调用read()/write()读写数据