How UART working?

JAHTKELD
Posts: 12
Joined: Tue Jul 02, 2019 8:51 am

How UART working?

Postby JAHTKELD » Sat Jun 05, 2021 7:04 am

Hello:

I'm trying to read 189 bytes from UART, but some times I doesnt work well.

Code: Select all

void init_uart() {
    const uart_config_t uart_config = {
        .baud_rate = 9600,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
		.rx_flow_ctrl_thresh = 122
    };
    uart_param_config(UART_NUM_1, &uart_config);
    uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, RTS_PIN, UART_PIN_NO_CHANGE);
    // We won't use a buffer for sending data.
    uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
    uart_set_mode(UART_NUM_1, UART_MODE_RS485_HALF_DUPLEX);

    xTaskCreate(rx_task, "uart_rx_task", 1024*5, NULL, configMAX_PRIORITIES, NULL);
    printf("create uart_rx_task\n");
}


static void rx_task()
{   
    static const char *RX_TASK_TAG = "RX_TASK";
    static unsigned int recv_index;
    static unsigned int i;

    esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
    uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE+1);
    uint8_t* recv_buffer = (uint8_t*) malloc(RX_BUF_SIZE+1);
    recv_index=0;

    
    while (1)
    {
      
        conts int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE,40/portTICK_RATE_MS);

        if (rxBytes > 0)
        {      
                //Save rec data
        	for(i=0;i<rxBytes;i++)
        	if((recv_index+i)<RX_BUF_SIZE) recv_buffer[recv_index+i] =data[i];
        	recv_index=recv_index+rxBytes;
        }
        else  
        {
            if(recv_index>0)  ProcessRecData();
            recv_index=0;
         }
          vTaskDelay(30/portTICK_RATE_MS);
     }

}
I always receive the first 120 bytes from uart_read_bytes(), then task wait 30ms (vTaskDelay(30/portTICK_RATE_MS)) and the next
uart_read_bytes() give me 0 and other times give me 69 bytes. When I receive 0, the ProcessRecData fail because the frame is incomplete, and the frame CRC is wrong (logical).

I dont understand why in the first step always a get the first 120 bytes (why 120?) and why at the second step sometimes get 0 bytes or 69 bytes. If the baudrate is 9600, the byte time is 1ms

I checked portTICK_RATE_MS and it is 10 and RX_BUF_SIZE is 1024 bytes.

The system is a Modbus communication with diferent frame sizes and i want to detect the frame with the time without data in the line, for that I use the 0 bytes received after a 30ms as end frame.

Some one have a idea why this happen?

Thank you in advance

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: How UART working?

Postby WiFive » Sat Jun 05, 2021 7:39 am

The fifo gets emptied at 120 bytes or timeout. Data won't be available until after the last byte is finished plus timeout.

JAHTKELD
Posts: 12
Joined: Tue Jul 02, 2019 8:51 am

Re: How UART working?

Postby JAHTKELD » Sat Jun 05, 2021 7:41 am

Can I change the 120 bytes of FIFO to 250 for example? how?

JAHTKELD
Posts: 12
Joined: Tue Jul 02, 2019 8:51 am

Re: How UART working?

Postby JAHTKELD » Sat Jun 05, 2021 8:02 am

120 bytes seems a hardware limit. is it?

well

first

Code: Select all

conts int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE,40/portTICK_RATE_MS);
I read 120 bytes

wait 30 ms

Code: Select all

 vTaskDelay(30/portTICK_RATE_MS);
in this moment I expect there are 30 new bytes in the FIFO

then I do

Code: Select all

conts int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE,40/portTICK_RATE_MS);
this means wait 40 ms more (40 bytes more), total time 70 ms

I dont understand
Data won't be available until after the last byte is finished plus timeout.
Do you mean that the
uart_read_bytes
give me 0 for the last 69 bytes if I try to read before 69ms+40ms?

thank you

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: How UART working?

Postby WiFive » Sat Jun 05, 2021 7:15 pm

in this moment I expect there are 30 new bytes in the FIFO
There are but uart_read_bytes reads the RX buffer not the fifo. The fifo is only emptied after the timeout happens at the last byte received + ~1ms which is around 70ms but possibly more. If you use uart events you can check for timeout.

JAHTKELD
Posts: 12
Joined: Tue Jul 02, 2019 8:51 am

Re: How UART working?

Postby JAHTKELD » Sun Jun 06, 2021 5:31 am

Then this means that the Uart working mode is

1 Data input in to the FIFO buffer
2.Data go from FIFO to Uart buffer when
a: the FIFO is full (120 bytes)
b: there is not more data after timeout*

* this timeout is not the timeout from uart_read_bytes, this is the timeout of one character, for example for 9600 baud 8bits is 1.04 ms

There may be data in the FIFO buffer and to call to uart_read_bytes and return 0 bytes data because is still being received data and the FIFO has not been filled

Thanks for the clarification

ESP_alisitsyn
Posts: 211
Joined: Fri Feb 01, 2019 4:02 pm
Contact:

Re: How UART working?

Postby ESP_alisitsyn » Tue Jun 08, 2021 8:30 am

@JAHTKELD
As an addition to other comments please consider my update below:

The 120 bytes = UART_FULL_THRESH_DEFAULT, means that UART_RX_FULL interrupt will happen when the FIFO received this amount of bytes. It can be set up to 128 in uart_intr_config_t configuration structure. When the host sending 128 bytes (for example) the ISR handler receives 120 bytes then waits for other 120 bytes which never comes and causes the uart_read_bytes() to return 0 bytes due to tick timeout.

I would advise the proper way to receive the Modbus frame correctly:
1. Configure UART_FULL_THRESHOLD and setup the UART driver.
2. Save portion of data received on UART_INTR_RXFIFO_FULL event into receiver buffer. The UART saves this data into ring buffer which can be read on UART_INTR_RXFIFO_TOUT event.
3. Configure the UART_RX_TOUT feature https://github.com/espressif/esp-idf/bl ... l_m.c#L238 to trigger UART_INTR_RXFIFO_TOUT when gap = number of symbols (=T3.5 usually) on current baudrate is detected in received data. https://github.com/espressif/esp-idf/bl ... l_m.c#L139. This feature has been updated to detect timeout reliably for Modbus and if triggered means that the complete Modbus frame is received and receiver buffer contains the received frame.

Note:
This algorithm allows to receive the frame reliably when the receive task is not suspended by higher priority tasks (example: Wifi, BT, NVS). Otherwise the UART receive and timeout events are shifted and cause the events processing errors. Also if the timer and UART handler functions are placed into flash memory it causes delay of interrupt activation that can be up to ~2ms when somewhere in your code the flash read/write functions are used (disable CPU cache). This causes issues with registration of RX and TOUT events in interrupt handler and possible errors of processing. The only reliable solution is to place the receiver code into custom UART IRAM-safe ISR handler to avoid possible latency with the interrupts. The simple example of the custom receiver interrupt: https://github.com/alisitsyn/modbus_sup ... al_example. The update will be published in official ESP_Modbus.
Let me know if you have any comments related to this.

Who is online

Users browsing this forum: No registered users and 143 guests