mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
uart: Fix two TX concurrency issues
1. Concurrency might cause ESP_ERR_TIMEOUT when calling uart_wait_tx_done 2. Concurrency might cause RTS line being de-assreted during tx transmission for rs485 mode
This commit is contained in:
parent
11c59a2e69
commit
f177b2c6c6
@ -809,6 +809,9 @@ static uint32_t UART_ISR_ATTR uart_enable_tx_write_fifo(uart_port_t uart_num, co
|
||||
UART_ENTER_CRITICAL_SAFE(&(uart_context[uart_num].spinlock));
|
||||
if (UART_IS_MODE_SET(uart_num, UART_MODE_RS485_HALF_DUPLEX)) {
|
||||
uart_hal_set_rts(&(uart_context[uart_num].hal), 0);
|
||||
// If any new things are written to fifo, then we can always clear the previous TX_DONE interrupt bit (if it was set)
|
||||
// Old TX_DONE bit might reset the RTS, leading new tx transmission failure for rs485 mode
|
||||
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_TX_DONE);
|
||||
uart_hal_ena_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TX_DONE);
|
||||
}
|
||||
uart_hal_write_txfifo(&(uart_context[uart_num].hal), pbuf, len, &sent_len);
|
||||
@ -1138,14 +1141,33 @@ esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait)
|
||||
if (res == pdFALSE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
// Check the enable status of TX_DONE: If already enabled, then let the isr handle the status bit;
|
||||
// If not enabled, then make sure to clear the status bit before enabling the TX_DONE interrupt bit
|
||||
UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock));
|
||||
bool is_rs485_mode = UART_IS_MODE_SET(uart_num, UART_MODE_RS485_HALF_DUPLEX);
|
||||
bool disabled = !(uart_hal_get_intr_ena_status(&(uart_context[uart_num].hal)) & UART_INTR_TX_DONE);
|
||||
// For RS485 mode, TX_DONE interrupt is enabled for every tx transmission, so there shouldn't be a case of
|
||||
// interrupt not enabled but raw bit is set.
|
||||
assert(!(is_rs485_mode &&
|
||||
disabled &&
|
||||
uart_hal_get_intraw_mask(&(uart_context[uart_num].hal)) & UART_INTR_TX_DONE));
|
||||
// If decided to register for the TX_DONE event, then we should clear any possible old tx transmission status.
|
||||
// The clear operation of RS485 mode should only be handled in isr or when writing to tx fifo.
|
||||
if (disabled && !is_rs485_mode) {
|
||||
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_TX_DONE);
|
||||
}
|
||||
UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock));
|
||||
xSemaphoreTake(p_uart_obj[uart_num]->tx_done_sem, 0);
|
||||
// FSM status register update comes later than TX_DONE interrupt raw bit raise
|
||||
// The maximum time takes for FSM status register to update is (6 APB clock cycles + 3 UART core clock cycles)
|
||||
// Therefore, to avoid the situation of TX_DONE bit being cleared but FSM didn't be recognized as IDLE (which
|
||||
// would lead to timeout), a delay of 2us is added in between.
|
||||
esp_rom_delay_us(2);
|
||||
if (uart_hal_is_tx_idle(&(uart_context[uart_num].hal))) {
|
||||
xSemaphoreGive(p_uart_obj[uart_num]->tx_mux);
|
||||
return ESP_OK;
|
||||
}
|
||||
if (!UART_IS_MODE_SET(uart_num, UART_MODE_RS485_HALF_DUPLEX)) {
|
||||
uart_hal_clr_intsts_mask(&(uart_context[uart_num].hal), UART_INTR_TX_DONE);
|
||||
}
|
||||
UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock));
|
||||
uart_hal_ena_intr_mask(&(uart_context[uart_num].hal), UART_INTR_TX_DONE);
|
||||
UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock));
|
||||
|
@ -142,6 +142,18 @@ FORCE_INLINE_ATTR void uart_ll_disable_intr_mask(uart_dev_t *hw, uint32_t mask)
|
||||
hw->int_ena.val &= (~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART raw interrupt status.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*
|
||||
* @return The UART interrupt status.
|
||||
*/
|
||||
static inline uint32_t uart_ll_get_intraw_mask(uart_dev_t *hw)
|
||||
{
|
||||
return hw->int_raw.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART interrupt status.
|
||||
*
|
||||
|
@ -206,6 +206,18 @@ static inline void uart_ll_disable_intr_mask(uart_dev_t *hw, uint32_t mask)
|
||||
hw->int_ena.val &= (~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART raw interrupt status.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*
|
||||
* @return The UART interrupt status.
|
||||
*/
|
||||
static inline uint32_t uart_ll_get_intraw_mask(uart_dev_t *hw)
|
||||
{
|
||||
return hw->int_raw.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART interrupt status.
|
||||
*
|
||||
|
@ -209,6 +209,18 @@ static inline void uart_ll_disable_intr_mask(uart_dev_t *hw, uint32_t mask)
|
||||
hw->int_ena.val &= (~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART raw interrupt status.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*
|
||||
* @return The UART interrupt status.
|
||||
*/
|
||||
static inline uint32_t uart_ll_get_intraw_mask(uart_dev_t *hw)
|
||||
{
|
||||
return hw->int_raw.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART interrupt status.
|
||||
*
|
||||
|
@ -209,6 +209,18 @@ static inline void uart_ll_disable_intr_mask(uart_dev_t *hw, uint32_t mask)
|
||||
hw->int_ena.val &= (~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART raw interrupt status.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*
|
||||
* @return The UART interrupt status.
|
||||
*/
|
||||
static inline uint32_t uart_ll_get_intraw_mask(uart_dev_t *hw)
|
||||
{
|
||||
return hw->int_raw.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART interrupt status.
|
||||
*
|
||||
|
@ -140,6 +140,18 @@ FORCE_INLINE_ATTR void uart_ll_disable_intr_mask(uart_dev_t *hw, uint32_t mask)
|
||||
hw->int_ena.val &= (~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART raw interrupt status.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*
|
||||
* @return The UART interrupt status.
|
||||
*/
|
||||
static inline uint32_t uart_ll_get_intraw_mask(uart_dev_t *hw)
|
||||
{
|
||||
return hw->int_raw.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART interrupt status.
|
||||
*
|
||||
|
@ -182,6 +182,18 @@ FORCE_INLINE_ATTR void uart_ll_disable_intr_mask(uart_dev_t *hw, uint32_t mask)
|
||||
hw->int_ena.val &= (~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART raw interrupt status.
|
||||
*
|
||||
* @param hw Beginning address of the peripheral registers.
|
||||
*
|
||||
* @return The UART interrupt status.
|
||||
*/
|
||||
static inline uint32_t uart_ll_get_intraw_mask(uart_dev_t *hw)
|
||||
{
|
||||
return hw->int_raw.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the UART interrupt status.
|
||||
*
|
||||
|
@ -59,6 +59,15 @@ typedef struct {
|
||||
*/
|
||||
#define uart_hal_ena_intr_mask(hal, mask) uart_ll_ena_intr_mask((hal)->dev, mask)
|
||||
|
||||
/**
|
||||
* @brief Get the UART raw interrupt status
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*
|
||||
* @return UART raw interrupt status
|
||||
*/
|
||||
#define uart_hal_get_intraw_mask(hal) uart_ll_get_intraw_mask((hal)->dev)
|
||||
|
||||
/**
|
||||
* @brief Get the UART interrupt status
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user