diff --git a/components/ulp/CMakeLists.txt b/components/ulp/CMakeLists.txt index 2c559d3aca..aa3ba2c3c4 100644 --- a/components/ulp/CMakeLists.txt +++ b/components/ulp/CMakeLists.txt @@ -43,7 +43,8 @@ if(CONFIG_ULP_COPROC_TYPE_LP_CORE) "lp_core/lp_core.c" "lp_core/shared/ulp_lp_core_memory_shared.c" "lp_core/shared/ulp_lp_core_lp_timer_shared.c" - "lp_core/lp_core_i2c.c") + "lp_core/lp_core_i2c.c" + "lp_core/lp_core_uart.c") list(APPEND includes "lp_core/include" diff --git a/components/ulp/cmake/CMakeLists.txt b/components/ulp/cmake/CMakeLists.txt index 2fc8c4854a..c097bcace7 100644 --- a/components/ulp/cmake/CMakeLists.txt +++ b/components/ulp/cmake/CMakeLists.txt @@ -85,7 +85,11 @@ elseif(ULP_COCPU_IS_LP_CORE) "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_startup.c" "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_utils.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c") + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c" + "${IDF_PATH}/components/hal/uart_hal_iram.c" + "${IDF_PATH}/components/hal/uart_hal.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_uart.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_print.c") target_link_options(${ULP_APP_NAME} PRIVATE "-nostartfiles") target_link_options(${ULP_APP_NAME} PRIVATE "-Wl,--no-warn-rwx-segments") @@ -96,6 +100,7 @@ elseif(ULP_COCPU_IS_LP_CORE) target_sources(${ULP_APP_NAME} PRIVATE ${ULP_S_SOURCES}) target_include_directories(${ULP_APP_NAME} PRIVATE "${IDF_PATH}/components/ulp/lp_core/lp_core/include" "${IDF_PATH}/components/ulp/lp_core/shared/include") + target_compile_definitions(${ULP_APP_NAME} PRIVATE IS_ULP_COCPU) else() foreach(ulp_s_source ${ULP_S_SOURCES}) diff --git a/components/ulp/lp_core/include/lp_core_uart.h b/components/ulp/lp_core/include/lp_core_uart.h new file mode 100644 index 0000000000..dfb2b5bd3a --- /dev/null +++ b/components/ulp/lp_core/include/lp_core_uart.h @@ -0,0 +1,89 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "esp_err.h" +#include "hal/uart_types.h" +#include "hal/gpio_types.h" + +/** + * @brief LP UART IO pins configuration + */ +typedef struct { + gpio_num_t tx_io_num; /*!< GPIO pin for UART Tx signal. Only GPIO#5 can be used as the UART Tx pin */ + gpio_num_t rx_io_num; /*!< GPIO pin for UART Rx signal. Only GPIO#4 can be used as the UART Rx pin */ + gpio_num_t rts_io_num; /*!< GPIO pin for UART RTS signal. Only GPIO#2 can be used as the UART RTS pin */ + gpio_num_t cts_io_num; /*!< GPIO pin for UART CTS signal. Only GPIO#3 can be used as the UART CTS pin */ +} lp_core_uart_pin_cfg_t; + +/** + * @brief LP UART protocol configuration + */ +typedef struct { + int baud_rate; /*!< LP UART baud rate */ + uart_word_length_t data_bits; /*!< LP UART byte size */ + uart_parity_t parity; /*!< LP UART parity mode */ + uart_stop_bits_t stop_bits; /*!< LP UART stop bits */ + uart_hw_flowcontrol_t flow_ctrl; /*!< LP UART HW flow control mode (cts/rts) */ + uint8_t rx_flow_ctrl_thresh; /*!< LP UART HW RTS threshold */ +} lp_core_uart_proto_cfg_t; + +/** + * @brief LP UART configuration parameters + */ +typedef struct { + lp_core_uart_pin_cfg_t uart_pin_cfg; /*!< LP UART pin configuration */ + lp_core_uart_proto_cfg_t uart_proto_cfg; /*!< LP UART protocol configuration */ + lp_uart_sclk_t lp_uart_source_clk; /*!< LP UART source clock selection */ +} lp_core_uart_cfg_t; + +/* Default LP UART GPIO settings */ +#define LP_UART_DEFAULT_GPIO_CONFIG() \ + .uart_pin_cfg.tx_io_num = GPIO_NUM_5, \ + .uart_pin_cfg.rx_io_num = GPIO_NUM_4, \ + .uart_pin_cfg.rts_io_num = GPIO_NUM_2, \ + .uart_pin_cfg.cts_io_num = GPIO_NUM_3, \ + +/* Default LP UART protocol config */ +#define LP_UART_DEFAULT_PROTO_CONFIG() \ + .uart_proto_cfg.baud_rate = 115200, \ + .uart_proto_cfg.data_bits = UART_DATA_8_BITS, \ + .uart_proto_cfg.parity = UART_PARITY_DISABLE, \ + .uart_proto_cfg.stop_bits = UART_STOP_BITS_1, \ + .uart_proto_cfg.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \ + .uart_proto_cfg.rx_flow_ctrl_thresh = 0, \ + +/* Default LP UART source clock config */ +#define LP_UART_DEFAULT_CLOCK_CONFIG() \ + .lp_uart_source_clk = LP_UART_SCLK_DEFAULT, \ + +/* Default LP UART GPIO settings and protocol parametes */ +#define LP_CORE_UART_DEFAULT_CONFIG() \ + { \ + LP_UART_DEFAULT_GPIO_CONFIG() \ + LP_UART_DEFAULT_PROTO_CONFIG() \ + LP_UART_DEFAULT_CLOCK_CONFIG() \ + } + +/** + * @brief Initialize and configure the LP UART to be used from the LP core + * + * @note The LP UART initialization must be called from the main core (HP CPU) + * + * @param cfg Configuration parameters + * @return esp_err_t ESP_OK when successful + */ +esp_err_t lp_core_uart_init(const lp_core_uart_cfg_t *cfg); + +#ifdef __cplusplus +} +#endif diff --git a/components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h b/components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h new file mode 100644 index 0000000000..43a956b9be --- /dev/null +++ b/components/ulp/lp_core/lp_core/include/ulp_lp_core_print.h @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Print from the LP core + * + * @note This function uses the LP UART peripheral to enable prints.The LP UART must be initialized with lp_core_uart_init() before using this function. + * @note This function is not a standard printf function and may not support all format specifiers or special characters. + * + * @param format string to be printed + * @param ... variable argument list + * + */ +void lp_core_printf(const char* format, ...); diff --git a/components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h b/components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h new file mode 100644 index 0000000000..2126e4fc56 --- /dev/null +++ b/components/ulp/lp_core/lp_core/include/ulp_lp_core_uart.h @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "esp_err.h" +#include "hal/uart_types.h" + +/** + * @brief Send data to the LP UART port if there is space available in the Tx FIFO + * + * This function will not wait for enough space in the Tx FIFO to be available. + * It will just fill the available Tx FIFO slots and return when the FIFO is full. + * If there are no empty slots in the Tx FIFO, this function will not write any data. + * + * @param lp_uart_num LP UART port number + * @param src data buffer address + * @param size data length to send + * + * @return - (-1) Error + * - OTHERS (>=0) The number of bytes pushed to the Tx FIFO + */ +int lp_core_uart_tx_chars(uart_port_t lp_uart_num, const void *src, size_t size); + +/** + * @brief Write data to the LP UART port + * + * This function will write data to the Tx FIFO. If a timeout value is configured, this function will timeout once the number of CPU cycles expire. + * + * @param lp_uart_num LP UART port number + * @param src data buffer address + * @param size data length to send + * @param timeout Operation timeout in CPU cycles. Set to -1 to wait forever. + * + * @return esp_err_t ESP_OK when successful + */ +esp_err_t lp_core_uart_write_bytes(uart_port_t lp_uart_num, const void *src, size_t size, int32_t timeout); + +/** + * @brief Read data from the LP UART port + * + * This function will read data from the Rx FIFO. If a timeout value is configured, then this function will timeout once the number of CPU cycles expire. + * + * @param lp_uart_num LP UART port number + * @param src data buffer address + * @param size data length to send + * @param timeout Operation timeout in CPU cycles. Set to -1 to wait forever. + * + * @return - (-1) Error + * - OTHERS (>=0) The number of bytes read from the Rx FIFO + */ +int lp_core_uart_read_bytes(uart_port_t lp_uart_num, void *buf, size_t size, int32_t timeout); + +#ifdef __cplusplus +} +#endif diff --git a/components/ulp/lp_core/lp_core/lp_core_print.c b/components/ulp/lp_core/lp_core/lp_core_print.c new file mode 100644 index 0000000000..658768ee36 --- /dev/null +++ b/components/ulp/lp_core/lp_core/lp_core_print.c @@ -0,0 +1,269 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "ulp_lp_core_uart.h" + +#define LP_UART_PORT_NUM LP_UART_NUM_0 +#define BINARY_SUPPORT 1 + +#define is_digit(c) ((c >= '0') && (c <= '9')) + +static void lp_uart_send_char(char c) +{ + int tx_len = 0; + int loop_cnt = 0; + + /* Write one byte to LP UART. Break after few iterations if we are stuck for any reason. */ + while (tx_len != 1 && loop_cnt < 1000) { + tx_len = lp_core_uart_tx_chars(LP_UART_PORT_NUM, (const void *)&c, 1); + loop_cnt++; + } +} + +// Ported over ROM function _cvt() +static int lp_core_cvt(unsigned long long val, char *buf, long radix, char *digits) +{ +#ifdef SUPPORT_LITTLE_RADIX + char temp[64]; +#else + char temp[32]; +#endif + char *cp = temp; + int length = 0; + + if (val == 0) { + /* Special case */ + *cp++ = '0'; + } else { + while (val) { + *cp++ = digits[val % radix]; + val /= radix; + } + } + while (cp != temp) { + *buf++ = *--cp; + length++; + } + *buf = '\0'; + return (length); +} + +// Ported over ROM function ets_vprintf() +static int lp_core_ets_vprintf(void (*putc)(char c), const char *fmt, va_list ap) +{ +#ifdef BINARY_SUPPORT + char buf[sizeof(long long) * 8]; +#else + char buf[32]; +#endif + char c, sign, *cp = buf; + int left_prec, right_prec, zero_fill, pad, pad_on_right, islong, islonglong; + long long val = 0; + int res = 0, length = 0; + + while ((c = *fmt++) != '\0') { + if (c == '%') { + c = *fmt++; + left_prec = right_prec = pad_on_right = islong = islonglong = 0; + if (c == '-') { + c = *fmt++; + pad_on_right++; + } + if (c == '0') { + zero_fill = true; + c = *fmt++; + } else { + zero_fill = false; + } + while (is_digit(c)) { + left_prec = (left_prec * 10) + (c - '0'); + c = *fmt++; + } + if (c == '.') { + c = *fmt++; + zero_fill++; + while (is_digit(c)) { + right_prec = (right_prec * 10) + (c - '0'); + c = *fmt++; + } + } else { + right_prec = left_prec; + } + sign = '\0'; + if (c == 'l') { + c = *fmt++; + islong = 1; + if (c == 'l') { + c = *fmt++; + islonglong = 1; + islong = 0; + } + } + switch (c) { + case 'p': + islong = 1; + case 'd': + case 'D': + case 'x': + case 'X': + case 'u': + case 'U': +#ifdef BINARY_SUPPORT + case 'b': + case 'B': +#endif + if (islonglong) { + val = va_arg(ap, long long); + } else if (islong) { + val = (long long)va_arg(ap, long); + } else { + val = (long long)va_arg(ap, int); + } + + if ((c == 'd') || (c == 'D')) { + if (val < 0) { + sign = '-'; + val = -val; + } + } else { + if (islonglong) { + ; + } else if (islong) { + val &= ((long long)1 << (sizeof(long) * 8)) - 1; + } else { + val &= ((long long)1 << (sizeof(int) * 8)) - 1; + } + } + break; + default: + break; + } + + switch (c) { + case 'p': + (*putc)('0'); + (*putc)('x'); + zero_fill = true; + left_prec = sizeof(unsigned long) * 2; + case 'd': + case 'D': + case 'u': + case 'U': + case 'x': + case 'X': + switch (c) { + case 'd': + case 'D': + case 'u': + case 'U': + length = lp_core_cvt(val, buf, 10, "0123456789"); + break; + case 'p': + case 'x': + length = lp_core_cvt(val, buf, 16, "0123456789abcdef"); + break; + case 'X': + length = lp_core_cvt(val, buf, 16, "0123456789ABCDEF"); + break; + } + cp = buf; + break; + case 's': + case 'S': + cp = va_arg(ap, char *); + if (cp == NULL) { + cp = ""; + } + length = 0; + while (cp[length] != '\0') + length++; + break; + case 'c': + case 'C': + c = va_arg(ap, int /*char*/); + (*putc)(c); + res++; + continue; +#ifdef BINARY_SUPPORT + case 'b': + case 'B': + length = left_prec; + if (left_prec == 0) { + if (islonglong) + length = sizeof(long long) * 8; + else if (islong) + length = sizeof(long) * 8; + else + length = sizeof(int) * 8; + } + for (int i = 0; i < length - 1; i++) { + buf[i] = ((val & ((long long)1 << i)) ? '1' : '.'); + } + cp = buf; + break; +#endif + case '%': + (*putc)('%'); + break; + default: + (*putc)('%'); + (*putc)(c); + res += 2; + } + pad = left_prec - length; + if (sign != '\0') { + pad--; + } + if (zero_fill) { + c = '0'; + if (sign != '\0') { + (*putc)(sign); + res++; + sign = '\0'; + } + } else { + c = ' '; + } + if (!pad_on_right) { + while (pad-- > 0) { + (*putc)(c); + res++; + } + } + if (sign != '\0') { + (*putc)(sign); + res++; + } + while (length-- > 0) { + c = *cp++; + (*putc)(c); + res++; + } + if (pad_on_right) { + while (pad-- > 0) { + (*putc)(' '); + res++; + } + } + } else { + (*putc)(c); + res++; + } + } + return (res); +} + +int lp_core_printf(const char* format, ...) +{ + /* Create a variable argument list */ + va_list ap; + va_start(ap, format); + + /* Pass the input string and the argument list to ets_vprintf() */ + int ret = lp_core_ets_vprintf(lp_uart_send_char, format, ap); + + va_end(ap); +} diff --git a/components/ulp/lp_core/lp_core/lp_core_uart.c b/components/ulp/lp_core/lp_core/lp_core_uart.c new file mode 100644 index 0000000000..fa00f7aa4e --- /dev/null +++ b/components/ulp/lp_core/lp_core/lp_core_uart.c @@ -0,0 +1,218 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_err.h" +#include "ulp_lp_core_uart.h" +#include "ulp_lp_core_utils.h" +#include "soc/lp_uart_struct.h" +#include "hal/uart_hal.h" + +#define LP_UART_ERR_INT_FLAG (UART_INTR_PARITY_ERR | UART_INTR_FRAM_ERR) +#define LP_UART_TX_INT_FLAG (UART_INTR_TX_DONE) +#define LP_UART_RX_INT_FLAG (UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_OVF) +#define LP_UART_TOUT_THRESH_DEFAULT (10U) +#define LP_UART_FULL_THRESH_DEFAULT (10U) + +/* LP UART HAL Context */ +uart_hal_context_t hal = { + .dev = (uart_dev_t *)UART_LL_GET_HW(LP_UART_NUM_0), +}; + +static esp_err_t lp_core_uart_check_timeout(uint32_t intr_mask, int32_t timeout, uint32_t *ticker) +{ + if (timeout > -1) { + /* If the timeout value is not -1, delay for 1 CPU cycle and keep track of ticks */ + ulp_lp_core_delay_cycles(1); + *ticker = *ticker + 1; + if (*ticker >= timeout) { + /* Disable and clear interrupt bits */ + uart_hal_disable_intr_mask(&hal, intr_mask); + uart_hal_clr_intsts_mask(&hal, intr_mask); + + return ESP_ERR_TIMEOUT; + } + } + + return ESP_OK; +} + +int lp_core_uart_tx_chars(uart_port_t lp_uart_num, const void *src, size_t size) +{ + (void)lp_uart_num; + uint32_t tx_len = 0; + + /* Argument sanity check */ + if (!src) { + /* Invalid input arguments */ + return -1; + } + + /* Nothing to do if the length is 0 */ + if (size == 0) { + return 0; + } + + /* Write the data to the Tx FIFO */ + uart_hal_write_txfifo(&hal, src, size, &tx_len); + + /* Return the number of bytes written */ + return tx_len; +} + +esp_err_t lp_core_uart_write_bytes(uart_port_t lp_uart_num, const void *src, size_t size, int32_t timeout) +{ + (void)lp_uart_num; + + /* Argument sanity check */ + if (!src) { + /* Invalid input arguments */ + return ESP_ERR_INVALID_ARG; + } + + /* Nothing to do if the length is 0 */ + if (size == 0) { + return ESP_OK; + } + + /* Enable the Tx done interrupt */ + uint32_t intr_mask = LP_UART_TX_INT_FLAG | LP_UART_ERR_INT_FLAG; + uart_hal_clr_intsts_mask(&hal, intr_mask); + uart_hal_ena_intr_mask(&hal, intr_mask); + + /* Transmit data */ + uint32_t tx_len; + uint32_t bytes_sent = 0; + int32_t remaining_bytes = size; + esp_err_t ret = ESP_OK; + uint32_t intr_status = 0; + uint32_t to = 0; + + while (remaining_bytes > 0) { + /* Write to the Tx FIFO */ + tx_len = 0; + uart_hal_write_txfifo(&hal, src + bytes_sent, remaining_bytes, &tx_len); + + if (tx_len) { + /* We have managed to write some data to the Tx FIFO. Check Tx interrupt status */ + while (1) { + /* Fetch the interrupt status */ + intr_status = uart_hal_get_intsts_mask(&hal); + if (intr_status & LP_UART_TX_INT_FLAG) { + /* Clear interrupt status and break */ + uart_hal_clr_intsts_mask(&hal, intr_mask); + break; + } else if ((intr_status & LP_UART_ERR_INT_FLAG)) { + /* Transaction error. Abort */ + return ESP_FAIL; + } + + /* Check for transaction timeout */ + ret = lp_core_uart_check_timeout(intr_mask, timeout, &to); + if (ret == ESP_ERR_TIMEOUT) { + /* Timeout */ + uart_hal_disable_intr_mask(&hal, intr_mask); + return ret; + } + } + + /* Update the byte counters */ + bytes_sent += tx_len; + remaining_bytes -= tx_len; + } else { + /* Tx FIFO does not have empty slots. Check for transaction timeout */ + ret = lp_core_uart_check_timeout(intr_mask, timeout, &to); + if (ret == ESP_ERR_TIMEOUT) { + /* Timeout */ + uart_hal_disable_intr_mask(&hal, intr_mask); + return ret; + } + } + } + + /* Disable the Tx done interrupt */ + uart_hal_disable_intr_mask(&hal, intr_mask); + + return ret; +} + +int lp_core_uart_read_bytes(uart_port_t lp_uart_num, void *buf, size_t size, int32_t timeout) +{ + (void)lp_uart_num; + + /* Argument sanity check */ + if (!buf) { + /* Invalid input arguments */ + return -1; + } + + /* Nothing to do if the length is 0 */ + if (size == 0) { + return 0; + } + + /* Set the Rx interrupt thresholds */ + uart_hal_set_rx_timeout(&hal, LP_UART_TOUT_THRESH_DEFAULT); + uart_hal_set_rxfifo_full_thr(&hal, LP_UART_FULL_THRESH_DEFAULT); + + /* Enable the Rx interrupts */ + uint32_t intr_mask = LP_UART_RX_INT_FLAG | LP_UART_ERR_INT_FLAG; + uart_hal_clr_intsts_mask(&hal, intr_mask); + uart_hal_ena_intr_mask(&hal, intr_mask); + + /* Receive data */ + int rx_len = 0; + uint32_t bytes_rcvd = 0; + int32_t remaining_bytes = size; + esp_err_t ret = ESP_OK; + uint32_t intr_status = 0; + uint32_t to = 0; + + while (remaining_bytes > 0) { + /* Read from the Rx FIFO + * We set rx_len to -1 to read all bytes in the Rx FIFO + */ + rx_len = -1; + uart_hal_read_rxfifo(&hal, (uint8_t *)(buf + bytes_rcvd), &rx_len); + + if (rx_len) { + /* We have some data to read from the Rx FIFO. Check Rx interrupt status */ + intr_status = uart_hal_get_intsts_mask(&hal); + if ((intr_status & UART_INTR_RXFIFO_FULL) || + (intr_status & UART_INTR_RXFIFO_TOUT)) { + /* This is expected. Clear interrupt status and break */ + uart_hal_clr_intsts_mask(&hal, intr_mask); + break; + } else if ((intr_status & UART_INTR_RXFIFO_OVF)) { + /* We reset the Rx FIFO if it overflows */ + uart_hal_clr_intsts_mask(&hal, intr_mask); + uart_hal_rxfifo_rst(&hal); + break; + } else if ((intr_status & LP_UART_ERR_INT_FLAG)) { + /* Transaction error. Abort */ + uart_hal_clr_intsts_mask(&hal, intr_mask); + uart_hal_disable_intr_mask(&hal, intr_mask); + return -1; + } + + /* Update the byte counters */ + bytes_rcvd += rx_len; + remaining_bytes -= rx_len; + } else { + /* We have no data to read from the Rx FIFO. Check for transaction timeout */ + ret = lp_core_uart_check_timeout(intr_mask, timeout, &to); + if (ret == ESP_ERR_TIMEOUT) { + break; + } + } + } + + /* Disable the Rx interrupts */ + uart_hal_disable_intr_mask(&hal, intr_mask); + + /* Return the number of bytes received */ + return bytes_rcvd; +} diff --git a/components/ulp/lp_core/lp_core_uart.c b/components/ulp/lp_core/lp_core_uart.c new file mode 100644 index 0000000000..d56cf2e8ac --- /dev/null +++ b/components/ulp/lp_core/lp_core_uart.c @@ -0,0 +1,135 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_err.h" +#include "lp_core_uart.h" +#include "driver/rtc_io.h" +#include "soc/lp_uart_struct.h" +#include "hal/uart_hal.h" +#include "hal/rtc_io_types.h" +#include "esp_clk_tree.h" +#include "esp_private/lp_periph_ctrl.h" + +#define LP_UART_PORT_NUM LP_UART_NUM_0 +#define LP_UART_TX_IDLE_NUM_DEFAULT (0U) + +/* LP UART HAL Context */ +uart_hal_context_t hal = { + .dev = (uart_dev_t *)UART_LL_GET_HW(LP_UART_NUM_0), +}; + +static esp_err_t lp_core_uart_param_config(const lp_core_uart_cfg_t *cfg) +{ + esp_err_t ret = ESP_OK; + + /* Argument sanity check */ + if ((cfg->uart_proto_cfg.rx_flow_ctrl_thresh > SOC_LP_UART_FIFO_LEN) || + (cfg->uart_proto_cfg.flow_ctrl > UART_HW_FLOWCTRL_MAX) || + (cfg->uart_proto_cfg.data_bits > UART_DATA_BITS_MAX)) { + // Invalid config + return ESP_ERR_INVALID_ARG; + } + + /* Initialize LP UART HAL with default parameters */ + uart_hal_init(&hal, LP_UART_PORT_NUM); + + /* Get LP UART source clock frequency */ + uint32_t sclk_freq = 0; + soc_module_clk_t clk_src = (soc_module_clk_t)(cfg->lp_uart_source_clk) ? (cfg->lp_uart_source_clk) : LP_UART_SCLK_DEFAULT; + ret = esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &sclk_freq); + if (ret != ESP_OK) { + // Unable to fetch LP UART source clock frequency + return ESP_FAIL; + } + + /* Override protocol parameters from the configuration */ + lp_periph_set_clk_src(LP_PERIPH_UART0_MODULE, clk_src); + uart_hal_set_baudrate(&hal, cfg->uart_proto_cfg.baud_rate, sclk_freq); + uart_hal_set_parity(&hal, cfg->uart_proto_cfg.parity); + uart_hal_set_data_bit_num(&hal, cfg->uart_proto_cfg.data_bits); + uart_hal_set_stop_bits(&hal, cfg->uart_proto_cfg.stop_bits); + uart_hal_set_tx_idle_num(&hal, LP_UART_TX_IDLE_NUM_DEFAULT); + uart_hal_set_hw_flow_ctrl(&hal, cfg->uart_proto_cfg.flow_ctrl, cfg->uart_proto_cfg.rx_flow_ctrl_thresh); + + /* Reset Tx/Rx FIFOs */ + uart_hal_rxfifo_rst(&hal); + uart_hal_txfifo_rst(&hal); + + return ret; +} + +static esp_err_t lp_uart_config_io(gpio_num_t pin, rtc_gpio_mode_t direction) +{ + /* Initialize LP_IO */ + esp_err_t ret = rtc_gpio_init(pin); + if (ret != ESP_OK) { + return ESP_FAIL; + } + + /* Set LP_IO direction */ + ret = rtc_gpio_set_direction(pin, direction); + if (ret != ESP_OK) { + return ESP_FAIL; + } + + /* Set LP_IO function */ + ret = rtc_gpio_iomux_func_sel(pin, 1); + + return ret; +} + +static esp_err_t lp_core_uart_set_pin(const lp_core_uart_cfg_t *cfg) +{ + esp_err_t ret = ESP_OK; + + /* Argument sanity check */ + if ((cfg->uart_pin_cfg.tx_io_num != GPIO_NUM_5) || + (cfg->uart_pin_cfg.rx_io_num != GPIO_NUM_4) || + (cfg->uart_pin_cfg.rts_io_num != GPIO_NUM_2) || + (cfg->uart_pin_cfg.cts_io_num != GPIO_NUM_3)) { + // Invalid IO config + return ESP_ERR_INVALID_ARG; + } + + /* Configure Tx Pin */ + ret = lp_uart_config_io(cfg->uart_pin_cfg.tx_io_num, RTC_GPIO_MODE_OUTPUT_ONLY); + /* Configure Rx Pin */ + ret = lp_uart_config_io(cfg->uart_pin_cfg.rx_io_num, RTC_GPIO_MODE_INPUT_ONLY); + /* Configure RTS Pin */ + ret = lp_uart_config_io(cfg->uart_pin_cfg.rts_io_num, RTC_GPIO_MODE_OUTPUT_ONLY); + /* Configure CTS Pin */ + ret = lp_uart_config_io(cfg->uart_pin_cfg.cts_io_num, RTC_GPIO_MODE_INPUT_ONLY); + + return ret; +} + +esp_err_t lp_core_uart_init(const lp_core_uart_cfg_t *cfg) +{ + esp_err_t ret = ESP_OK; + + /* Argument sanity check */ + if (!cfg) { + // NULL configuration + return ESP_ERR_INVALID_ARG; + } + + /* Configure LP UART protocol parameters */ + ret = lp_core_uart_param_config(cfg); + if (ret != ESP_OK) { + // Invalid protocol configuration + return ESP_FAIL; + } + + /* Configure LP UART IO pins */ + ret = lp_core_uart_set_pin(cfg); + if (ret != ESP_OK) { + // Invalid IO configuration + return ESP_FAIL; + } + + return ret; +} diff --git a/examples/system/.build-test-rules.yml b/examples/system/.build-test-rules.yml index ea517c99d1..6e0cc7570b 100644 --- a/examples/system/.build-test-rules.yml +++ b/examples/system/.build-test-rules.yml @@ -173,6 +173,14 @@ examples/system/ulp/lp_core/lp_i2c: enable: - if: SOC_LP_I2C_SUPPORTED == 1 +examples/system/ulp/lp_core/lp_uart/lp_uart_echo: + enable: + - if: SOC_UART_LP_NUM > 0 + +examples/system/ulp/lp_core/lp_uart/lp_uart_print: + enable: + - if: SOC_UART_LP_NUM > 0 + examples/system/ulp/ulp_fsm/ulp: disable: - if: SOC_ULP_FSM_SUPPORTED != 1 diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/CMakeLists.txt b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/CMakeLists.txt new file mode 100644 index 0000000000..0249365348 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/CMakeLists.txt @@ -0,0 +1,7 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +list(APPEND SDKCONFIG_DEFAULTS "sdkconfig.defaults") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(lp_uart_echo_example) diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/README.md b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/README.md new file mode 100644 index 0000000000..dbafcc983b --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/README.md @@ -0,0 +1,54 @@ +| Supported Targets | ESP32-C6 | +| ----------------- | -------- | + +# LP UART Echo Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +## Overview + +This example demonstrates the usage of the LP UART driver from the LP core by reading data written to a serial console and echoing it back while the main core is in deepsleep. + +## How to use example + +### Hardware Required + +To run this example, you should have an ESP32-C6 based development board and a host machine with a serial input connection. + +#### Pin Assignment: + +**Note:** The following pin assignments are used by default. + +| | Rx | Tx | +| ----------------------- | ------| ------| +| ESP32-C6 | GPIO4 | GPIO5 | +| Host machine | Tx | Rx | + +### Build and Flash + +Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +Use another serial monitor program/instance such as idf.py monitor, minicom or miniterm to send and receive data from the LP core. +The default baudrate used for the example is 115200. Care must be taken that the configuration matches on both the device and the serial terminal. + +## Example Output + +The log output from the serial monitor connected to the main core should indicate that the LP core and the LP UART peripheral have been successfully initialized. The main CPU would then enter deep sleep mode. + +```bash +Not an LP core wakeup. Cause = 0 +Initializing... +LP UART initialized successfully +LP core loaded with firmware and running successfully +Entering deep sleep... +``` + +The log output from the serial monitor connected to the LP core would be blank. Type using a keyboard and the monitor should echo the characters being typed. + +## Troubleshooting + +(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.) diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/main/CMakeLists.txt b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/main/CMakeLists.txt new file mode 100644 index 0000000000..2d86ebdb05 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/main/CMakeLists.txt @@ -0,0 +1,25 @@ +# Register the component +idf_component_register(SRCS "lp_uart_main.c" + INCLUDE_DIRS "" + REQUIRES ulp) + +# +# ULP support additions to component CMakeLists.txt. +# +# 1. The LP Core app name must be unique (if multiple components use LP Core). +set(ulp_app_name lp_core_${COMPONENT_NAME}) +# +# 2. Specify all C files. +# Files should be placed into a separate directory (in this case, lp_core/), +# which should not be added to COMPONENT_SRCS. +set(ulp_lp_core_sources "lp_core/main.c") + +# +# 3. List all the component source files which include automatically +# generated LP Core export file, ${ulp_app_name}.h: +set(ulp_exp_dep_srcs "lp_uart_main.c") + +# +# 4. Call function to build ULP binary and embed in project using the argument +# values above. +ulp_embed_binary(${ulp_app_name} "${ulp_lp_core_sources}" "${ulp_exp_dep_srcs}") diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/main/lp_core/main.c b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/main/lp_core/main.c new file mode 100644 index 0000000000..f276548bee --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/main/lp_core/main.c @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "ulp_lp_core_uart.h" +#include "ulp_lp_core_utils.h" + +#define LP_UART_PORT_NUM LP_UART_NUM_0 + +int main (void) +{ + esp_err_t ret; + uint8_t data[1024] = {0}; + int len = 0; + + while (1) { + /* Read data from the LP_UART */ + len = lp_core_uart_read_bytes(LP_UART_PORT_NUM, data, (sizeof(data) - 1), 10); + if (len > 0) { + /* Write data back to the LP_UART */ + ret = lp_core_uart_write_bytes(LP_UART_PORT_NUM, (const char *)data, len, 500); + if (ret != ESP_OK) { + /* Error in writing. Bail */ + continue; + } + } + } + + return 0; +} diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/main/lp_uart_main.c b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/main/lp_uart_main.c new file mode 100644 index 0000000000..c6126c1b0e --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/main/lp_uart_main.c @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_sleep.h" +#include "esp_err.h" +#include "lp_core_main.h" +#include "ulp_lp_core.h" +#include "lp_core_uart.h" + +extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_main_bin_start"); +extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end"); + +static void lp_uart_init(void) +{ + lp_core_uart_cfg_t cfg = LP_CORE_UART_DEFAULT_CONFIG(); + + ESP_ERROR_CHECK(lp_core_uart_init(&cfg)); + + printf("LP UART initialized successfully\n"); +} + +static void lp_core_init(void) +{ + /* Set LP core wakeup source as the HP CPU */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU, + }; + + /* Load LP core firmware */ + ESP_ERROR_CHECK(ulp_lp_core_load_binary(lp_core_main_bin_start, (lp_core_main_bin_end - lp_core_main_bin_start))); + + /* Run LP core */ + ESP_ERROR_CHECK(ulp_lp_core_run(&cfg)); + + printf("LP core loaded with firmware and running successfully\n"); +} + +void app_main(void) +{ + esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); + if (cause != ESP_SLEEP_WAKEUP_ULP) { + printf("Not an LP core wakeup. Cause = %d\n", cause); + printf("Initializing...\n"); + + /* Initialize LP_UART */ + lp_uart_init(); + + /* Load LP Core binary and start the coprocessor */ + lp_core_init(); + } + + /* Setup wakeup triggers */ + ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup()); + + /* Enter Deep Sleep */ + printf("Entering deep sleep...\n"); + esp_deep_sleep_start(); +} diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/sdkconfig.defaults b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/sdkconfig.defaults new file mode 100644 index 0000000000..53ae7d35bf --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_echo/sdkconfig.defaults @@ -0,0 +1,4 @@ +# Enable LP Core +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_TYPE_LP_CORE=y +CONFIG_ULP_COPROC_RESERVE_MEM=4096 diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_print/CMakeLists.txt b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/CMakeLists.txt new file mode 100644 index 0000000000..bcda9fb96a --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/CMakeLists.txt @@ -0,0 +1,7 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +list(APPEND SDKCONFIG_DEFAULTS "sdkconfig.defaults") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(lp_uart_print_example) diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_print/README.md b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/README.md new file mode 100644 index 0000000000..4124e72b5e --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/README.md @@ -0,0 +1,67 @@ +| Supported Targets | ESP32-C6 | +| ----------------- | -------- | + +# LP UART Print Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +## Overview + +This example demonstrates how to use print statements from a program running on the LP core. + +## How to use example + +### Hardware Required + +To run this example, you should have an ESP32-C6 based development board and a host machine with a serial input connection. + +#### Pin Assignment: + +**Note:** The following pin assignments are used by default. + + +| | Tx | +| ----------------------- | ------| +| ESP32-C6 | GPIO5 | +| Host machine | Rx | + +### Build and Flash + +Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +Use another serial monitor program/instance such as idf.py monitor, minicom or miniterm to send and receive data from the LP core. +The default baudrate used for the example is 115200. Care must be taken that the configuration matches on both the device and the serial terminal. + +## Example Output + +The log output from the serial monitor connected to the main core should indicate that the LP core and the LP UART peripheral have been successfully initialized. The main CPU would then enter deep sleep mode. + +```bash +Not an LP core wakeup. Cause = 0 +Initializing... +LP UART initialized successfully +LP core loaded with firmware and running successfully +Entering deep sleep... +``` + +The log output from the serial monitor connected to the LP core should display output as below - + +```bash +Hello from the LP core!! +This program has run 1 times +************************** +Hello from the LP core!! +This program has run 2 times +************************** +Hello from the LP core!! +This program has run 3 times +************************** +``` + +## Troubleshooting + +(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.) diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_print/main/CMakeLists.txt b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/main/CMakeLists.txt new file mode 100644 index 0000000000..2d86ebdb05 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/main/CMakeLists.txt @@ -0,0 +1,25 @@ +# Register the component +idf_component_register(SRCS "lp_uart_main.c" + INCLUDE_DIRS "" + REQUIRES ulp) + +# +# ULP support additions to component CMakeLists.txt. +# +# 1. The LP Core app name must be unique (if multiple components use LP Core). +set(ulp_app_name lp_core_${COMPONENT_NAME}) +# +# 2. Specify all C files. +# Files should be placed into a separate directory (in this case, lp_core/), +# which should not be added to COMPONENT_SRCS. +set(ulp_lp_core_sources "lp_core/main.c") + +# +# 3. List all the component source files which include automatically +# generated LP Core export file, ${ulp_app_name}.h: +set(ulp_exp_dep_srcs "lp_uart_main.c") + +# +# 4. Call function to build ULP binary and embed in project using the argument +# values above. +ulp_embed_binary(${ulp_app_name} "${ulp_lp_core_sources}" "${ulp_exp_dep_srcs}") diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_print/main/lp_core/main.c b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/main/lp_core/main.c new file mode 100644 index 0000000000..4a47ccb3a2 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/main/lp_core/main.c @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "ulp_lp_core_print.h" +#include "ulp_lp_core_utils.h" + +int main (void) +{ + static int iteration = 0; + const char separator[] = "**************************"; + + lp_core_printf("Hello from the LP core!!\r\n"); + lp_core_printf("This program has run %d times\r\n", ++iteration); + lp_core_printf("%s", separator); + lp_core_printf("\n"); + + return 0; +} diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_print/main/lp_uart_main.c b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/main/lp_uart_main.c new file mode 100644 index 0000000000..9f4149c1b8 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/main/lp_uart_main.c @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_sleep.h" +#include "esp_err.h" +#include "lp_core_main.h" +#include "ulp_lp_core.h" +#include "lp_core_uart.h" + +extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_main_bin_start"); +extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end"); + +static void lp_uart_init(void) +{ + lp_core_uart_cfg_t cfg = LP_CORE_UART_DEFAULT_CONFIG(); + + ESP_ERROR_CHECK(lp_core_uart_init(&cfg)); + + printf("LP UART initialized successfully\n"); +} + +static void lp_core_init(void) +{ + /* Set LP core wakeup source as the HP CPU */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER, + .lp_timer_sleep_duration_us = 1000000, + }; + + /* Load LP core firmware */ + ESP_ERROR_CHECK(ulp_lp_core_load_binary(lp_core_main_bin_start, (lp_core_main_bin_end - lp_core_main_bin_start))); + + /* Run LP core */ + ESP_ERROR_CHECK(ulp_lp_core_run(&cfg)); + + printf("LP core loaded with firmware and running successfully\n"); +} + +void app_main(void) +{ + esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); + if (cause != ESP_SLEEP_WAKEUP_ULP) { + printf("Not an LP core wakeup. Cause = %d\n", cause); + printf("Initializing...\n"); + + /* Initialize LP_UART */ + lp_uart_init(); + + /* Load LP Core binary and start the coprocessor */ + lp_core_init(); + } + + /* Setup wakeup triggers */ + ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup()); + + /* Enter Deep Sleep */ + printf("Entering deep sleep...\n"); + esp_deep_sleep_start(); +} diff --git a/examples/system/ulp/lp_core/lp_uart/lp_uart_print/sdkconfig.defaults b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/sdkconfig.defaults new file mode 100644 index 0000000000..f018081b4d --- /dev/null +++ b/examples/system/ulp/lp_core/lp_uart/lp_uart_print/sdkconfig.defaults @@ -0,0 +1,4 @@ +# Enable LP Core +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_TYPE_LP_CORE=y +CONFIG_ULP_COPROC_RESERVE_MEM=8192