C чтение блокировки вызовов при работе последовательного порта - PullRequest
6 голосов
/ 13 сентября 2011

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

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

#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;
}

1 Ответ

5 голосов
/ 13 сентября 2011

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

После установки скорости передачи данных некоторые типы оборудования 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);

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

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