mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'bugfix/uart_sw_flow_ctrl_xoff_char_v6.0' into 'release/v6.0'
fix(uart): fix uart sw flow ctrl XOFF char write to wrong reg on ESP32C6 (v6.0) See merge request espressif/esp-idf!50524
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -63,8 +63,9 @@ void bootloader_console_init(void)
|
||||
const int uart_tx_gpio = (CONFIG_ESP_CONSOLE_UART_TX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_TX_GPIO : U0TXD_GPIO_NUM;
|
||||
const int uart_rx_gpio = (CONFIG_ESP_CONSOLE_UART_RX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_RX_GPIO : U0RXD_GPIO_NUM;
|
||||
|
||||
// Switch to the new UART (this just changes UART number used for esp_rom_printf in ROM code).
|
||||
esp_rom_output_set_as_console(uart_num);
|
||||
// Switch to the new UART
|
||||
esp_rom_output_set_as_console(uart_num); // changes UART number used for esp_rom_printf in ROM code
|
||||
esp_rom_output_switch_buffer(uart_num);
|
||||
|
||||
// If console is attached to UART1 or if non-default pins are used,
|
||||
// need to reconfigure pins using GPIO matrix
|
||||
|
||||
@@ -282,10 +282,12 @@ esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t flow
|
||||
/**
|
||||
* @brief Set software flow control.
|
||||
*
|
||||
* The XON and XOFF characters are '0x11' and '0x13' respectively.
|
||||
*
|
||||
* @param uart_num UART port number, the max port number is (UART_NUM_MAX -1)
|
||||
* @param enable switch on or off
|
||||
* @param rx_thresh_xon low water mark
|
||||
* @param rx_thresh_xoff high water mark
|
||||
* @param enable Enable or disable software flow control feature
|
||||
* @param rx_thresh_xon Low RX FIFO water mark for TX to send XON character
|
||||
* @param rx_thresh_xoff High RX FIFO water mark for TX to send XOFF character
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
|
||||
@@ -2200,9 +2200,7 @@ esp_err_t uart_set_rx_full_threshold(uart_port_t uart_num, int threshold)
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock));
|
||||
if (uart_hal_get_intr_ena_status(&(uart_context[uart_num].hal)) & UART_INTR_RXFIFO_FULL) {
|
||||
uart_hal_set_rxfifo_full_thr(&(uart_context[uart_num].hal), threshold);
|
||||
}
|
||||
uart_hal_set_rxfifo_full_thr(&(uart_context[uart_num].hal), threshold);
|
||||
UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock));
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -2217,9 +2215,7 @@ esp_err_t uart_set_tx_empty_threshold(uart_port_t uart_num, int threshold)
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock));
|
||||
if (uart_hal_get_intr_ena_status(&(uart_context[uart_num].hal)) & UART_INTR_TXFIFO_EMPTY) {
|
||||
uart_hal_set_txfifo_empty_thr(&(uart_context[uart_num].hal), threshold);
|
||||
}
|
||||
uart_hal_set_txfifo_empty_thr(&(uart_context[uart_num].hal), threshold);
|
||||
UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ components/esp_driver_uart/test_apps/rs485:
|
||||
disable:
|
||||
- if: SOC_UART_SUPPORTED != 1
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32", "esp32h2"]
|
||||
- if: IDF_TARGET not in ["esp32", "esp32h2", "esp32s3"]
|
||||
temporary: true
|
||||
reason: lack of runners
|
||||
depends_components:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -14,20 +14,22 @@
|
||||
#include "esp_random.h" // for uint32_t esp_random()
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define UART_NUM1 (UART_NUM_1)
|
||||
#define UART_NUM (UART_NUM_1 - CONFIG_CONSOLE_UART_NUM)
|
||||
#define UART_BAUD_RATE (115200 * 10)
|
||||
#define BUF_SIZE (512)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#define UART1_RX_PIN (22)
|
||||
#define UART1_TX_PIN (23)
|
||||
// For RS485 Half-Duplex Mode manages DE/~RE
|
||||
#define RS485_DE_PIN (18) // For ESP32, let's use RTS signal to control DE/~RE pin
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
#define UART1_RX_PIN (4)
|
||||
#define UART1_TX_PIN (5)
|
||||
// For RS485 Half-Duplex Mode manages DE/~RE
|
||||
#define RS485_DE_PIN (12) // For ESP32H2, let's use DTR signal to control DE/~RE pin
|
||||
#define UART_RX_PIN (22)
|
||||
#define UART_TX_PIN (23)
|
||||
#define RS485_DE_PIN (18) // uses RTS or DTR signal to control DE/~RE pin
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32S3
|
||||
#define UART_RX_PIN (4)
|
||||
#define UART_TX_PIN (5)
|
||||
#define RS485_DE_PIN (12) // uses RTS or DTR signal to control DE/~RE pin
|
||||
#else // for build success only (no runner)
|
||||
#define UART_RX_PIN (0)
|
||||
#define UART_TX_PIN (0)
|
||||
#define RS485_DE_PIN (0)
|
||||
#endif
|
||||
|
||||
// Number of packets to be send during test
|
||||
@@ -158,7 +160,7 @@ static uint16_t buffer_fill_random(uint8_t *buffer, size_t length)
|
||||
return crc;
|
||||
}
|
||||
|
||||
static void rs485_init(void)
|
||||
static void rs485_init(uart_mode_t mode)
|
||||
{
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = UART_BAUD_RATE,
|
||||
@@ -170,19 +172,21 @@ static void rs485_init(void)
|
||||
.source_clk = UART_SCLK_DEFAULT,
|
||||
};
|
||||
ESP_LOGI(TAG, "RS485 port initialization...");
|
||||
TEST_ESP_OK(uart_wait_tx_idle_polling(UART_NUM1));
|
||||
// Configure UART1 parameters
|
||||
TEST_ESP_OK(uart_param_config(UART_NUM1, &uart_config));
|
||||
// Set UART1 pins
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
TEST_ESP_OK(uart_set_pin(UART_NUM1, UART1_TX_PIN, UART1_RX_PIN, RS485_DE_PIN, UART_PIN_NO_CHANGE));
|
||||
#elif CONFIG_IDF_TARGET_ESP32H2
|
||||
TEST_ESP_OK(uart_set_pin(UART_NUM1, UART1_TX_PIN, UART1_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, RS485_DE_PIN, UART_PIN_NO_CHANGE));
|
||||
#endif
|
||||
TEST_ESP_OK(uart_wait_tx_idle_polling(UART_NUM));
|
||||
// Configure UART port parameters
|
||||
TEST_ESP_OK(uart_param_config(UART_NUM, &uart_config));
|
||||
// Set UART pins
|
||||
if (mode == UART_MODE_RS485_HALF_DUPLEX) { // RTS toggle by software, DTR toggle by hardware in this mode
|
||||
TEST_ESP_OK(uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, RS485_DE_PIN, UART_PIN_NO_CHANGE));
|
||||
} else if (mode == UART_MODE_RS485_COLLISION_DETECT) { // no RTS toggle in this mode
|
||||
TEST_ESP_OK(uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, RS485_DE_PIN, UART_PIN_NO_CHANGE));
|
||||
} else {
|
||||
TEST_ASSERT(false);
|
||||
}
|
||||
// Install UART driver (we don't need an event queue here)
|
||||
TEST_ESP_OK(uart_driver_install(UART_NUM1, BUF_SIZE * 2, 0, 0, NULL, 0));
|
||||
// Setup rs485 half duplex mode
|
||||
TEST_ESP_OK(uart_set_mode(UART_NUM1, UART_MODE_RS485_HALF_DUPLEX));
|
||||
TEST_ESP_OK(uart_driver_install(UART_NUM, BUF_SIZE * 2, 0, 0, NULL, 0));
|
||||
// Setup the requested rs485 mode
|
||||
TEST_ESP_OK(uart_set_mode(UART_NUM, mode));
|
||||
}
|
||||
|
||||
static esp_err_t print_packet_data(const char *str, uint8_t *buffer, uint16_t buffer_size)
|
||||
@@ -209,7 +213,7 @@ static esp_err_t print_packet_data(const char *str, uint8_t *buffer, uint16_t bu
|
||||
// Slave test case for multi device
|
||||
static void rs485_slave(void)
|
||||
{
|
||||
rs485_init();
|
||||
rs485_init(UART_MODE_RS485_HALF_DUPLEX);
|
||||
uint8_t* slave_data = (uint8_t*) malloc(BUF_SIZE);
|
||||
uint16_t err_count = 0, good_count = 0;
|
||||
unity_send_signal("Slave_ready");
|
||||
@@ -217,15 +221,15 @@ static void rs485_slave(void)
|
||||
ESP_LOGI(TAG, "Start receive loop.");
|
||||
for (int pack_count = 0; pack_count < PACKETS_NUMBER; pack_count++) {
|
||||
//Read slave_data from UART
|
||||
int len = uart_read_bytes(UART_NUM1, slave_data, BUF_SIZE, PACKET_READ_TICS);
|
||||
int len = uart_read_bytes(UART_NUM, slave_data, BUF_SIZE, PACKET_READ_TICS);
|
||||
//Write slave_data back to UART
|
||||
if (len > 2) {
|
||||
esp_err_t status = print_packet_data("Received ", slave_data, len);
|
||||
|
||||
// If received packet is correct then send it back
|
||||
if (status == ESP_OK) {
|
||||
uart_write_bytes(UART_NUM1, (char*)slave_data, len);
|
||||
uart_wait_tx_idle_polling(UART_NUM1);
|
||||
uart_write_bytes(UART_NUM, (char*)slave_data, len);
|
||||
uart_wait_tx_idle_polling(UART_NUM);
|
||||
good_count++;
|
||||
} else {
|
||||
printf("Incorrect packet received.\r\n");
|
||||
@@ -238,9 +242,9 @@ static void rs485_slave(void)
|
||||
}
|
||||
ESP_LOGI(TAG, "Test completed. Received packets = %d, errors = %d", good_count, err_count);
|
||||
// Wait for packet to be sent
|
||||
uart_wait_tx_done(UART_NUM1, PACKET_READ_TICS);
|
||||
uart_wait_tx_done(UART_NUM, PACKET_READ_TICS);
|
||||
free(slave_data);
|
||||
uart_driver_delete(UART_NUM1);
|
||||
uart_driver_delete(UART_NUM);
|
||||
TEST_CHECK_PROC_FAIL(err_count, TEST_ALLOW_PROC_FAIL);
|
||||
}
|
||||
|
||||
@@ -250,7 +254,7 @@ static void rs485_slave(void)
|
||||
static void rs485_master(void)
|
||||
{
|
||||
uint16_t err_count = 0, good_count = 0;
|
||||
rs485_init();
|
||||
rs485_init(UART_MODE_RS485_HALF_DUPLEX);
|
||||
uint8_t* master_buffer = (uint8_t*) malloc(BUF_SIZE);
|
||||
uint8_t* slave_buffer = (uint8_t*) malloc(BUF_SIZE);
|
||||
// The master test case should be synchronized with slave
|
||||
@@ -263,10 +267,10 @@ static void rs485_master(void)
|
||||
// Print created packet for debugging
|
||||
esp_err_t status = print_packet_data("Send ", master_buffer, BUF_SIZE);
|
||||
TEST_ASSERT(status == ESP_OK);
|
||||
uart_write_bytes(UART_NUM1, (char*)master_buffer, BUF_SIZE);
|
||||
uart_wait_tx_idle_polling(UART_NUM1);
|
||||
uart_write_bytes(UART_NUM, (char*)master_buffer, BUF_SIZE);
|
||||
uart_wait_tx_idle_polling(UART_NUM);
|
||||
// Read translated packet from slave
|
||||
int len = uart_read_bytes(UART_NUM1, slave_buffer, BUF_SIZE, PACKET_READ_TICS);
|
||||
int len = uart_read_bytes(UART_NUM, slave_buffer, BUF_SIZE, PACKET_READ_TICS);
|
||||
// Check if the received packet is too short
|
||||
if (len > 2) {
|
||||
// Print received packet and check checksum
|
||||
@@ -283,11 +287,11 @@ static void rs485_master(void)
|
||||
err_count++;
|
||||
}
|
||||
}
|
||||
uart_wait_tx_done(UART_NUM1, PACKET_READ_TICS);
|
||||
uart_wait_tx_done(UART_NUM, PACKET_READ_TICS);
|
||||
// Free the buffer and delete driver at the end
|
||||
free(master_buffer);
|
||||
free(slave_buffer);
|
||||
uart_driver_delete(UART_NUM1);
|
||||
uart_driver_delete(UART_NUM);
|
||||
ESP_LOGI(TAG, "Test completed. Received packets = %d, errors = %d", good_count, err_count);
|
||||
TEST_CHECK_PROC_FAIL(err_count, TEST_ALLOW_PROC_FAIL);
|
||||
}
|
||||
@@ -298,3 +302,150 @@ static void rs485_master(void)
|
||||
* RS485 bus driver hardware to be connected to boards.
|
||||
*/
|
||||
TEST_CASE_MULTIPLE_DEVICES("RS485 half duplex uart multiple devices test.", "[RS485]", rs485_master, rs485_slave);
|
||||
|
||||
// The device under check: in UART_MODE_RS485_COLLISION_DETECT mode the receiver
|
||||
// stays enabled during transmission, so the device should be able to read back
|
||||
// from the bus the same data it has just sent. Under UART_MODE_RS485_COLLISION_DETECT
|
||||
// mode, RTS signal will not be asserted during transmission, instead, uses hardware
|
||||
// DTR signal to drive DE pin. And ~RE pin is grounded for continuous reception.
|
||||
static void rs485_recv_while_send(uart_mode_t mode)
|
||||
{
|
||||
rs485_init(mode);
|
||||
uint8_t *tx_buffer = (uint8_t *) malloc(BUF_SIZE);
|
||||
uint8_t *rx_buffer = (uint8_t *) malloc(BUF_SIZE);
|
||||
TEST_ASSERT_NOT_NULL(tx_buffer);
|
||||
TEST_ASSERT_NOT_NULL(rx_buffer);
|
||||
|
||||
uint16_t err_count = 0;
|
||||
// The device under check reads back its own transmission from the bus, so it must receive every byte on every round (no tolerance here)
|
||||
// The exchange is repeated PACKETS_NUMBER times, kept in sync with the peer by a signal each round
|
||||
for (int i = 0; i < PACKETS_NUMBER; i++) {
|
||||
// Wait until the peer is ready and listening (it must not drive the bus)
|
||||
unity_wait_for_signal("Peer_ready");
|
||||
|
||||
buffer_fill_random(tx_buffer, BUF_SIZE);
|
||||
TEST_ESP_OK(uart_flush_input(UART_NUM));
|
||||
int written = uart_write_bytes(UART_NUM, (const char *) tx_buffer, BUF_SIZE);
|
||||
TEST_ASSERT_EQUAL_INT(BUF_SIZE, written);
|
||||
TEST_ESP_OK(uart_wait_tx_done(UART_NUM, PACKET_READ_TICS));
|
||||
|
||||
// The data sent on the bus is received back while sending
|
||||
int read_len = uart_read_bytes(UART_NUM, rx_buffer, BUF_SIZE, PACKET_READ_TICS);
|
||||
bool ok = (read_len == BUF_SIZE) && (memcmp(tx_buffer, rx_buffer, BUF_SIZE) == 0);
|
||||
ESP_LOGI(TAG, "Packet %d: sent %d bytes, received %d bytes back while sending (%s)",
|
||||
i, written, read_len, ok ? "match" : "mismatch");
|
||||
if (!ok) {
|
||||
err_count++;
|
||||
}
|
||||
|
||||
// Signal the peer that this round is over so it can resynchronize
|
||||
unity_send_signal("Round_done");
|
||||
}
|
||||
// No missing bytes are allowed on the device that reads back its own data
|
||||
TEST_ASSERT_EQUAL_INT(0, err_count);
|
||||
|
||||
free(tx_buffer);
|
||||
free(rx_buffer);
|
||||
uart_driver_delete(UART_NUM);
|
||||
}
|
||||
|
||||
// The peer device: it only listens (never drives the bus) so that the device
|
||||
// under check can verify it receives its own transmitted data. The peer also
|
||||
// receives the same packet from the bus and verifies its integrity (CRC).
|
||||
static void rs485_quiet_peer(uart_mode_t mode)
|
||||
{
|
||||
rs485_init(mode);
|
||||
uint8_t *rx_buffer = (uint8_t *) malloc(BUF_SIZE);
|
||||
TEST_ASSERT_NOT_NULL(rx_buffer);
|
||||
|
||||
uint16_t err_count = 0;
|
||||
// Mirror the sender: run PACKETS_NUMBER rounds and tolerate a small percentage of failures in case a few bytes are dropped on the bus
|
||||
for (int i = 0; i < PACKETS_NUMBER; i++) {
|
||||
// Discard any residual/partial data from a previous round before announcing readiness for a fresh packet
|
||||
TEST_ESP_OK(uart_flush_input(UART_NUM));
|
||||
unity_send_signal("Peer_ready");
|
||||
|
||||
// The peer receives the packet sent on the bus and verifies it
|
||||
int read_len = uart_read_bytes(UART_NUM, rx_buffer, BUF_SIZE, PACKET_READ_TICS);
|
||||
ESP_LOGI(TAG, "Packet %d: peer received %d bytes", i, read_len);
|
||||
if (read_len != BUF_SIZE || print_packet_data("Peer received ", rx_buffer, read_len) != ESP_OK) {
|
||||
err_count++;
|
||||
}
|
||||
|
||||
// Wait for the sender to finish this round before flushing/reading again
|
||||
unity_wait_for_signal("Round_done");
|
||||
}
|
||||
TEST_CHECK_PROC_FAIL(err_count, TEST_ALLOW_PROC_FAIL);
|
||||
|
||||
free(rx_buffer);
|
||||
uart_driver_delete(UART_NUM);
|
||||
}
|
||||
|
||||
static void rs485_recv_while_send_coll_det(void)
|
||||
{
|
||||
rs485_recv_while_send(UART_MODE_RS485_COLLISION_DETECT);
|
||||
}
|
||||
|
||||
static void rs485_quiet_peer_coll_det(void)
|
||||
{
|
||||
rs485_quiet_peer(UART_MODE_RS485_COLLISION_DETECT);
|
||||
}
|
||||
|
||||
/*
|
||||
* This multi devices test case verifies that in UART_MODE_RS485_COLLISION_DETECT
|
||||
* mode the device receives back the data it sends out on the bus. It requires
|
||||
* RS485 bus driver hardware (with the receiver kept enabled) connected to the
|
||||
* boards. Only one device drives the bus.
|
||||
*/
|
||||
TEST_CASE_MULTIPLE_DEVICES("RS485 collision detect mode can receive while sending", "[RS485]", rs485_recv_while_send_coll_det, rs485_quiet_peer_coll_det);
|
||||
|
||||
// Both devices transmit (different data) at the same time to force a bus
|
||||
// collision. In UART_MODE_RS485_COLLISION_DETECT mode the received data then
|
||||
// differs from the transmitted data, which raises the collision flag.
|
||||
static void rs485_collision_dev(uart_mode_t mode, const char *self_ready, const char *peer_ready)
|
||||
{
|
||||
rs485_init(mode);
|
||||
uint8_t *tx_buffer = (uint8_t *) malloc(BUF_SIZE);
|
||||
TEST_ASSERT_NOT_NULL(tx_buffer);
|
||||
buffer_fill_random(tx_buffer, BUF_SIZE);
|
||||
|
||||
// Barrier: make sure both devices start transmitting at roughly the same time
|
||||
unity_send_signal(self_ready);
|
||||
unity_wait_for_signal(peer_ready);
|
||||
|
||||
bool collision_flag = false;
|
||||
// Both devices drive the bus simultaneously with different data, so the data
|
||||
// received back differs from the transmitted data and the collision flag gets
|
||||
// raised. Keep transmitting for the whole loop (do not stop on the first
|
||||
// detection): if a device stopped early, its peer would then transmit alone
|
||||
// and might never observe a collision.
|
||||
for (int i = 0; i < PACKETS_NUMBER; i++) {
|
||||
bool flag = false;
|
||||
uart_write_bytes(UART_NUM, (const char *) tx_buffer, BUF_SIZE);
|
||||
uart_wait_tx_done(UART_NUM, PACKET_READ_TICS);
|
||||
TEST_ESP_OK(uart_get_collision_flag(UART_NUM, &flag));
|
||||
collision_flag |= flag;
|
||||
}
|
||||
ESP_LOGI(TAG, "Collision detected: %s", collision_flag ? "yes" : "no");
|
||||
TEST_ASSERT_TRUE(collision_flag);
|
||||
|
||||
free(tx_buffer);
|
||||
uart_driver_delete(UART_NUM);
|
||||
}
|
||||
|
||||
static void rs485_collision_master_coll_det(void)
|
||||
{
|
||||
rs485_collision_dev(UART_MODE_RS485_COLLISION_DETECT, "Coll_master_ready", "Coll_slave_ready");
|
||||
}
|
||||
|
||||
static void rs485_collision_slave_coll_det(void)
|
||||
{
|
||||
rs485_collision_dev(UART_MODE_RS485_COLLISION_DETECT, "Coll_slave_ready", "Coll_master_ready");
|
||||
}
|
||||
|
||||
/*
|
||||
* This multi devices test case verifies the collision detection of the
|
||||
* UART_MODE_RS485_COLLISION_DETECT mode. Both devices transmit at the same time,
|
||||
* causing a bus collision that must be detected by the UART hardware.
|
||||
*/
|
||||
TEST_CASE_MULTIPLE_DEVICES("RS485 collision detect mode can detect collision", "[RS485]", rs485_collision_master_coll_det, rs485_collision_slave_coll_det);
|
||||
|
||||
@@ -15,6 +15,6 @@ from pytest_embedded_idf.utils import idf_parametrize
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
@idf_parametrize('target', ['esp32', 'esp32h2'], indirect=['target'])
|
||||
@idf_parametrize('target', ['esp32', 'esp32h2', 'esp32s3'], indirect=['target'])
|
||||
def test_rs485_multi_dev(case_tester) -> None: # type: ignore
|
||||
case_tester.run_all_multi_dev_cases(reset=True)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# For ESP32, only UART0 port has DTR/DSR signal
|
||||
# In order to run the rs485 test cases fully, use UART1 as the console uart, so that UART0 can be the test port
|
||||
CONFIG_ESP_CONSOLE_UART_CUSTOM=y
|
||||
CONFIG_ESP_CONSOLE_UART_CUSTOM_NUM_1=y
|
||||
@@ -16,11 +16,14 @@
|
||||
#include "driver/rtc_io.h"
|
||||
#include "hal/rtc_io_ll.h"
|
||||
#endif
|
||||
#include "hal/uart_ll.h"
|
||||
#include "hal/uart_periph.h"
|
||||
#include "soc/uart_pins.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/clk_tree_defs.h"
|
||||
#include "test_common.h"
|
||||
#include "esp_timer.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define BUF_SIZE (100)
|
||||
#define UART_BAUD_11520 (11520)
|
||||
@@ -637,6 +640,214 @@ TEST_CASE("uart in one-wire mode", "[uart]")
|
||||
TEST_ESP_OK(uart_driver_delete(uart_num));
|
||||
}
|
||||
|
||||
// XON/XOFF software flow control characters (must match the ones the driver programs into the hardware)
|
||||
#define TEST_UART_XON_CHAR (0x11)
|
||||
#define TEST_UART_XOFF_CHAR (0x13)
|
||||
|
||||
// A small, fixed amount of payload written to the UART to probe whether the transmitter is draining it.
|
||||
// Kept well within the smallest HW TX FIFO (the LP UART has only 16 bytes) so the write always fits and, since
|
||||
// the FIFO is empty at every call site, uart_write_bytes() never blocks waiting for FIFO space.
|
||||
#define TEST_UART_TX_PROBE_BYTES (8)
|
||||
|
||||
// The probe window: a running transmitter drains TEST_UART_TX_PROBE_BYTES in well under 1 ms at any reasonable
|
||||
// baud rate, so 100 ms is plenty to tell "draining" (tx goes idle) from "paused" (tx never goes idle).
|
||||
#define TEST_UART_TX_PROBE_WINDOW_MS (100)
|
||||
|
||||
// Queue a fixed chunk of payload through the driver's TX path (installed with no TX ring buffer, so the bytes
|
||||
// go straight to the HW TX FIFO). The payload byte must differ from XON/XOFF; its value is irrelevant to the
|
||||
// local TX flow control decision.
|
||||
static void test_uart_write_txfifo_probe(uart_port_t uart_num)
|
||||
{
|
||||
uint8_t payload[TEST_UART_TX_PROBE_BYTES];
|
||||
memset(payload, 0x55, sizeof(payload));
|
||||
uart_write_bytes(uart_num, payload, sizeof(payload));
|
||||
}
|
||||
|
||||
// Return true once TX has stopped draining (i.e. an XOFF was received and the transmitter paused).
|
||||
// Each iteration queues a small probe into the (empty) FIFO and waits for the transmitter to go idle: if it
|
||||
// cannot finish within the probe window the transmitter is paused. Otherwise it drained the probe (running),
|
||||
// leaving the FIFO empty again for the next iteration.
|
||||
static bool test_uart_wait_tx_paused(uart_port_t uart_num, int timeout_ms)
|
||||
{
|
||||
int64_t deadline = esp_timer_get_time() + (int64_t)timeout_ms * 1000;
|
||||
while (esp_timer_get_time() < deadline) {
|
||||
test_uart_write_txfifo_probe(uart_num);
|
||||
if (uart_wait_tx_done(uart_num, pdMS_TO_TICKS(TEST_UART_TX_PROBE_WINDOW_MS)) == ESP_ERR_TIMEOUT) {
|
||||
return true; // probe could not drain -> paused
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true once TX starts draining again (i.e. an XON was received and the transmitter resumed).
|
||||
// The probe bytes queued by the pause step are still sitting in the FIFO (nothing drains while paused), so we
|
||||
// just wait for the transmitter to finally go idle, which only happens once it resumes and shifts them out.
|
||||
static bool test_uart_wait_tx_resumed(uart_port_t uart_num, int timeout_ms)
|
||||
{
|
||||
int64_t deadline = esp_timer_get_time() + (int64_t)timeout_ms * 1000;
|
||||
while (esp_timer_get_time() < deadline) {
|
||||
if (uart_wait_tx_done(uart_num, pdMS_TO_TICKS(TEST_UART_TX_PROBE_WINDOW_MS)) == ESP_OK) {
|
||||
return true; // FIFO drained -> resumed
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait until the RX FIFO has accumulated more than the XOFF threshold (so an XOFF should have been sent).
|
||||
static bool test_uart_wait_rxfifo_filled(uart_dev_t *hw, uint32_t xoff_thresh, int timeout_ms)
|
||||
{
|
||||
int64_t deadline = esp_timer_get_time() + (int64_t)timeout_ms * 1000;
|
||||
while (esp_timer_get_time() < deadline) {
|
||||
if (uart_ll_get_rxfifo_len(hw) > xoff_thresh) {
|
||||
return true;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drain the whole HW RX FIFO directly (the driver's RX interrupts are disabled), dropping the level below the
|
||||
// XON threshold so the hardware sends an XON.
|
||||
static void test_uart_drain_rxfifo(uart_dev_t *hw)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
uint32_t len;
|
||||
while ((len = uart_ll_get_rxfifo_len(hw)) > 0) {
|
||||
if (len > sizeof(buf)) {
|
||||
len = sizeof(buf);
|
||||
}
|
||||
uart_ll_read_rxfifo(hw, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This test verifies both directions of the UART hardware software-flow-control (XON/XOFF) feature:
|
||||
* 1. Receiving flow control: once the UART receives an XOFF character it must stop transmitting, and resume
|
||||
* once it receives an XON character.
|
||||
* 2. Sending flow control: once the RX FIFO fills past the XOFF threshold the UART must transmit an XOFF
|
||||
* character, and once it is drained below the XON threshold it must transmit an XON character.
|
||||
*
|
||||
* For the HP UART port, the test taps the UART RX onto the console UART RX pad so the host (pytest) can inject
|
||||
* XON/XOFF (direction 1) over the very same serial connection it already uses to talk to the console, without
|
||||
* extra wiring. For direction 2, the console TX pad is temporarily re-routed to the UART TX signal so the host
|
||||
* can observe the XON/XOFF characters the UART sends. The console UART RX pad is a regular (HP) GPIO though,
|
||||
* while the LP UART can only use LP-capable (RTC) GPIOs, so it cannot borrow the console pads. Hence for the LP
|
||||
* UART port the test keeps the LP UART on its normal pins and a manual tester is expected to physically wire
|
||||
* the console/UART0 RX and TX lines to them. (CI only exercises HP UART.)
|
||||
*
|
||||
* The UART TX shifts data out at the configured baud rate regardless of where it is routed, so direction 1 is
|
||||
* observed on the DUT by watching the HW TX FIFO drain.
|
||||
*/
|
||||
TEST_CASE("uart software flow control (XON/XOFF)", "[uart_flow_ctrl]")
|
||||
{
|
||||
uart_port_param_t port_param = {};
|
||||
TEST_ASSERT(port_select(&port_param));
|
||||
uart_port_t uart_num = port_param.port_num;
|
||||
uart_dev_t *hw = UART_LL_GET_HW(uart_num);
|
||||
const bool is_hp_uart = (uart_num < SOC_UART_HP_NUM);
|
||||
|
||||
printf("Note that if you are in any terminal program, likely the XON/XOFF will be trapped by the shell. Run 'stty -ixon -ixoff' to let the keys pass through your terminal application!\n");
|
||||
|
||||
int rx_pin, tx_pin;
|
||||
if (is_hp_uart) {
|
||||
// HP UART: tap the UART RX onto the console UART RX pad so the host can inject XON/XOFF over the console.
|
||||
// The TX is left unrouted for now; it is hijacked onto the console TX pad later for the sending direction.
|
||||
rx_pin = uart_periph_signal[CONFIG_CONSOLE_UART_NUM].pins[SOC_UART_PERIPH_SIGNAL_RX].default_gpio;
|
||||
TEST_ASSERT(rx_pin >= 0);
|
||||
tx_pin = UART_PIN_NO_CHANGE;
|
||||
} else {
|
||||
// LP UART: keep both pins on their normal IOs; a manual tester wires the console/UART0 lines to them.
|
||||
printf("LP UART needs manual wiring of console UART lines to the UART pins (TX-to-RX, RX-to-TX)\n");
|
||||
rx_pin = port_param.rx_pin_num;
|
||||
tx_pin = port_param.tx_pin_num;
|
||||
}
|
||||
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 115200,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.source_clk = port_param.default_src_clk,
|
||||
};
|
||||
// No TX ring buffer: the direct FIFO writes below go straight to the HW TX FIFO, so monitoring the FIFO
|
||||
// level reflects the real transmitter state.
|
||||
TEST_ESP_OK(uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0));
|
||||
TEST_ESP_OK(uart_param_config(uart_num, &uart_config));
|
||||
TEST_ESP_OK(uart_set_pin(uart_num, tx_pin, rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
|
||||
|
||||
// RX FIFO thresholds and fill size that drive the *sending* direction (part 2). They must fit the HW FIFO,
|
||||
// which is much smaller on the LP UART (16 bytes) than on the HP UART (128 bytes).
|
||||
// - xoff_thresh: when the RX FIFO rises above this, the hardware sends XOFF
|
||||
// - xon_thresh: when the RX FIFO drops below this, the hardware sends XON
|
||||
// - fill_bytes: amount the host sends, chosen > xoff_thresh and <= HW FIFO length (so no overflow)
|
||||
int xon_thresh, xoff_thresh, fill_bytes;
|
||||
if (is_hp_uart) {
|
||||
xon_thresh = 10;
|
||||
xoff_thresh = 40;
|
||||
fill_bytes = 64;
|
||||
} else {
|
||||
xon_thresh = 2;
|
||||
xoff_thresh = 8;
|
||||
fill_bytes = 12;
|
||||
}
|
||||
|
||||
// Enable software flow control with the thresholds above.
|
||||
TEST_ESP_OK(uart_set_sw_flow_ctrl(uart_num, true, xon_thresh, xoff_thresh));
|
||||
|
||||
// ---- Part 1: receiving XON/XOFF pauses / resumes the transmitter ----
|
||||
printf("\n");
|
||||
printf("Send %#04x (XOFF - Ctrl+S) to stop UART transmission\n", TEST_UART_XOFF_CHAR);
|
||||
bool paused = test_uart_wait_tx_paused(uart_num, 20000);
|
||||
TEST_ASSERT_MESSAGE(paused, "UART transmitter did not pause after receiving XOFF");
|
||||
printf("UART transmission stopped\n");
|
||||
|
||||
printf("Send %#04x (XON - Ctrl+Q) to start UART transmission\n", TEST_UART_XON_CHAR);
|
||||
bool resumed = test_uart_wait_tx_resumed(uart_num, 20000);
|
||||
TEST_ASSERT_MESSAGE(resumed, "UART transmitter did not resume after receiving XON");
|
||||
printf("UART transmission resumed\n");
|
||||
|
||||
// Part 1 left the transmitter idle (uart_wait_tx_done returned OK); reset the TX FIFO anyway to guarantee a
|
||||
// clean slate before observing the auto XON/XOFF.
|
||||
uart_ll_txfifo_rst(hw);
|
||||
|
||||
// ---- Part 2: filling / draining the RX FIFO makes the UART send XOFF / XON ----
|
||||
// Disable the driver's RX interrupts so the RX FIFO is not auto-drained; we drain it explicitly below. This
|
||||
// guarantees the XOFF is sent (FIFO stays above the XOFF threshold) before we drop it below the XON threshold.
|
||||
TEST_ESP_OK(uart_disable_rx_intr(uart_num));
|
||||
uart_ll_rxfifo_rst(hw);
|
||||
|
||||
printf("\n");
|
||||
printf("Send %d bytes to fill RX FIFO\n", fill_bytes);
|
||||
fflush(stdout);
|
||||
|
||||
const int console_tx_pin = uart_periph_signal[CONFIG_CONSOLE_UART_NUM].pins[SOC_UART_PERIPH_SIGNAL_TX].default_gpio;
|
||||
const int console_tx_signal = uart_periph_signal[CONFIG_CONSOLE_UART_NUM].pins[SOC_UART_PERIPH_SIGNAL_TX].signal;
|
||||
const int uart_tx_signal = uart_periph_signal[uart_num].pins[SOC_UART_PERIPH_SIGNAL_TX].signal;
|
||||
|
||||
if (is_hp_uart) {
|
||||
// Make sure the prompt is fully sent, then hijack the console TX pad so the host reads the UART's TX.
|
||||
uart_wait_tx_idle_polling(CONFIG_CONSOLE_UART_NUM);
|
||||
gpio_func_sel(console_tx_pin, PIN_FUNC_GPIO);
|
||||
esp_rom_gpio_connect_out_signal(console_tx_pin, uart_tx_signal, false, false);
|
||||
}
|
||||
|
||||
bool filled = test_uart_wait_rxfifo_filled(hw, xoff_thresh, 20000); // by now an XOFF should have been sent
|
||||
test_uart_drain_rxfifo(hw); // dropping below XON threshold sends XON
|
||||
uart_wait_tx_idle_polling(uart_num); // let the XON finish transmitting
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
|
||||
if (is_hp_uart) {
|
||||
// Restore the console TX pad so the unity test result can be reported over the console again.
|
||||
esp_rom_gpio_connect_out_signal(console_tx_pin, console_tx_signal, false, false);
|
||||
}
|
||||
TEST_ASSERT_MESSAGE(filled, "RX FIFO was not filled past the XOFF threshold by the host");
|
||||
printf("Please manually read the TX signal to confirm that it actually sent XOFF and XON characters\n");
|
||||
|
||||
TEST_ESP_OK(uart_set_sw_flow_ctrl(uart_num, false, 0, 0));
|
||||
TEST_ESP_OK(uart_driver_delete(uart_num));
|
||||
}
|
||||
|
||||
static void uart_console_write_task(void *arg)
|
||||
{
|
||||
while (1) {
|
||||
@@ -651,7 +862,8 @@ TEST_CASE("uart auto baud rate detection", "[uart]")
|
||||
TEST_ASSERT(port_select(&port_param));
|
||||
// This is indeed a standalone feature, no need to specify the uart port, call port_select() to be compatible with pytest
|
||||
// And this test case no need to be tested twice on HP/LP uart ports both exist targets (also, LP UART does not support auto baud rate detection functionality)
|
||||
if (port_param.port_num < SOC_UART_HP_NUM) {
|
||||
uart_port_t uart_num = port_param.port_num;
|
||||
if (uart_num < SOC_UART_HP_NUM) {
|
||||
TaskHandle_t console_write_task = NULL;
|
||||
xTaskCreate(uart_console_write_task, "uart_console_write_task", 2048, NULL, 5, &console_write_task);
|
||||
vTaskDelay(20);
|
||||
@@ -666,9 +878,9 @@ TEST_CASE("uart auto baud rate detection", "[uart]")
|
||||
uart_bitrate_res_t res = {};
|
||||
|
||||
uart_get_baudrate(CONFIG_CONSOLE_UART_NUM, &actual_baudrate);
|
||||
TEST_ESP_OK(uart_detect_bitrate_start(UART_NUM_1, &conf)); // acquire a new uart port
|
||||
TEST_ESP_OK(uart_detect_bitrate_start(uart_num, &conf)); // acquire a new uart port
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
TEST_ESP_OK(uart_detect_bitrate_stop(UART_NUM_1, false, &res)); // no releasing
|
||||
TEST_ESP_OK(uart_detect_bitrate_stop(uart_num, false, &res)); // no releasing
|
||||
detected_baudrate = res.clk_freq_hz * 2 / res.pos_period; // assume the wave has a slow falling slew rate
|
||||
TEST_ASSERT_INT32_WITHIN(actual_baudrate * 0.03, actual_baudrate, detected_baudrate); // allow 3% error
|
||||
|
||||
@@ -676,9 +888,9 @@ TEST_CASE("uart auto baud rate detection", "[uart]")
|
||||
uart_set_baudrate(CONFIG_CONSOLE_UART_NUM, 38400);
|
||||
|
||||
uart_get_baudrate(CONFIG_CONSOLE_UART_NUM, &actual_baudrate);
|
||||
TEST_ESP_OK(uart_detect_bitrate_start(UART_NUM_1, NULL)); // use the previously acquired uart port
|
||||
TEST_ESP_OK(uart_detect_bitrate_start(uart_num, NULL)); // use the previously acquired uart port
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
TEST_ESP_OK(uart_detect_bitrate_stop(UART_NUM_1, true, &res)); // release the uart port
|
||||
TEST_ESP_OK(uart_detect_bitrate_stop(uart_num, true, &res)); // release the uart port
|
||||
detected_baudrate = res.clk_freq_hz * 2 / res.pos_period;
|
||||
TEST_ASSERT_INT32_WITHIN(actual_baudrate * 0.03, actual_baudrate, detected_baudrate);
|
||||
|
||||
@@ -686,9 +898,9 @@ TEST_CASE("uart auto baud rate detection", "[uart]")
|
||||
uart_set_baudrate(CONFIG_CONSOLE_UART_NUM, CONFIG_CONSOLE_UART_BAUDRATE);
|
||||
|
||||
uart_get_baudrate(CONFIG_CONSOLE_UART_NUM, &actual_baudrate);
|
||||
TEST_ESP_OK(uart_detect_bitrate_start(UART_NUM_1, &conf)); // acquire a new uart port again
|
||||
TEST_ESP_OK(uart_detect_bitrate_start(uart_num, &conf)); // acquire a new uart port again
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
TEST_ESP_OK(uart_detect_bitrate_stop(UART_NUM_1, true, &res)); // release it
|
||||
TEST_ESP_OK(uart_detect_bitrate_stop(uart_num, true, &res)); // release it
|
||||
detected_baudrate = res.clk_freq_hz * 2 / res.pos_period;
|
||||
TEST_ASSERT_INT32_WITHIN(actual_baudrate * 0.03, actual_baudrate, detected_baudrate);
|
||||
|
||||
|
||||
@@ -18,6 +18,38 @@ input_argv = {
|
||||
}
|
||||
|
||||
|
||||
def _run_uart_flow_ctrl_case(dut, case) -> None: # type: ignore
|
||||
# Only the HP UART port is exercised in CI: its RX/TX borrow the console UART RX/TX pads, so the
|
||||
# host can inject and observe the XON (0x11) / XOFF (0x13) characters over the console connection
|
||||
# without extra wiring. The LP UART port cannot borrow the console pads (LP-only GPIOs) and would
|
||||
# need UART0 to be physically wired to the LP UART pins, so it is left for manual testing only.
|
||||
dut.serial.hard_reset()
|
||||
dut._get_ready()
|
||||
dut.confirm_write(case.index, expect_str=f'Running {case.name}...')
|
||||
|
||||
dut.expect("select to test 'uart' or 'lp_uart' port", timeout=10)
|
||||
dut.write('uart')
|
||||
|
||||
# Drive the HP UART software-flow-control case (both directions) after the 'uart' port has been selected.
|
||||
# Part 1: the DUT reacts to XON/XOFF that we inject over the console connection.
|
||||
dut.expect_exact('Send 0x13 (XOFF - Ctrl+S) to stop UART transmission', timeout=10)
|
||||
dut.write(b'\x13')
|
||||
dut.expect_exact('UART transmission stopped', timeout=25)
|
||||
|
||||
dut.expect_exact('Send 0x11 (XON - Ctrl+Q) to start UART transmission', timeout=10)
|
||||
dut.write(b'\x11')
|
||||
dut.expect_exact('UART transmission resumed', timeout=25)
|
||||
|
||||
# Part 2: the DUT auto-sends XOFF/XON when its RX FIFO fills/drains. It temporarily borrows the console TX
|
||||
# pad, so we read the raw XOFF (0x13) then XON (0x11) bytes back over the console connection.
|
||||
dut.expect_exact('Send 64 bytes to fill RX FIFO', timeout=10)
|
||||
dut.write(b'A' * 64)
|
||||
dut.expect(b'\x13') # XOFF
|
||||
dut.expect(b'\x11') # XON
|
||||
|
||||
dut.expect_unity_test_output()
|
||||
|
||||
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32s3'], reason='skip due to duplication with test_uart_single_dev_psram')
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
@@ -41,6 +73,10 @@ def test_uart_single_dev(case_tester) -> None: # type: ignore
|
||||
# multi-dev cases, skip on generic runner
|
||||
continue
|
||||
|
||||
if 'uart_flow_ctrl' in case.groups:
|
||||
_run_uart_flow_ctrl_case(dut, case)
|
||||
continue
|
||||
|
||||
if 'hp-uart-only' not in case.groups:
|
||||
for uart_port in uart_ports:
|
||||
dut.serial.hard_reset()
|
||||
@@ -67,14 +103,21 @@ def test_uart_single_dev(case_tester) -> None: # type: ignore
|
||||
def test_uart_single_dev_psram(case_tester) -> None: # type: ignore
|
||||
dut = case_tester.first_dut
|
||||
for case in case_tester.test_menu:
|
||||
if 'wakeup' not in case.groups:
|
||||
dut.serial.hard_reset()
|
||||
dut._get_ready()
|
||||
dut.confirm_write(case.index, expect_str=f'Running {case.name}...')
|
||||
if 'wakeup' in case.groups:
|
||||
# multi-dev cases, skip on generic runner
|
||||
continue
|
||||
|
||||
dut.expect("select to test 'uart' or 'lp_uart' port", timeout=10)
|
||||
dut.write('uart')
|
||||
dut.expect_unity_test_output()
|
||||
if 'uart_flow_ctrl' in case.groups:
|
||||
_run_uart_flow_ctrl_case(dut, case)
|
||||
continue
|
||||
|
||||
dut.serial.hard_reset()
|
||||
dut._get_ready()
|
||||
dut.confirm_write(case.index, expect_str=f'Running {case.name}...')
|
||||
|
||||
dut.expect("select to test 'uart' or 'lp_uart' port", timeout=10)
|
||||
dut.write('uart')
|
||||
dut.expect_unity_test_output()
|
||||
|
||||
|
||||
# ESP32 only supports uart wakeup if signal routes through IOMUX
|
||||
|
||||
@@ -838,7 +838,7 @@ FORCE_INLINE_ATTR void uart_ll_set_sw_flow_ctrl(uart_dev_t *hw, uart_sw_flowctrl
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf1, xon_threshold, (flow_ctrl->xon_thrd) << UART_LL_REG_FIELD_BIT_SHIFT(hw));
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf1, xoff_threshold, (flow_ctrl->xoff_thrd) << UART_LL_REG_FIELD_BIT_SHIFT(hw));
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf0_sync, xon_character, flow_ctrl->xon_char);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf0_sync, xon_character, flow_ctrl->xoff_char);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf0_sync, xoff_character, flow_ctrl->xoff_char);
|
||||
} else {
|
||||
hw->swfc_conf0_sync.sw_flow_con_en = 0;
|
||||
hw->swfc_conf0_sync.xonoff_del = 0;
|
||||
|
||||
@@ -260,6 +260,9 @@ void ESP_SYSTEM_IRAM_ATTR call_start_cpu1(void)
|
||||
#elif !CONFIG_ESP_CONSOLE_USB_CDC
|
||||
esp_rom_install_uart_printf();
|
||||
esp_rom_output_set_as_console(CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM);
|
||||
#if CONFIG_ESP_CONSOLE_UART_CUSTOM
|
||||
esp_rom_output_switch_buffer(CONFIG_ESP_CONSOLE_UART_NUM);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
cpu_utility_ll_enable_debug(1);
|
||||
|
||||
@@ -741,10 +741,10 @@ typedef union {
|
||||
* This register stores the Xon flow control char.
|
||||
*/
|
||||
uint32_t xon_character:8;
|
||||
/** xoff_threshold : R/W; bitpos: [15:8]; default: 19;
|
||||
/** xoff_character : R/W; bitpos: [15:8]; default: 19;
|
||||
* This register stores the Xoff flow control char.
|
||||
*/
|
||||
uint32_t xoff_threshold:8;
|
||||
uint32_t xoff_character:8;
|
||||
/** xon_xoff_still_send : R/W; bitpos: [16]; default: 0;
|
||||
* In software flow control mode, UART Tx is disabled once UART Rx receives XOFF. In
|
||||
* this status, UART Tx can not transmit XOFF even the received data number is larger
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
CONFIG_ESP_CONSOLE_UART_CUSTOM=y
|
||||
CONFIG_ESP_CONSOLE_UART_CUSTOM_NUM_1=y
|
||||
|
||||
Reference in New Issue
Block a user