串口编程(一) :理论基础

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

mark

如果你使用虚拟机开发串口程序,可以采用windows的文件代替串口,然后windows下打开该串口既可以看到串口输出的内容,设置方式如下:
VM --> Setting :
mark
如果没有串口,可关机之后自行添加!

1.2 控制台终端

tty1-tty6为控制台终端,即无图形界面的命令行输入模式。一般使用Alt+[F1-F6]可进行终端切换。
查看tty[1-6]终端的属性如下:
mark

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下依次创建一个虚拟终端;相反,关闭将减少一个。
mark

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 输入选项

mark

2.2.2 c_oflag 输出选项

mark

2.2.3 c_cflag 控制选项

控制选项设置串行通信的波特率、数据位长度、停止位长度、奇偶校验等。
mark

2.2.4 c_lflag 本地选项

mark
设置输入字符的方式:

  • 标准模式:回显由客户端完成,用户输入的字符首先缓存到buf中,直到用户输入回车或换行后发送到服务端;用户每输入一个字符,直接本地回显。
  • 原始模式:回显由服务器完成,用户每输入一个字符,立刻发送到服务端,服务端将该字符回显到客户端。

2.2.5 c_cc 控制字符

mark

2.3 设置波特率

所谓波特率,即单位时间内传输的字符数(Byte/s).
mark
串口通信两端的波特率必须设置一致,否则无法通信

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
posted @ 2018-03-31 17:10  Jimmy_Nie  阅读(1339)  评论(0编辑  收藏  举报