读取\写入嵌入式Linux项目设备的问题

时间:2018-01-18 22:31:05

标签: c linux multithreading io file-descriptor

该项目由Web服务器(使用libwebsockets库)和Web服务器的电机控制插件组成,该插件将数据包写入并读入电机控制板以及与服务器通信。

我有两个文件描述符(fd)到嵌入式Linux板上的COM端口,一个用于写入,另一个用于读取。他们都是单独工作,但我不能让他们同时工作。

为了测试read(),我将COM端口的接收线挂钩到计算机,并从终端程序重复发送16字节的数据包。字节被接收到字节数组并通过Web服务器传递给GUI。对于此测试,不禁用写入并将COM端口写回终端。在此测试中,电机未连接到COM端口。

要测试write(),将禁用读取并运行Web服务器。我可以使用GUI控制电机,并将电机位置报告给服务器。

如果同时启用了读取和写入操作,则在运行Web服务器时似乎会阻止某些内容。电机不能启用,也不能运行。只要按Ctrl-C停止服务器,就会显示来自电机的一个或多个数据包。 电机开始移动,电机包数据传输到GUI。

由于某种原因读取阻塞吗?它独自工作。

通过检查read()调用的返回,我在不同的时间得到了以下输出:

EINTR - Interrupted system call
EIO   - Input/Output error
EBADF - Bad file descriptor

在读取和写入的最后一次测试(单独)之后,我得到的唯一错误是后者。退出程序后,我得到一串大约50个坏读(我正在向终端打印perror)。

读/写代码(下面)在一个单独的线程中运行,数据包在连续循环中处理。我试图将写入时间与调度程序中的波动隔离开来。写例程由一个Linux定时器驱动,频率为8.333ms(120Hz)和SIGALRM,因此" gotAlarm"变量

这是流程的问题吗?我不分叉任何进程,我希望Web服务器成为主要进程。

这是一个线程问题吗?读取和写入都在生成的线程中工作。

感谢您的投入!代码如下。

致电初始化fd' s:

mc_tx_fd = InitPort("/dev/ttyS1", "COM2", O_WRONLY | O_NOCTTY, B115200);
mc_rx_fd = InitPort("/dev/ttyS1", "COM2", O_RDONLY | O_NOCTTY, B115200);

InitPort功能:

int InitPort( char *port, char *name, int oflags, speed_t baudRate ) 
{
    int fd, rg, rs;                                 // File descriptor
    fd = open(port, oflags);                // Open the port like a file
    assert(fd > 0);                         // Open returns -1 on error

    struct termios options;                 // Initialize a termios struct
    rg = tcgetattr(fd, &options);           // Populate with current attributes
    if( rg < 0 ) {
        printf("Failed to get attr: %d, %s\n", fd, strerror(errno));
    }
    cfsetospeed (&options, baudRate);       // Set baud rate out
    cfsetispeed (&options, baudRate);       // Set baud rate in (same as baud rate out)

    options.c_cflag &= ~CSIZE;              // Clear bit-length flag so it can be set
        //8N1 Serial Mode
        options.c_cflag &= ~CSTOPB;         // Set stop bit:        1
        options.c_cflag &= ~CRTSCTS;        // Set flow control:    none

    options.c_cc[VMIN]  = 1;
    options.c_cc[VTIME] = 2;    
    cfmakeraw(&options);

    options.c_cflag |= (CLOCAL | CREAD);    // Enable receiver, and set local mode
    rs = tcsetattr(fd, TCSANOW, &options);      // Set new attributes to hardware
    if( rs < 0 ) {
        printf("Failed to set attr: %d, %s\n", fd, strerror(errno));
    }
    return fd;
}

这是在创建新线程时调用的SendMotorPacket函数:

void *SendMotorPacket( void *arg ) 
{
    char timez, checksum;
    static unsigned short rcvTimer = 0;
    int flags, rcvAzPos, bytesRead, wc, wo, r, t;

    while( 1 ) {

        // receive byte from motor

        r = read( mc_rx_fd, inByte, 1 );
        if( r == -1 ) {
            if( errno == EINTR ) {
                continue;
            } else {
                perror("read");
            }
        }

        inChar = inByte[0];

        // FSM to receive packet
        switch( receiveState ) {

            case MOTOR_HEAD_1:
                if( inChar == 0xA5 ) {
                    receiveState = MOTOR_HEAD_2;
                    checksum = 0;
                }
                break;

            case MOTOR_HEAD_2:
                if( inChar == 0x52 ) {
                    receiveState = MOTOR_DATA;
                    idx = 0;
                    checksum = 0xA5 + 0x52;
                } else {
                    receiveState = MOTOR_HEAD_1;
                }
                break;

            case MOTOR_DATA:
                readPacket[idx++] = inChar;
                checksum += inChar;
                if( idx >= 16 ) {
                    receiveState = MOTOR_CHECKSUM;
                }
                break;

            case MOTOR_CHECKSUM:
                if( checksum == inChar ) {

                    // Check status bits and set global variables
                    mc.az_status = *(signed short *)&readPacket[2];
                    mc.el_status = *(signed short *)&readPacket[10];

                    // If motor is disabled, set global variable for feedback to GUI
                    if( !az.enable ) {
                        mc.az_position = *(signed int *)&readPacket[4];
                    }

                    if( !el.enable ) {
                        mc.el_position = *(signed int *)&readPacket[12];
                    }

                }

                receiveState = MOTOR_HEAD_1;

                break;

            default:

                break;

        } 

        // TRANSMIT

        if( gotAlarm ) {

            mc.checkSum = 0;

            gotAlarm = 0;

            // Call both motor position functions to update position
            AzMotorPos();
            ElMotorPos();

            if ( az.enable ) {
                mc.az_control |= 0x0001;
            } else {
                mc.az_control &= ~0x0001;
            }

            if ( el.enable ) {
                mc.el_control |= 0x0001;
            } else {
                mc.el_control &= ~0x0001;
            }

            *( unsigned short * )&writePacket   = mc.header;
            *( signed short * )&writePacket[2]  = mc.az_control;
            *( signed short * )&writePacket[4]  = mc.az_status;
            *( signed int   * )&writePacket[6]  = mc.az_position;
            *( signed short * )&writePacket[10] = mc.el_control;
            *( signed short * )&writePacket[12] = mc.el_status;
            *( signed int   * )&writePacket[14] = mc.el_position;

            // Calculate checksum for all bytes in packet
            for( int i = 0; i < 18; i++ ) {
                mc.checkSum += writePacket[i];
            }

            *(unsigned char * )&writePacket[18] = mc.checkSum;

            write( mc_tx_fd, writePacket, MC_PACKET_SIZE );
        }
    }
}

这是线程创建:

void CreatePacketThread ( void ) 
{
    int err = -1; 

    err = pthread_create( &packetThread, NULL, &SendMotorPacket, NULL );

    if (err != 0)
            printf("\ncan't create MC SEND thread :[%s]\n\n", strerror(err));
        else
            printf("\nMC SEND THREAD created successfully\n\n");
}

更新:

运行Web服务器(lwsws)然后退出导致一个EIO错误:

read: Input/output error

和多个EBADF错误:

read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor
read: Bad file descriptor

其中约有50个。

1 个答案:

答案 0 :(得分:1)

我通过将接收例程移动到单独的线程来解决问题。回顾一下,Web服务器(来自libwebsockets库的LWSWS程序)在Linux单板计算机(SBC)上以无头(自动启动和运行)模式运行。电机控制代码是LWSWS的一个插件,它现在产生两个线程:一个用于将数据包发送到电机控制板,另一个用于接收数据包。

最初,我从这个双线程设计开始,但显然在那时还有其他问题。在提出这个问题之前,我已经将接收例程移到了发送线程中,试图让read()和write()都工作。

工作组合似乎是两个线程,每个线程都有自己的COM端口文件描述符。令我感到惊讶的是,正如@thekamilz在评论中所建议的那样,不需要互斥锁,但很高兴它有效。如果我需要扩展项目,我希望这个设计能够运作;每个外围设备都在自己的线程上进行通信。