0

I have a program in 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");
}

The program communicates with the AVR processor. The program works on a single-board PC. Communication timing is here: Comunication timing

  • Why is the delay between receiving and trasmiting?
  • The problem is in read(ser_1, &read_buf, sizeof(read_buf)); This command ends 4ms after the last character. Why?
  • The port setting is wrong?
sawdust
  • 16,103
  • 3
  • 40
  • 50
Krasik
  • 9
  • 4
  • You do realize that it is logically impossible for the "clock_gettime(CLOCK_MONOTONIC, &time_actual);" system call, itself, take absolutely no time whatsoever, to execute, itself, right? And that by the time clock_gettime() enters the kernel, and reads the system clock, some time has elapsed since the kernel exited after completed read(), and returning to userspace? – Sam Varshavchik Mar 12 '20 at 13:04
  • @SamVarshavchik Not four milliseconds. Four microseconds maybe. With vDSO, maybe forty nanoseconds. – user253751 Mar 12 '20 at 13:07
  • Why does the graph show that the program writes `RX` instead of `Ra\0` the second time? – user253751 Mar 12 '20 at 13:09
  • Perhaps there was an decoding error... I don't know ... – Krasik Mar 12 '20 at 14:25
  • Please post the actual messages produced by your program, i.e. the printf()s. – sawdust Mar 12 '20 at 19:09
  • @sawdust This is from printf(): Dalsi kolo: -0.000708592 Vymena dat: -0.018154120 ReadMessage:ZXCDFRTE 9 Dalsi kolo: -0.018277040 Vymena dat: -0.035206728 ReadMessage:ZXCDFRTE 9 Dalsi kolo: -0.035398230 Vymena dat: -0.052284207 ReadMessage:ZXCDFRTE 9 Dalsi kolo: -0.052351627 – Krasik Mar 12 '20 at 21:24
  • @sawdust This is from fprintf(): Start: -0.000708592 -----: -0.001264392 After: -0.018154120 ZXCDFRTE Start: -0.018277040 -----: -0.018327331 After: -0.035206728 ZXCDFRTE Start: -0.035398230 -----: -0.035449397 After: -0.052284207 ZXCDFRTE Start: -0.052351627 -----: -0.052378166 After: -0.069211312 ZXCDFRTE – Krasik Mar 12 '20 at 21:26
  • 2
    Do you get yourself the impression that your way of providing that information in the comments is satisfying? If not try [edit]ing your question and using this information: https://stackoverflow.com/editing-help – Yunnosch Mar 12 '20 at 21:36
  • *"Why is there an unexpected delay ..."* -- No, this *"delay"* of several milliseconds is typical, and is part of the *latency* between the end of the received byte on the wire (what you show in your timing diagram) and when the **read()** syscall returns to your program. There are techniques to reduce this latency, e.g. see [High delay in RS232 communication on a PXA270](https://stackoverflow.com/questions/4667141/high-delay-in-rs232-communication-on-a-pxa270), as well as kernel configuration options. – sawdust Mar 12 '20 at 22:12
  • Your Linux program is not *"reading"* from (nor directly writing to) the UART hardware. That is done asynchronously with your program by the device driver. Your program is merely fetching bytes from a system buffer, so the "end" of the message in the timing diagram has no correlation to when the **read()** syscall returns in your program. Study [Linux serial drivers](http://www.linux.it/~rubini/docs/serial/serial.html) – sawdust Mar 12 '20 at 22:18
  • @sawdust I understand, thank you. I don't like operating systems :-) Your link is great. Thank you. – Krasik Mar 13 '20 at 10:27

1 Answers1

0

When receiving data from UARTs, giving the data to the application is usually triggered by:

  • reaching a certain level in the RX FIFO in the UART controller
  • a timeout when no new data has been received; common ranges for such timeouts are around 3 byte times

The timeout from 3 byte times matches your 4ms nearly.

ensc
  • 6,704
  • 14
  • 22
  • In canonik mode "\ x0" terminates reception, right? Timeout is about 4.5ms .... 4 byte is 4.16 ms. I still don't know where the problem is. Can I set timeout? – Krasik Mar 12 '20 at 14:30
  • You will have to read the datasheet of your AVR, but I do not think that its UART handles `\000` in a special way. Your operating system waits for the HW to signal reception which only happens after reaching a certain FIFO watermark level and/or a timeout. – ensc Mar 12 '20 at 14:46
  • This program does not run on AVR but on linux PC. AVR is on the other side. AVR has no problem. AVR responds "immediately." – Krasik Mar 12 '20 at 15:09
  • The UART of the used AVR might be more simple (no FIFO) so that RX is signaled immediately. But when you look into the datasheet of the UART of the PC, you will see that it has an RX FIFO with the described behavior. – ensc Mar 12 '20 at 16:17
  • @Krasik *"In canonik mode "\ x0" terminates reception, right?"* -- Wrong. (1) Your Linux program explicitly configures non-canonical mode, so there are no characters that *"terminates reception"* (which is actually a different operation from "satisfies the **read()** request"). (2) The NUL character cannot be used for any termios special characters (e.g. line termination AKA end-of-line character, EOL), because the 0x0 value is used to indicate `no value` for the special character (i.e. disable the feature). – sawdust Mar 12 '20 at 22:42
  • @sawdust I'm distracted. You're right. I swapped "tty.c_lflag &= ~ICANON;" for "tty.c_lflag |= ICANON;" and "\0x0" and "\n". But the result is the same... – Krasik Mar 13 '20 at 09:22