2

Linux下串口编程​_嵌入式技术干货的技术博客_51CTO博客

 1 year ago
source link: https://blog.51cto.com/u_11822586/5592411
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1.1 串口的名称

1.1.2 PC机的串口节点名称

串口的节点在/dev下边。

Linux下串口编程​_串口

计算机本身的串口: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);​

函数功能: 设置输出波特率.​

波特率可以设置以下的值:

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 设置串口属性参数

进行串口编程时,最重要的是tcgetattrtcsetattr这两个函数,这两个函数可以设置串口的参数和获取串口已经配置的参数。​

比如: 波特率、停止位、数据位、奇偶校验位等. ​

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 );​


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK