mirror of
https://github.com/esphome/esphome.git
synced 2026-10-12 06:14:17 +00:00
[usb_uart] Fix invalid stop bits and parity enum mappings (#20408)
This commit is contained in:
@@ -10,8 +10,8 @@ namespace esphome::uart {
|
||||
|
||||
static const char *const TAG = "uart";
|
||||
|
||||
// UART parity strings indexed by UARTParityOptions enum (0-2): NONE, EVEN, ODD
|
||||
PROGMEM_STRING_TABLE(UARTParityStrings, "NONE", "EVEN", "ODD", "UNKNOWN");
|
||||
// UART parity strings indexed by UARTParityOptions enum (0-4): NONE, EVEN, ODD, MARK, SPACE
|
||||
PROGMEM_STRING_TABLE(UARTParityStrings, "NONE", "EVEN", "ODD", "MARK", "SPACE", "UNKNOWN");
|
||||
|
||||
void UARTDevice::check_uart_settings(uint32_t baud_rate, uint8_t stop_bits, UARTParityOptions parity,
|
||||
uint8_t data_bits) {
|
||||
|
||||
@@ -18,6 +18,9 @@ enum UARTParityOptions {
|
||||
UART_CONFIG_PARITY_NONE,
|
||||
UART_CONFIG_PARITY_EVEN,
|
||||
UART_CONFIG_PARITY_ODD,
|
||||
// Only USB serial adapters (usb_uart) support these
|
||||
UART_CONFIG_PARITY_MARK,
|
||||
UART_CONFIG_PARITY_SPACE,
|
||||
};
|
||||
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
|
||||
@@ -158,6 +158,8 @@ void HostUartComponent::setup() {
|
||||
options.c_cflag |= PARENB;
|
||||
options.c_cflag |= PARODD;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
// Set stop bits
|
||||
if (this->stop_bits_ == 2) {
|
||||
|
||||
@@ -38,6 +38,8 @@ uint16_t LibreTinyUARTComponent::get_config() {
|
||||
case UART_CONFIG_PARITY_ODD:
|
||||
config |= SERIAL_PARITY_ODD;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
config |= (this->data_bits_ - 4) << 8;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.const import CONF_DATA_BITS, CONF_PARITY, CONF_STOP_BITS
|
||||
from esphome.components.esp32 import VARIANT_ESP32P4, get_esp32_variant
|
||||
from esphome.components.uart import CONF_DEBUG_PREFIX, CONF_FLUSH_TIMEOUT, UARTComponent
|
||||
from esphome.components.uart import (
|
||||
CONF_DEBUG_PREFIX,
|
||||
CONF_FLUSH_TIMEOUT,
|
||||
UARTComponent,
|
||||
UARTParityOptions,
|
||||
)
|
||||
from esphome.components.usb_host import (
|
||||
get_max_packet_size,
|
||||
register_usb_client,
|
||||
@@ -38,7 +43,6 @@ def is_usb_uart_channel(uart_id: ID, full_config: ConfigType) -> bool:
|
||||
)
|
||||
|
||||
|
||||
UARTParityOptions = usb_uart_ns.enum("UARTParityOptions")
|
||||
UART_PARITY_OPTIONS = {
|
||||
"NONE": UARTParityOptions.UART_CONFIG_PARITY_NONE,
|
||||
"EVEN": UARTParityOptions.UART_CONFIG_PARITY_EVEN,
|
||||
|
||||
@@ -132,15 +132,12 @@ bool USBUartTypeCH34X::config_step(USBUartChannelBase *channel, uint8_t step, bo
|
||||
factor = 256 - factor;
|
||||
|
||||
uint16_t value = 0xC0;
|
||||
if (channel->stop_bits_ == UART_CONFIG_STOP_BITS_2)
|
||||
if (channel->line_stop_bits_() == LineStopBits::LINE_STOP_BITS_2)
|
||||
value |= 4;
|
||||
switch (channel->parity_) {
|
||||
case UART_CONFIG_PARITY_NONE:
|
||||
break;
|
||||
default:
|
||||
value |= 8 | ((channel->parity_ - 1) << 4);
|
||||
break;
|
||||
}
|
||||
// Parity enable, then 0 = odd, 1 = even, 2 = mark, 3 = space
|
||||
const LineParity parity = channel->line_parity_();
|
||||
if (parity != LineParity::LINE_PARITY_NONE)
|
||||
value |= 8 | ((static_cast<uint8_t>(parity) - 1) << 4);
|
||||
value |= channel->data_bits_ - 5;
|
||||
value <<= 8;
|
||||
value |= 0x8C;
|
||||
|
||||
@@ -107,8 +107,8 @@ bool USBUartTypeCP210X::config_step(USBUartChannelBase *channel, uint8_t step, b
|
||||
this->config_transfer_(USB_VENDOR_IFC | usb_host::USB_DIR_OUT, IFC_ENABLE, 1, channel->index_);
|
||||
return true;
|
||||
case 1: {
|
||||
uint16_t line_control = channel->stop_bits_;
|
||||
line_control |= static_cast<uint8_t>(channel->parity_) << 4;
|
||||
uint16_t line_control = static_cast<uint8_t>(channel->line_stop_bits_());
|
||||
line_control |= static_cast<uint8_t>(channel->line_parity_()) << 4;
|
||||
line_control |= channel->data_bits_ << 8;
|
||||
ESP_LOGD(TAG, "Line control value 0x%X", line_control);
|
||||
this->config_transfer_(USB_VENDOR_IFC | usb_host::USB_DIR_OUT, SET_LINE_CTL, line_control, channel->index_);
|
||||
|
||||
@@ -362,34 +362,8 @@ bool USBUartTypeFT23XX::config_step(USBUartChannelBase *channel, uint8_t step, b
|
||||
}
|
||||
case 2: { // set line properties (data bits / parity / stop bits)
|
||||
uint16_t value = channel->data_bits_;
|
||||
switch (channel->parity_) {
|
||||
case UART_CONFIG_PARITY_NONE:
|
||||
value |= (0x00 << 8);
|
||||
break;
|
||||
case UART_CONFIG_PARITY_ODD:
|
||||
value |= (0x01 << 8);
|
||||
break;
|
||||
case UART_CONFIG_PARITY_EVEN:
|
||||
value |= (0x02 << 8);
|
||||
break;
|
||||
case UART_CONFIG_PARITY_MARK:
|
||||
value |= (0x03 << 8);
|
||||
break;
|
||||
case UART_CONFIG_PARITY_SPACE:
|
||||
value |= (0x04 << 8);
|
||||
break;
|
||||
}
|
||||
switch (channel->stop_bits_) {
|
||||
default: // 1 bit
|
||||
value |= (0x00 << 11);
|
||||
break;
|
||||
case UART_CONFIG_STOP_BITS_1_5:
|
||||
value |= (0x01 << 11);
|
||||
break;
|
||||
case UART_CONFIG_STOP_BITS_2:
|
||||
value |= (0x02 << 11);
|
||||
break;
|
||||
}
|
||||
value |= static_cast<uint8_t>(channel->line_parity_()) << 8;
|
||||
value |= static_cast<uint8_t>(channel->line_stop_bits_()) << 11;
|
||||
value |= (0x00 << 14);
|
||||
this->config_transfer_(USB_VENDOR_DEV | usb_host::USB_DIR_OUT, 0x04, value,
|
||||
channel->cdc_dev_.bulk_interface_number + 1);
|
||||
|
||||
@@ -261,34 +261,9 @@ bool USBUartTypePL2303::config_step(USBUartChannelBase *channel, uint8_t step, b
|
||||
encode_baud_divisor(line_coding, baud);
|
||||
}
|
||||
|
||||
// Stop bits: 0=1, 1=1.5, 2=2
|
||||
switch (channel->get_stop_bits()) {
|
||||
case 2:
|
||||
line_coding[4] = 2;
|
||||
break;
|
||||
default:
|
||||
line_coding[4] = 0;
|
||||
break;
|
||||
}
|
||||
line_coding[4] = static_cast<uint8_t>(channel->line_stop_bits_());
|
||||
|
||||
// Parity: 0=none, 1=odd, 2=even, 3=mark, 4=space
|
||||
switch (channel->parity_) {
|
||||
case UART_CONFIG_PARITY_ODD:
|
||||
line_coding[5] = 1;
|
||||
break;
|
||||
case UART_CONFIG_PARITY_EVEN:
|
||||
line_coding[5] = 2;
|
||||
break;
|
||||
case UART_CONFIG_PARITY_MARK:
|
||||
line_coding[5] = 3;
|
||||
break;
|
||||
case UART_CONFIG_PARITY_SPACE:
|
||||
line_coding[5] = 4;
|
||||
break;
|
||||
default:
|
||||
line_coding[5] = 0;
|
||||
break;
|
||||
}
|
||||
line_coding[5] = static_cast<uint8_t>(channel->line_parity_());
|
||||
|
||||
// Data bits
|
||||
line_coding[6] = channel->get_data_bits();
|
||||
|
||||
@@ -270,9 +270,10 @@ void USBUartComponent::dump_config() {
|
||||
" Flush Timeout: %" PRIu32 " ms\n"
|
||||
" Debug: %s\n"
|
||||
" Dummy receiver: %s",
|
||||
channel->index_, channel->baud_rate_, channel->data_bits_, PARITY_NAMES[channel->parity_],
|
||||
STOP_BITS_NAMES[channel->stop_bits_], channel->flush_timeout_ms_, YESNO(channel->debug_),
|
||||
YESNO(channel->dummy_receiver_));
|
||||
channel->index_, channel->baud_rate_, channel->data_bits_,
|
||||
PARITY_NAMES[static_cast<uint8_t>(channel->line_parity_())],
|
||||
STOP_BITS_NAMES[static_cast<uint8_t>(channel->line_stop_bits_())], channel->flush_timeout_ms_,
|
||||
YESNO(channel->debug_), YESNO(channel->dummy_receiver_));
|
||||
}
|
||||
}
|
||||
void USBUartComponent::start_input(USBUartChannelBase *channel) {
|
||||
@@ -515,14 +516,17 @@ bool USBUartTypeCdcAcm::config_step(USBUartChannelBase *channel, uint8_t step, b
|
||||
// sends RSTACK.
|
||||
uint32_t baud = channel->baud_rate_;
|
||||
std::vector<uint8_t> line_coding = {
|
||||
static_cast<uint8_t>(baud & 0xFF), static_cast<uint8_t>((baud >> 8) & 0xFF),
|
||||
static_cast<uint8_t>((baud >> 16) & 0xFF), static_cast<uint8_t>((baud >> 24) & 0xFF),
|
||||
static_cast<uint8_t>(channel->stop_bits_), // bCharFormat: 0=1stop, 1=1.5stop, 2=2stop
|
||||
static_cast<uint8_t>(channel->parity_), // bParityType: 0=None, 1=Odd, 2=Even, 3=Mark, 4=Space
|
||||
static_cast<uint8_t>(channel->data_bits_), // bDataBits
|
||||
static_cast<uint8_t>(baud & 0xFF),
|
||||
static_cast<uint8_t>((baud >> 8) & 0xFF),
|
||||
static_cast<uint8_t>((baud >> 16) & 0xFF),
|
||||
static_cast<uint8_t>((baud >> 24) & 0xFF),
|
||||
static_cast<uint8_t>(channel->line_stop_bits_()), // bCharFormat
|
||||
static_cast<uint8_t>(channel->line_parity_()), // bParityType
|
||||
static_cast<uint8_t>(channel->data_bits_), // bDataBits
|
||||
};
|
||||
ESP_LOGD(TAG, "SET_LINE_CODING: baud=%u stop=%u parity=%u data=%u", (unsigned) baud, channel->stop_bits_,
|
||||
(unsigned) channel->parity_, channel->data_bits_);
|
||||
ESP_LOGD(TAG, "SET_LINE_CODING: baud=%u stop=%s parity=%s data=%u", (unsigned) baud,
|
||||
STOP_BITS_NAMES[static_cast<uint8_t>(channel->line_stop_bits_())],
|
||||
PARITY_NAMES[static_cast<uint8_t>(channel->line_parity_())], channel->data_bits_);
|
||||
this->config_transfer_(CDC_REQUEST_TYPE, CDC_SET_LINE_CODING, 0, channel->cdc_dev_.interrupt_interface_number,
|
||||
line_coding);
|
||||
return true;
|
||||
@@ -675,6 +679,32 @@ bool USBUartComponent::run_config_machine_() {
|
||||
return true;
|
||||
}
|
||||
|
||||
LineStopBits USBUartChannelBase::line_stop_bits_() const {
|
||||
switch (this->stop_bits_) {
|
||||
case UART_CONFIG_STOP_BITS_1_5:
|
||||
return LineStopBits::LINE_STOP_BITS_1_5;
|
||||
case UART_CONFIG_STOP_BITS_2:
|
||||
return LineStopBits::LINE_STOP_BITS_2;
|
||||
default:
|
||||
return LineStopBits::LINE_STOP_BITS_1;
|
||||
}
|
||||
}
|
||||
|
||||
LineParity USBUartChannelBase::line_parity_() const {
|
||||
switch (this->parity_) {
|
||||
case uart::UART_CONFIG_PARITY_ODD:
|
||||
return LineParity::LINE_PARITY_ODD;
|
||||
case uart::UART_CONFIG_PARITY_EVEN:
|
||||
return LineParity::LINE_PARITY_EVEN;
|
||||
case uart::UART_CONFIG_PARITY_MARK:
|
||||
return LineParity::LINE_PARITY_MARK;
|
||||
case uart::UART_CONFIG_PARITY_SPACE:
|
||||
return LineParity::LINE_PARITY_SPACE;
|
||||
default:
|
||||
return LineParity::LINE_PARITY_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void USBUartChannelBase::load_settings(bool /*dump_config*/) {
|
||||
// The per-channel control transfers already log their values at debug level.
|
||||
this->parent_->apply_channel_settings(this);
|
||||
|
||||
@@ -71,21 +71,33 @@ enum CH34xChipType : uint8_t {
|
||||
CHIP_UNKNOWN = 0xFF,
|
||||
};
|
||||
|
||||
enum UARTParityOptions {
|
||||
UART_CONFIG_PARITY_NONE = 0,
|
||||
UART_CONFIG_PARITY_ODD,
|
||||
UART_CONFIG_PARITY_EVEN,
|
||||
UART_CONFIG_PARITY_MARK,
|
||||
UART_CONFIG_PARITY_SPACE,
|
||||
};
|
||||
|
||||
// 1 and 2 are the stop bit count the uart component uses, so a count set through the
|
||||
// UARTComponent API (as serial_proxy does) means the same here. 1.5 has no count.
|
||||
enum UARTStopBitsOptions {
|
||||
UART_CONFIG_STOP_BITS_1 = 0,
|
||||
UART_CONFIG_STOP_BITS_1_5,
|
||||
UART_CONFIG_STOP_BITS_2,
|
||||
UART_CONFIG_STOP_BITS_1 = 1,
|
||||
UART_CONFIG_STOP_BITS_2 = 2,
|
||||
UART_CONFIG_STOP_BITS_1_5 = 3,
|
||||
};
|
||||
|
||||
// Stop bits and parity as adapters take them on the wire: CDC SET_LINE_CODING, and the
|
||||
// CP210x, FTDI and PL2303 requests, all use these values
|
||||
enum class LineStopBits : uint8_t {
|
||||
LINE_STOP_BITS_1 = 0,
|
||||
LINE_STOP_BITS_1_5 = 1,
|
||||
LINE_STOP_BITS_2 = 2,
|
||||
};
|
||||
|
||||
enum class LineParity : uint8_t {
|
||||
LINE_PARITY_NONE = 0,
|
||||
LINE_PARITY_ODD = 1,
|
||||
LINE_PARITY_EVEN = 2,
|
||||
LINE_PARITY_MARK = 3,
|
||||
LINE_PARITY_SPACE = 4,
|
||||
};
|
||||
|
||||
// Indexed by LineParity
|
||||
static const char *const PARITY_NAMES[] = {"NONE", "ODD", "EVEN", "MARK", "SPACE"};
|
||||
// Indexed by LineStopBits
|
||||
static const char *const STOP_BITS_NAMES[] = {"1", "1.5", "2"};
|
||||
|
||||
class RingBuffer {
|
||||
@@ -156,22 +168,6 @@ class USBUartChannelBase : public uart::UARTComponent, public Parented<USBUartCo
|
||||
// Re-apply the current line settings (baud, parity, etc) to this already-open channel.
|
||||
void load_settings(bool dump_config) override;
|
||||
using UARTComponent::load_settings; // also bring in the no-arg overload for convenience
|
||||
void set_parity(UARTParityOptions parity) {
|
||||
this->parity_ = parity;
|
||||
// Keep the base-class parity in sync so uart::UARTComponent::get_parity() reports the configured value.
|
||||
// MARK/SPACE have no uart:: equivalent and report as NONE.
|
||||
switch (parity) {
|
||||
case UART_CONFIG_PARITY_EVEN:
|
||||
uart::UARTComponent::set_parity(uart::UART_CONFIG_PARITY_EVEN);
|
||||
break;
|
||||
case UART_CONFIG_PARITY_ODD:
|
||||
uart::UARTComponent::set_parity(uart::UART_CONFIG_PARITY_ODD);
|
||||
break;
|
||||
default:
|
||||
uart::UARTComponent::set_parity(uart::UART_CONFIG_PARITY_NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void set_debug(bool debug) { this->debug_ = debug; }
|
||||
void set_dummy_receiver(bool dummy_receiver) { this->dummy_receiver_ = dummy_receiver; }
|
||||
void set_debug_prefix(const char *prefix) { this->debug_prefix_ = StringRef(prefix); }
|
||||
@@ -194,6 +190,8 @@ class USBUartChannelBase : public uart::UARTComponent, public Parented<USBUartCo
|
||||
// Not directly instantiable; construct a concrete channel type instead.
|
||||
USBUartChannelBase(uint8_t index, uint16_t buffer_size) : input_buffer_(RingBuffer(buffer_size)), index_(index) {}
|
||||
void check_logger_conflict() override {}
|
||||
LineStopBits line_stop_bits_() const;
|
||||
LineParity line_parity_() const;
|
||||
// Larger structures first (8+ bytes)
|
||||
RingBuffer input_buffer_;
|
||||
LockFreeQueue<UsbOutputChunk, USB_OUTPUT_CHUNK_COUNT> output_queue_;
|
||||
@@ -205,7 +203,6 @@ class USBUartChannelBase : public uart::UARTComponent, public Parented<USBUartCo
|
||||
CdcEps cdc_dev_{};
|
||||
StringRef debug_prefix_{};
|
||||
// 4-byte fields
|
||||
UARTParityOptions parity_{UART_CONFIG_PARITY_NONE};
|
||||
uint32_t flush_timeout_ms_{100};
|
||||
// 1-byte fields (no padding between groups)
|
||||
std::atomic<bool> input_started_{true};
|
||||
|
||||
Reference in New Issue
Block a user