From 801f934ac96cb9308fb2aa084da0943821aa3615 Mon Sep 17 00:00:00 2001 From: Omar Chebib Date: Tue, 27 Dec 2022 11:27:29 +0100 Subject: [PATCH] Dedicated GPIO: add software UART implementation for Xtensa targets The software UART example is now compatible with Xtensa targets that have dedicated GPIOs. In other words, this example is now compatible with ESP32-S2 and ESP32-S3. --- .../dedicated_gpio/.build-test-rules.yml | 3 - .../dedicated_gpio/soft_uart/README.md | 8 +- .../components/soft_uart/CMakeLists.txt | 14 +- .../components/soft_uart/xtensa/soft_uart.S | 156 ++++++++++++++++++ 4 files changed, 170 insertions(+), 11 deletions(-) create mode 100644 examples/peripherals/dedicated_gpio/soft_uart/components/soft_uart/xtensa/soft_uart.S diff --git a/examples/peripherals/dedicated_gpio/.build-test-rules.yml b/examples/peripherals/dedicated_gpio/.build-test-rules.yml index afb83378d2d5..2e3f3d8d00d6 100644 --- a/examples/peripherals/dedicated_gpio/.build-test-rules.yml +++ b/examples/peripherals/dedicated_gpio/.build-test-rules.yml @@ -20,6 +20,3 @@ examples/peripherals/dedicated_gpio/soft_uart: - if: SOC_DEDICATED_GPIO_SUPPORTED != 1 temporary: false reason: Target doesn't support dedicated GPIO - - if: IDF_TARGET in ["esp32s2", "esp32s3"] - temporary: true - reason: Xtensa targets not supported yet diff --git a/examples/peripherals/dedicated_gpio/soft_uart/README.md b/examples/peripherals/dedicated_gpio/soft_uart/README.md index bfb18d6dcae2..e4cfb1891039 100644 --- a/examples/peripherals/dedicated_gpio/soft_uart/README.md +++ b/examples/peripherals/dedicated_gpio/soft_uart/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | -| ----------------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | -------- | -------- | -------- | -------- | # Example: UART software emulation using dedicated/fast GPIOs @@ -22,7 +22,7 @@ Due to the tight timing requirements of SW bit banging, the `asm_emulate_uart` f ### Hardware Required -* A development board with a RISC-V Espressif SoC (e.g., ESP32-C3 or ESP32-C2) +* A development board with one of the supported chips (see the list above.) * A USB cable for Power supply and programming * Some jumper wires to connect the UART to an external UART-to-USB adapter. @@ -34,7 +34,7 @@ Due to the strict timing requirements of the UART emulation, the UART emulation ### Build and flash the project -* Set the target of the project to a RISC-V-based one. For example: +* Set the target of the project to a compatible one. For example: ``` idf.py set-target esp32c3 ``` diff --git a/examples/peripherals/dedicated_gpio/soft_uart/components/soft_uart/CMakeLists.txt b/examples/peripherals/dedicated_gpio/soft_uart/components/soft_uart/CMakeLists.txt index b5080ea64da3..fd3f255d4888 100644 --- a/examples/peripherals/dedicated_gpio/soft_uart/components/soft_uart/CMakeLists.txt +++ b/examples/peripherals/dedicated_gpio/soft_uart/components/soft_uart/CMakeLists.txt @@ -1,9 +1,15 @@ set(srcs "soft_uart.c") -if(CONFIG_IDF_TARGET_ARCH_RISCV) - list(APPEND srcs "riscv/soft_uart.S") -elseif(CONFIG_IDF_TARGET_ARCH_XTENSA) - message(FATAL_ERROR "Xtensa targets not supported yet") +# During CMake early expansion, Kconfig constants are not defined yet, thus, to +# avoid having CMake falsely fail on all targets, do not send FATAL_ERROR at that moment +if(CONFIG_SOC_DEDICATED_GPIO_SUPPORTED) + if(CONFIG_IDF_TARGET_ARCH_RISCV) + list(APPEND srcs "riscv/soft_uart.S") + else() + list(APPEND srcs "xtensa/soft_uart.S") + endif() +elseif(NOT CMAKE_BUILD_EARLY_EXPANSION) + message(FATAL_ERROR "Target doesn't support dedicated gpios") endif() diff --git a/examples/peripherals/dedicated_gpio/soft_uart/components/soft_uart/xtensa/soft_uart.S b/examples/peripherals/dedicated_gpio/soft_uart/components/soft_uart/xtensa/soft_uart.S new file mode 100644 index 000000000000..1741bfe2ed1b --- /dev/null +++ b/examples/peripherals/dedicated_gpio/soft_uart/components/soft_uart/xtensa/soft_uart.S @@ -0,0 +1,156 @@ +/* + * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + #include "sdkconfig.h" + + #if CONFIG_IDF_TARGET_ESP32S2 + + .macro WRITE_GPIO_OUT mask value + wr_mask_gpio_out \value, \mask + .endm + + .macro READ_GPIO_IN reg + get_gpio_in \reg + .endm + + #else // CONFIG_IDF_TARGET_ESP32S3 + + .macro WRITE_GPIO_OUT mask value + ee.wr_mask_gpio_out \value, \mask + .endm + + .macro READ_GPIO_IN reg + ee.get_gpio_in \reg + .endm + + #endif + + .section .text + + /** + * @brief Send bytes on the emulated UART. + * + * @param tx_buffer (a2) Buffer to send on the TX line. Guaranteed not NULL by the caller. + * @param tx_size (a3) Size of tx_buffer. Guaranteed not 0 by the caller. + * @param tx_bit (a4) Offset of TX I/O in the dedicated GPIO register. + * @param baudrate (a5) CPU clock cycles taken by each bit. + * + * The C signature of this routine would be: + * void emulate_uart_send(const uint8_t* tx, uint32_t tx_size, uint32_t tx_bit, uint32_t baudrate_delay); + */ + .global emulate_uart_send + .type emulate_uart_send, @function +emulate_uart_send: + entry a1, 16 + /* Convert tx_bit into a bitmask */ + ssl a4 + movi a4, 1 + sll a4, a4 + /* Set the line to high */ + movi a7, 0xff + WRITE_GPIO_OUT a4, a7 + mov a10, a5 + /* By calling delay here, we guarantee that in the following code, the next register window will + * be free, as such, there won't be any window overflow */ + call8 uart_delay +uart_send_loop: + /* Start bit, clear/reset TX bit */ + movi a7, 0 + WRITE_GPIO_OUT a4, a7 + /* Wait for the given amount of CPU cycles */ + mov a10, a5 + call8 uart_delay + /* Load the next byte and send it on the line */ + l8ui a6, a2, 0 + /* Use the upper bits of a6 to keep track of the remaining bits to send */ + movi a7, 0xff00 + or a6, a6, a7 +uart_send_next_bit: + /* Take the LSB of a6 */ + mov a7, a4 + bbci a6, 0, uart_send_bit_0 + j uart_send_bit_end +uart_send_bit_0: + movi a7, 0 +uart_send_bit_end: + WRITE_GPIO_OUT a4, a7 + srli a6, a6, 1 + /* Check if we still have bits to send */ + movi a7, 0x100 + /* Wait for the given amount of CPU cycles */ + mov a10, a5 + call8 uart_delay + bge a6, a7, uart_send_next_bit + /* Increment the tx_buffer and continue */ + addi a2, a2, 1 + addi a3, a3, -1 + /* Stop bit, set TX bit */ + WRITE_GPIO_OUT a4, a4 + mov a10, a5 + call8 uart_delay + bnez a3, uart_send_loop + retw + + /* Register a2 contains the number of cycles to wait */ + .align 4 +uart_delay: + entry a1, 16 + rsr.ccount a3 + add a2, a2, a3 +uart_delay_loop: + rsr.ccount a3 + blt a3, a2, uart_delay_loop + retw + + /** + * @brief Receive bytes from the emulated UART. + * + * @param rx_buffer (a2) Buffer to store the received bytes in. Guaranteed not NULL by the caller. + * @param rx_size (a3) Size of rx_buffer. Guaranteed not 0 by the caller. + * @param rx_bit (a4) Offset of RX I/O in the dedicated GPIO register. + * @param baudrate (a5) CPU clock cycles taken by each bit. + * + * The C signature of this routine would be: + * void emulate_uart_receive(uint8_t *rx_buffer, uint32_t tx_size, uint32_t rx_bit, uint32_t baudrate_delay); + */ + .global emulate_uart_receive + .type emulate_uart_receive, @function +emulate_uart_receive: + entry a1, 16 + +read_next_byte: + /* a6 contains the current bit being received */ + movi a6, 0x1 + /* Wait for the start bit (0) */ +wait_start_bit: + READ_GPIO_IN a7 + bbs a7, a4, wait_start_bit + /* a7 will now store the final byte */ + movi a7, 0 + /* Wait 1.5 baudrate cycle, this will let us read the next bit in the middle on its period */ + srli a10, a5, 1 + call8 uart_delay +read_next_bit: + mov a10, a5 + call8 uart_delay + /* Read the RX line and store the bit */ + READ_GPIO_IN a8 + bbc a8, a4, read_not_set_bit + /* Write the bit 1 in the final byte */ + or a7, a7, a6 +read_not_set_bit: + slli a6, a6, 1 + movi a8, 0x100 + bne a6, a8, read_next_bit + /* Save the received bit in the buffer */ + s8i a7, a2, 0 + addi a2, a2, 1 + /* Wait a baudrate period to make sure stop bit is being sent */ + mov a10, a5 + call8 uart_delay + /* Check if we have received all the characters */ + addi a3, a3, -1 + bnez a3, read_next_byte + retw