0

I try to read and write data by two different UART. I create two thread to comunicate with theese UARTs. Every thread use diffent UART

Some Time, when I write data to UART, write function return with all byte whiuout error, but elettrical port not send all byte.

If I comment second thread, I haven't this error.

Why? Somebody know the problem?

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h> 
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <stdint.h>
#include "log4c.h"
#include <pthread.h>
pthread_t threadId[2];          /*!< Array di pthread_t. In presenza di micro, vengono avviati 2 thread, uno per ogni micro per la generazione del crc*/
/** \struct parameters
   * \brief Struttura contenente i parametri scambiati con il thread che calcola il crc
   */
struct parameters {
    uint8_t* Buffer; /*!< Puntatore al buffer contenente i dati di cui calcolare il crc   */
    uint32_t length; /*!< Lunghezza in byte del buffer contenente i dati   */
    uint32_t crc;    /*!< Contiene il valore del crc calcolato dal micro   */
    uint8_t microNumber; /*!< Valore numerico che identifica il micro: 1->micro1, 2->micro2   */
    
};
int fdMicroFirst; /*!<Memorizza l'identificativo numerico del microcontrollore 1 */
int fdMicroSecond; /*!<Memorizza l'identificativo numerico del microcontrollore 2 */


/*
 * 
 */
int setInterfaceAttribs(int speed, int parity, int fd) {
    struct termios tty;
    if (tcgetattr(fd, &tty) != 0) {
        printf("error %d from tcgetattr", errno);
        return -1;
    }

    cfsetospeed(&tty, speed);
    cfsetispeed(&tty, speed);

    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
    // disable IGNBRK for mismatched speed tests; otherwise receive break
    // as \000 chars
    tty.c_iflag &= ~IGNBRK; // disable break processing
    tty.c_lflag = 0; // no signaling chars, no echo,
    // no canonical processing
    tty.c_oflag = 0; // no remapping, no delays
    
    tty.c_iflag &= ~(IXON | IXOFF | IXANY | INLCR | ICRNL); // shut off xon/xoff ctrl
    //INLCR | ICRNL in questo modo viene disabilitata la conversione da Carriege return (CR) a new line(NL) e viceversa.
    //Se non vengono disabilitate queste due configurazioni 0x0d viene letto come 0x0a
    tty.c_cflag |= (CLOCAL | CREAD); // ignore modem controls,
    // enable reading
    tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
    tty.c_cflag |= parity;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("error %d from tcsetattr", errno);
        return -1;
    }
    return 0;
}

void setBlocking(int should_block, int fd) {
    struct termios tty;
    memset(&tty, 0, sizeof tty);
    if (tcgetattr(fd, &tty) != 0) {
        printf("error %d from tggetattr", errno);
        return;
    }

    tty.c_cc[VMIN] = should_block ? 4 : 0;
    tty.c_cc[VTIME] = 2.5; // 0.5 seconds read timeout era impostato a 5

    if (tcsetattr(fd, TCSANOW, &tty) != 0)
        printf("error %d setting term attributes", errno);
}

int openSerialCommunication(char *portname, int speed, int microNumber) {
    int retVal = -1;
    int fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0) {
        printf("error %d opening %s: %s", errno, portname, strerror(errno));
        return -1;
    }
    if (speed == 115200) {
        speed = B115200;
    } else if (speed == 460800) {
        speed = B460800;
    } else if (speed == 921600) {
        speed = B921600;
    }
    if (microNumber == 1) {
        fdMicroFirst = fd;
        //retVal = setInterfaceAttribs(B115200, 0, fdMicroFirst); // set speed to 115,200 bps, 8n1 (no parity)


        retVal = setInterfaceAttribs(speed, 0, fdMicroFirst); // set speed to 115,200 bps, 8n1 (no parity)
        //retVal = setInterfaceAttribs(B460800, 0, fdMicroFirst); // set speed to 115,200 bps, 8n1 (no parity)
        setBlocking(0, fdMicroFirst); // set no blocking
        if (tcflush(fdMicroFirst, TCOFLUSH) != 0) {
            printf("Errore flush Output Buffer Micro %d\n", microNumber);
        }
    } else if (microNumber == 2) {
        fdMicroSecond = fd;
        //retVal = setInterfaceAttribs(B115200, 0, fdMicroSecond); // set speed to 115,200 bps, 8n1 (no parity)
        retVal = setInterfaceAttribs(speed, 0, fdMicroSecond); // set speed to 115,200 bps, 8n1 (no parity)
        setBlocking(0, fdMicroSecond); // set no blocking
        if (tcflush(fdMicroSecond, TCOFLUSH) != 0) {
            printf("Errore flush Output Buffer Micro %d\n", microNumber);
        }
    }

    return retVal;
}
void closeSerialCommunication() {
    if (fdMicroFirst != -1) {
        close(fdMicroFirst);
    }
    if (fdMicroSecond != -1) {
        close(fdMicroSecond);
    }
}
uint32_t calculateCrc(uint8_t* buffer, uint16_t bufferLengthCrc, int microNumber) {
    
       
    uint8_t write_buffer[1100];
    uint32_t crc = 0;
    int fdMicro = -1;
    uint8_t readBuffer [4] = {0x00, 0x00, 0x00, 0x00};
    int numB = 0;
    int n = 0;
    int nTemp;
    uint8_t crcBuffer [4] = {0x00, 0x00, 0x00, 0x00};
    int counter = 0;
    struct timespec startTime, stopTime;
    uint16_t dimBufferTosend = 0;
    uint16_t numnByteSent = 0;

    if (microNumber == 1) {

        fdMicro = fdMicroFirst;
    } else if (microNumber == 2) {
        fdMicro = fdMicroSecond;
    }
     
    //I primi due byte che vengono inviati alla seriale rappresentano il numero di byte 
    //che costituiscono il dato inviato

    if (bufferLengthCrc <= 0xFFFF) {
        write_buffer[0] = (bufferLengthCrc & 0xFF00) >> 8;
        write_buffer[1] = (bufferLengthCrc & 0x00FF);
        //printf("UART Write Buffer: %x %x\n",write_buffer[0],write_buffer[1]);
        for (int i = 0; i < bufferLengthCrc; i++) {
            write_buffer[i + 2] = buffer[i];
            //printf("Write Buffer: %x ", write_buffer[i + 2]);

            /*
                        printf("Write Buffer: %x \n",write_buffer[i + 2]);
             */
        }
        
            
        printf("Flushing Micro %d with fd: %d\n", microNumber, fdMicro);
        if (tcflush(fdMicro, TCOFLUSH) != 0) {
            printf("Errore flush Output Buffer Micro %d\n", microNumber);
        }
        
        dimBufferTosend = bufferLengthCrc + 2;
        numnByteSent = 0;
        numB = 1;
        while (numnByteSent < dimBufferTosend && numB > 0) {
            numB = write(fdMicro, write_buffer + numnByteSent, dimBufferTosend - numnByteSent);
            
            
            numnByteSent = numnByteSent + numB;
            printf("Byte scritti su Micro %d:%d\n", microNumber, numnByteSent);
            
            printf( "Byte rimanenti da scrivere su Micro %d:%d\n", microNumber, dimBufferTosend - numnByteSent);


        }
        if (numnByteSent >= dimBufferTosend) {
            //printf("UART Buffer: \n");
            //clock_gettime(CLOCK_REALTIME, &startTime);
            while (n < 4) {
                nTemp = read(fdMicro, readBuffer, sizeof readBuffer); // read up to 100 characters if ready to read
                //printf("ntemp:%d ",nTemp);

                if (nTemp != 0 && nTemp != -1) {
                    int j = 0;
                    for (int k = n; k < n + nTemp; k++) {
                        crcBuffer[k] = readBuffer[j];
                        printf(" 0x%x 0x%x ", crcBuffer[k], readBuffer[j]);
                        j++;
                    }
                    printf("UART Buffer Response Micro %d: \n", microNumber);
                    n = n + nTemp;


                } else {
                    if (counter == 3) {
                        n = 4;
                        for (int i = 0; i < bufferLengthCrc + 2; i++) {
                           //printf("Write Buffer: %x ", write_buffer[i]);

                        }
                        printf("CRC Ricevuto: 0x%x 0x%x 0x%x 0x%x\n", crcBuffer[0], crcBuffer[1], crcBuffer[2], crcBuffer[3]);
                        printf( "Risposta CRC da MICRO %d non pervenuta\n", microNumber);
                        while (1) {
                            usleep(1000);
                        }

                    } else {
                        counter = counter + 1;
                    }
                }
            }
        }
        
        
        printf("CRC Ricevuto: 0x%x 0x%x 0x%x 0x%x\n", crcBuffer[0], crcBuffer[1], crcBuffer[2], crcBuffer[3]);

        crc = (crcBuffer[0] << 24)+(crcBuffer[1] << 16)+(crcBuffer[2] << 8)+(crcBuffer[3]);

    }

    return crc;

}

///Thread calcolo  CRC
void *
crcCalcThread(void *_args) {
    printf("crccalcthread\n");
    /* Cast the arguments to the usable struct type */
    struct parameters *args = (struct parameters *) _args;

    /* Place the result into the struct itself (on the heap) */
    
    args->crc = calculateCrc(args->Buffer, args->length, args->microNumber);
    printf("after  crccalcthread\n");
    return 0;
    //pthread_exit(NULL);
}

int main(int argc, char** argv) {
    
    
    
    uint8_t Buffer[1100];
    uint32_t crc32pol1;
    uint32_t crc32pol2;
    //int retValInitMutex=pthread_mutex_init(&(lockWrite), NULL);
    
    
    openSerialCommunication("/dev/ttySTM4",115200,1);
    openSerialCommunication("/dev/ttySTM3",115200,2);
    
    while(1)
    {
       
        for (int i = 0; i < 1024; i++) {
            Buffer[i] = 0x03;
            //printf("Write Buffer: %x ", write_buffer[i + 2]);

        }
        struct parameters *argsFirstMicro = calloc(sizeof (struct parameters), 1);
        argsFirstMicro->Buffer = Buffer;
        argsFirstMicro->length = 1024;
        argsFirstMicro->microNumber = 1;

        int err = pthread_create(&(threadId[0]), NULL, &crcCalcThread, argsFirstMicro);
        if (err != 0)
            printf("can't create thread :[%s]\n", strerror(err));
        else
            printf("crcCalcThread created successfully!\n");

        //MICRO 2
        /*crc32pol2 = crc32(Buffer, (packetLength + slPDU_SAFETY_CODE_NUM_BYTE+ paddingNum), CRC32_POLY_CH2_INV);
        log4c_category_log(slcat, LOG4C_PRIORITY_DEBUG, "CRC2 NO MICRO:%x\n", crc32pol2);
        printf("CRC2 NO MICRO:%x", crc32pol2);*/
        struct parameters *argsSecondMicro = calloc(sizeof (struct parameters), 1);
        argsSecondMicro->Buffer = Buffer;
        argsSecondMicro->length = 1024;
        argsSecondMicro->microNumber = 2;
        
        err = pthread_create(&(threadId[1]), NULL, &crcCalcThread, argsSecondMicro);
        if (err != 0)
            printf( "can't create thread :[%s]\n", strerror(err));
        else
            printf("crcCalcThread created successfully!\n");
        pthread_join(threadId[0], NULL);
        
        pthread_join(threadId[1], NULL);

        crc32pol1 = argsFirstMicro->crc;
        /*
                 Clean up the struct instance 
         */
        free(argsFirstMicro);
        argsFirstMicro = NULL;
        crc32pol2 = argsSecondMicro->crc;
        // Clean up the struct instance 
        free(argsSecondMicro);
        argsSecondMicro = NULL;
        usleep(4);
    }
    
        
    return (EXIT_SUCCESS);
    
}
sawdust
  • 16,103
  • 3
  • 40
  • 50
Luke
  • 1
  • Your code has numerous issues besides being rather inefficient (e.g. each thread is repeatedly allowed to terminate and then recreated) and sloppy when handling error conditions (e.g. program proceeds even when **open()** of nonexistent device node fails). Termios initialization is low-quality; see [Setting Terminal Modes Properly](https://www.gnu.org/software/libc/manual/html_node/Setting-Modes.html). Your understanding of *"no blocking"* is incorrect; see https://stackoverflow.com/questions/25996171/linux-blocking-vs-non-blocking-serial-read/26006680#26006680. – sawdust Dec 27 '21 at 23:57
  • *"elettrical port not send all byte"* -- How was this determined? BTW your program is accessing serial terminals, not the UARTs. The OS does not allow direct access to the hardware. See [Linux serial drivers](http://www.linux.it/~rubini/docs/serial/serial.html) – sawdust Dec 28 '21 at 00:12
  • `tty.c_cc[VTIME] = 2.5;` -- The VTIME value expects an integer, and not a fixed point value. Your use of **tcflush()** is questionable. Are you sure you understand what it does? Are you confusing it with **tcdrain()**? – sawdust Dec 28 '21 at 10:22
  • @sawdust , the test program we created communicates with two MCPs for the calculation of 2 crc32s. Since the MCP has given us errors in reception we have put a HW RS232 sniffer and we have seen that the Linux program sometimes does not send all the serial characters, but the write returns the value as if it had sent all of them. If I stop a thread, I no longer have this problem. How can this happen if I use two distinct serials? – Luke Dec 29 '21 at 09:17
  • for us blocking means that it remains indefinitely waiting for some character to arrive. We don't want it to hang, but after a 250ms timeout it will continue running. For this we configure vmin = 0 and vtime> 0. We have set 2.5 VTIME but we haven't build errors – Luke Dec 29 '21 at 09:20

0 Answers0