ESPHome 2026.5.0-dev
Loading...
Searching...
No Matches
uart_component_rp2040.cpp
Go to the documentation of this file.
1#ifdef USE_RP2040
6#include "esphome/core/log.h"
7
8#include <hardware/uart.h>
9
10#ifdef USE_LOGGER
12#endif
13
14namespace esphome::uart {
15
16static const char *const TAG = "uart.arduino_rp2040";
17
19 uint16_t config = 0;
20
21 if (this->parity_ == UART_CONFIG_PARITY_NONE) {
22 config |= UART_PARITY_NONE;
23 } else if (this->parity_ == UART_CONFIG_PARITY_EVEN) {
24 config |= UART_PARITY_EVEN;
25 } else if (this->parity_ == UART_CONFIG_PARITY_ODD) {
26 config |= UART_PARITY_ODD;
27 }
28
29 switch (this->data_bits_) {
30 case 5:
31 config |= SERIAL_DATA_5;
32 break;
33 case 6:
34 config |= SERIAL_DATA_6;
35 break;
36 case 7:
37 config |= SERIAL_DATA_7;
38 break;
39 case 8:
40 config |= SERIAL_DATA_8;
41 break;
42 }
43
44 if (this->stop_bits_ == 1) {
45 config |= SERIAL_STOP_BIT_1;
46 } else {
47 config |= SERIAL_STOP_BIT_2;
48 }
49
50 return config;
51}
52
54 auto setup_pin_if_needed = [](InternalGPIOPin *pin) {
55 if (!pin) {
56 return;
57 }
59 if ((pin->get_flags() & mask) != gpio::Flags::FLAG_NONE) {
60 pin->setup();
61 }
62 };
63
64 setup_pin_if_needed(this->rx_pin_);
65 if (this->rx_pin_ != this->tx_pin_) {
66 setup_pin_if_needed(this->tx_pin_);
67 }
68
69 uint16_t config = get_config();
70
71 constexpr uint32_t valid_tx_uart_0 = __bitset({0, 12, 16, 28});
72 constexpr uint32_t valid_tx_uart_1 = __bitset({4, 8, 20, 24});
73
74 constexpr uint32_t valid_rx_uart_0 = __bitset({1, 13, 17, 29});
75 constexpr uint32_t valid_rx_uart_1 = __bitset({5, 9, 21, 25});
76
77 int8_t tx_hw = -1;
78 int8_t rx_hw = -1;
79
80 if (this->tx_pin_ != nullptr) {
81 if (this->tx_pin_->is_inverted()) {
82 ESP_LOGD(TAG, "An inverted TX pin %u can only be used with SerialPIO", this->tx_pin_->get_pin());
83 } else {
84 if (((1 << this->tx_pin_->get_pin()) & valid_tx_uart_0) != 0) {
85 tx_hw = 0;
86 } else if (((1 << this->tx_pin_->get_pin()) & valid_tx_uart_1) != 0) {
87 tx_hw = 1;
88 } else {
89 ESP_LOGD(TAG, "TX pin %u can only be used with SerialPIO", this->tx_pin_->get_pin());
90 }
91 }
92 }
93
94 if (this->rx_pin_ != nullptr) {
95 if (this->rx_pin_->is_inverted()) {
96 ESP_LOGD(TAG, "An inverted RX pin %u can only be used with SerialPIO", this->rx_pin_->get_pin());
97 } else {
98 if (((1 << this->rx_pin_->get_pin()) & valid_rx_uart_0) != 0) {
99 rx_hw = 0;
100 } else if (((1 << this->rx_pin_->get_pin()) & valid_rx_uart_1) != 0) {
101 rx_hw = 1;
102 } else {
103 ESP_LOGD(TAG, "RX pin %u can only be used with SerialPIO", this->rx_pin_->get_pin());
104 }
105 }
106 }
107
108 // Determine which hardware UART to use. A pin that is not specified
109 // should not prevent hardware UART selection — one-way UART is valid.
110 // When both pins are configured, both must be HW-capable and agree on UART number.
111 // When only one pin is configured (nullptr other), use that pin's HW UART.
112 // If a pin is configured but not HW-capable (inverted/invalid), fall back to SerialPIO.
113 int8_t hw_uart = -1;
114 const bool tx_configured = (this->tx_pin_ != nullptr);
115 const bool rx_configured = (this->rx_pin_ != nullptr);
116
117 if (tx_configured && rx_configured) {
118 // Both pins configured — both must map to the same hardware UART
119 if (tx_hw != -1 && rx_hw != -1 && tx_hw == rx_hw) {
120 hw_uart = tx_hw;
121 }
122 } else if (tx_configured) {
123 hw_uart = tx_hw;
124 } else if (rx_configured) {
125 hw_uart = rx_hw;
126 }
127
128#ifdef USE_LOGGER
129 if (hw_uart != -1 && logger::global_logger->get_uart() == hw_uart) {
130 ESP_LOGD(TAG, "Using SerialPIO as UART%d is taken by the logger", hw_uart);
131 hw_uart = -1;
132 }
133#endif
134
135 if (hw_uart == -1) {
136 ESP_LOGV(TAG, "Using SerialPIO");
137 pin_size_t tx = this->tx_pin_ == nullptr ? NOPIN : this->tx_pin_->get_pin();
138 pin_size_t rx = this->rx_pin_ == nullptr ? NOPIN : this->rx_pin_->get_pin();
139 auto *serial = new SerialPIO(tx, rx, this->rx_buffer_size_); // NOLINT(cppcoreguidelines-owning-memory)
140 serial->begin(this->baud_rate_, config);
141 if (this->tx_pin_ != nullptr && this->tx_pin_->is_inverted())
142 gpio_set_outover(tx, GPIO_OVERRIDE_INVERT);
143 if (this->rx_pin_ != nullptr && this->rx_pin_->is_inverted())
144 gpio_set_inover(rx, GPIO_OVERRIDE_INVERT);
145 this->serial_ = serial;
146 } else {
147 ESP_LOGV(TAG, "Using Hardware Serial");
148 SerialUART *serial;
149 if (hw_uart == 0) {
150 serial = &Serial1;
151 } else {
152 serial = &Serial2;
153 }
154 if (this->tx_pin_ != nullptr)
155 serial->setTX(this->tx_pin_->get_pin());
156 if (this->rx_pin_ != nullptr)
157 serial->setRX(this->rx_pin_->get_pin());
158 serial->setFIFOSize(this->rx_buffer_size_);
159 serial->begin(this->baud_rate_, config);
160 this->serial_ = serial;
161 this->hw_serial_ = true;
162 }
163}
164
166 ESP_LOGCONFIG(TAG, "UART Bus:");
167 LOG_PIN(" TX Pin: ", tx_pin_);
168 LOG_PIN(" RX Pin: ", rx_pin_);
169 if (this->rx_pin_ != nullptr) {
170 ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_);
171 }
172 ESP_LOGCONFIG(TAG,
173 " Baud Rate: %u baud\n"
174 " Data Bits: %u\n"
175 " Parity: %s\n"
176 " Stop bits: %u",
177 this->baud_rate_, this->data_bits_, LOG_STR_ARG(parity_to_str(this->parity_)), this->stop_bits_);
178 if (this->hw_serial_) {
179 ESP_LOGCONFIG(TAG, " Using hardware serial");
180 } else {
181 ESP_LOGCONFIG(TAG, " Using SerialPIO");
182 }
183}
184
185void RP2040UartComponent::write_array(const uint8_t *data, size_t len) {
186 this->serial_->write(data, len);
187#ifdef USE_UART_DEBUGGER
188 for (size_t i = 0; i < len; i++) {
189 this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
190 }
191#endif
192}
194 if (!this->check_read_timeout_())
195 return false;
196 *data = this->serial_->peek();
197 return true;
198}
199bool RP2040UartComponent::read_array(uint8_t *data, size_t len) {
200 if (!this->check_read_timeout_(len))
201 return false;
202 this->serial_->readBytes(data, len);
203#ifdef USE_UART_DEBUGGER
204 for (size_t i = 0; i < len; i++) {
205 this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
206 }
207#endif
208 return true;
209}
210size_t RP2040UartComponent::available() { return this->serial_->available(); }
212 ESP_LOGVV(TAG, " Flushing");
213 this->serial_->flush();
215}
216
217} // namespace esphome::uart
218#endif // USE_RP2040
virtual uint8_t get_pin() const =0
virtual bool is_inverted() const =0
bool read_array(uint8_t *data, size_t len) override
void write_array(const uint8_t *data, size_t len) override
bool check_read_timeout_(size_t len=1)
CallbackManager< void(UARTDirection, uint8_t)> debug_callback_
@ FLAG_OPEN_DRAIN
Definition gpio.h:29
@ FLAG_NONE
Definition gpio.h:26
@ FLAG_PULLUP
Definition gpio.h:30
@ FLAG_PULLDOWN
Definition gpio.h:31
Logger * global_logger
Definition logger.cpp:272
const char *const TAG
Definition spi.cpp:7
const LogString * parity_to_str(UARTParityOptions parity)
Definition uart.cpp:36
UARTFlushResult
Result of a flush() call.
@ UART_FLUSH_RESULT_ASSUMED_SUCCESS
Platform cannot report result; success is assumed.
std::string size_t len
Definition helpers.h:1045
static void uint32_t