该项目由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个。
答案 0 :(得分:1)
我通过将接收例程移动到单独的线程来解决问题。回顾一下,Web服务器(来自libwebsockets库的LWSWS程序)在Linux单板计算机(SBC)上以无头(自动启动和运行)模式运行。电机控制代码是LWSWS的一个插件,它现在产生两个线程:一个用于将数据包发送到电机控制板,另一个用于接收数据包。
最初,我从这个双线程设计开始,但显然在那时还有其他问题。在提出这个问题之前,我已经将接收例程移到了发送线程中,试图让read()和write()都工作。
工作组合似乎是两个线程,每个线程都有自己的COM端口文件描述符。令我感到惊讶的是,正如@thekamilz在评论中所建议的那样,不需要互斥锁,但很高兴它有效。如果我需要扩展项目,我希望这个设计能够运作;每个外围设备都在自己的线程上进行通信。