gpt4 book ai didi

c - 嵌入式 Linux 项目的设备读取/写入问题

转载 作者:太空宇宙 更新时间:2023-11-04 10:12:15 24 4
gpt4 key购买 nike

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

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

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

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

当同时启用读取和写入时,运行 Web 服务器时似乎会发生阻塞。电机无法启用,也无法运行。一旦我按下 Ctrl-C 停止服务器,来自电机的一个或多个数据包似乎已被处理。电机开始移动,电机数据包数据传输到 GUI。

读取是否由于某种原因阻塞?它单独工作。

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

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

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

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

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

是线程问题吗?读取和写入都在生成的线程中进行。

感谢您的参与!代码如下。

初始化 fd 的调用:

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

初始化端口函数:

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 个。

最佳答案

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

最初,我是从这种双线程设计开始的,但显然当时还有其他问题。在问这个问题之前,我已将接收例程移动到发送线程中,以尝试让 read() 和 write() 都正常工作。

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

关于c - 嵌入式 Linux 项目的设备读取/写入问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48331369/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com