Linux下的串口开发
2021-04-25
关键字:串行接口、串口属性设置
1、什么是串口?
串口(Serial Port)是一种常见的计算机接口,由于它所需的连线少、通信控制简单且成本低廉,在行业内,尤其是嵌入式领域内得到了广泛的使用。
串口是一种通信方式,其包含了多种接口标准,其中最常见的是:RS232标准。即9针D型接口标准。如下图所示:
RS232各针序号如下图所示:
DTE即俗话说的“串口公头”,DCE即“串口母头”。
9针中每一针都有其用途,具体如下所示:
针号 | 功能说明 | 缩写 |
1 | 数据载波检测 | DCD |
2 | 接收数据 | RXD |
3 | 发送数据 | TXD |
4 | 数据终端准备 | DTR |
5 | 信号地 | GND |
6 | 数据设备准备好 | DSR |
7 | 请求发送 | RTS |
8 | 清除发送 | CTS |
9 | 振铃提示 | BELL |
虽然RS232标准有9根数据针脚,但实际上仅需要凑齐RXD, TXD与GND三根针脚就能完成最基础的数据通信了。这也是为什么在嵌入式开发板中见到的串口经常都是三根针或四根针的原因。三根针的就表明仅需RXD, TXD与GND,四根针则多了一个VCC针用于供电。
上表每一根针的具体含义都包含在RS232标准的细节之中,因嵌入式领域很少会用到完整的9针串口通信,因此就不再此赘述了,对我们而言只需要知道最少三根针即可完成通信即可。
一般而言,串口协议所支持的最高数据传输速率是 115kbps,即115200的波特率,换算成字节率就是14.4kb/s的传输速率。但仍有一些增强型的串口可以达到更高的通信速率。不过串口高速传输也是有代价的,通信速率越高,有效传输距离就越近,成本也越高。一般而言,串口的通信距离不应超过12米。远距离串口通信应借助Modem设备中继传输。
2、串口开发
Linux对串口已经有了很好的支持,串口在Linux中是以字符设备的形式存在的。通常在 /dev/ 目录下 ttyS 前缀的设备节点即表示串口设备,不同串口设备以顺序增长的数字来区分。ttyS0 即表示第一个串口设备,一般都是系统串口设备,即嵌入式开发板中用来查看板子打印的设备节点。
因为串口设备是以字符设备的形式存在的,因此我们就可以以POSIX形式的接口来对它做IO操作了,一般就是 open, read, write和close,就不再详细介绍了。
这里重点关注下串口的属性设置。
串口属性一般包括:波特率、数据位、停止位和奇偶校验位等参数。
在Linux系统中用结构体 termios 来存储这些参数,这一结构体位于 termios.h 中,具体定义如下:
struct termios { unsigned short c_iflag; //输入模式标志 unsigned short c_oflag; //输出模式标志 unsigned short c_cflag; //控制模式标志 unsigned short c_lflag; //本地模式标志 unsigned char c_line; //线路规则 unsigned char c_cc[NCC]; //控制字 };
其中每个字段的常用值如下表示所:
c_iflag字段:
IGNPAR | 忽略帧错误和奇偶校验错误 |
INPCK | 启用输入奇偶检测 |
ISTRIP | 去掉第8位 |
INLCR | 将输入中的NL翻译为CR |
IGNCR | 忽略输入中的回车 |
ICRNL | 将输入中的回车翻译为新行(除非设置了IGNCR) |
IXON | 启用输出的XON/XOFF流控制 |
IXOFF | 启用输入的XON/XOFF流控制 |
c_oflag字段:
ONLCR | 将输出中的新行符映射为回车-换行 |
OCRNL | 将输出中的回车映射为新行符 |
ONOCR | 不在第0列输出回车 |
ONLRET | 不输出回车 |
OFILL | 发送填充字符作为延时,而不是使用定时来延时。 |
c_cflag字段:
CSIZE | 字符长掩码。取值为CS5,CS6,CS7或CS8 |
CSTOPB | 设置两个停止位,而不是一个。 |
CREAD | 打开接受者 |
PARENB | 允许输出产生奇偶信息以及输入的奇偶校验 |
PARODD | 输入和输出的奇校验 |
CLOCAL | 忽略modem控制线 |
c_lflag字段:
ISIG | 当接受到字符INTR, QUIT, SUSP或DSUSP时,产生相应的信号 |
ICANON | 启用标准模式,允许使用特殊字符EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS和WERASE,以及按行的缓冲 |
ECHO | 回显输入字符 |
ECHOE | 如果同时设置了ICANON,字符ERASE探险前一个输入字符,WERASE擦除前一个词 |
ECHOK | 如果同时设置了ICANON,字符KILL删除当前行 |
ECHONL | 如果同时设置了ICANON,回显字符NL,即使没有设置ECHO |
NOFLSH | 禁止在产生SIGINT, SIGQUIT和SIGSUSP信号时刷新输入和输出队列 |
TOSTOP | 向试图写控制终端的后台进程组发送SIGTTOU信号 |
c_cc字段成员下标值及其所表示的含义:
VINTR | (003,ETX,ctrl+c,或者0177, DEL)中断字符。发出SIGINT信号。当设置ISIG时可被识别,不再作为输入传递 |
VQUIT | (034, FS, crtl+\)退出字符。发出SIGQUIT信号。当设置ISIG时可被识别,不再作为输入传递 |
VERASE | (0177, DEL, 或者010, BS, ctrl+H)删除字符。删除上一个还没有删掉的字符,但不删除上一个EOF或者行首。当设置ICANON时可被识别,不再作为输入传递 |
VKILL | (025, NAK, ctrl-U, 或者ctrl+X或@)终止字符。删除自上一个EOF或行首以来的输入。当设置ICANON时可被识别,不再作为输入传递 |
VEOF | (004, EOT, ctrl+D)文件尾字符。更精确地说,这个字符使得tty缓冲中的内容被送到等待输入的用户程序中,而不必等到EOL。如果它是一行的第一个字符,那么用户程序的read()将返回0,指示读到了EOF。当设置ICANON时可被识别,不再作为输入传递 |
VMIN | 非canonical模式读的最小字符数。 |
VEOL | (0, NUL)附加的行尾字符。当设置ICANON时可被识别 |
VTIME | 非canonical模式读时的延时,以十分之一秒为单位。 |
VSTART | (021, DC1, Ctrl+Q)开始字符。重新开始被Stop字符中止的输出。当设置IXON时可被识别,不再作为输入传递 |
VSTOP | (023, DC3, ctrl+S)停止字符。停止输出,直到输入Start字符。当设置IXON时可被识别,不再作为输入传递。 |
VSUSP | (032, SUB, ctrl+Z)挂起字符。发送SIGTSTP信号。当设置ISIG时可被识别,不再作为输入传递。 |
根据上表,需要什么功能或属性,则直接在相应字段上相应位进行上述位的与或非与即可。
termios.h 中为设置串口属性还提供了以下函数:
#include <termios.h> #include <unistd.h> int tcgetattr(int fd, struct termios* tp); int tcsetattr(int fd, int optional_actions, struct termios* tp); int tcsendbreak(int fd, int duration); int tcdrain(int fd); int tcflush(int fd, int queue_selector); int tcflow(int fd, int action); int cfmakeraw(struct termios* tp); speed_t cfgetispeed(struct termios* tp); speed_t cfgetospeed(struct termios* tp); int cfsetispeed(struct termios* tp, speed_t speed); int cfsetospeed(struct termios* tp, speed_t speed);
tcgetattr()与tcsetattr()两个函数用于读取/设置指定串口的属性。 optional_actions 表示设置的参数什么时候起作用:TCSANOW--立即生效;TCSADRAIN--fd上所有的输出都被传输后生效;TCSAFLUSH--所有引用fd对象的数据都在传输出去后生效。
cfmakeraw()函数设置终端为原始数据方式,即恢复出厂设置。
最后四个函数用于读取/设置输入/输出的波特率。speed_t类型的值为 B0, B50, B1200, ... , B19200, B38400, ... , B115200, B230400。分别对应指定的波特率。