Page 1 of 1

Driver UART problems

Posted: Wed Jan 29, 2020 10:32 pm
by Suky_mtch
Hi everyone,
I am working on a project that implements RS485 half duplex messages. To implement it, use UART1 on an STM32F750 as follows:

Code: Select all

static RS485_DATA rs485Data;

static void RS485_RxChar(UARTDriver *uartp, uint16_t c) {
   (void)uartp;

   // circular buffer
   if(rs485Data.flags.fullBufferRx == 0){
      rs485Data.bufferRx[rs485Data.ptrRxWData++] = c;
      if(++rs485Data.countRxData == LENGTH_BUFFER_UART_RX){
         rs485Data.flags.fullBufferRx = 1;
      }
      if(rs485Data.ptrRxWData == LENGTH_BUFFER_UART_RX){
         rs485Data.ptrRxWData = 0;
      }
   }
}

static void RS485_TxEnd(UARTDriver *uartp){
   RS485_SET_RECEPTION();
}

static UARTConfig uart_cfg1 = {
   NULL,                                 // End of transmission buffer callback.
   RS485_TxEnd,                           // Physical end of transmission callback.
   NULL,                                 // Receive buffer filled callback.
   RS485_RxChar,                           // Character received while out if the @p UART_RECEIVE state.
   NULL,                                 // Receive error callback.
   NULL,                                 // Receiver timeout callback.
   0,                                    // Receiver timeout value in terms of number of bit duration.
   9600,                                 // Bit rate.
   0 | (USART_CR1_M0 | USART_CR1_PCE),            // CR1 register. 9 bits (8 + paridad) Even
   0,                                     // CR2 register.
   0                                    // CR3 register.
};


void RS485_PutArray(uint8_t *ptrData, uint16_t lenght){
    RS485_SET_TRANSMISSION();
    chThdSleepMicroseconds(100);
    uartStartSend(uartp, lenght, (const void *)ptrData);
    return;
}

void RS485_ProcessBuffer(void){
   static uint8_t buffer[256];
   uint16_t length;

   switch(rs485Data.state){
   case RS485_GET_HEADER1:
      if(rs485Data.countRxData == 0){
         return;
      }

      length = rs485Data.countRxData;
      do{
         RS485_GetArray(&buffer[0], 1);
         if(buffer[0] == ((RS485_HEADER >> 8) & 0xFF)){
            rs485Data.state = RS485_GET_HEADER2;
            return;
         }
         length--;
      }while(length != 0);
      break;
   case RS485_GET_HEADER2:
      if(rs485Data.countRxData < 2){
         return;
      }
      RS485_GetArray(&buffer[1], 1);

      if(buffer[1] == (RS485_HEADER & 0xFF)){
         RS485_GetArray(&buffer[2], 1);
         rs485Data.totalLength = buffer[2];

         if(rs485Data.totalLength != 0){
            if(rs485Data.countRxData >= (rs485Data.totalLength - 1)){
               rs485Data.state = RS485_PROCESS_DATA;
            }else{
               rs485Data.state = RS485_GET_ALL_DATA;
            }
         }else{
            rs485Data.state = RS485_GET_HEADER1;
         }
      }else{
         rs485Data.state = RS485_GET_HEADER1;
      }
      break;
   case RS485_GET_ALL_DATA:
      if(rs485Data.countRxData >= (rs485Data.totalLength - 1)){
         rs485Data.state = RS485_PROCESS_DATA;
      }
      break;
   case RS485_PROCESS_DATA:
      RS485_GetArray(&buffer[0], rs485Data.totalLength - 1);
      // ...
      rs485Data.state = RS485_GET_HEADER1;
      break;
   case RS485_PUT_DATA:
      break;      
   }
}


static THD_WORKING_AREA(wa_rs485, 1024);
static THD_FUNCTION(RS485_Tasks, arg) {
   (void)arg;

   while(TRUE){
      chThdSleepMilliseconds(10);
      RS485_ProcessBuffer();
   }
}


void RS485_Initialize(void){

   RS485_SET_RECEPTION();
   uartStart(&UARTD1, &uart_cfg1);

   rs485Data.state = RS485_GET_HEADER1;
   chThdCreateStatic(wa_rs485, sizeof(wa_rs485), NORMALPRIO + 2, RS485_Tasks, NULL);
}



The problem that occurs is that after a couple of hours running (messages are received every 2 sec of about 20 bytes in average), it begins to be observed that a package (header, length, data, checksum) is not received immediately, but it seems to be in a queue. That is, I send a 20-byte message, receive 8, stays in a state of "waiting for the total" (RS485_GET_ALL_DATA), which occurs when another message is sent.
Then it is like the second message "pushes" the data that was queued and the entire frame is received.

Can you think of what the mistake might be?

Thanks! regards.

Re: Driver UART problems

Posted: Thu Jan 30, 2020 8:22 am
by Giovanni
Hi,

Are you handling communication errors? those cause bytes to not be received so you wait for 20 chars but only get 19, the 20th will be the first of the next message and that is the delay you observe.

Probably you just need robust error handling using a state machine, timeouts etc.

Giovanni

Re: Driver UART problems

Posted: Thu Jan 30, 2020 1:45 pm
by Suky_mtch
Hi,
No, I don't handle errors. Because no bytes are really lost, but they are queued and pushed when they receive the next frame.
Can it happen that the DMA does not call the callback function for any reason?

Regards

Re: Driver UART problems

Posted: Thu Jan 30, 2020 1:52 pm
by Giovanni
The driver would be stuck without DMA IRQ.

Giovanni

Re: Driver UART problems

Posted: Mon Feb 03, 2020 5:23 pm
by Suky_mtch
Hi,

I solved it by stopping and restarting reception after each transmission:

Code: Select all

void RS485_PutArray(uint8_t *ptrData, uint16_t lenght){
   uint8_t delay;
   UARTDriver *uartp;
   USART_TypeDef *u;

   uartp = &UARTD1;
   u = uartp->usart;
   u->CR1 = u->CR1 & (~USART_CR1_RE);
   dmaStreamDisable(uartp->dmarx);
    RS485_PROTOCOLS_SET_TRANSMISSION();
   chThdSleepMicroseconds(100);
   uartStartSend(uartp, lenght, (const void *)ptrData);
   return;
}

static void RS485_TxEnd(UARTDriver *uartp){
   uint32_t mode;
   USART_TypeDef *u;

   uartp->rxstate = UART_RX_IDLE;
   mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC | STM32_DMA_CR_TCIE;
   dmaStreamSetMemory0(uartp->dmarx, &uartp->rxbuf);
   dmaStreamSetTransactionSize(uartp->dmarx, 1);
   dmaStreamSetMode(uartp->dmarx, uartp->dmamode | mode);

   RS485_PROTOCOLS_SET_RECEPTION();
   dmaStreamEnable(uartp->dmarx);
   u = uartp->usart;
   u->CR1 = u->CR1 | USART_CR1_RE;
}



Regards