本文简单介绍一下终端设备如何建立输入和输出连接。
在UNIX系统中,提供了一组可以控制terminal driver的程序。
如图所示,在应用程序与底层的Terminal Dirver之间,为一般的读写控制器。
这种方式是我们研究的主要方式。
Struct termios符合POSIX规范。termios 函数族提供了一个常规的终端接口,用于控制非同步通信端口。终端设备主要由termios结构中所定的值,以及一些函数的呼叫加以控制。
Termios结构参数值可分为:输入模式、输出模式、控制模式、本地模式、特殊控制字元五类
其定义如下:
#include <termios.h>
struct termios {
tcflag_t c_iflag; /*input mode*/
tcflag_t c_oflag; /*output mode*/
tcflag_t c_cflag; /*control mode*/
tcflag_t c_lflag; /*local mode*/
cc_t c_cc[NCCS]; /*特殊控制字元*/
}
Input mode (
输入模式)
Input mode可以在输入值传给程序之前控制其处理的方式
其中输入值可能是由序列埠或键盘的终端驱动程序所接收到的字元。
我们可以利用termios结构的c_iflag的标志来加以控制,其定义的方式皆以OR来加以组合。
c_iflag的巨集:
– BRKINT
命令行出现中断时,可产生一插断
– IGNBRK
忽略命令行中的中断
– IGNCR
忽略收到的RETURN
– ICRNL
将收到的RETURN传回下一行
– INLCR
将收到的换行符号转换为Return
– IGNPAR
忽略带有同位错误的字元
– PARMRK
标志同位错误
– INPCK
对字元进行同位检查
– ISTRIP
刪除所有汇入的字元(预设为7位元)
– IXOFF
设定软件的输入数据流(FLOW)
– IXON
设定软件的输出数据流(FLOW)
Output mode (
输出模式)
Output mode主要负责控制输出字元的处理方式
输出字元在传送到序列埠或显示器之前是如何被程序来处理。
输出模式是利用termios结构的c_oflag的标志来加以控制,其定义的方式皆以OR来加以组合。
c_oflag的巨集:
– OPOST
启用具体实现自行定义的输出处理
– ONLCR
将输出中的新行符映射为
回车(RETURN 键)-换行
– ONOCR
不在第 0 列输出回车
– ONLRET
不输出回车,换行也会执行Return
– OFILL
发送填充字符作为延时,而不是使用定时来延时
– OFDEL
填充字符是 ASCII DEL (0177)。如果不设置,填充字符则是 ASCII NULL
– OCRNL
忽将任何RETURN键输出转换为换行。
– FFDLY
进表延时掩码。取值为 FF0 或 FF1。
– NLDLY
新行延时掩码。取值为 NL0 和 NL1。
– CRDLY
回车延时掩码。取值为 CR0, CR1, CR2, 或 CR3。
– TABDLY
水平跳格延时掩码。取值为 TAB0, TAB1, TAB2, TAB3 (或 XTABS)。取值为 TAB3,即 XTABS,将扩展跳格为空格 (每个跳格符填充 8 个空格)。
– BSDLY
回退延时掩码。取值为 BS0 或 BS1。
Control mode (
控制模式)
Control mode主要用于控制终端设备的硬件设置。
利用termios结构的c_cflag的标志来加以控制
控制模式用在序列线连接到数据设备时,也可以用在与终端设备的交谈。
一般来说,改变终端设备的组态要比使用termios的控制模式来改变行(lines)的行为来得容易。
c_oflag的巨集:
– CREAD
打开字元的接受者
– CLOCAL
忽略 modem 控制线
– CS5
传送或接收字元时用5bits
– CS6
传送或接收字元时用6bits
– CS7
传送或接收字元时用7bits
– CS8
传送或接收字元时用8bits
– CSTOPB
每一字元使用2个停上位元
– HUPCL
在最后一个进程关闭设备后,降低 modem 控制线 (挂断)。
– PARENB
允许输出产生奇偶信息以及输入的奇偶校验(启用同位产生与侦测)
– PARODD
输入和输出是奇校验(使用奇同位而非偶同位)
Local mode (
局部模式)
Local mode主要用来控制终端设备不同的特色
利用termios结构里的c_lflag的标志来设定局部模式
在巨集中有两个比较重要的标志
一为ECHO,它可以让你阻止键入字元的回应。
另一个为ICANON(正规模式)标志,它可以对所接收的字元在两种不同的终端设备模式之间来回切换。
c_lflag的巨集:
– ECHO
回显输入字符
– ECHOE
如果同时设置了 ICANON,字符 ERASE 擦除前一个输入字符,WERASE 擦除前一个词。
– ECHOK
如果同时设置了 ICANON,字符 KILL 删除当前行。
– ECHONL
如果同时设置了 ICANON,回显字符 NL,即使没有设置 ECHO。
– ICANON
启用标准模式 (canonical mode)。允许使用特殊字符 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, 和 WERASE,以及按行的缓冲。
– ISIG
当接受到字符 INTR, QUIT, SUSP, 或 DSUSP 时,产生相应的信号。
– IEXTEN
启用实现自定义的输入处理。这个标志必须与 ICANON 同时使用,才能解释特殊字符 EOL2,LNEXT,REPRINT 和 WERASE,IUCLC 标志才有效。(启动特殊函数之执行)
– NOFLSH
关闭queue中的flush
– TOSTOP
向试图写控制终端的后台进程组发送 SIGTTOU 信号(传送欲写入的信息到后台处理)。
特殊控制字元
可提供使用者设定一些特殊的功能
如Ctrl+C的字元组合。
特殊控制字元主要是利用termios结构里c_cc的阵列成员来做设定。
c_cc阵列主要用于正规与非正规两种环境,但要注意的是正规与非正规不可混为一谈。
正规模式的c_cc阵列:
– VEOF
EOF字元
– VEOL
EOL字元
– VERASE
ERASE字元
– VINTR
INTR字元
– VKILL
KILL字元
– VQUIT
QUIT字元
– VSUSP
SUSP字元
– VSTART
START字元
– VSTOP
STOP字元
非正规模式的c_cc阵列:
– VINTR
INTR字元
– VQUIT
QUIT字元
– VSUSP
SUSP字元
– VSTART
START字元
– VSTOP
STOP字元
– VMIN
MIN值
– VTIME
TIME值
下表为一些字元的描述
字元
|
描述
|
INTR
|
使terminal driver传送SIGINT信号到终端。
|
QUIT
|
使terminal driver传送SIGQUIT信号到终端。
|
ERASE
|
使terminal driver刪除此行的最后一个字元。
|
KILL
|
使terminal driver刪除整行。
|
EOF
|
使terminal driver将此行所有的字元传送到读取输入的应用程序,如果此行为空行,则read呼叫就传回0字元,就和读到档尾一样。
|
EOL
|
此一字元就像一个行的终止符号。
|
SUSP
|
使terminal driver传送SIGSUSP信号到终端相连接的process。
|
STOP
|
此一字元可以防止对终端的进一步输入,主要被用来支援XON/XOFF的数据流控制。
|
START
|
此字元在STOP字元之后重新开始输出。
|
MIN与TIME
MIN主要是表示能满足read的最小字元数。
TIME是代表一个十分之一秒的timer。
MIN与TIME组合有以下四种:
1、 MIN = 0 , TIME =0
有READ立即回传
否则传回 0 ,不读取任何字元
2、 MIN = 0 , TIME >0
READ传回读到的字元,或在十分之一秒后传回TIME
若来不及读到任何字元,则传回0
3、 MIN > 0 , TIME =0
READ会等待,直到MIN字元可读
4、 MIN > 0 , TIME > 0
每一格字元之间计时器即会被启动
READ会在读到MIN字元,传回值或TIME的字元计时(1/10秒)超过时将值传回
其他重要的函数:tcgetattr、tcsetatt
一个为tcgetattr函数
此呼叫以将当前终端介质的参数值写入termios_p所指向的结构
其原型如下:
#include <termios.h>
int tcgetattr(int fd,struct termois termios_p);
/*取得终端介质初始值*/
另一个为tcsetatt函数
此函数可以依actions栏位来对终端介质的参数做设定
其原型如下:
int tcsetattr(int fd,int actions,const struct
termios *termios_p);
其action栏位有TCSANOW、TCSADRAIN及TCSAFLUSH三种可能:
Action
栏位
|
描述
|
TCSANOW
|
立即将值改变
|
TCSADRAIN
|
当前输出完成时将值改变
|
TCSAFLUSH
|
同TCSADRAIN,但会舍弃当前所有值
|
Speed Setup (终端速度设定)
终端的速度设定可以由特定的函数来设定,其输入与输出的设定可以分开来处理。
其函数的原型如下:
#include <termios.h>
speed_t cfgetispeed(const struct termios *) /*取得输入速度*/
speed_t cfgetospeed(const struct termios *)/*取得输出速度*/
int cfsetispeed(struct termios *,speed_t speed)/*设定输入速度*/
int cfsetospeed(struct termios *,speed_speed) /*设定输出速度*/
上面的四个函数只能作用在termios结构,而不可以直接作用于port。
如果要重新设定速度:
– 则必须先利用tcgetattr函数取得目前的设定,
– 然后使用以上函数来设定速度
– 最后再以tcsetattr函数来将设定值写入。
函数中的speed参数以下列方式来表示:
– B0
挂断终端
– B1200
1200 baud
– B2400
2400 baud
– B9600
9600 baud
– B19200
19200 baud
– B38400
38400 baud
较早的Linux系统虽然没有更快地设定值,但可以使用setserial命令来设定其他如57600、115200等速度。
可直接用在档案描述子(descriptor)的函数
其函数原型如下:
#include <termios.h>
int tcdrain(int fd)
tcdrain函数可以让呼叫的程式等待直到所有写入 fd 引用的对象的输出都被传输。
int tcflow(int fd, int flowtype)
tcflow函数用来挂起或重新启动输出 fd 引用的对象上的数据传输或接收,取决于 action 的值。
int tcflush(int fd, int in_out_selector)
tcflush函数可以丢弃要写入 引用的对象,但是尚未传输的数据,或者收到但是尚未读取的数据,取决于 queue_selector 的值
另一好用的函数 cfmakeraw(struct termios*)
只要呼叫此函数则都会自动产生如下的设定:
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代表termios structure*/
归纳一下:终端的连接步骤:
利用
timeios
结构设置串口异步通信端口的工作模式
1、
打开串口设备,确认设备是否存在(
fd>0?
)
2、
使得当前设置指向指定的端口
(
tcgetattr
函数)
3、
设置终端传输的各种模式的参数(输入、输出模式、设置工作频率等等)
4、
使以上的参数设置写入设定(
tcsetattr
函数)
5、
写一个
简单的串口设备数据访问程序验证。(
write(),read()
)