您的位置:首页 > 博客中心 > 网络系统 >

linux串口编程

时间:2022-04-03 16:26

  按照对linux系统的理解,串口编程的顺序无非就是open,read,write,close,而串口有波特率、数据位等重要参数需要设置,因此还应该用到设置函数,那么接下来就带着这几个问题去学习linux下的串口编程。

1、open

  linux串口编程其实也是文件编程,首先要用open函数打开串口设备,获得文件描述符,open函数的简介参照:

  首先需要关心的是需要打开的文件名,它肯定是/dev路径下的某个设备,串口设备一般叫做ttyS*ttySAC*,ttyUSB*,我以前就知道这些,后来发现竟然还有叫ttyO*的(当时调试的时候看见/dev目录下有ttyS*的,我就不假思索的,理所当然的代码中写的是ttyS*,结果程序运行时这玩意还成功打开了,但设置的时候就出问题,万万没想到啊,当时用的那个平台ttyS*不是串口,相同目录下还有叫ttyO*的文件,那才是串口)。

  串口设备明显是可读写的,因此传入的第二个参数为O_RDWR。

  因此打开串口1的操作为:

fd = open("/dev/ttySAC1", O_RDWR);

  如果得到的fd不等于-1则表示成功打开串口设备了。

  我在使用过程中遇到过串口无法打开的问题,open返回值为-1。查阅资料后使用命令:sudo chmod 666 /dev/ttyUSB0 修改串口设备权限后就能成功打开了。

  open函数传入的第二个参数一般会用到O_NOCTTY这个标志,它表示阻止操作系统将打开的文件指定为进程的控制终端,如果没有指定这个标志,那么任何一个输入都将会影响用户的进程。我不知道它具体是怎么影响的,但是最好还是加上这个标志。

  此外O_NONBLOCK这个标志也比较常用,它表示以非阻塞模式打开文件,当调用read的时候,如果没有数据也会立即返回-1。有些人会用O_NDELAY这个标志,关于这个标志的解释网上就有很多说法了,我看到就有三种说法:

  1、与O_NONBLOCK一样也是以非阻塞模式打开,但如果没有读取到数据,O_NDELAY返回的是0,而O_NONBLOCK返回的是-1,并且会设置errno为EAGAIN。

  2、O_NDELAY表示这个程序不关心DCD信号线所处的状态,端口的另一端是否激活或者停止。如果用户不指定了这个标志,则进程将会一直处在睡眠状态,直到DCD信号线被激活。

  3、与O_NONBLOCK等价。

  为了探究答案,我在一份linux3.14.38的源代码中搜索,发现这两个标志的值与平台相关。我在Ubuntu中直接用printf打印出来两个标志的值是完全一样的,并且read函数返回的值为都是-1,并不是第一条说的那样O_NDELAY返回的是0。

  我暂时也没有更多的平台去验证,暂且认为网上的那些说法都是基于作者自己正在使用的平台上说的,而在我使用的Ubuntu中O_NDELAY与O_NONBLOCK是完全相同的。

  所以打开串口的操作应为下列2句中的一句:

fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY);
fd = open("/dev/ttySAC1", O_RDWR | O_NOCTTY | O_NONBLOCK);

2、串口设置

  打开串口设备之后还需要对串口进行设置。使用tcsetattr函数设置串口,函数原型为:

int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);

  成功返回0,失败返回-1。

  第一个参数fd表示打开的串口文件描述符。

  第二个参数optional_actions用于控制修改起作用的时间。可以取下列值:

    TCSANOW:不等数据传输完毕就立即改变属性。
    TCSADRAIN:等待所有数据传输结束才改变属性。
    TCSAFLUSH:清空输入输出缓冲区才改变属性。

  第三个参数struct termios中包含了串口属性,struct termios定义为:

1 #define NCCS 19
2 struct termios {
3     tcflag_t c_iflag;        /* input mode flags */
4     tcflag_t c_oflag;        /* output mode flags */
5     tcflag_t c_cflag;        /* control mode flags */
6     tcflag_t c_lflag;        /* local mode flags */
7     cc_t c_line;            /* line discipline */
8     cc_t c_cc[NCCS];        /* control characters */
9 };

  看到这种不认识的数据类型就头大,实际上tcflag_t类型就是unsigned int,而cc_t类型就是unsigned char。

  由于struct termios里面的成员太多,因此使用tcgetattr函数先获取到原来的属性,然后再修改我们关心的属性。

  调用tcgetattr函数获取属性,tcgetattr函数原型为:

int tcgetattr(int fd, struct termios *termios_p);

  成功返回0,失败返回-1。

  一般来说串口需要关心的属性为:波特率、数据位、校验位、停止位、流控。下面逐一说明这些数据在哪里改。

  波特率:

    与波特率相关的成员为c_cflag,其中键值(域)CBAUD表示波特率,该域中的位不同的组合表示不同的波特率,表示波特率也用的也是宏,支持的宏如下:

B0        
B50        
B75        
B110    
B134    
B150    
B200    
B300    
B600    
B1200    
B1800    
B2400    
B4800    
B9600    
B19200    
B38400    
B57600  
B115200 
B230400

    比如将波特率修改为115200的代码就可以这样写:

1 tcgetattr(fd, &termios_uart);
2 termios_uart.c_cflag &= ~CBAUD;
3 termios_uart.c_cflag |= B115200;
4 tcsetattr(fd, TCSANOW, &termios_uart);

    在linux中提供了专门设置波特率的函数,用cfsetispeed和cfsetospeed设置输入输出波特率,还有一个cfsetspeed函数,它们的函数原型为:

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

    其speed_t类型其实就是unsigned int类型,其取值也正是CBAUD域中可以选择的数据。

    但是看到这里就有疑问了,c_cflag成员中没有将输入波特率和输出波特率分开,这里为什么会有几个不同的设置波特率的函数,这个问题在另一篇博客中仔细探究。

    那么设置波特率的代码就变成了这样:

 

1 tcgetattr(fd, &termios_uart);
2 cfsetspeed(&termios_uart, B115200);
3 tcsetattr(fd, TCSANOW, &termios_uart);

 

  数据位:

    与数据位相关的成员为c_cflag,其中键值(域)CSIZE表示数据位,与波特率的设置一样,该域中的位不同的组合表示不同的数据位,也可以用宏来表示,支持的宏如下:

  CS5,CS6,CS7,CS8,分别表示数据位为5位、6位、7位、8位。

    将数据位设置为8位就可以这样写:

1 tcgetattr(fd, &termios_uart);
2 termios_uart.c_cflag &= ~CSIZE;
3 termios_uart.c_cflag |= CS8;
4 tcsetattr(fd, TCSANOW, &termios_uart);

  校验位:

    c_cflag中键值PARENB置1表示使用奇偶校验,否则表示不使用校验,在PARENB置1的前提下,键值 PARODD置1表示使用奇校验,否则使用偶校验。

    另外在c_iflag中也有与校验相关的位。如下表:

c_iflag中与校验相关的位
IGNPAR Ignore framing errors and parity errors.
PARMRK If IGNPAR is not set, prefix a character with a parity error or framing error
with \377 \0. If neither IGNPAR nor PARMRK is set, read a character with a par‐
ity error or framing error as \0.
INPCK Enable input parity checking.
ISTRIP Strip off eighth bit.

 

 

  停止位:

    c_cflag中键值CSTOPB置1表示使用2个停止位,否则表示使用1个停止位。

  流控:

    c_cflag中键值CRTSCTS置1表示使用硬件流控,否则表示不使用硬件流控。

    软件流控则在c_iflag成员中定义,c_iflag成员中和软件流控相关的键值及解释如下:

Enable software flow control (outgoing)

 

 

 

    

  

本类排行

今日推荐

热门手游