Я использую Termios на встроенной платформе Linux (с ограниченными ресурсами) (в C) для отправки команд и получения данных от различных периферийных устройств tty (CP2102 USB-UART).По-видимому, есть разные способы сделать это, и многие из них не подходят.Я пробовал мало с небольшим успехом, я столкнулся с чем-то, что работает, но я не уверен, что это оптимально или даже правильно:
#define BAUDRATE 115200
#define DEVICE "/dev/ttyUSB0"
#define DATA "BLK\r"
int handler(void){
char buf[255];
struct pollfd fds[1];
int fd, ret, res, retry = 0;
connect:
fd = open(DEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd == 0){
perror(DEVICE);
printf("Failed to open %s\n",DEVICE);
sleepms(2000);
if(retry++<5) goto connect;
//exit(-1);
}
set_interface_attribs (fd, BAUDRATE, 0); // 8n1 no parity
set_blocking (fd, 0); // not blocking
fds[0].fd = fd; // streams
fds[0].events = POLLRDNORM;
for (;;)
{
int count = write(fd, DATA, strlen(DATA));
ret = poll(fds, 1, 1000);
if (ret > 0){
if (fds[0].revents & POLLHUP){ printf("Hangup\n"); close(fd); goto connect;}
if (fds[0].revents & POLLRDNORM){
res = read(fd,buf,255);
if(!res){close(fd); goto connect;}
buf[res-2]=0;
printf("Received %d bytes : %s",res,buf);
}
}
}
}
В основном команда отправляется, затем ее опрос, пока некоторые данные не поступят или тайм-аут не произойдет,Это работало в течение более 24 часов и не показывало проблем со связью, однако есть одна проблема: если периферийное устройство отключено, то я получаю уведомление о «видеовстрече», но оно никогда не переподключается, я ожидал, что это произойдет, так как я закрываю файл и повторяю соединение с интерфейсом.
Кроме того, опрос является лучшим подходом здесь?я не хочу тратить процессорное время в чистой потере при опросе (если только опрос не имеет некоторого механизма для высвобождения процессорного времени другим потокам), но я все еще хочу, чтобы вызов блокировался до истечения времени ожидания или получения ответа (т.е. я не хочу отправлятькоманда и получить ответ позже с обратным вызовом).Ответ приходит от периферийного устройства менее чем за несколько миллисекунд.
nb Я знаю, что некоторые глаза кровоточат из-за заявления goto, не беспокойтесь, goto использовался здесь для быстрого тестирования подхода «переподключения» (который не работал) но в конце концов, если соединение должно быть перезапущено, оно будет в отдельной функции, goto никогда не будет использоваться в окончательной реализации.
EDIT:
После перезаписи.Это работает, но все еще есть проблемы, в основном, когда я отключаю периферийное устройство, Linux случайным образом сохранит имя порта или изменит его.Иногда он будет сохранять имя порта для нескольких последовательных отключений, иногда он будет переименовывать его и некоторое время придерживаться нового имени.Поэтому мне нужно найти способ идентифицировать периферийное устройство по USB и получить текущее имя порта, каким бы оно ни было.
При отключении выдает ошибку 9 из tcgetattr (исходя из set_interface_attribs или set_blocking, я думаю), но как только он снова подключаетсяи если Linux не меняет имя порта, то он сразу же повторно подключается и перезапускается для отправки и получения, как и должно, однако, когда Linux переименовывает порт, это приводит к ошибке.
int fd=-1,retry=0;
struct pollfd fds[1];
int connect(void){
if(fd==-1) fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
else return 0; // already opened
printf("Connecting to fd #%d\n",fd);
if (fd==0){
perror(MODEMDEVICE);
printf("Failed to open %s\n",MODEMDEVICE);
retry++;
sleepms(2000);
return -1;
}
set_interface_attribs (fd, BAUDRATE, 0); // 8n1 no parity
set_blocking (fd, 0); // not blocking
fds[0].fd = fd; // streams
fds[0].events = POLLERR|POLLHUP|POLLRDNORM;
return 0;
}
int handlePoll(){
int ret=0,res=0;
char buf[255];
ret = poll(fds, 1, 1000); // 1000ms
if (ret > 0){
if (fds[0].revents & POLLERR){ close(fd); fd=-1; return -1; } // IO error
if (fds[0].revents & POLLHUP){ close(fd); fd=-1; return -2; } // interface closed
if (fds[0].revents & POLLRDNORM){
res = read(fd,buf,255);
if(!res){ close(fd); return -3; } // data receive error
buf[res-2]=0;
printf("Received %d bytes : %s\n",res,buf);
return 0;
}
return -4; // unknown error
}
return -5; // timeout
}
int sendCMD(char* cmd){
int res=1;
retry=0;
while(res && retry<5){ res=connect(); }
if(res) return -7;
if(retry>5) return -8;
int len = strlen(cmd);
int count = write(fd, cmd, len);
if(count<len){ return -6;}
return handlePoll();
}
int main(void){
while(1){
switch(sendCMD(DATA)){
case -1: printf("IO error\n"); break;
case -2: printf("Interface closed error\n"); break;
case -3: printf("data receive error\n"); break;
case -4: printf("Unknown error\n"); break;
case -5: printf("Timeout\n"); break;
case -6: printf("Command send error\n"); sleepms(200); break;
case -7: printf("Interface open error\n"); break;
case -8: printf("Cannot open interface after 5 try\n"); break;
default: break;
}
}
}
Я думаю, что должно бытьлучший способ справиться с отключением ( Обнаружение того, что символическое устройство отключено в Linux с помощью termios api (c ++) )