чтение блоков последовательного порта по неизвестной причине - PullRequest
4 голосов
/ 20 декабря 2011

Я пытаюсь подключить бесконтактный считыватель смарт-карт через UART (usbserial), используя среду termios под Linux. Код отлично работает на ПК, но когда я кросс-компилирую и пробую его на цели ARM9, он может открыть устройство и даже записать команду на устройство, но команда чтения блокируется на неопределенный срок. Вот фрагмент кода:

int mifare_rdr_init(struct mifare_1K * ptr, char *rdr_devnode)
{   
    bzero(ptr, sizeof(struct mifare_1K));           // zero the entire structure
    // open serial device
    int fd = open(rdr_devnode, O_RDWR|O_NOCTTY );
    if (fd == -1) {
    perror("Failed to open serial device ");
    return 1;
    }
    ptr->serialfd = fd;                 // save file descriptor

    ptr->serialdev.c_iflag = 0;                 // no i/p flags
    ptr->serialdev.c_oflag = 0;                 // o/p flags
    ptr->serialdev.c_cflag = ( CS8 | CREAD | B38400 );      // 8 bits, receive enable, baud for rdr
    ptr->serialdev.c_lflag = ( ICANON );                // CANONICAL mode, means read till newline char '\n'.
    // control chars 
        // commented below line as suggested by A.H below, since it's not needed in CANONICAL mode
    // ptr->serialdev.c_cc[VMIN] = 1;               // read unblocks only after at least one received char.

    // flush all i/o garbage data if present
    tcflush(ptr->serialfd,TCIOFLUSH);

    int ret = 0;
    // apply settings
    ret = tcsetattr(ptr->serialfd,TCSANOW,&ptr->serialdev);
    if (ret == -1) {
        perror("tcsetattr() failed ");
        return 2;
    }
    return 0;
    }

int get_mifare_rdr_version(struct mifare_1K *ptr, char *data)
{
    // flush all i/o garbage data if present
    tcflush(ptr->serialfd,TCIOFLUSH);

    int chars_written = write(ptr->serialfd,"$1V\n",4);
    if( chars_written < 0 ) {
        perror("Failed to write serial device ");
        return 1;
    }
        printf("cmd sent, read version...\n");   // this prints, so I know cmd sent...
    int chars_read = read(ptr->serialfd,ptr->data_buf,14);
    if( chars_read < 0 ) {
        perror("Failed to read serial device ");
        return 2;
    }
    // copy data to user buffer
        printf("reading done.\n");    // this doesn't print...
    return 0;
}

Структура mifare_1K содержит дескриптор файла для последовательного устройства, структуру termios и различные буферы, которые я использую. Устройство, как я упоминал, является устройством usb-to-serial (module: ftdi_sio). Он настроен на 38400 @ 8-N-1 в каноническом режиме termios.

Канонический режим, потому что ответы считывателя заканчиваются на '\ n', поэтому его лучше обрабатывать в каноническом режиме, поскольку он читает устройство до получения '\ n' (пожалуйста, исправьте меня, если я ошибаюсь).

Сначала я вызываю init () fn, а затем get_rdr_version (). Строка «cmd sent, read version ...» напечатана, поэтому я знаю, что она умеет писать, но не печатает строку «read done». после этого.

Другое дело, что если я удалю кард-ридер и подключу этот порт к gtkterm (терминальной программе последовательного порта) на другом ПК, я не получу «$ 1V \ n» на этом gtkterm ?? !! , Затем, после небольшого RnD, я обнаружил, что если я перезагружаю систему, к которой подключен считыватель, тогда только я получаю этот cmd «$ 1V \ n» на другом Gtkterm. Если я попытаюсь снова без перезагрузки, этот cmd не будет виден на этом Gkterm ... подсказка, но он еще не разобрался.

Это что-то вроде того, что cmd записывается в файл устройства, но не сливается на реальное устройство? Есть ли способ проверить это?

Любая помощь высоко ценится, так как я застрял на этом некоторое время ... спасибо.

ОБНОВЛЕНИЕ:

Хорошо, у меня все получилось, немного изменив код, как показано ниже.

// open serial device
    int fd = open(rdr_devnode, O_RDWR|O_NOCTTY|O_NDELAY );  // O_NDELAY ignores the status of DCD line, all read/write calls after this will be non-blocking
    fcntl(fd,F_SETFL,0);   // restore read/write blocking behavior
    if (fd == -1) {
    perror("Failed to open serial device ");
    return 1;
    }

Это модифицированный раздел кода, который открывает порт в моей функции init (). Два изменения:

1) O_NDELAY добавляется к флагам в вызове open (), который игнорирует линию Data Carrier Detect (DCD), чтобы увидеть, подключен ли другой конец и готов ли он к связи или нет. Первоначально он использовался для модемов, которые мне не нужны, на самом деле у меня его нет вообще, поскольку я использую usbserial. Но этот флаг также делает дальнейшие вызовы read () и write () неблокирующими. Обратите внимание: я думал, что об этом позаботятся, добавив CLOCAL в cflag struct termios, что я пробовал, но не сработало.

2) fcntl (fd, F_SETFL, 0) восстанавливает поведение блокировки дальнейших вызовов read () и write ().

Это комбо отлично работает для меня. Причина only , которую я не публикую в качестве ответа, заключается в том, что я еще не понимаю, почему он работал на ПК без этой модификации, поскольку это было то же аппаратное обеспечение. Фактически, я смог прочитать данные с устройства чтения смарт-карт на ARM9 TARGET, используя minicom , но не мою программу. Я собираюсь проверить документацию FT232BL, чтобы узнать, каков статус DCD по умолчанию.

В любом случае, я нашел этот бит информации в Руководство по последовательному программированию для операционных систем POSIX . Объяснение кому-нибудь ??? Конечно, я выложу ответ, когда узнаю.

Приветствия:)

Ответы [ 2 ]

3 голосов
/ 07 января 2013

Только что столкнулся с такими же симптомами на Raspberry Pi с USB-модулем Telegesis, я добавляю это как еще одну точку данных.

В моем случае причина оказалась отсутствующим флагом RTS.Telegesis ожидает управления потоком CRTSCTS и не будет отправлять какие-либо данные в Raspberry, не видя RTS.Непонятные аспекты заключались в том, что а) тот же код прекрасно работает на ПК, и б) он отлично работал на Raspberry при первом подключении Telegesis, но при последующих открытиях / dev / ttyUSB0 никаких данных не будет видноRaspberry.

По некоторым причинам кажется, что на ARM флаг RTS сбрасывается при закрытии устройства, но не устанавливается снова, тогда как на x86 / x64 флаг RTS остается установленным.Исправление здесь состоит в том, чтобы просто установить флаг RTS, если он еще не установлен - например,

#include <sys/ioctl.h>
//...
int rtscts = 0;
if (ioctl (fd, TIOCMGET, &rtscts) != 0)
{
  // handle error
}
else if (!(rtscts & TIOCM_RTS))
{
  rtscts |= TIOCM_RTS;
  if (ioctl (fd, TIOCMSET, &rtscts) != 0)
  {
    // handle error
  }
}

. Я отмечаю, что в вашем случае вы не используете управление потоком данных, поэтому вышеприведенное вполне может бытьприменимо.Однако именно ваш вопрос и упоминание о том, что Minicom сработал, побудили нас найти решение нашей проблемы - так что спасибо вам за это!

1 голос
/ 28 декабря 2011

Три вещи, которые вы можете проверить:

Смешение канонического режима / неканонического режима 1

Вы смешиваете канонический режим и материал неканонического режима:

ptr->serialdev.c_lflag = ( ICANON );
// ...
ptr->serialdev.c_cc[VMIN] = 1;

Страница man termios(3) сообщает о VMIN:

VMIN Минимальное количество символов для не -канонического чтения.

Очевидно, что ваш тайм-аут не будет работать так, как вы думаете.

Смешение канонического режима / неканонического режима 2

Дополнительно на странице руководства сказано:

Все эти значения символьных индексов различны, за исключением того, что VTIME, VMIN может иметь то же значение, что и VEOL, VEOF соответственно. В не- В каноническом режиме значение специального символа заменяется тайм-аутом. имея в виду. Для объяснения VMIN и VTIME, см. Описание неканонический режим ниже.

Поэтому, пожалуйста, проверьте, отличаются ли определения этих констант для ваших двух платформ. Возможно, логика EOL / EOF испорчена неправильной настройкой. И EOL, и EOF могут вызвать возврат от read.

Не инициализировано c_cc

Ваш код не показывает правильную инициализацию массива c_cc. Вы также не читаете существующие настройки и не предоставляете подходящие значения по умолчанию для значений, необходимых для канонических режимов. Код, показанный до сих пор, даже не очищает значения. Поэтому могут быть использованы непредсказуемые значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...