1.1 串口的名称
1.1.2 PC机的串口节点名称
串口的节点在/dev下边。
计算机本身的串口:ttySx
USB串口:ttyUSBx
虚拟机右下角可以将USB串口设备挂载到dev下。
1.1.2 串口测试命令: microcom
命令使用方法:
[root@wbyq ]# microcom --help
BusyBox v1.23.2 (2020-09-04 15:21:06 CST) multi-call binary.
Usage: microcom [-d DELAY] [-t TIMEOUT] [-s SPEED] [-X] TTY
Copy bytes for stdin to TTY and from TTY to stdout
-d Wait up to DELAY ms for TTY output before sending every
next byte to it
-t Exit if both stdin and TTY are silent for TIMEOUT ms
-s Set serial line to SPEED
-X Disable special meaning of NUL and Ctrl-X from stdin
参数如下:
-d 表示延时时间。发送一个字节后等待一段时间发送下一个字节.
-t 表示超时时间,超多少时间就自动退出。单位为ms
-s 表示波特率。
-X 禁用stdin中NUL和Ctrl-X特殊含义。
TTY 指定串口设备节点。如SAC0。
测试命令如下:
[root@wbyq ]# microcom -t 5000 -s 115200 /dev/ttySAC2
1.2 串口基本操作
1.2.1 打开串口
打开串口连接的时候,程序在open函数中除了Read+Write模式以外还需要指定O_NOCTTY选项:
fd=open("/dev/ttyUSB0",O_RDWR|O_NOCTTY);
标志O_NOCTTY告诉系统这个程序不会成为这个端口上的“控制终端”。如果不这样做的话,所有的输入,比如键盘上过来的Ctrl+C中止信号等等,会影响到你的进程。但是通常情况下,用户程序不会使用这个选项。
1.2.2向串口写数据
Linux下一切皆文件,写数据直接使用write、fputs等函数即可直接向串口发送数据。
n = write(fd, "1234\r\n", 6);
1.2.3从串口读数据
读数据的时候需要找准时机,需要知道串口何时有数据,可以使用linux下的轮询机制进行监控串口的文件描述符。
比如: poll、select、epoll机制等。也可以使用异步通知机制:fasync。
cnt=read(fd,buff,1024);
1.2.4 关闭串口
可以使用close系统调用关闭串口: close(fd);
1.3 配置串口
1.3.1 刷新输入输出缓冲区
函数原型: int tcflush(int fd, int queue_selector);
函数功能: 丢弃写入或者收到但是没有读取的数据.
函数参数:
int文件描述符.
int queue_selector:
TCIFLUSH
刷新已接收但未读取的数据。
TCOFLUSH
刷新已写入但未传输的数据。
TCIOFLUSH 刷新已接收但未读取的数据,以及已写入但未传输的数据。
1.3.2 设置输入输出波特率
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);
函数功能: 设置输出波特率.
波特率可以设置以下的值:
B0
B50
B75
B110
B134
B150
B200
B300
B600
B1200
B1800
B2400
B4800
B9600
B19200
B38400
B57600
B115200
B230400
波特率设置示例:
struct termios uart_cfg;
memset(&uart_cfg,0,sizeof(struct termios));
cfsetispeed(&uart_cfg,B115200); //设置输入波特率
cfsetospeed(&uart_cfg,B115200); //设置输出波特率
1.3.3 设置串口属性参数
进行串口编程时,最重要的是tcgetattr和tcsetattr这两个函数,这两个函数可以设置串口的参数和获取串口已经配置的参数。
比如: 波特率、停止位、数据位、奇偶校验位等.
int tcgetattr(int fd, struct termios *termios_p);
函数功能: 获取终端属性参数.
int tcsetattr(int fd, int optional_actions,const struct termios *termios_p);
函数功能: 设置终端属性参数.
参数介绍:
int文件描述符.
int设置更改的时机.
TCSANOW
更改立即发生。
TCSADRAIN
在传输完所有写入fd的输出之后,将发生更改。
TCSAFLUSH
将所有输出写入fd引用的对象之后,将发生更改发送,并且所有已接收但未读取的输入将被丢弃在进行更改之前。
const struct termios *termios_p : 保存配置属性的结构体
配置示例:
struct termios uart_cfg;
memset(&uart_cfg,0,sizeof(struct termios));
cfsetispeed(&uart_cfg,B115200); //设置输入波特率
cfsetospeed(&uart_cfg,B115200); //设置输出波特率
uart_cfg.c_cflag &= ~CSIZE; //清除串口的数据位
uart_cfg.c_cflag |= CS8; //配置串口的数据位为 8 位
uart_cfg.c_cflag &= ~CSTOPB; //配置的停止位:1 个
uart_cfg.c_cflag |= CREAD; //使能接收
uart_cfg.c_cflag &= ~PARENB; //不使用奇偶校验
tcsetattr(fd,TCSANOW,&uart_cfg);
1.4 struct termios 结构体介绍
Struct termios结构体包含了以下成员:
tcflag_t c_iflag; /* 输入模式设置 */
tcflag_t c_oflag; /* 输出模式设置 */
tcflag_t c_cflag; /* 控制模式设置 */
tcflag_t c_lflag; /* 本地模式 */
cc_t c_cc[NCCS]; /* 控制字符 */
1.4.1 c_iflag
输入模式标志,控制终端输入方式
c_iflag参数表:
键 值 | 说 明 |
IGNBRK | 忽略BREAK键输入 |
BRKINT | 如果设置了IGNBRK,BREAK键输入将被忽略 |
IGNPAR | 忽略奇偶校验错误 |
PARMRK | 标识奇偶校验错误 |
INPCK | 允许输入奇偶校验 |
ISTRIP | 去除字符的第8个比特 |
INLCR | 将输入的NL(换行)转换成CR(回车) |
IGNCR | 忽略输入的回车 |
ICRNL | 将输入的回车转化成换行(如果IGNCR未设置的情况下) |
IUCLC | 将输入的大写字符转换成小写字符(非POSIX) |
IXON | 允许输出时对XON/XOFF流进行控制 |
IXANY | 输入任何字符将重启停止的输出 |
IXOFF | 允许输入时对XON/XOFF流进行控制 |
IMAXBEL | 当输入队列满的时候开始响铃 |
1.4.2 c_oflag
输出模式标志,控制终端输出方式。
c_oflag参数:
键 值 | 说 明 |
OPOST | 处理后输出 |
OLCUC | 将输出的小写字符转换成大写字符(非POSIX) |
ONLCR | 将输出的NL(换行)转换成CR(回车)及NL(换行) |
OCRNL | 将输出的CR(回车)转换成NL(换行) |
ONOCR | 第一行不输出回车符 |
ONLRET | 不输出回车符 |
OFILL | 发送填充字符以延迟终端输出 |
OFDEL | 以ASCII码的DEL作为填充字符,如果未设置该参数,填充字符为NUL |
NLDLY | 换行输出延时,可以取NL0(不延迟)或NL1(延迟0.1s) |
CRDLY | 回车延迟,取值范围为:CR0、CR1、CR2和 CR3 |
TABDLY | 水平制表符输出延迟,取值范围为:TAB0、TAB1、TAB2和TAB3 |
BSDLY | 空格输出延迟,可以取BS0或BS1 |
VTDLY | 垂直制表符输出延迟,可以取VT0或VT1 |
FFDLY | 换页延迟,可以取FF0或FF1 |
1.4.3 c_cflag
控制模式标志,指定终端硬件控制信息.
c_cflag参数:
键 值 | 说 明 |
CBAUD | 波特率(4+1位)(非POSIX) |
CBAUDEX | 附加波特率(1位)(非POSIX) |
CSIZE | 数据位长度,取值范围为CS5、CS6、CS7或CS8 例如: newtio.c_cflag &= ~CSIZE;//清除串口的数据位 newtio.c_cflag |= CS8; //配置串口的数据位为8位 |
CSTOPB | 设置两个停止位 例如: newtio.c_cflag |= CSTOPB; //配置的停止位:2个 newtio.c_cflag &= ~CSTOPB; //配置的停止位:1个 |
CREAD | 使用接收功能 例如: newtio.c_cflag |= CREAD; |
PARENB | 使用奇偶校验 例如: newtio.c_cflag &= ~PARENB 不使用奇偶校验 newtio.c_cflag |= PARENB 使用奇偶校验 |
PARODD | 对输入使用奇偶校验,对输出使用偶校验 |
HUPCL | 关闭设备时挂起 |
CLOCAL | 忽略调制解调器线路状态 |
CRTSCTS | 使用RTS/CTS流控制 |
1.4.4 c_lflag
本地模式标志,控制终端编辑功能
c_lflag参数:
键 值 | 说 明 |
ISIG | 当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号 |
ICANON | 使用标准输入模式. 例如: options.c_lflag &= ~(ICANON ); //不使用规范模式(非标准模式) options.c_lflag |= ICANON; //使用规范模式 (标准模式) 默认情况下,终端采用的是标准模式,终端收到数据时,是按照行读取的。终端需要收到\n才会返回。 |
XCASE | 在ICANON和XCASE同时设置的情况下,终端只使用大写。 |
ECHO | 显示输入字符 |
ECHOE | 如果ICANON同时设置,ERASE将删除输入的字符 |
ECHOK | 如果ICANON同时设置,KILL将删除当前行 |
ECHONL | 如果ICANON同时设置,即使ECHO没有设置依然显示换行符 |
ECHOPRT | 如果ECHO和ICANON同时设置,将删除打印出的字符(非POSIX) |
TOSTOP | 向后台输出发送SIGTTOU信号 |
IGNCR | 忽略输入的回车。 例如: options.c_iflag |= IGNCR; //忽略回车字符 options.c_iflag &= ~ IGNCR; //不忽略回车字符 |
ICRNL | 在输入中将回车转换为换行符(除非设置了IGNCR) 例如: options.c_iflag |= ICRNL; //在输入中将回车转换为换行符 options.c_iflag &= ICRNL;//不转换 |
1.4.5 c_cc[NCCS]
控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等。
c_cc支持的控制字符:
宏 | 说 明 | 宏 | 说 明 |
VINTR | Interrupt字符 | VEOL | 附加的End-of-file字符 |
VQUIT | Quit字符 | VTIME | 非规范模式读取时的超时时间 (单位是百毫秒) |
VERASE | Erase字符 | VSTOP | Stop字符 |
VKILL | Kill字符 | VSTART | Start字符 |
VEOF | End-of-file字符 | VSUSP | Suspend字符 |
VMIN | 非规范模式读取时的最小字符数 |
在串口编程模式下,open未设置O_NONBLOCK或O_NDELAY的情况下。
c_cc[VTIME]和c_cc[VMIN]映像read函数的返回。
VTIME定义等待的时间,单位是百毫秒(通常是一个8位的unsigned char变量,取值不能大于cc_t)。
VMIN定义了要求等待的最小字节数,这个字节数可能是0。
如果VTIME取0,VMIN定义了要求等待读取的最小字节数。函数read()只有在读取了VMIN个字节的数据或者收到一个信号的时候才返回。
如果VMIN取0,VTIME定义了即使没有数据可以读取,read()函数返回前也要等待几百毫秒的时间量。这时,read()函数不需要像其通常情况那样要遇到一个文件结束标志才返回0。
如果VTIME和VMIN都不取0,VTIME定义的是当接收到第一个字节的数据后开始计算等待的时间量。如果当调用read函数时可以得到数据,计时器马上开始计时。如果当调用read函数时还没有任何数据可读,则等接收到第一个字节的数据后,计时器开始计时。函数read可能会在读取到VMIN个字节 的数据后返回,也可能在计时完毕后返回,这主要取决于哪个条件首先实现。不过函数至少会读取到一个字节的数据,因为计时器是在读取到第一个数据时开始计时的。
如果VTIME和VMIN都取0,即使读取不到任何数据,函数read也会立即返回。同时,返回值0表示read函数不需要等待文件结束标志就返回了。
例如:
newtio.c_cc[VTIME]=1; //表示间隔时间100毫秒. 100毫秒间隔外的数据就认为是新的一帧数据。 newtio.c_cc[VMIN]=10;//表示一次最大读取10个数据,如果接受了100个数据,read函数要读取10次才可以读取完毕,每次读取10个数据。 |
1.5 终端I/O的两种输入处理模式
Linux系统终端I/O有两种输入处理模式:
(1) 规范方式输入处理。在这种方式中,终端输入以行为单位进行处理。对于每个读要求,终端驱动程序最多返回一行。
(2) 非规范方式输入处理。输入字符不以行为单位进行装配,如果不作特殊处理,则默认方式是规范方式。
想要修改非规范方式输入处理,可以修改c_lflag本地模式选项,将ICANON属性去掉,ICANON就是规范化方式。 例如: options.c_lflag &= ~(ICANON );