Explorar el Código

Merge branch 'feature/dedicated_gpios_xtensa' into 'master'

Dedicated GPIO: add software UART implementation for Xtensa targets

See merge request espressif/esp-idf!21918
Omar Chebib hace 2 años
padre
commit
74d87aef0c

+ 0 - 3
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

+ 4 - 4
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
 ```

+ 10 - 4
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()
 
 

+ 156 - 0
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