Skip to content

Commit fae1c8d

Browse files
committed
feat: Add UART support
This commit adds support for UART with interrupt possibility and helper functions especially for RX path
1 parent db20fc4 commit fae1c8d

File tree

61 files changed

+18469
-303
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+18469
-303
lines changed

.check_copyright_config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ ignore:
1818
include:
1919
- src/target/*/ld/*.ld
2020
- example/ld/*.ld
21+
- src/target/*/include/soc/uart_reg.h

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ A complete example project is provided in the [example](example/) directory. It
3131

3232
See the [example README](example/README.md) for build instructions.
3333

34+
## Function Naming Conventions
35+
36+
Functions that contain `rominit` (e.g., `stub_target_uart_rominit_intr_attach`) require ROM preinitialization by entering download mode to work correctly. These functions are simplified compared to full implementation and expect some initialization done by ROM. They should only be called in contexts where the ROM download mode has been entered.
37+
3438
## Project Structure
3539

3640
The library uses a three-layer architecture to eliminate circular dependencies and maximize code reuse:

example/stub_main.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <esp-stub-lib/flash.h>
1414
#include <esp-stub-lib/err.h>
1515
#include <esp-stub-lib/mem_utils.h>
16+
#include <target/uart.h>
1617

1718
#include "stub_main.h"
1819

@@ -37,6 +38,24 @@ static void example_mem_utils(void)
3738
(void)stub_lib_mem_is_tcm(0x0);
3839
}
3940

41+
static int handle_test_uart(void)
42+
{
43+
void *uart_rx_interrupt_handler = NULL;
44+
stub_target_uart_wait_idle(UART_NUM_0);
45+
46+
stub_target_uart_init(UART_NUM_0, 115200);
47+
stub_target_uart_set_rx_timeout(UART_NUM_0, 10);
48+
(void)stub_target_uart_get_rxfifo_count(UART_NUM_0);
49+
(void)stub_target_uart_get_intr_flags(UART_NUM_0);
50+
(void)stub_target_uart_read_rxfifo_byte(UART_NUM_0);
51+
(void)stub_target_uart_rx_one_char();
52+
stub_target_uart_rominit_intr_attach(UART_NUM_0, 5, uart_rx_interrupt_handler, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);
53+
stub_target_uart_tx_one_char('A');
54+
stub_target_uart_tx_flush();
55+
56+
return 0;
57+
}
58+
4059
static __attribute__((unused)) int handle_test1(va_list ap)
4160
{
4261
(void)ap;

include/esp-stub-lib/rom_wrappers.h

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,6 @@ void stub_lib_delay_us(uint32_t us);
2929
*/
3030
uint16_t stub_lib_crc16_le(uint16_t crc, const uint8_t *buf, uint32_t len);
3131

32-
/**
33-
* @brief Transmit a single byte over UART.
34-
*
35-
* @param c Byte to transmit.
36-
* @return 0 if successful, non-zero if error occurred.
37-
*/
38-
uint8_t stub_lib_uart_tx_one_char(uint8_t c);
39-
40-
/**
41-
* @brief Receive a single byte (blocking) from UART.
42-
*
43-
* @return Received byte.
44-
*/
45-
uint8_t stub_lib_uart_rx_one_char(void);
46-
47-
/**
48-
* @brief Flush any buffered transmit data.
49-
*/
50-
void stub_lib_uart_tx_flush(void);
51-
5232
#ifdef __cplusplus
5333
}
5434
#endif // __cplusplus

src/rom_wrappers.c

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77

88
extern void ets_delay_us(uint32_t us);
99
extern uint16_t crc16_le(uint16_t crc, const uint8_t *buf, uint32_t len);
10-
extern uint8_t uart_tx_one_char(uint8_t ch);
11-
extern uint8_t uart_rx_one_char(void);
12-
extern void uart_tx_flush(void);
1310

1411
void stub_lib_delay_us(uint32_t us)
1512
{
@@ -20,18 +17,3 @@ uint16_t stub_lib_crc16_le(uint16_t crc, const uint8_t *buf, uint32_t len)
2017
{
2118
return crc16_le(crc, buf, len);
2219
}
23-
24-
uint8_t stub_lib_uart_tx_one_char(uint8_t c)
25-
{
26-
return uart_tx_one_char(c);
27-
}
28-
29-
uint8_t stub_lib_uart_rx_one_char(void)
30-
{
31-
return uart_rx_one_char();
32-
}
33-
34-
void stub_lib_uart_tx_flush(void)
35-
{
36-
uart_tx_flush();
37-
}

src/target/CMakeLists.txt

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,31 @@ add_subdirectory(${ESP_TARGET} ${ESP_TARGET_LIB})
1010
# Build common library
1111
add_subdirectory(common ${ESP_COMMON_LIB})
1212

13+
# Get target type to handle both STATIC and INTERFACE libraries
14+
get_target_property(target_type ${ESP_TARGET_LIB} TYPE)
15+
1316
# Apply common configurations to target library
1417
# This ensures strong symbols override weak ones
15-
target_link_options(${ESP_TARGET_LIB}
16-
PUBLIC -Wl,--whole-archive
17-
)
18+
if(target_type STREQUAL "INTERFACE_LIBRARY")
19+
# target_link_options(${ESP_TARGET_LIB}
20+
# INTERFACE -Wl,--whole-archive
21+
# )
22+
23+
target_include_directories(${ESP_TARGET_LIB}
24+
INTERFACE ${CMAKE_CURRENT_LIST_DIR}/${ESP_TARGET}/include
25+
)
26+
27+
target_link_libraries(${ESP_TARGET_LIB} INTERFACE ${ESP_BASE_LIB})
28+
else()
29+
target_link_options(${ESP_TARGET_LIB}
30+
PUBLIC -Wl,--whole-archive
31+
)
32+
33+
target_include_directories(${ESP_TARGET_LIB}
34+
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/${ESP_TARGET}/include
35+
)
1836

19-
# Set common include directories for target library
20-
target_include_directories(${ESP_TARGET_LIB}
21-
PUBLIC ${CMAKE_CURRENT_LIST_DIR}/${ESP_TARGET}/include
22-
)
37+
target_link_libraries(${ESP_TARGET_LIB} PUBLIC ${ESP_BASE_LIB})
38+
endif()
2339

24-
# Setup dependency chain: target → base, common → base + target
25-
target_link_libraries(${ESP_TARGET_LIB} PUBLIC ${ESP_BASE_LIB})
2640
target_link_libraries(${ESP_COMMON_LIB} PUBLIC ${ESP_BASE_LIB} ${ESP_TARGET_LIB})

src/target/base/include/target/uart.h

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,126 @@
77
#pragma once
88

99
#include <stdint.h>
10+
#include <stdbool.h>
11+
#include "soc_caps.h"
1012

13+
/**
14+
* @brief UART port numbers
15+
*
16+
* Available UART ports depend on the chip's SOC_UART_HP_NUM capability.
17+
*/
18+
typedef enum {
19+
UART_NUM_0, /*!< UART port 0 */
20+
UART_NUM_1, /*!< UART port 1 */
21+
#if SOC_UART_HP_NUM > 2
22+
UART_NUM_2, /*!< UART port 2 */
23+
#endif
24+
#if SOC_UART_HP_NUM > 3
25+
UART_NUM_3, /*!< UART port 3 */
26+
#endif
27+
#if SOC_UART_HP_NUM > 4
28+
UART_NUM_4, /*!< UART port 4 */
29+
#endif
30+
UART_NUM_MAX, /*!< UART port max */
31+
} uart_port_t;
32+
33+
#define UART_INTR_RXFIFO_FULL (0x1<<0)
34+
#define UART_INTR_RXFIFO_TOUT (0x1<<8)
35+
36+
/**
37+
* @brief Wait until UART is idle
38+
*
39+
* @param uart_num UART port number
40+
*/
41+
void stub_target_uart_wait_idle(uint8_t uart_num);
42+
43+
/**
44+
* @brief Initialize UART
45+
*
46+
* @param uart_num UART port number
47+
* @param baudrate Baud rate
48+
*/
1149
void stub_target_uart_init(uint8_t uart_num, uint32_t baudrate);
50+
51+
/**
52+
* @brief Set UART baud rate
53+
*
54+
* @param uart_num UART port number
55+
* @param baudrate Baud rate
56+
*/
57+
void stub_target_uart_set_baudrate(uint8_t uart_num, uint32_t baudrate);
58+
59+
/**
60+
* @brief Attach interrupt handler to UART and configure interrupts
61+
*
62+
* @note This function requires ROM preinit (download mode initialization) to work correctly.
63+
* It uses ROM functions (ets_isr_attach, ets_isr_unmask) that are only available
64+
* after the ROM has entered download mode and completed its initialization.
65+
* Functions that contain "rominit" have this requirement.
66+
*
67+
* @param uart_num UART port number
68+
* @param intr_num CPU interrupt source
69+
* @param handler Interrupt handler function pointer
70+
* @param flags Interrupt enable flags (bitwise OR of UART_INTR_* values)
71+
*/
72+
void stub_target_uart_rominit_intr_attach(uint8_t uart_num, int intr_num, void *handler, uint32_t flags);
73+
74+
/**
75+
* @brief Get and clear UART interrupt status
76+
*
77+
* This function returns the interrupt status and automatically clears it.
78+
* Call this at the beginning of your interrupt handler.
79+
*
80+
* @param uart_num UART port number
81+
* @return Bitmask of active interrupts that were cleared
82+
*/
83+
uint32_t stub_target_uart_get_intr_flags(uint8_t uart_num);
84+
85+
/**
86+
* @brief Get number of bytes available in RX FIFO
87+
*
88+
* @param uart_num UART port number
89+
* @return Number of bytes currently in the RX FIFO
90+
*/
91+
uint32_t stub_target_uart_get_rxfifo_count(uint8_t uart_num);
92+
93+
/**
94+
* @brief Read a single byte from UART RX FIFO
95+
*
96+
* This function reads one byte from the RX FIFO without checking
97+
* if data is available. Call stub_target_uart_get_rxfifo_count()
98+
* first to ensure data is available.
99+
*
100+
* @param uart_num UART port number
101+
* @return The byte read from the FIFO
102+
*/
103+
uint8_t stub_target_uart_read_rxfifo_byte(uint8_t uart_num);
104+
105+
/**
106+
* @brief Configure RX timeout threshold
107+
*
108+
* @param uart_num UART port number
109+
* @param timeout Timeout value in bit times (1-126, 0 to disable)
110+
* timeout mean the multiple of UART packets (~11 bytes) that can be received before timeout
111+
*/
112+
void stub_target_uart_set_rx_timeout(uint8_t uart_num, uint8_t timeout);
113+
114+
/**
115+
* @brief Transmit a single byte over UART.
116+
*
117+
* @param c Byte to transmit.
118+
* @return 0 if successful, non-zero if error occurred.
119+
*/
120+
uint8_t stub_target_uart_tx_one_char(uint8_t c);
121+
122+
/**
123+
* @brief Receive a single byte (blocking) from UART.
124+
*
125+
* @return Received byte.
126+
*/
127+
uint8_t stub_target_uart_rx_one_char(void);
128+
129+
/**
130+
* @brief Flush any buffered transmit data.
131+
*/
132+
void stub_target_uart_tx_flush(void);

src/target/common/src/uart.c

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,105 @@
44
* SPDX-License-Identifier: Apache-2.0 OR MIT
55
*/
66

7+
#include <stdbool.h>
78
#include <stdint.h>
89
#include <stddef.h>
910
#include <target/uart.h>
11+
#include <target/soc_utils.h>
12+
#include <soc/uart_reg.h>
13+
14+
extern void ets_update_cpu_frequency(uint32_t ticks_per_us);
15+
extern void uartAttach(void *rxBuffer);
16+
extern void Uart_Init(uint8_t uart_no);
17+
extern uint32_t ets_clk_get_cpu_freq(void);
18+
extern void uart_tx_switch(uint8_t uart_no);
19+
extern void uart_tx_wait_idle(uint8_t uart_num);
20+
extern void uart_div_modify(uint8_t uart_no, uint32_t divisor);
21+
extern void ets_isr_attach(int int_num, void *handler, void *arg);
22+
extern void ets_isr_unmask(int int_num);
23+
extern void uart_tx_flush(void);
24+
extern uint8_t uart_tx_one_char(uint8_t ch);
25+
extern uint8_t uart_rx_one_char(void);
26+
27+
void __attribute__((weak)) stub_target_uart_wait_idle(uint8_t uart_num)
28+
{
29+
uart_tx_wait_idle(uart_num);
30+
}
1031

1132
void __attribute__((weak)) stub_target_uart_init(uint8_t uart_num, uint32_t baudrate)
1233
{
13-
(void)baudrate;
14-
(void)uart_num;
34+
ets_update_cpu_frequency(ets_clk_get_cpu_freq() / 1000000);
35+
uartAttach(NULL);
36+
Uart_Init(uart_num);
37+
uart_tx_switch(uart_num);
38+
stub_target_uart_set_baudrate(uart_num, baudrate);
39+
}
40+
41+
void __attribute__((weak)) stub_target_uart_set_baudrate(uint8_t uart_num, uint32_t baudrate)
42+
{
43+
uint32_t clock = ets_clk_get_cpu_freq() << 4;
44+
uint32_t divisor = clock / baudrate;
45+
stub_target_uart_wait_idle(uart_num);
46+
uart_div_modify(uart_num, divisor);
47+
// TODO: Consider decimal part
48+
}
49+
50+
void stub_target_uart_rominit_intr_attach(uint8_t uart_num, int intr_num, void *handler, uint32_t flags)
51+
{
52+
// Clear pending interrupt flags
53+
WRITE_PERI_REG(UART_INT_CLR_REG(uart_num), 0xFFFFFFFFU);
54+
55+
ets_isr_attach(intr_num, handler, NULL);
56+
57+
if (flags != 0U) {
58+
SET_PERI_REG_MASK(UART_INT_ENA_REG(uart_num), flags);
59+
}
60+
61+
ets_isr_unmask(1 << intr_num);
62+
}
63+
64+
uint32_t __attribute__((weak)) stub_target_uart_get_intr_flags(uint8_t uart_num)
65+
{
66+
uint32_t status = READ_PERI_REG(UART_INT_ST_REG(uart_num));
67+
// Clear the interrupts
68+
WRITE_PERI_REG(UART_INT_CLR_REG(uart_num), status);
69+
return status;
70+
}
71+
72+
uint32_t __attribute__((weak)) stub_target_uart_get_rxfifo_count(uint8_t uart_num)
73+
{
74+
uint32_t status = READ_PERI_REG(UART_STATUS_REG(uart_num));
75+
return (status >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT_V;
76+
}
77+
78+
uint8_t __attribute__((weak)) stub_target_uart_read_rxfifo_byte(uint8_t uart_num)
79+
{
80+
return (uint8_t)(READ_PERI_REG(UART_FIFO_REG(uart_num)) & UART_RXFIFO_RD_BYTE_V);
81+
}
82+
83+
void __attribute__((weak)) stub_target_uart_set_rx_timeout(uint8_t uart_num, uint8_t timeout)
84+
{
85+
uint32_t conf1 = READ_PERI_REG(UART_CONF1_REG(uart_num));
86+
conf1 &= ~(UART_RX_TOUT_THRHD_M | UART_RX_TOUT_EN_M);
87+
88+
if (timeout > 0U) {
89+
conf1 |= (((uint32_t)timeout & UART_RX_TOUT_THRHD_V) << UART_RX_TOUT_THRHD_S) | UART_RX_TOUT_EN;
90+
}
91+
92+
WRITE_PERI_REG(UART_CONF1_REG(uart_num), conf1);
93+
}
94+
95+
uint8_t stub_target_uart_tx_one_char(uint8_t c)
96+
{
97+
return uart_tx_one_char(c);
98+
}
99+
100+
uint8_t stub_target_uart_rx_one_char(void)
101+
{
102+
return uart_rx_one_char();
103+
}
104+
105+
void __attribute__((weak)) stub_target_uart_tx_flush(void)
106+
{
107+
uart_tx_flush();
15108
}

src/target/esp32/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_library(${ESP_TARGET_LIB} STATIC ${srcs})
77

88
target_link_options(${ESP_TARGET_LIB}
99
PUBLIC -T${CMAKE_CURRENT_LIST_DIR}/ld/esp32.rom.ld
10+
PUBLIC -T${CMAKE_CURRENT_LIST_DIR}/ld/esp32.rom.redefined.ld
1011
PUBLIC -T${CMAKE_CURRENT_LIST_DIR}/ld/esp32.rom.libc-funcs.ld
1112
PUBLIC -T${CMAKE_CURRENT_LIST_DIR}/ld/esp32.rom.libgcc.ld
1213
)

0 commit comments

Comments
 (0)