串口编程(一) :理论基础
1. 终端设备
Linux具备多种终端设备类型:
- 当前终端
- 前台控制台终端
- 串口
- 虚拟终端
/dev/tty /dev/tty 5 0 system:/dev/tty //当前终端
/dev/console /dev/console 5 1 system:console //前台控制台终端
/dev/ptmx /dev/ptmx 5 2 system //用于创建虚拟终端
/dev/vc/0 /dev/vc/0 4 0 system:vtmaster
rfcomm /dev/rfcomm 216 0-255 serial
ttyprintk /dev/ttyprintk 5 3 console
serial /dev/ttyS 4 64-111 serial //物理串口
pty_slave /dev/pts 136 0-1048575 pty:slave //虚拟终端从设备
pty_master /dev/ptm 128 0-1048575 pty:master //虚拟终端主设备
以上这些终端设备统一由TTY(TeleTYpewriter进行管理,从而可以屏蔽硬件的实现。
1.1 物理串口
物理串行接口,即 /dev/ttyS[n],(ttyS0为com1,ttyS1为com2),其主、从设备号(主设备号:系统用来确定驱动程序(如USB、硬盘设备);从设备号:驱动程序用来确定具体的设备)如下:
jimmy@jimmy-vm:~$ cat /sys/class/tty/ttyS0/dev
4:64
jimmy@jimmy-vm:~$ ls /dev/ttyS0 -l
crw-rw---- 1 root dialout 4, 64 3月 31 14:23 /dev/ttyS0
如果你使用虚拟机开发串口程序,可以采用windows的文件代替串口,然后windows下打开该串口既可以看到串口输出的内容,设置方式如下:
VM --> Setting :
如果没有串口,可关机之后自行添加!
1.2 控制台终端
tty1-tty6为控制台终端,即无图形界面的命令行输入模式。一般使用Alt+[F1-F6]可进行终端切换。
查看tty[1-6]终端的属性如下:
1.3 虚拟终端
现在一般都是通过网络连接Linux服务器,如telnet和SSH;另外,有时候在图形界面下打开一个虚拟终端(如shell口);这两类情况的终端类型皆为虚拟终端。
1.3.1 /dev/ptmx虚拟设备
该设备用于创建虚拟网络终端设备master/slave配对设备。要打开一个未使用的虚拟终端,通过调用posix_openpt(),来打开设备/dev/ptmx,每次open这个文件,会返回一个独立的master设备文件描述符,通过它可以找到slave设备,且slave设备会在/dev/pts目录下被创建。
1.3.2 /dev/pts虚拟终端
通过网络telnet或SSH到linux,将在/dev/pts下依次创建一个虚拟终端;相反,关闭将减少一个。
1.4 当前终端
1.4.1 当前控制终端
/dev/console代表当前系统前台所使用的实际控制台终端(tty1-tty6).
1.4.2 当前终端
/dev/tty,无论是控制终端(tty1-tty6),还是通过SSH连接的虚拟终端,它都代表自己。
2. 终端属性
为了控制终端正常的工作,终端的属性一般包含如下:
- 输入属性:由终端驱动程序控制输入属性
- 输出属性:由终端驱动程序控制输出属性,如新行映射成CR/NL等
- 控制属性:影响物理串口的特征,如:停止位、波特率等
- 本地属性:影响驱动程序与用户之间的界面,如:向终端发送信息是否回显
- 线路规程属性:用来设置是否为标准的线路规程
- 控制字符:设置专用字符
可以使用 stty -a
来查看终端所有属性:
jimmy@jimmy-vm:~$ stty -a
speed 38400 baud; rows 59; columns 207; line = 0;
intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc
2.1 设置/获取终端属性
为了方便统一管理,Linux使用了一个结构体管理这些属性:
/* /usr/include/asm-generic/termbits.h */
#define NCC 8
struct termio {
unsigned short c_iflag; /* input mode flags */
unsigned short c_oflag; /* output mode flags */
unsigned short c_cflag; /* control mode flags */
unsigned short c_lflag; /* local mode flags */
unsigned char c_line; /* line discipline */
unsigned char c_cc[NCC]; /* control characters */
};
2.1.1 tcgetattr()
#include <termios.h>
int tcgetattr(int fd, struct termios *termios_p)
参数:
1. fd: 打开的终端设备的文件描述符
2. termios_p:存储获取的属性信息
返回值:
1. 0 on success
2. 1 on failure
2.1.2 tcsetattr()
#include <termios.h>
int tcsetattr(int fd, int optional_actions, const struct termios *termios_p)
参数:
1. fd: 打开的终端设备的文件描述符
2. optional_actions
2.1 TCSANOW: 改变立刻生效;
2.2 TCSADRAIN: 改变在所有写入fd的输出都被传输后生效;
2.3 TCSAFLUSH: 改变在所有写入fd所指向的对象的输出传输后生效;所有已接收但未读入的消息在改变发生前丢弃;
3. termios_p:存储设置的属性信息
返回值:
1. 0 on success
2. 1 on failure
2.2 终端属性选项
2.2.1 c_iflag 输入选项
2.2.2 c_oflag 输出选项
2.2.3 c_cflag 控制选项
控制选项设置串行通信的波特率、数据位长度、停止位长度、奇偶校验等。
2.2.4 c_lflag 本地选项
设置输入字符的方式:
- 标准模式:回显由客户端完成,用户输入的字符首先缓存到buf中,直到用户输入回车或换行后发送到服务端;用户每输入一个字符,直接本地回显。
- 原始模式:回显由服务器完成,用户每输入一个字符,立刻发送到服务端,服务端将该字符回显到客户端。
2.2.5 c_cc 控制字符
2.3 设置波特率
所谓波特率,即单位时间内传输的字符数(Byte/s).
串口通信两端的波特率必须设置一致,否则无法通信。
typedef unsigned char cc_t;
typedef unsigned int speed_t;
typedef unsigned int tcflag_t;
/*获取输入、输出波特率*/
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);
返回值:
0: on success
1: on failure
2.4 设置帧数据位宽
可以设置每帧的数据位宽为5-8bit,通信前,双方必须约定好位宽。
struct termios options;
options.c_cflag &= ~CSIZE; //清除现有的数据位宽
options.c_cflag |= CS8; //设置数据位宽为8bit
2.5 设置奇偶校验
奇偶校验为串行通信中简单的差错校验机制;通信前,通信双发约定使用奇校验还是偶校验,多少位的校验位。
设置为8N1, 即数据位宽8bit,无奇偶校验,1位停止位:
struct termios options;
options.c_cflag &= ~PARENB; //去除奇偶校验
options.c_cflag &= ~CSTOPB; //清除停止位
options.c_cflag &= ~CSIZE; //清除现有的数据位宽
options.c_cflag |= CS8; //设置位宽为8bit
设置为7E1, 即数据位宽7bit,偶(EVEN)校验,1位停止位:
struct termios options;
options.c_cflag |= PARENB; //开启奇偶校验
ptions.c_cflag &= ~PARODD; //关闭奇校验
options.c_cflag &= ~CSTOPB; //清除停止位
options.c_cflag &= ~CSIZE; //清除现有的数据位宽
options.c_cflag |= CS7; //设置位宽为8bit
设置为7O1, 即数据位宽7bit,奇(odd)校验,1位停止位:
struct termios options;
options.c_cflag |= PARENB; //开启奇偶校验
ptions.c_cflag |= PARODD; //开启奇校验
options.c_cflag &= ~CSTOPB; //清除停止位
options.c_cflag &= ~CSIZE; //清除现有的数据位宽
options.c_cflag |= CS7; //设置位宽为8bit
2.6 数据流控制
使用何种方式来标记数据传输的开始和结束。
设置 | 具体代码 |
---|---|
不使用数据流控制 | options.c_cflag &= ~CRTSCTS |
硬件 | options.c_cflag |
软件 | options.c_cflag |