Skip to content

Commit 94b1e5d

Browse files
committed
Add half-duplex UART support for ESP-IDF 5.4.1 and configure UART parameters
1 parent bf8a1a6 commit 94b1e5d

5 files changed

Lines changed: 502 additions & 2 deletions

File tree

CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
idf_component_register(
2+
SRCS
3+
"src/EspUartBackend.cpp"
4+
"src/SmartServoBus.cpp"
5+
"src/angle.cpp"
6+
"src/half_duplex_uart_541.cpp"
7+
INCLUDE_DIRS
8+
"src"
9+
REQUIRES
10+
driver
11+
esp_common
12+
log
13+
PRIV_REQUIRES
14+
soc
15+
)

src/EspUartBackend.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ void EspUartBackend::uartRoutine() {
4141
.parity = UART_PARITY_DISABLE,
4242
.stop_bits = UART_STOP_BITS_1,
4343
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
44+
.source_clk = UART_SCLK_DEFAULT,
4445
};
4546

4647
ESP_ERROR_CHECK(half_duplex::uart_param_config(m_uart, &uart_config));
@@ -141,7 +142,7 @@ size_t EspUartBackend::uartReceive(uint8_t* buff, size_t bufcap) {
141142
}
142143

143144
void EspUartBackend::send(const lw::Packet& pkt, QueueHandle_t responseQueue, bool expect_response, bool priority) {
144-
struct tx_request req = { 0 };
145+
struct tx_request req = {0};
145146
req.size = (uint8_t)pkt._data.size();
146147
req.expect_response = expect_response;
147148
req.responseQueue = responseQueue;

src/half_duplex_uart.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#if (!defined(ESP_IDF_VERSION) || ESP_IDF_VERSION < 0x040400)
66
#include "half_duplex_uart_33.h"
7-
#else
7+
#elif (ESP_IDF_VERSION >= 0x040400 && ESP_IDF_VERSION < 0x050000)
88
#include "half_duplex_uart_44.h"
9+
#else
10+
#include "half_duplex_uart_541.h"
911
#endif

src/half_duplex_uart_541.cpp

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
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

Comments
 (0)