1+ /*
2+ * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
3+ *
4+ * SPDX-License-Identifier: Apache-2.0
5+ *
6+ * ESP-IDF 5.4.1 compatible version of half_duplex_uart
7+ * Based on latest ESP-IDF UART example patterns
8+ */
9+
10+ #include " half_duplex_uart_541.h"
11+ #include " driver/uart.h"
12+ #include " driver/gpio.h"
13+ #include " esp_log.h"
14+ #include " freertos/FreeRTOS.h"
15+ #include " freertos/semphr.h"
16+ #include < cstring>
17+
18+ static const char *TAG = " half_duplex_uart_541" ;
19+
20+ namespace lx16a {
21+ namespace half_duplex {
22+
23+ // Structure to hold half-duplex UART state
24+ typedef struct {
25+ uart_port_t uart_num;
26+ gpio_num_t half_duplex_pin;
27+ SemaphoreHandle_t tx_mutex;
28+ bool is_installed;
29+ } half_duplex_uart_context_t ;
30+
31+ static half_duplex_uart_context_t uart_contexts[UART_NUM_MAX] = {};
32+
33+ esp_err_t uart_driver_install (uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size,
34+ int queue_size, QueueHandle_t* uart_queue, int intr_alloc_flags) {
35+ if (uart_num >= UART_NUM_MAX) {
36+ ESP_LOGE (TAG, " Invalid UART number: %d" , uart_num);
37+ return ESP_ERR_INVALID_ARG;
38+ }
39+
40+ esp_err_t ret = ::uart_driver_install (uart_num, rx_buffer_size, tx_buffer_size,
41+ queue_size, uart_queue, intr_alloc_flags);
42+
43+ if (ret == ESP_OK) {
44+ uart_contexts[uart_num].uart_num = uart_num;
45+ uart_contexts[uart_num].is_installed = true ;
46+ uart_contexts[uart_num].tx_mutex = xSemaphoreCreateMutex ();
47+
48+ if (uart_contexts[uart_num].tx_mutex == NULL ) {
49+ ESP_LOGE (TAG, " Failed to create mutex for UART %d" , uart_num);
50+ ::uart_driver_delete (uart_num);
51+ return ESP_ERR_NO_MEM;
52+ }
53+ }
54+
55+ return ret;
56+ }
57+
58+ esp_err_t uart_driver_delete (uart_port_t uart_num) {
59+ if (uart_num >= UART_NUM_MAX) {
60+ return ESP_ERR_INVALID_ARG;
61+ }
62+
63+ if (uart_contexts[uart_num].tx_mutex ) {
64+ vSemaphoreDelete (uart_contexts[uart_num].tx_mutex );
65+ uart_contexts[uart_num].tx_mutex = NULL ;
66+ }
67+
68+ uart_contexts[uart_num].is_installed = false ;
69+ uart_contexts[uart_num].half_duplex_pin = GPIO_NUM_NC;
70+
71+ return ::uart_driver_delete (uart_num);
72+ }
73+
74+ esp_err_t uart_param_config (uart_port_t uart_num, const uart_config_t *uart_config) {
75+ return ::uart_param_config (uart_num, uart_config);
76+ }
77+
78+ esp_err_t uart_set_pin (uart_port_t uart_num, int tx_io_num, int rx_io_num,
79+ int rts_io_num, int cts_io_num) {
80+ return ::uart_set_pin (uart_num, tx_io_num, rx_io_num, rts_io_num, cts_io_num);
81+ }
82+
83+ void uart_set_half_duplex_pin (uart_port_t uart_num, gpio_num_t pin) {
84+ if (uart_num >= UART_NUM_MAX || pin == GPIO_NUM_NC) {
85+ ESP_LOGE (TAG, " Invalid parameters for half-duplex pin setup" );
86+ return ;
87+ }
88+
89+ uart_contexts[uart_num].half_duplex_pin = pin;
90+
91+ // Configure the pin for half-duplex operation
92+ gpio_config_t io_conf = {};
93+ io_conf.intr_type = GPIO_INTR_DISABLE;
94+ io_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; // Open drain for half-duplex
95+ io_conf.pin_bit_mask = (1ULL << pin);
96+ io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
97+ io_conf.pull_up_en = GPIO_PULLUP_ENABLE; // Pull-up for idle state
98+ gpio_config (&io_conf);
99+
100+ // Set UART pins - use the same pin for both TX and RX in half-duplex mode
101+ ::uart_set_pin (uart_num, pin, pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
102+
103+ ESP_LOGI (TAG, " Half-duplex pin %d configured for UART %d" , pin, uart_num);
104+ }
105+
106+ static void switch_to_tx_mode (uart_port_t uart_num) {
107+ half_duplex_uart_context_t *ctx = &uart_contexts[uart_num];
108+ if (ctx->half_duplex_pin != GPIO_NUM_NC) {
109+ // Set pin as output for transmission
110+ gpio_set_direction (ctx->half_duplex_pin , GPIO_MODE_OUTPUT);
111+ // Small delay to ensure pin mode is switched
112+ vTaskDelay (pdMS_TO_TICKS (1 ));
113+ }
114+ }
115+
116+ static void switch_to_rx_mode (uart_port_t uart_num) {
117+ half_duplex_uart_context_t *ctx = &uart_contexts[uart_num];
118+ if (ctx->half_duplex_pin != GPIO_NUM_NC) {
119+ // Set pin as input for reception
120+ gpio_set_direction (ctx->half_duplex_pin , GPIO_MODE_INPUT);
121+ // Small delay to ensure pin mode is switched
122+ vTaskDelay (pdMS_TO_TICKS (1 ));
123+ }
124+ }
125+
126+ int uart_tx_chars (uart_port_t uart_num, const char * buffer, uint32_t len) {
127+ if (uart_num >= UART_NUM_MAX || buffer == NULL || len == 0 ) {
128+ return -1 ;
129+ }
130+
131+ half_duplex_uart_context_t *ctx = &uart_contexts[uart_num];
132+ if (!ctx->is_installed || ctx->tx_mutex == NULL ) {
133+ ESP_LOGE (TAG, " UART %d not properly initialized" , uart_num);
134+ return -1 ;
135+ }
136+
137+ // Take mutex to ensure exclusive access during transmission
138+ if (xSemaphoreTake (ctx->tx_mutex , pdMS_TO_TICKS (1000 )) != pdTRUE) {
139+ ESP_LOGE (TAG, " Failed to take TX mutex for UART %d" , uart_num);
140+ return -1 ;
141+ }
142+
143+ int bytes_written = -1 ;
144+
145+ // Switch to TX mode
146+ switch_to_tx_mode (uart_num);
147+
148+ // Wait for any previous transmission to complete
149+ esp_err_t ret = ::uart_wait_tx_done (uart_num, pdMS_TO_TICKS (100 ));
150+ if (ret != ESP_OK) {
151+ ESP_LOGW (TAG, " Wait for TX done failed: %s" , esp_err_to_name (ret));
152+ }
153+
154+ // Send data
155+ bytes_written = ::uart_write_bytes (uart_num, buffer, len);
156+
157+ if (bytes_written > 0 ) {
158+ // Wait for transmission to complete
159+ ret = ::uart_wait_tx_done (uart_num, pdMS_TO_TICKS (100 ));
160+ if (ret != ESP_OK) {
161+ ESP_LOGW (TAG, " Wait for TX completion failed: %s" , esp_err_to_name (ret));
162+ }
163+ }
164+
165+ // Switch back to RX mode
166+ switch_to_rx_mode (uart_num);
167+
168+ // Release mutex
169+ xSemaphoreGive (ctx->tx_mutex );
170+
171+ return bytes_written;
172+ }
173+
174+ int uart_read_bytes (uart_port_t uart_num, void * buf, uint32_t length, TickType_t ticks_to_wait) {
175+ return ::uart_read_bytes (uart_num, buf, length, ticks_to_wait);
176+ }
177+
178+ esp_err_t uart_flush_input (uart_port_t uart_num) {
179+ return ::uart_flush_input (uart_num);
180+ }
181+
182+ esp_err_t uart_flush (uart_port_t uart_num) {
183+ return ::uart_flush (uart_num);
184+ }
185+
186+ esp_err_t uart_wait_tx_done (uart_port_t uart_num, TickType_t ticks_to_wait) {
187+ return ::uart_wait_tx_done (uart_num, ticks_to_wait);
188+ }
189+
190+ esp_err_t uart_get_buffered_data_len (uart_port_t uart_num, size_t * size) {
191+ return ::uart_get_buffered_data_len (uart_num, size);
192+ }
193+
194+ esp_err_t uart_set_baudrate (uart_port_t uart_num, uint32_t baudrate) {
195+ return ::uart_set_baudrate (uart_num, baudrate);
196+ }
197+
198+ esp_err_t uart_get_baudrate (uart_port_t uart_num, uint32_t * baudrate) {
199+ return ::uart_get_baudrate (uart_num, baudrate);
200+ }
201+
202+ esp_err_t uart_set_word_length (uart_port_t uart_num, uart_word_length_t data_bit) {
203+ return ::uart_set_word_length (uart_num, data_bit);
204+ }
205+
206+ esp_err_t uart_get_word_length (uart_port_t uart_num, uart_word_length_t * data_bit) {
207+ return ::uart_get_word_length (uart_num, data_bit);
208+ }
209+
210+ esp_err_t uart_set_stop_bits (uart_port_t uart_num, uart_stop_bits_t stop_bits) {
211+ return ::uart_set_stop_bits (uart_num, stop_bits);
212+ }
213+
214+ esp_err_t uart_get_stop_bits (uart_port_t uart_num, uart_stop_bits_t * stop_bits) {
215+ return ::uart_get_stop_bits (uart_num, stop_bits);
216+ }
217+
218+ esp_err_t uart_set_parity (uart_port_t uart_num, uart_parity_t parity_mode) {
219+ return ::uart_set_parity (uart_num, parity_mode);
220+ }
221+
222+ esp_err_t uart_get_parity (uart_port_t uart_num, uart_parity_t * parity_mode) {
223+ return ::uart_get_parity (uart_num, parity_mode);
224+ }
225+
226+ } // namespace half_duplex
227+ } // namespace lx16a
0 commit comments