【问题标题】:Why is there an unexpected delay on UART serial communication?为什么 UART 串​​行通信出现意外延迟?
【发布时间】:2020-03-12 13:00:01
【问题描述】:

我有一个 C 程序:

#include <stdio.h>
#include <string.h> 
#include <stdlib.h>
#include <time.h>   // clock_gettime
#include <signal.h> // obsluha signalu

// Linux headers
#include <fcntl.h>      // Contains file controls like O_RDWR
#include <errno.h>      // Error integer and strerror() function
#include <termios.h>    // Contains POSIX terminal control definitions
#include <unistd.h>     // write(), read(), close()

// Global variable
int ser_1;          // Serial port
bool cykl=true;     // Podmínka trvani nekonecneho cyklu

void init_serial_1()
{
    // Nastaví sériový port ttyS1
    // 8bit
    // None parita
    // 1 start
    // 1 stop
    // 9600

    // Open the serial port. Change device path as needed (currently set to an standard FTDI USB-UART cable type device)
    ser_1 = open("/dev/ttyS1", O_RDWR);

    // Create new termios struc, we call it 'tty' for convention
    struct termios tty;
    memset(&tty, 0, sizeof tty);

    // Read in existing settings, and handle any error
    if(tcgetattr(ser_1, &tty) != 0) {
        printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
    }

    tty.c_cflag &= ~PARENB; // Clear parity bit, disable parity
    tty.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common)
    tty.c_cflag |= CS8; // 8 bits per byte (most common)
    tty.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common)
    tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)

    tty.c_lflag &= ~ICANON;
    tty.c_lflag &= ~ECHO; // Disable echo
    tty.c_lflag &= ~ECHOE; // Disable erasure
    tty.c_lflag &= ~ECHONL; // Disable new-line echo
    tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes

    tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
    tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
    // tty.c_oflag &= ~OXTABS; // Prevent conversion of tabs to spaces (NOT PRESENT ON LINUX)
    // tty.c_oflag &= ~ONOEOT; // Prevent removal of C-d chars (0x004) in output (NOT PRESENT ON LINUX)

    tty.c_cc[VTIME] = 10;    // Wait for up to 1s (10 deciseconds), returning as soon as any data is received.
    tty.c_cc[VMIN] = 0;

    // Set in/out baud rate to be 9600
    cfsetispeed(&tty, B9600);
    cfsetospeed(&tty, B9600);

    // Save tty settings, also checking for error
    if (tcsetattr(ser_1, TCSANOW, &tty) != 0) {
        printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
    }



    /*
    // Write to serial port
    unsigned char msg[] = { 'H', 'e', 'l', 'l', 'o', '\r' };
    write(ser_1, "Hello, world!", 13);

    // Allocate memory for read buffer, set size according to your needs
    char read_buf [256];
    memset(&read_buf, '\0', sizeof(read_buf));

    // Read bytes. The behaviour of read() (e.g. does it block?,
    // how long does it block for?) depends on the configuration
    // settings above, specifically VMIN and VTIME
    int num_bytes = read(ser_1, &read_buf, sizeof(read_buf));

    // n is the number of bytes read. n may be 0 if no bytes were received, and can also be -1 to signal an error.
    if (num_bytes < 0) {
    printf("Error reading: %s", strerror(errno));
    }

    // Here we assume we received ASCII data, but you might be sending raw bytes (in that case, don't try and
    // print it to the screen like this!)
    printf("Read %i bytes. Received message: %s", num_bytes, read_buf);

    close(ser_1);*/
}

// Obsluha signálu
void sigint_handler(int sugnum)
{
    cykl=false;
}

float difftimespec(struct timespec *ts1, struct timespec *ts2)
{
    return difftime(ts1->tv_sec,ts2->tv_sec) + (ts1->tv_nsec - ts2->tv_nsec)*1e-9;
}

// Hlavní program
int main()
{
    // Mereni casu, zacatek
    struct timespec time_start, time_actual;
    clock_gettime(CLOCK_MONOTONIC, &time_start);

    // Inicializace obsluhy signalu
    signal(SIGINT, sigint_handler);

    // Inicializace serioveho portu
    init_serial_1();
    // Buffer pro cteni
    char read_buf [256];
    memset(&read_buf, '\0', sizeof(read_buf));
    int num_bytes;  // Nuber of read charakters

    // Otevreni souboru pro data
    FILE *file;
    file=fopen("data.txt","a+t");   //apend, read, write, text

    // Cyklicky se opakujíci cast
    while(cykl)
    {
        // Get actual time
        clock_gettime(CLOCK_MONOTONIC, &time_actual);
        fprintf(file, "Start: %.9f\n",difftimespec(&time_start,&time_actual));

        printf("Dalsi kolo: %.9f\n", difftimespec(&time_start,&time_actual));

        write(ser_1,"Ra\0",3);  // write to serial command

        clock_gettime(CLOCK_MONOTONIC, &time_actual);
        fprintf(file, "-----: %.9f\n",difftimespec(&time_start,&time_actual));


        num_bytes = read(ser_1, &read_buf, sizeof(read_buf));

        clock_gettime(CLOCK_MONOTONIC, &time_actual);
        printf("Vymena dat: %.9f\n", difftimespec(&time_start,&time_actual));

        printf("ReadMessage:%s %d\n", read_buf, num_bytes);

        fprintf(file, "After: %.9f  %s\n",difftimespec(&time_start,&time_actual),read_buf);
    }

    close(ser_1);
    fclose(file);
    printf("\n\n Konec ...\n");
}

程序与 AVR 处理器通信。该程序在单板 PC 上运行。通讯时间在这里: Comunication timing

  • 为什么接收和发送之间会有延迟?
  • 问题出在read(ser_1, &amp;read_buf, sizeof(read_buf)); 这个命令在最后一个字符后4ms结束。为什么?
  • 端口设置错误?

【问题讨论】:

  • 您确实意识到“clock_gettime(CLOCK_MONOTONIC, &time_actual);”在逻辑上是不可能的系统调用本身绝对不需要任何时间来执行,对吧?而且到clock_gettime()进入内核,读取系统时钟的时候,距离内核完成read()退出,返回用户空间已经过去了一段时间?
  • @SamVarshavchik 不是四毫秒。可能是四微秒。使用 vDSO,可能需要 40 纳秒。
  • 为什么图表显示程序第二次写入RX而不是Ra\0
  • 可能有解码错误...我不知道...
  • 您是否觉得您在 cmets 中提供该信息的方式令人满意?如果不尝试edit提出您的问题并使用此信息:stackoverflow.com/editing-help

标签: c linux serial-port delay uart


【解决方案1】:

从 UART 接收数据时,将数据提供给应用程序通常由以下触发:

  • 在 UART 控制器的 RX FIFO 中达到某个级别
  • 没有收到新数据时的超时;此类超时的常见范围约为 3 字节时间

3 字节时间的超时时间几乎与您的 4 毫秒相匹配。

【讨论】:

  • 在canonik模式下“\x0”会终止接收,对吧?超时时间约为 4.5 毫秒 .... 4 字节为 4.16 毫秒。我仍然不知道问题出在哪里。我可以设置超时吗?
  • 您必须阅读 AVR 的数据表,但我认为它的 UART 不会以特殊方式处理 \000。您的操作系统等待硬件发出接收信号,这仅在达到某个 FIFO 水印级别和/或超时后才会发生。
  • 这个程序不在 AVR 上运行,而是在 linux PC 上运行。 AVR 在另一边。 AVR没有问题。 AVR“立即”响应。
  • 所用 AVR 的 UART 可能更简单(无 FIFO),因此 RX 会立即发出信号。但是,当您查看 PC UART 的数据表时,您会发现它有一个具有上述行为的 RX FIFO。
  • @Krasik "在 canonik 模式下 "\x0" 会终止接收,对吗?" -- 错误。 (1)你的Linux程序显式配置了非规范模式,所以没有字符“终止接收”(这实际上是不同于“满足read() 请求”)。 (2) NUL 字符不能用于任何 termios 特殊字符(例如行终止 AKA end-of-line character, EOL),因为 0x0 值用于指示特殊字符的no value(即禁用该功能) .
猜你喜欢
  • 1970-01-01
  • 2012-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多