【linux】串口编程(一)——配置串口

来源:互联网 发布:一年php工资7千 编辑:程序博客网 时间:2024/06/05 19:25

目前遇到的串口编程都是用于通信,很少作为终端显示。以前没有对串口编程做深入研究,本次以libmodbus源码中对串口的设置为例,详解总结串口编程时配置的属性(struct termios)

以libmodbus中_modbus_rtu_connect函数为例

【1】打开串口设备

static int _modbus_rtu_connect(modbus_t *ctx){    struct termios tios;    speed_t speed;    modbus_rtu_t *ctx_rtu = ctx->backend_data;    /*      O_RDWR:读写      O_NOCTTY:该端口不作为终端使用,如果没有设置该标志位,一些输入信号会影响到程序      O_NDELAY:非阻塞模式      O_EXCL:未知,待测????    */    ctx->s = open(ctx_rtu->device, O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL);    if (ctx->s == -1) {        fprintf(stderr, "ERROR Can't open the device %s (%s)\n",                ctx_rtu->device, strerror(errno));        return -1;    }

【2】保存旧属性

    tcgetattr(ctx->s, &(ctx_rtu->old_tios));

【3】设置输出、输入波特率

    memset(&tios, 0, sizeof(struct termios));    switch (ctx_rtu->baud) {    case 110:        speed = B110;        break;    case 300:        speed = B300;        break;    case 600:        speed = B600;        break;    case 1200:        speed = B1200;        break;    case 2400:        speed = B2400;        break;    case 4800:        speed = B4800;        break;    case 9600:        speed = B9600;        break;    case 19200:        speed = B19200;        break;    case 38400:        speed = B38400;        break;    case 57600:        speed = B57600;        break;    case 115200:        speed = B115200;        break;    default:        speed = B9600;    }    if ((cfsetispeed(&tios, speed) < 0) ||        (cfsetospeed(&tios, speed) < 0)) {        close(ctx->s);        ctx->s = -1;        return -1;    }

【4】设置控制属性

CREAD、CLOCAL这两个选项可以保证你的程序不会变成端口的所有者,而端口所有者必须去处理发散性作业控制和挂断信号,同时还保证了串行接口驱动会读取过来的数据字节
如果串口不是用来做终端显示,这两个选项就够了

    tios.c_cflag |= (CREAD | CLOCAL);

【5】设置数据位,先清空后设置

    tios.c_cflag &= ~CSIZE;    switch (ctx_rtu->data_bit) {    case 5:        tios.c_cflag |= CS5;        break;    case 6:        tios.c_cflag |= CS6;        break;    case 7:        tios.c_cflag |= CS7;        break;    case 8:    default:        tios.c_cflag |= CS8;        break;    }

【6】设置停止位

    if (ctx_rtu->stop_bit == 1)        tios.c_cflag &=~ CSTOPB;    else /* 2 */        tios.c_cflag |= CSTOPB;

【7】设置奇偶校验位

PARENB Enable parity bit:使能校验
PARODD Use odd parity instead of even:使用奇校验

【7.1】不使用校验位
    if (ctx_rtu->parity == 'N') {        /* None */        tios.c_cflag &=~ PARENB;    } 
【7.2】使用偶校验
    else if (ctx_rtu->parity == 'E') {        /* Even */        tios.c_cflag |= PARENB;        tios.c_cflag &=~ PARODD;    } 
【7.3】使用奇校验
    else {        /* Odd */        tios.c_cflag |= PARENB;        tios.c_cflag |= PARODD;    }

【8】终端输入(行输入)的特性

ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals:使能信号中断
ICANON Enable canonical input (else raw);使用规范的输入流,否则是原始输入流
XCASE Map uppercase \lowercase (obsolete):大小写映射(不再推荐使用)
ECHO Enable echoing of input characters:使能回显
ECHOE Echo erase character as BS-SP-BS
ECHOK Echo NL after kill character
ECHONL Echo NL
NOFLSH Disable flushing of input buffers after interrupt or quit characters
IEXTEN Enable extended functions
ECHOCTL Echo control characters as ^char and delete as ~?
ECHOPRT Echo erased character as character erased
ECHOKE BS-SP-BS entire line on line kill
FLUSHO Output being flushed
PENDIN Retype pending input at next read or input char
TOSTOP Send SIGTTOU for background output
规范输入(或称为标准输入)是面向行的。
输入字符被放到一个缓冲区中,可以被用户交互式地编辑,直到收到一个CR(回车)或(换行)字符。
Canonical input is line-oriented. Input characters are put
into a buffer which can be edited interactively by the user
until a CR (carriage return) or LF (line feed) character is
received.

原始输入流是未经过加工的。输入字符在接收时完全按照接收到的字符进行传递。
通常,在使用原始输入时,您将取消选择ICANON、ECHO、ECHOE和ISIG选项
Raw input is unprocessed. Input characters are passed
through exactly as they are received, when they are
received. Generally you’ll deselect the ICANON, ECHO,
ECHOE, and ISIG options when using raw input

##### 【8.1】作为原始流,屏蔽终端输入(行输入)的特性

    tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

【9】输入标志设置

INPCK Enable parity check:使能奇偶校验
IGNPAR Ignore parity errors:忽略奇偶校验错误
PARMRK Mark parity errors:标记奇偶校验错误
ISTRIP Strip parity bits:跳过奇偶校验位
IXON Enable software flow control (outgoing):使能软件流控(向外)
IXOFF Enable software flow control (incoming):使能软件流控(向内)
IXANY Allow any character to start flow again:允许任何字符重新开始流(???)
IGNBRK Ignore break condition:忽略打破条件
BRKINT Send a SIGINT when a break condition is detected:当检测到中断条件时发送一个SIGINT
INLCR Map NL to CR:将换行映射为回车
IGNCR Ignore CR:忽略回车
ICRNL Map CR to NL:将回车映射为换行
IUCLC Map uppercase to lowercase:将大写映射为小写
IMAXBEL Echo BEL on input line too long:当输入行太长时,显示为BEL

【9.1】设置输入奇偶校验
    if (ctx_rtu->parity == 'N') {        /* None */        tios.c_iflag &= ~INPCK;    } else {        tios.c_iflag |= INPCK;    }
【9.2】禁止软件流控
/* Software flow control is disabled */tios.c_iflag &= ~(IXON | IXOFF | IXANY);

【10】输出标志设置

OPOST Postprocess output (not set = raw output):处理后再输出(原始流不设置)
ONLCR Map NL to CR-NL:将换行映射为回车-换行

ONCLR ant others needs OPOST to be enabled:OPOST使能后,ONCLR才有效

【10.1】原始流输出
tios.c_oflag &=~ OPOST;

【11】字节流控制设置

VMIN Minimum number of characters to read:读取最少的字节数

VTIME Time to wait for data (tenths of seconds):等待数据时间(单位是十分之一秒)

UNIX串行接口驱动程序提供了指定字符和包超时的能力。
c_cc数组的两个元素用于超时:VMIN和VTIME。
在规范输入模式下、open或fcntl时使用O_NDELAY选项时,上述功能将被忽略(失效)。

UNIX serial interface drivers provide the ability to specify character and packet timeouts. Two elements of the c_cc array are used for timeouts: VMIN and VTIME. Timeouts are ignored in canonical input mode or when the NDELAY option is set on the file via open or fcntl.

VMIN指定要读取的字节的最小数量。如果设置为0,在VTIME指定的时间内等待读取任意字符。
注意,这并不意味着read N个字节将等待N个字符的出现。
相反,超时后,read调用将返回立即可用的字符或read时请求的字符数量(两者取最小的那个)。

VMIN specifies the minimum number of characters to read. If   it is set to 0, then the VTIME value specifies the time to   wait for every character read. Note that this does not mean   that a read call for N bytes will wait for N characters to   come in. Rather, the timeout will apply to the first   character and the read call will return the number of   characters immediately available (up to the number you   request).

如果VMIN不为0,VTIME指定等待第一个字符读取的时间。如果在给定的时间内读取字符,那么任何读都会阻塞(等待),直到所有的VMIN字符被读取。也就是说,一旦第一个字符被读取,串行接口驱动程序就会期望接收整包字符(VMIN字节总数)。如果在允许的时间内没有读取字符,则读取的调用返回0。该方法允许您告诉串行驱动程序,您需要确切的N字节,任何读调用将返回0或N字节。然而,超时只适用于第一个字符读取,因此如果由于某种原因,驱动程序在N字节包中丢失了一个字符,那么read调用可能会永远阻塞,等待额外的输入字符。
(注:上述为翻译,不太理解。亲测的现象总结如下:
1、VMIN小于read指定字符个数:在超时范围内获取VMIN个字符时,返回;
2、VMIN大于read指定字符个数:在超时范围内获取read指定字符个数时,返回;
3、超时会受到新数据到达时间的影响:如果在超时范围内没有新数据到达,将因为超时返回;如果在超时范围内有新数据到达,将重新计时,直到上述两种情况之一才会返回,否则会一直等待。)

   If VMIN is non-zero, VTIME specifies the time to wait for the first character read. If a character is read within the time given, any read will block (wait) until all VMIN  characters are read. That is, once the first character is read, the serial interface driver expects to receive an  entire packet of characters (VMIN bytes total). If no  character is read within the time allowed, then the call to read returns 0. This method allows you to tell the serial  driver you need exactly N bytes and any read call will  return 0 or N bytes. However, the timeout only applies to the first character read, so if for some reason the driver   misses one character inside the N byte packet then the read  call could block forever waiting for additional input characters.

VTIME指定等待输入字符的时间。
如果VTIME设置为0(默认),则读取将无限期地阻塞(等待),除非在open或fcntl中指定O_NDELAY选项。

   VTIME specifies the amount of time to wait for incoming  characters in tenths of seconds. If VTIME is set to 0 (the default), reads will block (wait) indefinitely unless the NDELAY option is set on the port with open or fcntl.Unused because we use open with the NDELAY option 
【11.1】因为已经在open中使用O_NDELAY选项,所以不再使用c_cc
    tios.c_cc[VMIN] = 0;    tios.c_cc[VTIME] = 0;
【12】设置串口属性
   if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) {        close(ctx->s);        ctx->s = -1;        return -1;    }}#if HAVE_DECL_TIOCSRS485    /* The RS232 mode has been set by default */    ctx_rtu->serial_mode = MODBUS_RTU_RS232;#endif    return 0;}
原创粉丝点击