【问题标题】:stty serial port settings for parity not persistent用于奇偶校验的 stty 串行端口设置不持久
【发布时间】:2019-12-03 05:22:39
【问题描述】:

我有一个打开串行端口的 netcore 应用程序,一旦检测到奇偶校验错误,就会在控制台上写入“奇偶校验错误”。它在 Windows 10 中运行良好,但无法在 Linux 下运行。

我的假设是操作系统没有将奇偶校验错误传递给 netcore。

为了检查我运行的端口设置:

stty -D /dev/ttyS0 -ignpar inpck

然后我运行:

stty -D /dev/ttyS0 -a 

并且设置似乎按预期正确设置(-ignpar inpck)。

然后我运行我的 netcore 3 应用程序,但未检测到奇偶校验错误。

所以我跑了

stty -D /dev/ttyS0 -a 

用于验证设置,但这些似乎被重置(-ignpar -inpck)

如何强制我的应用在启用 inpck 属性的情况下运行?
有没有办法让inpck默认启用?

谢谢。

更新:netcore 3 应用奇偶校验错误检测在 Windows 10 中运行良好,但在 linux 下无法运行。我的假设是:

  • A) netcore 运行时未将奇偶校验设置传递给驱动程序 (不太可能)
  • B) 操作系统忽略了指令。

【问题讨论】:

  • "如何强制我的应用使用 inpck fuse 运行..." -- 这不是 "fuse",它是通常只可编程一次。 termios 手册页将 INPCK 及其类型称为“常量”、“标志”和“属性”。所以使用正确的行话,并选择这三个中的任何一个而不是 "fuse"
  • stty 命令只是 shell 中利用 termios API 的一种方法。应用程序应使用 termios API 将串行终端配置为确切的情况要求(而不是依赖于启动时的预期配置)。如果您使用的应用程序环境不允许访问 termios API,那么您可能使用了不适当的方法。顺便说一句,来自用户空间的“9 位串行”是有问题的。
  • 感谢您阐明 stty 的范围 不完全是 9 位的。我只需要检测奇偶校验错误以识别第 9 位强制为 1 的消息的第一个字节。因此 netcore 应用程序将端口奇偶校验设置为“空间”并等待奇偶校验错误开始读取传入缓冲区.它在 windows 10 中运行良好,但在 linux 下无法运行。我的假设是:A)netcore 运行时没有将奇偶校验设置传递给驱动程序(不太可能)B)操作系统忽略了指令。
  • 如果您的 UART 使用 DMA 或者它是 USB 串行适配器,那么正确识别具有奇偶校验错误的特定字符可能会产生问题。 IMO 您需要使用编程 I/O(而不是 DMA)来检测特定接收帧中的奇偶校验错误,即可以在 ISR 接收到每个字节时检查状态。
  • 设置 INPCK 属性(以测试奇偶校验)不是一个足够的 termios 配置。您还需要clear IGNPAR(报告错误)和set PARMRK(详细错误指示)。有错误的字符前面将有0xFF0x00 的字节(即总共三个字节)。如果您不配置详细指示,则错误字符将简单地替换为空字节。

标签: linux serial-port parity stty 9-bit-serial


【解决方案1】:

stty 命令只是 shell 中利用 termios API 的一种方法。
应用程序应使用 termios API 将串行终端配置为符合具体情况的要求(而不是依赖于启动时的预期配置)。
如果您使用的应用环境不允许访问 termios API,那么您可能使用了不适当的方法。

您知道任何可以以某种方式明确地对奇偶校验错误做出反应的 linux 应用程序吗?

以下 C 程序从串行终端读取行(即规范模式),并配置为使用 8 位字符帧检测标记(或 1)作为奇偶校验位。

#define SERIALTERMINAL      "/dev/ttyS0"
#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= CLOCAL | CREAD;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag |= PARENB;      /* enable parity */
    tty.c_cflag &= ~PARODD;     /* Even parity */
    tty.c_cflag |= CMSPAR;      /* force Even parity to SPACE */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    tty.c_lflag |= ICANON | ISIG;  /* canonical input */
    tty.c_lflag &= ~(ECHO | ECHOE | ECHONL | IEXTEN);

    tty.c_iflag &= ~IGNCR;  /* preserve carriage return */
    tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | IMAXBEL);
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);   /* no SW flowcontrol */
    tty.c_iflag |= IGNBRK;  /* ignore breaks */
    tty.c_iflag &= ~ISTRIP;
    tty.c_iflag &= ~IGNPAR; /* report error */
    tty.c_iflag |= INPCK;   /* test parity */
    tty.c_iflag |= PARMRK;  /* verbose parity err */

    tty.c_oflag &= ~OPOST;

    tty.c_cc[VEOL] = 0;
    tty.c_cc[VEOL2] = 0;
    tty.c_cc[VEOF] = 0x04;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}


int main(void)
{
    char *portname = SERIALTERMINAL;
    int fd;
    int wlen;

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("Error opening %s: %s\n", portname, strerror(errno));
        return -1;
    }
    /*baudrate 115200, 8 bits, Space for parity, 1 stop bit */
    set_interface_attribs(fd, B115200);

    /* simple output */
    wlen = write(fd, "Hello!\n", 7);
    if (wlen != 7) {
        printf("Error from write: %d, %d\n", wlen, errno);
    }
    tcdrain(fd);    /* delay for output */


    /* simple canonical input, read lines */
    do {
        unsigned char buf[81];
        unsigned char *p;
        int rdlen;

        rdlen = read(fd, buf, sizeof(buf) - 1);
        if (rdlen > 0) {
            buf[rdlen] = 0;
            printf("Read %d:", rdlen);
            /* first display as hex numbers then ASCII */
            for (p = buf; rdlen-- > 0; p++) {
                printf(" 0x%x", *p);
                if (*p < ' ')
                    *p = '.';   /* replace any control chars */
            }
            printf("\n    \"%s\"\n\n", buf);
        } else if (rdlen < 0) {
            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
        } else {  /* rdlen == 0 */
            printf("Nothing read. EOF?\n");
        }               
        /* repeat read */
    } while (1);
}

该程序在具有内置 16550A 串行端口的旧 Linux (Ubuntu 14.04.2 LTS) PC 上执行。

[    2.656593] 00:08: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A

此串行端口似乎无法传输 8 位奇偶校验(11 位帧),但似乎能够读取 8 位奇偶校验。

串行数据由具有 9 位 UART 功能的 SBC 生成。使用示波器捕获帧,以确认 8S1 和 8E1 帧的长度为 11 位。
(FTDI USB 到 RS232 转换器在生成所有 8 位字符的奇偶校验配置时并不可靠。)


当发送方配置为 8 位和空间用于奇偶校验(与程序匹配)时,PC 程序读取“ABCDEFG\n”为:

Read 8: 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0xa
    "ABCDEFG."

数据被正确读取。


当发送方配置为 8 位和偶校验时,PC 程序读取“ABCDEFG\n”为:

Read 14: 0x41 0x42 0xff 0x0 0x43 0x44 0xff 0x0 0x45 0xff 0x0 0x46 0x47 0xa
    "AB�.CD�.E�.FG."

读取(正确)识别出三个具有标记而不是空格作为奇偶校验位的字符。
每个带有“奇偶校验错误”的字符前面都有0xFF 0x00 的字节(即总共三个字节)。

请注意,当实际接收到的数据是0xFF(没有奇偶校验错误)时,termios 会将该数据扩展为0xFF 0xFF 的两个字节。所以请注意,当下一个数据是0x00 时,这不是错误指示。 IOW 读取 0xFF 0xFF 0x00 转换为实际数据 0xFF 0x00
但是当实际接收到的数据是0xFF有奇偶校验错误时,读取返回0xFF 0x00 0xFF(即没有与错误指示结合的扩展)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-01
    • 2012-01-22
    • 2011-01-13
    • 2019-05-19
    • 2014-05-01
    相关资源
    最近更新 更多