Răsfoiți Sursa

Merge branch 'bugfix/fix_uart_set_rx_timeout_feature_v41' into 'release/v4.1'

Bugfix/fix uart set rx timeout feature (backport v4.1)

See merge request espressif/esp-idf!11538
Jiang Jiang Jian 4 ani în urmă
părinte
comite
d3f11b1e9d

+ 16 - 0
components/driver/include/driver/uart.h

@@ -70,6 +70,8 @@ typedef enum {
 typedef struct {
     uart_event_type_t type; /*!< UART event type */
     size_t size;            /*!< UART data size for UART_DATA event*/
+    bool timeout_flag;      /*!< UART data read timeout flag for UART_DATA event (no new data received during configured RX TOUT)*/
+                            /*!< If the event is caused by FIFO-full interrupt, then there will be no event with the timeout flag before the next byte coming.*/
 } uart_event_t;
 
 typedef intr_handle_t uart_isr_handle_t;
@@ -845,6 +847,20 @@ esp_err_t uart_wait_tx_idle_polling(uart_port_t uart_num);
   */
 esp_err_t uart_set_loop_back(uart_port_t uart_num, bool loop_back_en);
 
+/**
+  * @brief Configure behavior of UART RX timeout interrupt.
+  *
+  * When always_rx_timeout is true, timeout interrupt is triggered even if FIFO is full.
+  * This function can cause extra timeout interrupts triggered only to send the timeout event.
+  * Call this function only if you want to ensure timeout interrupt will always happen after a byte stream.
+  *
+  * @param uart_num UART number
+  * @param always_rx_timeout_en Set to false enable the default behavior of timeout interrupt,
+  *                             set it to true to always trigger timeout interrupt.
+  *
+  */
+void uart_set_always_rx_timeout(uart_port_t uart_num, bool always_rx_timeout_en);
+
 #ifdef __cplusplus
 }
 #endif

+ 30 - 9
components/driver/uart.c

@@ -52,13 +52,13 @@ static const char* UART_TAG = "uart";
         return (ret_val); \
     }
 
-#define UART_EMPTY_THRESH_DEFAULT  (10)
-#define UART_FULL_THRESH_DEFAULT  (120)
-#define UART_TOUT_THRESH_DEFAULT   (10)
-#define UART_CLKDIV_FRAG_BIT_WIDTH  (3)
-#define UART_TX_IDLE_NUM_DEFAULT   (0)
-#define UART_PATTERN_DET_QLEN_DEFAULT (10)
-#define UART_MIN_WAKEUP_THRESH      (SOC_UART_MIN_WAKEUP_THRESH)
+#define UART_EMPTY_THRESH_DEFAULT       (10)
+#define UART_FULL_THRESH_DEFAULT        (120)
+#define UART_TOUT_THRESH_DEFAULT        (10)
+#define UART_CLKDIV_FRAG_BIT_WIDTH      (3)
+#define UART_TX_IDLE_NUM_DEFAULT        (0)
+#define UART_PATTERN_DET_QLEN_DEFAULT   (10)
+#define UART_MIN_WAKEUP_THRESH          (SOC_UART_MIN_WAKEUP_THRESH)
 
 #define UART_INTR_CONFIG_FLAG ((UART_INTR_RXFIFO_FULL) \
                             | (UART_INTR_RXFIFO_TOUT) \
@@ -104,6 +104,7 @@ typedef struct {
     intr_handle_t intr_handle;          /*!< UART interrupt handle*/
     uart_mode_t uart_mode;              /*!< UART controller actual mode set by uart_set_mode() */
     bool coll_det_flg;                  /*!< UART collision detection flag */
+    bool rx_always_timeout_flg;         /*!< UART always detect rx timeout flag */
 
     //rx parameters
     int rx_buffered_len;                  /*!< UART cached data length */
@@ -805,6 +806,10 @@ static void UART_ISR_ATTR uart_rx_intr_handler_default(void *param)
                 pat_flg = 0;
             }
             if (p_uart->rx_buffer_full_flg == false) {
+                rx_fifo_len = uart_hal_get_rxfifo_len(&(uart_context[uart_num].hal));
+                if ((p_uart_obj[uart_num]->rx_always_timeout_flg) && !(uart_intr_status & UART_INTR_RXFIFO_TOUT)) {
+                    rx_fifo_len--; // leave one byte in the fifo in order to trigger uart_intr_rxfifo_tout
+                }
                 uart_hal_read_rxfifo(&(uart_context[uart_num].hal), p_uart->rx_data_buf, &rx_fifo_len);
                 uint8_t pat_chr = 0;
                 uint8_t pat_num = 0;
@@ -822,6 +827,7 @@ static void UART_ISR_ATTR uart_rx_intr_handler_default(void *param)
                     uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_FULL);
                     uart_event.type = UART_DATA;
                     uart_event.size = rx_fifo_len;
+                    uart_event.timeout_flag = (uart_intr_status & UART_INTR_RXFIFO_TOUT) ? true : false;
                     UART_ENTER_CRITICAL_ISR(&uart_selectlock);
                     if (p_uart->uart_select_notif_callback) {
                         p_uart->uart_select_notif_callback(uart_num, UART_SELECT_READ_NOTIF, &HPTaskAwoken);
@@ -1294,6 +1300,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b
         p_uart_obj[uart_num]->uart_num = uart_num;
         p_uart_obj[uart_num]->uart_mode = UART_MODE_UART;
         p_uart_obj[uart_num]->coll_det_flg = false;
+        p_uart_obj[uart_num]->rx_always_timeout_flg = false;
         p_uart_obj[uart_num]->tx_fifo_sem = xSemaphoreCreateBinary();
         xSemaphoreGive(p_uart_obj[uart_num]->tx_fifo_sem);
         p_uart_obj[uart_num]->tx_done_sem = xSemaphoreCreateBinary();
@@ -1490,8 +1497,12 @@ esp_err_t uart_set_tx_empty_threshold(uart_port_t uart_num, int threshold)
 esp_err_t uart_set_rx_timeout(uart_port_t uart_num, const uint8_t tout_thresh)
 {
     UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG);
-    UART_CHECK((tout_thresh < 127), "tout_thresh max value is 126", ESP_ERR_INVALID_ARG);
-
+    // get maximum timeout threshold
+    uint16_t tout_max_thresh = uart_hal_get_max_rx_timeout_thrd(&(uart_context[uart_num].hal));
+    if (tout_thresh > tout_max_thresh) {
+        ESP_LOGE(UART_TAG, "tout_thresh = %d > maximum value = %d", tout_thresh, tout_max_thresh);
+        return ESP_ERR_INVALID_ARG;
+    }
     UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock));
     uart_hal_set_rx_timeout(&(uart_context[uart_num].hal), tout_thresh);
     UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock));
@@ -1543,3 +1554,13 @@ esp_err_t uart_set_loop_back(uart_port_t uart_num, bool loop_back_en)
     uart_hal_set_loop_back(&(uart_context[uart_num].hal), loop_back_en);
     return ESP_OK;
 }
+
+void uart_set_always_rx_timeout(uart_port_t uart_num, bool always_rx_timeout)
+{
+    uint16_t rx_tout = uart_hal_get_rx_tout_thr(&(uart_context[uart_num].hal));
+    if (rx_tout) {
+        p_uart_obj[uart_num]->rx_always_timeout_flg = always_rx_timeout;
+    } else {
+        p_uart_obj[uart_num]->rx_always_timeout_flg = false;
+    }
+}

+ 19 - 8
components/freemodbus/port/portserial.c

@@ -103,7 +103,7 @@ void vMBPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
     }
 }
 
-static void vMBPortSerialRxPoll(size_t xEventSize)
+static USHORT usMBPortSerialRxPoll(size_t xEventSize)
 {
     BOOL xReadStatus = TRUE;
     USHORT usCnt = 0;
@@ -114,17 +114,18 @@ static void vMBPortSerialRxPoll(size_t xEventSize)
             // Get received packet into Rx buffer
             for(usCnt = 0; xReadStatus && (usCnt < xEventSize); usCnt++ ) {
                 // Call the Modbus stack callback function and let it fill the buffers.
-                xReadStatus = pxMBFrameCBByteReceived(); // callback to execute receive FSM state machine
+                xReadStatus = pxMBFrameCBByteReceived(); // callback to execute receive FSM
             }
             uart_flush_input(ucUartNumber);
             // Send event EV_FRAME_RECEIVED to allow stack process packet
-#ifndef MB_TIMER_PORT_ENABLED
+#if !CONFIG_FMB_TIMER_PORT_ENABLED
             // Let the stack know that T3.5 time is expired and data is received
             (void)pxMBPortCBTimerExpired(); // calls callback xMBRTUTimerT35Expired();
 #endif
             ESP_LOGD(TAG, "RX: %d bytes\n", usCnt);
         }
     }
+    return usCnt;
 }
 
 BOOL xMBPortSerialTxPoll(void)
@@ -136,7 +137,7 @@ BOOL xMBPortSerialTxPoll(void)
         // Continue while all response bytes put in buffer or out of buffer
         while((bNeedPoll) && (usCount++ < MB_SERIAL_BUF_SIZE)) {
             // Calls the modbus stack callback function to let it fill the UART transmit buffer.
-            bNeedPoll = pxMBFrameCBTransmitterEmpty( ); // callback to transmit FSM state machine
+            bNeedPoll = pxMBFrameCBTransmitterEmpty( ); // callback to transmit FSM
         }
         ESP_LOGD(TAG, "MB_TX_buffer send: (%d) bytes\n", (uint16_t)usCount);
         // Waits while UART sending the packet
@@ -151,15 +152,23 @@ BOOL xMBPortSerialTxPoll(void)
 static void vUartTask(void *pvParameters)
 {
     uart_event_t xEvent;
+    USHORT usResult = 0;
     for(;;) {
         if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) {
             ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber);
             switch(xEvent.type) {
                 //Event of UART receving data
                 case UART_DATA:
-                    ESP_LOGD(TAG,"Receive data, len: %d", xEvent.size);
-                    // Read received data and send it to modbus stack
-                    vMBPortSerialRxPoll(xEvent.size);
+                    ESP_LOGD(TAG,"Data event, length: %d", xEvent.size);
+                    // This flag set in the event means that no more
+                    // data received during configured timeout and UART TOUT feature is triggered
+                    if (xEvent.timeout_flag) {
+                        // Get buffered data length
+                        ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size));
+                        // Read received data and send it to modbus stack
+                        usResult = usMBPortSerialRxPoll(xEvent.size);
+                        ESP_LOGD(TAG,"Timeout occured, processed: %d bytes", usResult);
+                    }
                     break;
                 //Event of HW FIFO overflow detected
                 case UART_FIFO_OVF:
@@ -251,12 +260,14 @@ BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate,
                                     MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
     MB_PORT_CHECK((xErr == ESP_OK), FALSE,
             "mb serial driver failure, uart_driver_install() returned (0x%x).", xErr);
-#ifndef MB_TIMER_PORT_ENABLED
+#if !CONFIG_FMB_TIMER_PORT_ENABLED
     // Set timeout for TOUT interrupt (T3.5 modbus time)
     xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
     MB_PORT_CHECK((xErr == ESP_OK), FALSE,
             "mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", xErr);
 #endif
+    // Set always timeout flag to trigger timeout interrupt even after rx fifo full
+    uart_set_always_rx_timeout(ucUartNumber, true);
     // Create a task to handle UART events
     BaseType_t xStatus = xTaskCreate(vUartTask, "uart_queue_task", MB_SERIAL_TASK_STACK_SIZE,
                                         NULL, MB_SERIAL_TASK_PRIO, &xMbTaskHandle);

+ 21 - 5
components/freemodbus/port/portserial_m.c

@@ -96,7 +96,7 @@ void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
     }
 }
 
-static void vMBMasterPortSerialRxPoll(size_t xEventSize)
+static USHORT usMBMasterPortSerialRxPoll(size_t xEventSize)
 {
     BOOL xReadStatus = TRUE;
     USHORT usCnt = 0;
@@ -109,8 +109,11 @@ static void vMBMasterPortSerialRxPoll(size_t xEventSize)
             uiRxBufferPos = 0;
             for(usCnt = 0; xReadStatus && (usCnt < usLength); usCnt++ ) {
                 // Call the Modbus stack callback function and let it fill the stack buffers.
-                xReadStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM state machine
+                xReadStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM
             }
+#if !CONFIG_FMB_TIMER_PORT_ENABLED
+            pxMBMasterPortCBTimerExpired();
+#endif
             // The buffer is transferred into Modbus stack and is not needed here any more
             uart_flush_input(ucUartNumber);
             ESP_LOGD(TAG, "Received data: %d(bytes in buffer)\n", (uint32_t)usCnt);
@@ -118,6 +121,7 @@ static void vMBMasterPortSerialRxPoll(size_t xEventSize)
     } else {
         ESP_LOGE(TAG, "%s: bRxState disabled but junk data (%d bytes) received. ", __func__, xEventSize);
     }
+    return usCnt;
 }
 
 BOOL xMBMasterPortSerialTxPoll(void)
@@ -145,15 +149,25 @@ BOOL xMBMasterPortSerialTxPoll(void)
 static void vUartTask(void* pvParameters)
 {
     uart_event_t xEvent;
+    USHORT usResult = 0;
     for(;;) {
         if (xQueueReceive(xMbUartQueue, (void*)&xEvent, portMAX_DELAY) == pdTRUE) {
             ESP_LOGD(TAG, "MB_uart[%d] event:", ucUartNumber);
             switch(xEvent.type) {
                 //Event of UART receiving data
                 case UART_DATA:
-                    ESP_LOGD(TAG,"Receive data, len: %d.", xEvent.size);
-                    // Read received data and send it to modbus stack
-                    vMBMasterPortSerialRxPoll(xEvent.size);
+                    ESP_LOGD(TAG,"Data event, len: %d.", xEvent.size);
+                    // This flag set in the event means that no more
+                    // data received during configured timeout and UART TOUT feature is triggered
+                    if (xEvent.timeout_flag) {
+                        // Get buffered data length
+                        ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size));
+                        // Read received data and send it to modbus stack
+                        usResult = usMBMasterPortSerialRxPoll(xEvent.size);
+                        ESP_LOGD(TAG,"Timeout occurred, processed: %d bytes", usResult);
+                        // Block receiver task until data is not processed
+                        vTaskSuspend(NULL);
+                    }
                     break;
                 //Event of HW FIFO overflow detected
                 case UART_FIFO_OVF:
@@ -249,6 +263,8 @@ BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
     xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
     MB_PORT_CHECK((xErr == ESP_OK), FALSE,
             "mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", xErr);
+    // Set always timeout flag to trigger timeout interrupt even after rx fifo full
+    uart_set_always_rx_timeout(ucUartNumber, true);
     // Create a task to handle UART events
     BaseType_t xStatus = xTaskCreate(vUartTask, "uart_queue_task", MB_SERIAL_TASK_STACK_SIZE,
                                         NULL, MB_SERIAL_TASK_PRIO, &xMbTaskHandle);

+ 62 - 25
components/soc/esp32/include/hal/uart_ll.h

@@ -395,31 +395,6 @@ static inline void uart_ll_set_tx_idle_num(uart_dev_t *hw, uint32_t idle_num)
     hw->idle_conf.tx_idle_num = idle_num;
 }
 
-/**
- * @brief  Configure the timeout value for receiver receiving a byte, and enable rx timeout function.
- *
- * @param  hw Beginning address of the peripheral registers.
- * @param  tout_thr The timeout value. The rx timeout function will be disabled if `tout_thr == 0`.
- *
- * @return None.
- */
-static inline void uart_ll_set_rx_tout(uart_dev_t *hw, uint8_t tout_thr)
-{
-    // The tout_thresh = 1, defines TOUT interrupt timeout equal to
-    // transmission time of one symbol (~11 bit) on current baudrate
-    if (hw->conf0.tick_ref_always_on == 0) {
-        //Hardware issue workaround: when using ref_tick, the rx timeout threshold needs increase to 10 times.
-        //T_ref = T_apb * APB_CLK/(REF_TICK << CLKDIV_FRAG_BIT_WIDTH)
-        tout_thr = tout_thr * UART_LL_TOUT_REF_FACTOR_DEFAULT;
-    }
-    if(tout_thr > 0) {
-        hw->conf1.rx_tout_thrhd = tout_thr;
-        hw->conf1.rx_tout_en = 1;
-    } else {
-        hw->conf1.rx_tout_en = 0;
-    }
-}
-
 /**
  * @brief  Configure the transmiter to send break chars.
  *
@@ -822,4 +797,66 @@ static inline void uart_ll_inverse_signal(uart_dev_t *hw, uint32_t inv_mask)
     hw->conf0.val = conf0_reg.val;
 }
 
+/**
+ * @brief  Configure the timeout value for receiver receiving a byte, and enable rx timeout function.
+ *
+ * @param  hw Beginning address of the peripheral registers.
+ * @param  tout_thr The timeout value as a bit time. The rx timeout function will be disabled if `tout_thr == 0`.
+ *
+ * @return None.
+ */
+static inline void uart_ll_set_rx_tout(uart_dev_t *hw, uint16_t tout_thr)
+{
+    if (hw->conf0.tick_ref_always_on == 0) {
+        //Hardware issue workaround: when using ref_tick, the rx timeout threshold needs increase to 10 times.
+        //T_ref = T_apb * APB_CLK/(REF_TICK << CLKDIV_FRAG_BIT_WIDTH)
+        tout_thr = tout_thr * UART_LL_TOUT_REF_FACTOR_DEFAULT;
+    } else {
+        //If APB_CLK is used: counting rate is BAUD tick rate / 8
+        tout_thr = (tout_thr + 7) / 8;
+    }
+    if (tout_thr > 0) {
+        hw->conf1.rx_tout_thrhd = tout_thr;
+        hw->conf1.rx_tout_en = 1;
+    } else {
+        hw->conf1.rx_tout_en = 0;
+    }
+}
+/**
+ * @brief  Get the timeout value for receiver receiving a byte.
+ *
+ * @param  hw Beginning address of the peripheral registers.
+ *
+ * @return tout_thr The timeout threshold value. If timeout feature is disabled returns 0.
+ */
+static inline uint16_t uart_ll_get_rx_tout_thr(uart_dev_t *hw)
+{
+    uint16_t tout_thrd = 0;
+    if (hw->conf1.rx_tout_en > 0) {
+        if (hw->conf0.tick_ref_always_on == 0) {
+            tout_thrd = (uint16_t)(hw->conf1.rx_tout_thrhd / UART_LL_TOUT_REF_FACTOR_DEFAULT);
+        } else {
+            tout_thrd = (uint16_t)(hw->conf1.rx_tout_thrhd  << 3);
+        }
+    }
+    return tout_thrd;
+}
+
+/**
+ * @brief  Get UART maximum timeout threshold.
+ *
+ * @param  hw Beginning address of the peripheral registers.
+ *
+ * @return maximum timeout threshold.
+ */
+static inline uint16_t uart_ll_max_tout_thrd(uart_dev_t *hw)
+{
+    uint16_t tout_thrd = 0;
+    if (hw->conf0.tick_ref_always_on == 0) {
+        tout_thrd = (uint16_t)(UART_RX_TOUT_THRHD_V / UART_LL_TOUT_REF_FACTOR_DEFAULT);
+    } else {
+        tout_thrd = (uint16_t)(UART_RX_TOUT_THRHD_V  << 3);
+    }
+    return tout_thrd;
+}
 #undef UART_LL_TOUT_REF_FACTOR_DEFAULT

+ 47 - 18
components/soc/esp32s2beta/include/hal/uart_ll.h

@@ -349,24 +349,6 @@ static inline void uart_ll_set_tx_idle_num(uart_dev_t *hw, uint32_t idle_num)
     hw->idle_conf.tx_idle_num = idle_num;
 }
 
-/**
- * @brief  Configure the timeout value for receiver receiving a byte, and enable rx timeout function.
- *
- * @param  hw Beginning address of the peripheral registers.
- * @param  tout_thr The timeout value. The rx timeout function will be disabled if `tout_thr == 0`.
- *
- * @return None.
- */
-static inline void uart_ll_set_rx_tout(uart_dev_t *hw, uint8_t tout_thr)
-{
-    if(tout_thr > 0) {
-        hw->mem_conf.rx_tout_thrhd = tout_thr;
-        hw->conf1.rx_tout_en = 1;
-    } else {
-        hw->conf1.rx_tout_en = 0;
-    }
-}
-
 /**
  * @brief  Configure the transmiter to send break chars.
  *
@@ -767,3 +749,50 @@ static inline void uart_ll_inverse_signal(uart_dev_t *hw, uint32_t inv_mask)
     conf0_reg.dtr_inv |= (inv_mask & UART_SIGNAL_DTR_INV) ? 1 : 0;
     hw->conf0.val = conf0_reg.val;
 }
+
+/**
+ * @brief  Configure the timeout value for receiver receiving a byte, and enable rx timeout function.
+ *
+ * @param  hw Beginning address of the peripheral registers.
+ * @param  tout_thrd The timeout value as UART bit time. The rx timeout function will be disabled if `tout_thrd == 0`.
+ *
+ * @return None.
+ */
+static inline void uart_ll_set_rx_tout(uart_dev_t *hw, uint16_t tout_thrd)
+{
+    uint16_t tout_val = tout_thrd;
+    if(tout_thrd > 0) {
+        hw->mem_conf.rx_tout_thrhd = tout_val;
+        hw->conf1.rx_tout_en = 1;
+    } else {
+        hw->conf1.rx_tout_en = 0;
+    }
+}
+
+/**
+ * @brief  Get the timeout value for receiver receiving a byte.
+ *
+ * @param  hw Beginning address of the peripheral registers.
+ *
+ * @return tout_thr The timeout threshold value. If timeout feature is disabled returns 0.
+ */
+static inline uint16_t uart_ll_get_rx_tout_thr(uart_dev_t *hw)
+{
+    uint16_t tout_thrd = 0;
+    if(hw->conf1.rx_tout_en > 0) {
+        tout_thrd = hw->mem_conf.rx_tout_thrhd;
+    }
+    return tout_thrd;
+}
+
+/**
+ * @brief  Get UART maximum timeout threshold.
+ *
+ * @param  hw Beginning address of the peripheral registers.
+ *
+ * @return maximum timeout threshold.
+ */
+static inline uint16_t uart_ll_max_tout_thrd(uart_dev_t *hw)
+{
+    return UART_RX_TOUT_THRHD_V;
+}

+ 42 - 5
components/soc/include/hal/uart_hal.h

@@ -127,13 +127,14 @@ typedef struct {
 /**
  * @brief  Read data from the UART rxfifo
  *
- * @param  hal Context of the HAL layer
- * @param  buf Pointer to the buffer used to store the read data. The buffer size should be large than 128 byts
- * @param  rd_len The length has been read out from the rxfifo
+ * @param[in] hal Context of the HAL layer
+ * @param[in] buf Pointer to the buffer used to store the read data. The buffer size should be large than 128 bytes
+ * @param[inout] inout_rd_len As input, the size of output buffer to read (set to 0 to read all available data).
+ *                            As output, returns the actual size written into the output buffer.
  *
  * @return None
  */
-void uart_hal_read_rxfifo(uart_hal_context_t *hal, uint8_t *buf, int *rd_len);
+void uart_hal_read_rxfifo(uart_hal_context_t *hal, uint8_t *buf, int *inout_rd_len);
 
 /**
  * @brief  Write data into the UART txfifo
@@ -430,6 +431,42 @@ void uart_hal_get_sclk(uart_hal_context_t *hal, uart_sclk_t *sclk);
  */
 void uart_hal_set_loop_back(uart_hal_context_t *hal, bool loop_back_en);
 
+/**
+ * @brief  Calculate uart symbol bit length, as defined in configuration.
+ *
+ * @param  hw Beginning address of the peripheral registers.
+ *
+ * @return number of bits per UART symbol.
+ */
+uint8_t uart_hal_get_symb_len(uart_hal_context_t *hal);
+
+/**
+ * @brief  Get UART maximum timeout threshold.
+ *
+ * @param  hw Beginning address of the peripheral registers.
+ *
+ * @return maximum timeout threshold value for target.
+ */
+uint16_t uart_hal_get_max_rx_timeout_thrd(uart_hal_context_t *hal);
+
+/**
+ * @brief  Get the timeout threshold value set for receiver.
+ *
+ * @param  hw Beginning address of the peripheral registers.
+ *
+ * @return tout_thr The timeout value. If timeout is disabled then returns 0.
+ */
+#define uart_hal_get_rx_tout_thr(hal) uart_ll_get_rx_tout_thr((hal)->dev)
+
+/**
+ * @brief  Get the length of readable data in UART rxfifo.
+ *
+ * @param  hw Beginning address of the peripheral registers.
+ *
+ * @return The readable data length in rxfifo.
+ */
+#define uart_hal_get_rxfifo_len(hal) uart_ll_get_rxfifo_len((hal)->dev)
+
 #ifdef __cplusplus
 }
-#endif
+#endif

+ 30 - 6
components/soc/src/hal/uart_hal.c

@@ -76,11 +76,6 @@ void uart_hal_set_at_cmd_char(uart_hal_context_t *hal, uart_at_cmd_t *at_cmd)
     uart_ll_set_at_cmd_char(hal->dev, at_cmd);
 }
 
-void uart_hal_set_rx_timeout(uart_hal_context_t *hal, const uint8_t tout)
-{
-    uart_ll_set_rx_tout(hal->dev, tout);
-}
-
 void uart_hal_get_sclk(uart_hal_context_t *hal, uart_sclk_t *sclk)
 {
     uart_ll_get_sclk(hal->dev, sclk);
@@ -153,4 +148,33 @@ void uart_hal_init(uart_hal_context_t *hal, int uart_num)
     uart_ll_set_tx_idle_num(hal->dev, 0);
     // Disable hw-flow control
     uart_ll_set_hw_flow_ctrl(hal->dev, UART_HW_FLOWCTRL_DISABLE, 100);
-}
+}
+
+uint8_t uart_hal_get_symb_len(uart_hal_context_t *hal)
+{
+    uint8_t symbol_len = 1; // number of bits per symbol including start
+    uart_parity_t parity_mode;
+    uart_stop_bits_t stop_bit;
+    uart_word_length_t data_bit;
+    uart_ll_get_data_bit_num(hal->dev, &data_bit);
+    uart_ll_get_stop_bits(hal->dev, &stop_bit);
+    uart_ll_get_parity(hal->dev, &parity_mode);
+    symbol_len += (data_bit < UART_DATA_BITS_MAX) ? (uint8_t)data_bit + 5 : 8;
+    symbol_len += (stop_bit > UART_STOP_BITS_1) ? 2 : 1;
+    symbol_len += (parity_mode > UART_PARITY_DISABLE) ? 1 : 0;
+    return symbol_len;
+}
+
+void uart_hal_set_rx_timeout(uart_hal_context_t *hal, const uint8_t tout)
+{
+    uint8_t symb_len = uart_hal_get_symb_len(hal);
+    uart_ll_set_rx_tout(hal->dev, symb_len * tout);
+}
+
+uint16_t uart_hal_get_max_rx_timeout_thrd(uart_hal_context_t *hal)
+{
+    uint8_t symb_len = uart_hal_get_symb_len(hal);
+    uint16_t max_tout_thresh = uart_ll_max_tout_thrd(hal->dev);
+    return (max_tout_thresh / symb_len);
+}
+

+ 6 - 4
components/soc/src/hal/uart_hal_iram.c

@@ -40,9 +40,11 @@ void uart_hal_write_txfifo(uart_hal_context_t *hal, const uint8_t *buf, uint32_t
     uart_ll_write_txfifo(hal->dev, buf, fill_len);
 }
 
-void uart_hal_read_rxfifo(uart_hal_context_t *hal, uint8_t *buf, int *rd_len)
+void uart_hal_read_rxfifo(uart_hal_context_t *hal, uint8_t *buf, int *inout_rd_len)
 {
-    uint16_t read_len = uart_ll_get_rxfifo_len(hal->dev);
-    *rd_len = read_len;
-    uart_ll_read_rxfifo(hal->dev, buf, read_len);
+    if (*inout_rd_len <= 0) {
+        *inout_rd_len = uart_ll_get_rxfifo_len(hal->dev);
+    }
+    uart_ll_read_rxfifo(hal->dev, buf, *inout_rd_len);
 }
+