diff --git a/examples/peripherals/dedicated_gpio/.build-test-rules.yml b/examples/peripherals/dedicated_gpio/.build-test-rules.yml index afb83378d2..2e3f3d8d00 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 bfb18d6dca..e4cfb18910 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 b5080ea64d..fd3f255d48 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 0000000000..1741bfe2ed --- /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