C чтение блокировки вызова при работе с последовательным портом

Я пытаюсь написать программу C в Linux для отправки и получения данных от микроконтроллера через последовательный порт. В качестве теста я настроил микроконтроллер на немедленное отображение всех отправленных символов. Я проверил, что это работает в minicom, а также с использованием «cat» и «echo» для отправки и получения данных.

Однако, когда я пытаюсь сделать то же самое в программе на C, мой вызов read блокируется навсегда. Я устанавливаю последовательный порт в неканонический режим с MIN равным «1» и ВРЕМЕНЕМ «0». Мой тест minicom доказывает, что микроконтроллер возвращает символы по мере их ввода, поэтому я ожидаю, что чтение вернется после того, как вызов записи отправил символы. Я сравнил свой код с несколькими онлайн-примерами и не нашел ничего, чего мне не хватало. Я пробовал несколько перестановок кода ниже, но безуспешно. Может ли кто-нибудь обнаружить проблему?

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>

#define UART_SPEED B115200

char buf[512];

void init_serial (int fd)
{
    struct termios termios;
    int res;

    res = tcgetattr (fd, &termios);
    if (res < 0) {
        fprintf (stderr, "Termios get error: %s\n", strerror (errno));
        exit (-1);
    }

    cfsetispeed (&termios, UART_SPEED);
    cfsetospeed (&termios, UART_SPEED);

    termios.c_iflag &= ~(IGNPAR | IXON | IXOFF);
    termios.c_iflag |= IGNPAR;

    termios.c_cflag &= ~(CSIZE | PARENB | CSTOPB | CREAD | CLOCAL);
    termios.c_cflag |= CS8;
    termios.c_cflag |= CREAD;
    termios.c_cflag |= CLOCAL;

    termios.c_lflag &= ~(ICANON | ECHO);
    termios.c_cc[VMIN] = 1;
    termios.c_cc[VTIME] = 0;

    res = tcsetattr (fd, TCSANOW, &termios);
    if (res < 0) {
        fprintf (stderr, "Termios set error: %s\n", strerror (errno));
        exit (-1);
    }
}

int main (int argc, char **argv)
{
    int fd;
    int res;
    int i;

    if (argc < 2) {
        fprintf (stderr, "Please enter device name\n");
        return -1;
    }

    fd = open (argv[1], O_RDWR | O_NOCTTY);
    if (fd < 0) {
        fprintf (stderr, "Cannot open %s: %s\n", argv[1], strerror(errno));
        return -1;
    }

    init_serial (fd);

    res = write (fd, "P=20\r\n", 6);
    if (res < 0) {
        fprintf (stderr, "Write error: %s\n", strerror(errno));
        return -1;
    }
    tcdrain (fd);

    res = read (fd, buf, 512);
    printf ("%d\n", res);
    if (res < 0) {
        fprintf (stderr, "Read error: %s\n", strerror(errno));
        return -1;
    }

    for (i=0; i<res; i++) {
        printf ("%c", buf[i]);
    }

    return 0;
}

person FazJaxton    schedule 13.09.2011    source источник
comment
Стоит подтвердить с помощью последовательного кабеля обратной связи. Код, как указано выше, работает для меня.   -  person Andrew Edgecombe    schedule 13.09.2011
comment
Так как это работает для Эндрю, я предлагаю вам попробовать настроить контрольные линии (CRTSCTS). Также: возможно удаленная сторона более требовательна к четности, чем вы, тоже и не эхо. Если у вас все еще есть коммутационная коробка: используйте ее.   -  person wildplasser    schedule 13.09.2011


Ответы (1)


Возможно, вы захотите вставить некоторые задержки или зациклить ожидание ввода.

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

После записи шести символов немедленно выполняется чтение с тайм-аутом 0,1 секунды. Возможно, что не все символы из write() закончили передаваться до read(), не говоря уже о времени ответа удаленного устройства.

Например, одно из решений:

init_serial (fd);
usleep (100000);   // delay 0.1 seconds (Linux) so term parameters have time to change

res = write (fd, "P=20\r\n", 6);
if (res < 0) {
    fprintf (stderr, "Write error: %s\n", strerror(errno));
    return -1;
}
tcdrain (fd);
usleep (250000);  // delay 0.25 for device to respond and return data

res = read (fd, buf, 512);

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

person wallyk    schedule 13.09.2011
comment
Спасибо за ответ. Однако при TIME=0 и MIN=1 мой вызов чтения блокируется в ожидании какой-либо обратной связи. Он не возвращает 0 (вообще не возвращает), поэтому циклы не помогут. Однако больше персонажей, позволяющих сериалу стабилизироваться, кажется хорошей идеей. Я попробую увеличить размер записи, может поможет. - person FazJaxton; 14.09.2011
comment
Вы были правы, это были задержки! Я установил задержку в 0,5 секунды, и это не сработало, но кажется, что 2 секунды работают каждый раз. Я реализую более умный тест, чтобы дождаться готовности соединения, но этот простой тест доказал проблему. Я действительно удивлен, что Linux работает таким образом (требует, чтобы вы вручную ждали, и тем временем прерывает ввод-вывод), но я рад, что это решено. Еще раз спасибо! - person FazJaxton; 14.09.2011
comment
@FazJaxton: Благодаря тому, что Linux ведет себя с большой отзывчивостью - что на самом деле является причиной - вы можете использовать это в качестве фундаментальной основы, например, для эмуляции поведения Windows. - person wallyk; 14.09.2011