代码之家  ›  专栏  ›  技术社区  ›  Arkaik zx81

如果不是用minicom打开一次,就不能从串行端口读取数据

  •  1
  • Arkaik zx81  · 技术社区  · 6 年前

    我已经在C中实现了一个从串行端口的阻塞读取。我的目标是有一个在新数据到达之前阻塞的读取。

    下面是我如何实现串行伪对象(为了让代码更清晰,我去掉了多线程保护)。

    typedef struct
    {
        int fd;
        se_serial_speed_t speed;
        se_serial_parity_t parity;
        bool flow_control;
    }se_serial_t;
    
    int se_serial_constructor(se_serial_t** self, char* serial_port)
    {
        int fd;
    
        if(NULL != *self)
        {
            return ERR_NNULL_PTR;
        }
        else if(0 != access(serial_port, F_OK))
        {
            ERRNO("Serial port is not available");
            return ERR_ILLEGAL_PARAM;
        }
        else
        {
            if(-1 == (fd = open(serial_port, O_RDWR | O_NOCTTY)))
            {
                ERRNO("Error opening %s in rw mode", serial_port);
                return ERR_OFILE_FAIL;
            }
            else if(NULL == (*self = malloc(sizeof(se_serial_t))))
            {
                ERROR("Error allocating memory for Serial");
                return ERR_MALLOC_FAIL;
            }
            (*self)->fd = fd;
        }
        return ERR_OK;
    }
    
    int se_serial_configure_interface(se_serial_t* self, se_serial_speed_t speed, se_serial_parity_t parity, bool flow_control)
    {
        struct termios options;
    
        if(NULL == self)
        {
            return ERR_NULL_PTR;
        }
        else
        {
            if(0 != tcgetattr(self->fd,  &options))
            {
                ERRNO("Unable to get serial port current configuration");
            }
            if(0 != cfsetospeed(&options, speed))
            {
                ERRNO("Unable to set serial port output speed");
            }
            if(0 != cfsetispeed(&options, speed))
            {
                ERRNO("Unable to set serial port input speed");
            }
    
            switch(parity)
            {
            case SE_SERIAL_PARITY_8N1:
                options.c_cflag &= ~PARENB;
                options.c_cflag &= ~CSTOPB;
                options.c_cflag &= ~CSIZE;
                options.c_cflag |= CS8;
                break;
            case SE_SERIAL_PARITY_7E1:
                options.c_cflag |= PARENB;
                options.c_cflag &= ~PARODD;
                options.c_cflag &= ~CSTOPB;
                options.c_cflag &= ~CSIZE;
                options.c_cflag |= CS7;
                break;
            case SE_SERIAL_PARITY_7O1:
                options.c_cflag |= PARENB;
                options.c_cflag |= PARODD;
                options.c_cflag &= ~CSTOPB;
                options.c_cflag &= ~CSIZE;
                options.c_cflag |= CS7;
                break;
            case SE_SERIAL_PARITY_7S1:
                options.c_cflag &= ~PARENB;
                options.c_cflag &= ~CSTOPB;
                options.c_cflag &= ~CSIZE;
                options.c_cflag |= CS8;
                break;
            default:
                WARNING("Unable to set serial port parity");
                break;
            }
    
            if(flow_control)
                options.c_cflag |= CRTSCTS;
            else
                options.c_cflag &= ~CRTSCTS;
    
            options.c_cc[VMIN] = 1;
            options.c_cc[VTIME] = 0;
    
            if(0 != tcsetattr(self->fd, TCSANOW, &options))
            {
                ERRNO("Error configuring serial port");
                return ERR_SERIAL_CONF_FAIL;
            }
            self->speed = speed;
            self->parity = parity;
            self->flow_control = flow_control;
        }
        return ERR_OK;
    }
    
    int se_serial_read(se_serial_t* self, uint8_t* buffer, int size)
    {
        int bytes_read = 0;
        int ret;
    
        if(NULL == self)
        {
            return ERR_NULL_PTR;
        }
        else
        {
            while(bytes_read < size)
            {
                if(0 > (ret = read(self->fd, &(buffer[bytes_read]), size - bytes_read)))
                {
                    ERROR("Error reading from %s : %d\n", self->serial_port, ret);
                    return ERR_RFILE_FAIL;
                }
                bytes_read += ret;
            }
            size = bytes_read;
        }
    
        return size;
    }
    

    我正在与之通信的设备在启动后每秒发送一个11字节的帧。

    所以在初始化串口后,我在无限循环中接收帧,然后打印它们。

    se_serial_t* serial = NULL;
    uint8_t buffer[1024] = {0};
    int ret = 0;
    int i;
    
    if(0 > (ret = se_serial_constructor(&serial, "/dev/ttyUSB0")))
    {
        ERROR("Error creating serial : %d", ret);
        return ERR_SERIAL_CREATION_FAIL;
    }
    else if(0 > (ret = se_serial_configure_interface(serial, SE_SERIAL_SPEED_B115200, SE_SERIAL_PARITY_8N1, false)))
    {
        ERROR("Error configuring serial interface : %d", ret);
        return ERR_SERIAL_CONFIG_FAIL;
    }
    
    while(1)
    {
        if(0 > (ret = se_serial_read(serial, buffer, 11)))
        {
            ERROR("Error reading from serial : %d", ret);
            return ret;
        }
        else
        {
            for(i=0;i<ret;i++)
            {
                printf("%02x ", buffer[i]);
            }
            printf("\n");
        }
    }
    

    奇怪的是,即使我知道设备正在发送帧,读取也会永远阻塞。

    但是,如果我用另一个程序(如minicom)打开端口,我可以接收其中的帧。一旦用minicom打开了端口,并且我退出了它,我的程序就可以正常工作,直到下次重新启动我的计算机。

    如果我重新启动设备,代码会阻塞,直到它开始发送帧并很好地接收帧。

    我也试过树莓皮3,以确保它不是一个配置问题,我的笔记本电脑,但我得到了同样的结果。

    1 回复  |  直到 6 年前
        1
  •  2
  •   sawdust    6 年前

    奇怪的是,即使我知道设备正在发送帧,读取也会永远阻塞。

    一旦用minicom打开了端口,并且我退出了它,我的程序就可以正常工作,直到下次重新启动我的计算机。

    一点也不奇怪。

    (顺便说一句 “奇怪” 是一种基于观点的描述,不传递任何技术信息来帮助调试。)

    串行终端的默认模式通常是系统引导后的规范模式(用于传输文本)。
    读取()

    您可以使用 stty -F /dev/ttyUSB0 -a 命令,然后发现 伊肯 属性前面没有连字符。

    将串行终端配置为非规范模式,这是程序显然也希望串行终端在其中操作的模式。
    但是,您的程序仅为波特率、成帧和流控制配置termios参数。


    由于许多其他相关属性也应该为非规范模式设置或清除,因此宏 是对代码最简单的编辑。
    插入

    cfmakeraw(&options);
    

    在波特率和奇偶校验配置之间。
    请注意,如果数据不完全是ASCII文本,则使用7位数据帧可能会导致损坏,因此在程序中支持这三种模式是不一致的。

    options.c_cflag |= (CLOCAL | CREAD);
    

    与我通信的设备发送一个11字节的帧

    “框架” “框架”

    推荐文章