Skip to content

Commit

Permalink
Merge branch 'feature/dedicated_gpios_xtensa' into 'master'
Browse files Browse the repository at this point in the history
Dedicated GPIO: add software UART implementation for Xtensa targets

See merge request espressif/esp-idf!21918
  • Loading branch information
o-marshmallow committed May 4, 2023
2 parents 1438d9a + 801f934 commit 74d87ae
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 11 deletions.
3 changes: 0 additions & 3 deletions examples/peripherals/dedicated_gpio/.build-test-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 4 additions & 4 deletions examples/peripherals/dedicated_gpio/soft_uart/README.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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.

Expand All @@ -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
```
Expand Down
Original file line number Diff line number Diff line change
@@ -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()


Expand Down
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 74d87ae

Please sign in to comment.