Linux下串口输入输出有两种工作模式:规范模式(Canonical Mode)和非规范(Noncanonical Mode)模式。规范模式中串口数据按行为单位处理,应用程序每次读操作,驱动最多返回一行;每次写操作,驱动以行为单位接收。非规范模式,数据按字节流处理。默认情况下,如不做特殊处理,异步串口的工作模式为规范模式,然而在嵌入式Linux编程中,异步串口通信几乎是非规范模式,因此本文只讲解该模式下的通信。

每个串口设备在内核中都有一个输入队列和一个输出队列,以及一个行规程(line discipline)模块。应用程序编程中对串口的配置主要是控制这三个部分的一些特性以及硬件寄存器。在Linux系统里,串口的这些特性都包含在termios结构中,可以通过命令man termios查看所有细节以及相关函数接口说明。在Linux下该结构如下:

struct termios {
unsinged int c_iflag;
unsinged int c_oflag;
unsinged int c_cflag;
unsinged int c_lflag;
unsinged char c_line;
unsinged char c_cc[32];
unsinged int c_ispeed;
unsinged int c_ospeed;
};

其中,c_iflag表示输入控制标识,c_oflag表示输出控制标识,c_cflag表示物理串行线的控制标识,c_lflag表示驱动与应用程序交互行为控制的标识,c_cc表示可更改的控制字符,c_ispeed和c_ospeed分别表示输入输出波特率,c_line表示行规程控制字符。所有这些控制标识非常多,大约有70种左右,从这里可以看出,串口终端的编程是非常复杂的,至于每个标识的具体意义可以查看手册。

与日常串口编程相关的串口函数有如下六个:

int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions,const struct termios *termios_p);
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);

以上函数都是Linux中针对串口编程的ioctl系统调用的封装。tcgetattr用获取串口各种控制标识属性,tcsetattr用于设置各种标识属性以控制串口及其相关驱动。cfgetispeed和cfsetispeed用于获取串口的输入波特率和设置输入波特率;cfgetospeed和cfsetospeed用于获取串口的输出波特率和设置输出波特率。

串口编程的流程:打开串口->设置串口->读/写串口->关闭串口。

用系统调用open打开串口,open(“/dev/ttyS0”, O_RDWR | O_NOCTTY)。注意,这里一定要用模式标识O_NOCTTY,表示该串口为非控制终端,否则输入数据中的任何特殊组合都会导致应用程序出现意想不到的结果,比如程序异常退出等。在open时还需要注意的是,要以阻塞模式打开串口设备,这样,c_cc数组中的VMIN和VTIME特殊控制字才会起作用;如果使用非阻塞模式标识O_NONBLOCK打开串口,那么在c_cc数组中的VMIN和VTIME的设置无意义。

串口设置:

tcgetattr(fd, &options);
o &= ~CSTOPB;
o &= ~CSIZE;
o &= ~PARENB;
o &= ~PARODD;
o |= CS8;
o &= ~CRTSCTS;
o &= ~(ICANON | IEXTEN | ISIG | ECHO);
o &= ~OPOST;
o &= ~(ICRNL | INPCK | ISTRIP | IXON | BRKINT);
o[VMIN] = 1;
o[VTIME] = 0;
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
tcsetattr(fd, TCSANOW, &options);

在对串口控制属性设置时,先是从驱动中获取它的属性,然后根据需要进行修改,最后将修改返回给驱动。注意到,上面的代码片段对oflag、iflag、cflag、lflag以及c_cc数组都做了修改。在串口通信中将lflag的ICANON清除,表示串口使用非规范模式通信,即以字节流的方式进行数据的输入和输出,同时关闭串口驱动的回显功能、扩展输入处理功能以及中断信号。在输入控制中,如果串口接收到Break信号,那么无需产生SIGINT信号;关闭输入校验,不要截断8bit数据为ascii的7bit数据,关闭软流控,关闭回车符和新行符的转换。在输出控制中,将输出的总控制开关OPOST清除,那么输出的所有特殊处理都不再起作用。在控制标识中,设置通信字长为8bit,停止位为1bit,无奇偶校验,无硬件流控。设置特殊字符的VMIN和VTIME分别为1和0,表示如果串口没有接收到数据就永远阻塞,而一旦接收到一个字节,read系统调用就返回。

串口是嵌入式Linux系统中非常常见的接口,掌握其编程方法就必不可少,希望本文能够给您带来启发和理解。

相关推荐