From 665fb0e27838e8d5743a51364dda0afdd43888f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 5 Nov 2024 10:56:14 +0100 Subject: [PATCH] Flatten Uart module, remove unnecessary data, replace methods with `apply_config` (#2449) * Flatten uart config * Do not remember at_command config * Don't save config values in memory * Move config implementations to Info * Changelog * Remove unused methods * apply_config, SetConfig * Fix test * simplify futures * Update esp-hal/CHANGELOG.md Co-authored-by: Sergio Gasquez Arcos --------- Co-authored-by: Sergio Gasquez Arcos --- esp-hal/CHANGELOG.md | 4 + esp-hal/Cargo.toml | 1 + esp-hal/MIGRATING-0.21.md | 14 + esp-hal/src/uart.rs | 1190 +++++++++++++------------ examples/src/bin/embassy_serial.rs | 7 +- examples/src/bin/lp_core_uart.rs | 2 +- examples/src/bin/serial_interrupts.rs | 6 +- hil-test/tests/uart.rs | 12 +- 8 files changed, 647 insertions(+), 589 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 3c8eede38..46cc9ce38 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `gpio::{GpioPin, AnyPin, Flex, Output, OutputOpenDrain}::split()` to obtain peripheral interconnect signals. (#2418) - `gpio::Input::{split(), into_peripheral_output()}` when used with output pins. (#2418) - `gpio::Output::peripheral_input()` (#2418) +- `{Uart, UartRx, UartTx}::apply_config()` (#2449) +- `{Uart, UartRx, UartTx}` now implement `embassy_embedded_hal::SetConfig` (#2449) ### Changed @@ -46,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow users to create DMA `Preparation`s (#2455) - The `rmt::asynch::RxChannelAsync` and `rmt::asynch::TxChannelAsync` traits have been moved to `rmt` (#2430) - Calling `AnyPin::output_signals` on an input-only pin (ESP32 GPIO 34-39) will now result in a panic. (#2418) +- UART configuration types have been moved to `esp_hal::uart` (#2449) ### Fixed @@ -74,6 +77,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed the pin type parameters from `lcd_cam::cam::{RxEightBits, RxSixteenBits}` (#2388) - Most of the async-specific constructors (`new_async`, `new_async_no_transceiver`) have been removed. (#2430) - The `configure_for_async` DMA functions have been removed (#2430) +- The `Uart::{change_baud, change_stop_bits}` functions have been removed (#2449) ## [0.21.1] diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 71f0a184c..307ec0c91 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -25,6 +25,7 @@ defmt = { version = "0.3.8", optional = true } delegate = "0.12.0" digest = { version = "0.10.7", default-features = false, optional = true } document-features = "0.2.10" +embassy-embedded-hal = "0.2.0" embassy-futures = "0.1.1" embassy-sync = "0.6.0" embassy-usb-driver = { version = "0.1.0", optional = true } diff --git a/esp-hal/MIGRATING-0.21.md b/esp-hal/MIGRATING-0.21.md index 46708dfa2..edb869273 100644 --- a/esp-hal/MIGRATING-0.21.md +++ b/esp-hal/MIGRATING-0.21.md @@ -230,3 +230,17 @@ The previous signal function have been replaced by `split`. This change affects `into_peripheral_output`, `split` (for output pins only) and `peripheral_input` have been added to the GPIO drivers (`Input`, `Output`, `OutputOpenDrain` and `Flex`) instead. + +## Changes to peripheral configuration + +### The `uart::config` module has been removed + +The module's contents have been moved into `uart`. + +```diff +-use esp_hal::uart::config::Config; ++use esp_hal::uart::Config; +``` + +If you work with multiple configurable peripherals, you may want to import the `uart` module and +refer to the `Config` struct as `uart::Config`. diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index a67c4b0e6..f37c16a56 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -55,7 +55,7 @@ //! ### Sending and Receiving Data //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{config::Config, Uart}; +//! # use esp_hal::uart::{Config, Uart}; //! # use esp_hal::gpio::Io; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! # let mut uart1 = Uart::new_with_config( @@ -72,7 +72,7 @@ //! ### Splitting the UART into RX and TX Components //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::uart::{config::Config, Uart}; +//! # use esp_hal::uart::{Config, Uart}; //! # use esp_hal::gpio::Io; //! # let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! # let mut uart1 = Uart::new_with_config( @@ -128,11 +128,11 @@ use core::{marker::PhantomData, sync::atomic::Ordering, task::Poll}; +use embassy_embedded_hal::SetConfig; use embassy_sync::waitqueue::AtomicWaker; use enumset::{EnumSet, EnumSetType}; use portable_atomic::AtomicBool; -use self::config::Config; use crate::{ clock::Clocks, dma::PeripheralMarker, @@ -221,224 +221,220 @@ pub enum ClockSource { RefTick, } +// see +const UART_FULL_THRESH_DEFAULT: u16 = 120; +// see +const UART_TOUT_THRESH_DEFAULT: u8 = 10; + +/// Number of data bits +/// +/// This enum represents the various configurations for the number of data +/// bits used in UART communication. The number of data bits defines the +/// length of each transmitted or received data frame. +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum DataBits { + /// 5 data bits per frame. + DataBits5 = 0, + /// 6 data bits per frame. + DataBits6 = 1, + /// 7 data bits per frame. + DataBits7 = 2, + /// 8 data bits per frame (most common). + DataBits8 = 3, +} + +/// Parity check +/// +/// Parity is a form of error detection in UART communication, used to +/// ensure that the data has not been corrupted during transmission. The +/// parity bit is added to the data bits to make the number of 1-bits +/// either even or odd. +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Parity { + /// No parity bit is used (most common). + ParityNone, + /// Even parity: the parity bit is set to make the total number of + /// 1-bits even. + ParityEven, + /// Odd parity: the parity bit is set to make the total number of 1-bits + /// odd. + ParityOdd, +} + +/// Number of stop bits +/// +/// The stop bit(s) signal the end of a data packet in UART communication. +/// This enum defines the possible configurations for the number of stop +/// bits. +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum StopBits { + /// 1 stop bit. + STOP1 = 1, + /// 1.5 stop bits. + STOP1P5 = 2, + /// 2 stop bits. + STOP2 = 3, +} + /// UART Configuration -pub mod config { +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Config { + /// The baud rate (speed) of the UART communication in bits per second + /// (bps). + pub baudrate: u32, + /// Number of data bits in each frame (5, 6, 7, or 8 bits). + pub data_bits: DataBits, + /// Parity setting (None, Even, or Odd). + pub parity: Parity, + /// Number of stop bits in each frame (1, 1.5, or 2 bits). + pub stop_bits: StopBits, + /// Clock source used by the UART peripheral. + pub clock_source: ClockSource, + /// Threshold level at which the RX FIFO is considered full. + pub rx_fifo_full_threshold: u16, + /// Optional timeout value for RX operations. + pub rx_timeout: Option, +} - // see - const UART_FULL_THRESH_DEFAULT: u16 = 120; - // see - const UART_TOUT_THRESH_DEFAULT: u8 = 10; - - /// Number of data bits - /// - /// This enum represents the various configurations for the number of data - /// bits used in UART communication. The number of data bits defines the - /// length of each transmitted or received data frame. - #[derive(PartialEq, Eq, Copy, Clone, Debug)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum DataBits { - /// 5 data bits per frame. - DataBits5 = 0, - /// 6 data bits per frame. - DataBits6 = 1, - /// 7 data bits per frame. - DataBits7 = 2, - /// 8 data bits per frame (most common). - DataBits8 = 3, +impl Config { + /// Sets the baud rate for the UART configuration. + pub fn baudrate(mut self, baudrate: u32) -> Self { + self.baudrate = baudrate; + self } - /// Parity check - /// - /// Parity is a form of error detection in UART communication, used to - /// ensure that the data has not been corrupted during transmission. The - /// parity bit is added to the data bits to make the number of 1-bits - /// either even or odd. - #[derive(PartialEq, Eq, Copy, Clone, Debug)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum Parity { - /// No parity bit is used (most common). - ParityNone, - /// Even parity: the parity bit is set to make the total number of - /// 1-bits even. - ParityEven, - /// Odd parity: the parity bit is set to make the total number of 1-bits - /// odd. - ParityOdd, + /// Configures the UART to use no parity check. + pub fn parity_none(mut self) -> Self { + self.parity = Parity::ParityNone; + self } - /// Number of stop bits - /// - /// The stop bit(s) signal the end of a data packet in UART communication. - /// This enum defines the possible configurations for the number of stop - /// bits. - #[derive(PartialEq, Eq, Copy, Clone, Debug)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum StopBits { - /// 1 stop bit. - STOP1 = 1, - /// 1.5 stop bits. - STOP1P5 = 2, - /// 2 stop bits. - STOP2 = 3, + /// Configures the UART to use even parity check. + pub fn parity_even(mut self) -> Self { + self.parity = Parity::ParityEven; + self } - /// UART Configuration - #[derive(Debug, Copy, Clone)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub struct Config { - /// The baud rate (speed) of the UART communication in bits per second - /// (bps). - pub baudrate: u32, - /// Number of data bits in each frame (5, 6, 7, or 8 bits). - pub data_bits: DataBits, - /// Parity setting (None, Even, or Odd). - pub parity: Parity, - /// Number of stop bits in each frame (1, 1.5, or 2 bits). - pub stop_bits: StopBits, - /// Clock source used by the UART peripheral. - pub clock_source: super::ClockSource, - /// Threshold level at which the RX FIFO is considered full. - pub rx_fifo_full_threshold: u16, - /// Optional timeout value for RX operations. - pub rx_timeout: Option, + /// Configures the UART to use odd parity check. + pub fn parity_odd(mut self) -> Self { + self.parity = Parity::ParityOdd; + self } - impl Config { - /// Sets the baud rate for the UART configuration. - pub fn baudrate(mut self, baudrate: u32) -> Self { - self.baudrate = baudrate; - self - } - - /// Configures the UART to use no parity check. - pub fn parity_none(mut self) -> Self { - self.parity = Parity::ParityNone; - self - } - - /// Configures the UART to use even parity check. - pub fn parity_even(mut self) -> Self { - self.parity = Parity::ParityEven; - self - } - - /// Configures the UART to use odd parity check. - pub fn parity_odd(mut self) -> Self { - self.parity = Parity::ParityOdd; - self - } - - /// Sets the number of data bits for the UART configuration. - pub fn data_bits(mut self, data_bits: DataBits) -> Self { - self.data_bits = data_bits; - self - } - - /// Sets the number of stop bits for the UART configuration. - pub fn stop_bits(mut self, stop_bits: StopBits) -> Self { - self.stop_bits = stop_bits; - self - } - - /// Sets the clock source for the UART configuration. - pub fn clock_source(mut self, source: super::ClockSource) -> Self { - self.clock_source = source; - self - } - - /// Calculates the total symbol length in bits based on the configured - /// data bits, parity, and stop bits. - pub fn symbol_length(&self) -> u8 { - let mut length: u8 = 1; // start bit - length += match self.data_bits { - DataBits::DataBits5 => 5, - DataBits::DataBits6 => 6, - DataBits::DataBits7 => 7, - DataBits::DataBits8 => 8, - }; - length += match self.parity { - Parity::ParityNone => 0, - _ => 1, - }; - length += match self.stop_bits { - StopBits::STOP1 => 1, - _ => 2, // esp-idf also counts 2 bits for settings 1.5 and 2 stop bits - }; - length - } - - /// Sets the RX FIFO full threshold for the UART configuration. - pub fn rx_fifo_full_threshold(mut self, threshold: u16) -> Self { - self.rx_fifo_full_threshold = threshold; - self - } - - /// Sets the RX timeout for the UART configuration. - pub fn rx_timeout(mut self, timeout: Option) -> Self { - self.rx_timeout = timeout; - self - } + /// Sets the number of data bits for the UART configuration. + pub fn data_bits(mut self, data_bits: DataBits) -> Self { + self.data_bits = data_bits; + self } - impl Default for Config { - fn default() -> Config { - Config { - baudrate: 115_200, - data_bits: DataBits::DataBits8, - parity: Parity::ParityNone, - stop_bits: StopBits::STOP1, - clock_source: { - cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32h2, lp_uart))] { - super::ClockSource::Xtal - } else { - super::ClockSource::Apb - } + /// Sets the number of stop bits for the UART configuration. + pub fn stop_bits(mut self, stop_bits: StopBits) -> Self { + self.stop_bits = stop_bits; + self + } + + /// Sets the clock source for the UART configuration. + pub fn clock_source(mut self, source: ClockSource) -> Self { + self.clock_source = source; + self + } + + /// Calculates the total symbol length in bits based on the configured + /// data bits, parity, and stop bits. + pub fn symbol_length(&self) -> u8 { + let mut length: u8 = 1; // start bit + length += match self.data_bits { + DataBits::DataBits5 => 5, + DataBits::DataBits6 => 6, + DataBits::DataBits7 => 7, + DataBits::DataBits8 => 8, + }; + length += match self.parity { + Parity::ParityNone => 0, + _ => 1, + }; + length += match self.stop_bits { + StopBits::STOP1 => 1, + _ => 2, // esp-idf also counts 2 bits for settings 1.5 and 2 stop bits + }; + length + } + + /// Sets the RX FIFO full threshold for the UART configuration. + pub fn rx_fifo_full_threshold(mut self, threshold: u16) -> Self { + self.rx_fifo_full_threshold = threshold; + self + } + + /// Sets the RX timeout for the UART configuration. + pub fn rx_timeout(mut self, timeout: Option) -> Self { + self.rx_timeout = timeout; + self + } +} + +impl Default for Config { + fn default() -> Config { + Config { + baudrate: 115_200, + data_bits: DataBits::DataBits8, + parity: Parity::ParityNone, + stop_bits: StopBits::STOP1, + clock_source: { + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2, lp_uart))] { + ClockSource::Xtal + } else { + ClockSource::Apb } - }, - rx_fifo_full_threshold: UART_FULL_THRESH_DEFAULT, - rx_timeout: Some(UART_TOUT_THRESH_DEFAULT), - } + } + }, + rx_fifo_full_threshold: UART_FULL_THRESH_DEFAULT, + rx_timeout: Some(UART_TOUT_THRESH_DEFAULT), } } +} - /// Configuration for the AT-CMD detection functionality - pub struct AtCmdConfig { - /// Optional idle time before the AT command detection begins, in clock - /// cycles. - pub pre_idle_count: Option, - /// Optional idle time after the AT command detection ends, in clock - /// cycles. - pub post_idle_count: Option, - /// Optional timeout between characters in the AT command, in clock - /// cycles. - pub gap_timeout: Option, - /// The character that triggers the AT command detection. - pub cmd_char: u8, - /// Optional number of characters to detect as part of the AT command. - pub char_num: Option, - } +/// Configuration for the AT-CMD detection functionality +pub struct AtCmdConfig { + /// Optional idle time before the AT command detection begins, in clock + /// cycles. + pub pre_idle_count: Option, + /// Optional idle time after the AT command detection ends, in clock + /// cycles. + pub post_idle_count: Option, + /// Optional timeout between characters in the AT command, in clock + /// cycles. + pub gap_timeout: Option, + /// The character that triggers the AT command detection. + pub cmd_char: u8, + /// Optional number of characters to detect as part of the AT command. + pub char_num: Option, +} - impl AtCmdConfig { - /// Creates a new `AtCmdConfig` with the specified configuration. - /// - /// This function sets up the AT command detection parameters, including - /// pre- and post-idle times, a gap timeout, the triggering command - /// character, and the number of characters to detect. - pub fn new( - pre_idle_count: Option, - post_idle_count: Option, - gap_timeout: Option, - cmd_char: u8, - char_num: Option, - ) -> AtCmdConfig { - Self { - pre_idle_count, - post_idle_count, - gap_timeout, - cmd_char, - char_num, - } +impl AtCmdConfig { + /// Creates a new `AtCmdConfig` with the specified configuration. + /// + /// This function sets up the AT command detection parameters, including + /// pre- and post-idle times, a gap timeout, the triggering command + /// character, and the number of characters to detect. + pub fn new( + pre_idle_count: Option, + post_idle_count: Option, + gap_timeout: Option, + cmd_char: u8, + char_num: Option, + ) -> AtCmdConfig { + Self { + pre_idle_count, + post_idle_count, + gap_timeout, + cmd_char, + char_num, } } } @@ -484,10 +480,6 @@ where rx: UartRx { uart: unsafe { self.uart.clone_unchecked() }, phantom: PhantomData, - at_cmd_config: None, - rx_timeout_config: None, - #[cfg(not(esp32))] - symbol_len: config.symbol_length(), }, tx: UartTx { uart: self.uart, @@ -516,10 +508,45 @@ pub struct UartTx<'d, M, T = AnyUart> { pub struct UartRx<'d, M, T = AnyUart> { uart: PeripheralRef<'d, T>, phantom: PhantomData, - at_cmd_config: Option, - rx_timeout_config: Option, - #[cfg(not(esp32))] - symbol_len: u8, +} + +impl SetConfig for Uart<'_, M, T> +where + T: Instance, + M: Mode, +{ + type Config = Config; + type ConfigError = Error; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config) + } +} + +impl SetConfig for UartRx<'_, M, T> +where + T: Instance, + M: Mode, +{ + type Config = Config; + type ConfigError = Error; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config) + } +} + +impl SetConfig for UartTx<'_, M, T> +where + T: Instance, + M: Mode, +{ + type Config = Config; + type ConfigError = Error; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config) + } } impl<'d, M, T> UartTx<'d, M, T> @@ -536,6 +563,14 @@ where self } + /// Change the configuration. + /// + /// Note that this also changes the configuration of the RX half. + pub fn apply_config(&mut self, config: &Config) -> Result<(), Error> { + self.uart.info().apply_config(config)?; + Ok(()) + } + /// Writes bytes pub fn write_bytes(&mut self, data: &[u8]) -> Result { let count = data.len(); @@ -736,6 +771,14 @@ where self } + /// Change the configuration. + /// + /// Note that this also changes the configuration of the TX half. + pub fn apply_config(&mut self, config: &Config) -> Result<(), Error> { + self.uart.info().apply_config(config)?; + Ok(()) + } + /// Fill a buffer with received bytes pub fn read_bytes(&mut self, buf: &mut [u8]) -> Result<(), Error> { cfg_if::cfg_if! { @@ -840,98 +883,6 @@ where fifo_cnt } - /// Configures the RX-FIFO threshold - /// - /// # Errors - /// `Err(Error::InvalidArgument)` if provided value exceeds maximum value - /// for SOC : - /// - `esp32` **0x7F** - /// - `esp32c6`, `esp32h2` **0xFF** - /// - `esp32c3`, `esp32c2`, `esp32s2` **0x1FF** - /// - `esp32s3` **0x3FF** - fn set_rx_fifo_full_threshold(&mut self, threshold: u16) -> Result<(), Error> { - #[cfg(esp32)] - const MAX_THRHD: u16 = 0x7F; - #[cfg(any(esp32c6, esp32h2))] - const MAX_THRHD: u16 = 0xFF; - #[cfg(any(esp32c3, esp32c2, esp32s2))] - const MAX_THRHD: u16 = 0x1FF; - #[cfg(esp32s3)] - const MAX_THRHD: u16 = 0x3FF; - - if threshold > MAX_THRHD { - return Err(Error::InvalidArgument); - } - - self.register_block() - .conf1() - .modify(|_, w| unsafe { w.rxfifo_full_thrhd().bits(threshold as _) }); - - Ok(()) - } - - /// Configures the Receive Timeout detection setting - /// - /// # Arguments - /// `timeout` - the number of symbols ("bytes") to wait for before - /// triggering a timeout. Pass None to disable the timeout. - /// - /// # Errors - /// `Err(Error::InvalidArgument)` if the provided value exceeds the maximum - /// value for SOC : - /// - `esp32`: Symbol size is fixed to 8, do not pass a value > **0x7F**. - /// - `esp32c2`, `esp32c3`, `esp32c6`, `esp32h2`, esp32s2`, esp32s3`: The - /// value you pass times the symbol size must be <= **0x3FF** - fn set_rx_timeout(&mut self, timeout: Option) -> Result<(), Error> { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - const MAX_THRHD: u8 = 0x7F; // 7 bits - } else { - const MAX_THRHD: u16 = 0x3FF; // 10 bits - } - } - - let register_block = self.register_block(); - - if let Some(timeout) = timeout { - // the esp32 counts directly in number of symbols (symbol len fixed to 8) - #[cfg(esp32)] - let timeout_reg = timeout; - // all other count in bits, so we need to multiply by the symbol len. - #[cfg(not(esp32))] - let timeout_reg = timeout as u16 * self.symbol_len as u16; - - if timeout_reg > MAX_THRHD { - return Err(Error::InvalidArgument); - } - - cfg_if::cfg_if! { - if #[cfg(esp32)] { - let reg_thrhd = register_block.conf1(); - } else if #[cfg(any(esp32c6, esp32h2))] { - let reg_thrhd = register_block.tout_conf(); - } else { - let reg_thrhd = register_block.mem_conf(); - } - } - reg_thrhd.modify(|_, w| unsafe { w.rx_tout_thrhd().bits(timeout_reg) }); - } - - cfg_if::cfg_if! { - if #[cfg(any(esp32c6, esp32h2))] { - let reg_en = register_block.tout_conf(); - } else { - let reg_en = register_block.conf1(); - } - } - reg_en.modify(|_, w| w.rx_tout_en().bit(timeout.is_some())); - - sync_regs(register_block); - self.rx_timeout_config = timeout; - - Ok(()) - } - /// Disables all RX-related interrupts for this UART instance. /// /// This function clears and disables the `receive FIFO full` interrupt, @@ -1014,10 +965,6 @@ where UartRx { uart: self.uart, phantom: PhantomData, - at_cmd_config: self.at_cmd_config, - rx_timeout_config: self.rx_timeout_config, - #[cfg(not(esp32))] - symbol_len: self.symbol_len, } } } @@ -1039,10 +986,6 @@ where UartRx { uart: self.uart, phantom: PhantomData, - at_cmd_config: self.at_cmd_config, - rx_timeout_config: self.rx_timeout_config, - #[cfg(not(esp32))] - symbol_len: self.symbol_len, } } } @@ -1153,10 +1096,6 @@ where self.tx.uart.info().register_block() } - fn sync_regs(&self) { - sync_regs(self.register_block()); - } - /// Split the UART into a transmitter and receiver /// /// This is particularly useful when having two tasks correlating to @@ -1176,7 +1115,7 @@ where } /// Configures the AT-CMD detection settings - pub fn set_at_cmd(&mut self, config: config::AtCmdConfig) { + pub fn set_at_cmd(&mut self, config: AtCmdConfig) { let register_block = self.register_block(); #[cfg(not(any(esp32, esp32s2)))] @@ -1212,9 +1151,7 @@ where .clk_conf() .modify(|_, w| w.sclk_en().set_bit()); - self.sync_regs(); - - self.rx.at_cmd_config = Some(config); + sync_regs(register_block); } /// Listen for the given interrupts @@ -1252,174 +1189,10 @@ where self.rx.read_byte() } - /// Change the number of stop bits - pub fn change_stop_bits(&mut self, stop_bits: config::StopBits) -> &mut Self { - #[cfg(esp32)] - { - // workaround for hardware issue, when UART stop bit set as 2-bit mode. - if stop_bits == config::StopBits::STOP2 { - self.register_block() - .rs485_conf() - .modify(|_, w| w.dl1_en().bit(stop_bits == config::StopBits::STOP2)); - - self.register_block().conf0().modify(|_, w| { - if stop_bits == config::StopBits::STOP2 { - unsafe { w.stop_bit_num().bits(1) } - } else { - unsafe { w.stop_bit_num().bits(stop_bits as u8) } - } - }); - } - } - - #[cfg(not(esp32))] - self.register_block() - .conf0() - .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); - - self - } - - fn change_data_bits(&mut self, data_bits: config::DataBits) -> &mut Self { - self.register_block() - .conf0() - .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); - - self - } - - fn change_parity(&mut self, parity: config::Parity) -> &mut Self { - self.register_block().conf0().modify(|_, w| match parity { - config::Parity::ParityNone => w.parity_en().clear_bit(), - config::Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), - config::Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), - }); - - self - } - - #[cfg(any(esp32c2, esp32c3, esp32s3))] - fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource) { - let clocks = Clocks::get(); - let clk = match clock_source { - ClockSource::Apb => clocks.apb_clock.to_Hz(), - ClockSource::Xtal => clocks.xtal_clock.to_Hz(), - ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.to_Hz(), - }; - - if clock_source == ClockSource::RcFast { - unsafe { crate::peripherals::RTC_CNTL::steal() } - .clk_conf() - .modify(|_, w| w.dig_clk8m_en().variant(true)); - // esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH); - crate::rom::ets_delay_us(5); - } - - let max_div = 0b1111_1111_1111 - 1; - let clk_div = clk.div_ceil(max_div * baudrate); - self.register_block().clk_conf().write(|w| unsafe { - w.sclk_sel().bits(match clock_source { - ClockSource::Apb => 1, - ClockSource::RcFast => 2, - ClockSource::Xtal => 3, - }); - w.sclk_div_a().bits(0); - w.sclk_div_b().bits(0); - w.sclk_div_num().bits(clk_div as u8 - 1); - w.rx_sclk_en().bit(true); - w.tx_sclk_en().bit(true) - }); - - let divider = (clk << 4) / (baudrate * clk_div); - let divider_integer = (divider >> 4) as u16; - let divider_frag = (divider & 0xf) as u8; - self.register_block() - .clkdiv() - .write(|w| unsafe { w.clkdiv().bits(divider_integer).frag().bits(divider_frag) }); - } - - #[cfg(any(esp32c6, esp32h2))] - fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource) { - let clocks = Clocks::get(); - let clk = match clock_source { - ClockSource::Apb => clocks.apb_clock.to_Hz(), - ClockSource::Xtal => clocks.xtal_clock.to_Hz(), - ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.to_Hz(), - }; - - let max_div = 0b1111_1111_1111 - 1; - let clk_div = clk.div_ceil(max_div * baudrate); - - // UART clocks are configured via PCR - let pcr = unsafe { crate::peripherals::PCR::steal() }; - - if self.is_instance(unsafe { crate::peripherals::UART0::steal() }) { - pcr.uart0_conf() - .modify(|_, w| w.uart0_rst_en().clear_bit().uart0_clk_en().set_bit()); - - pcr.uart0_sclk_conf().modify(|_, w| unsafe { - w.uart0_sclk_div_a().bits(0); - w.uart0_sclk_div_b().bits(0); - w.uart0_sclk_div_num().bits(clk_div as u8 - 1); - w.uart0_sclk_sel().bits(match clock_source { - ClockSource::Apb => 1, - ClockSource::RcFast => 2, - ClockSource::Xtal => 3, - }); - w.uart0_sclk_en().set_bit() - }); - } else { - pcr.uart1_conf() - .modify(|_, w| w.uart1_rst_en().clear_bit().uart1_clk_en().set_bit()); - - pcr.uart1_sclk_conf().modify(|_, w| unsafe { - w.uart1_sclk_div_a().bits(0); - w.uart1_sclk_div_b().bits(0); - w.uart1_sclk_div_num().bits(clk_div as u8 - 1); - w.uart1_sclk_sel().bits(match clock_source { - ClockSource::Apb => 1, - ClockSource::RcFast => 2, - ClockSource::Xtal => 3, - }); - w.uart1_sclk_en().set_bit() - }); - } - - let clk = clk / clk_div; - let divider = clk / baudrate; - let divider = divider as u16; - - self.register_block() - .clkdiv() - .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); - - self.sync_regs(); - } - - #[cfg(any(esp32, esp32s2))] - fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource) { - let clk = match clock_source { - ClockSource::Apb => Clocks::get().apb_clock.to_Hz(), - // ESP32(/-S2) TRM, section 3.2.4.2 (6.2.4.2 for S2) - ClockSource::RefTick => crate::soc::constants::REF_TICK.to_Hz(), - }; - - self.register_block() - .conf0() - .modify(|_, w| w.tick_ref_always_on().bit(clock_source == ClockSource::Apb)); - - let divider = clk / baudrate; - - self.register_block() - .clkdiv() - .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); - } - - /// Modify UART baud rate and reset TX/RX fifo. - pub fn change_baud(&mut self, baudrate: u32, clock_source: ClockSource) { - self.change_baud_internal(baudrate, clock_source); - self.txfifo_reset(); - self.rxfifo_reset(); + /// Change the configuration. + pub fn apply_config(&mut self, config: &Config) -> Result<(), Error> { + self.rx.apply_config(config)?; + Ok(()) } #[inline(always)] @@ -1443,13 +1216,7 @@ where self.rx.disable_rx_interrupts(); self.tx.disable_tx_interrupts(); - self.rx - .set_rx_fifo_full_threshold(config.rx_fifo_full_threshold)?; - self.rx.set_rx_timeout(config.rx_timeout)?; - self.change_baud_internal(config.baudrate, config.clock_source); - self.change_data_bits(config.data_bits); - self.change_parity(config.parity); - self.change_stop_bits(config.stop_bits); + self.rx.uart.info().apply_config(&config)?; // Setting err_wr_mask stops uart from storing data when data is wrong according // to reference manual @@ -1457,9 +1224,6 @@ where .conf0() .modify(|_, w| w.err_wr_mask().set_bit()); - // Reset Tx/Rx FIFOs - self.rxfifo_reset(); - self.txfifo_reset(); crate::rom::ets_delay_us(15); // Make sure we are starting in a "clean state" - previous operations might have @@ -1472,7 +1236,7 @@ where } fn is_instance(&self, other: impl Instance) -> bool { - self.tx.uart.info() == other.info() + self.tx.uart.info().is_instance(other) } #[inline(always)] @@ -1502,26 +1266,6 @@ where PeripheralClockControl::reset(self.tx.uart.peripheral()); rst_core(self.register_block(), false); } - - fn rxfifo_reset(&mut self) { - fn rxfifo_rst(reg_block: &RegisterBlock, enable: bool) { - reg_block.conf0().modify(|_, w| w.rxfifo_rst().bit(enable)); - sync_regs(reg_block); - } - - rxfifo_rst(self.register_block(), true); - rxfifo_rst(self.register_block(), false); - } - - fn txfifo_reset(&mut self) { - fn txfifo_rst(reg_block: &RegisterBlock, enable: bool) { - reg_block.conf0().modify(|_, w| w.txfifo_rst().bit(enable)); - sync_regs(reg_block); - } - - txfifo_rst(self.register_block(), true); - txfifo_rst(self.register_block(), false); - } } impl crate::private::Sealed for Uart<'_, Blocking, T> where T: Instance {} @@ -1826,19 +1570,20 @@ pub(crate) enum RxEvent { /// is dropped it disables the interrupt again. The future returns the event /// that was initially passed, when it resolves. #[must_use = "futures do nothing unless you `.await` or poll them"] -struct UartRxFuture<'d> { +struct UartRxFuture { events: EnumSet, - uart: &'d Info, - state: &'d State, + uart: &'static Info, + state: &'static State, registered: bool, } -impl<'d> UartRxFuture<'d> { - pub fn new(uart: &'d Info, state: &'d State, events: impl Into>) -> Self { +impl UartRxFuture { + fn new(uart: impl Peripheral

, events: impl Into>) -> Self { + crate::into_ref!(uart); Self { events: events.into(), - uart, - state, + uart: uart.info(), + state: uart.state(), registered: false, } } @@ -1882,7 +1627,7 @@ impl<'d> UartRxFuture<'d> { } } -impl core::future::Future for UartRxFuture<'_> { +impl core::future::Future for UartRxFuture { type Output = EnumSet; fn poll( @@ -1903,7 +1648,7 @@ impl core::future::Future for UartRxFuture<'_> { } } -impl Drop for UartRxFuture<'_> { +impl Drop for UartRxFuture { fn drop(&mut self) { // Although the isr disables the interrupt that occurred directly, we need to // disable the other interrupts (= the ones that did not occur), as @@ -1913,18 +1658,19 @@ impl Drop for UartRxFuture<'_> { } #[must_use = "futures do nothing unless you `.await` or poll them"] -struct UartTxFuture<'d> { +struct UartTxFuture { events: EnumSet, - uart: &'d Info, - state: &'d State, + uart: &'static Info, + state: &'static State, registered: bool, } -impl<'d> UartTxFuture<'d> { - pub fn new(uart: &'d Info, state: &'d State, events: impl Into>) -> Self { +impl UartTxFuture { + fn new(uart: impl Peripheral

, events: impl Into>) -> Self { + crate::into_ref!(uart); Self { events: events.into(), - uart, - state, + uart: uart.info(), + state: uart.state(), registered: false, } } @@ -1954,7 +1700,7 @@ impl<'d> UartTxFuture<'d> { } } -impl core::future::Future for UartTxFuture<'_> { +impl core::future::Future for UartTxFuture { type Output = (); fn poll( @@ -1975,7 +1721,7 @@ impl core::future::Future for UartTxFuture<'_> { } } -impl Drop for UartTxFuture<'_> { +impl Drop for UartTxFuture { fn drop(&mut self) { // Although the isr disables the interrupt that occurred directly, we need to // disable the other interrupts (= the ones that did not occur), as @@ -2005,7 +1751,7 @@ where } } -impl<'d, T> UartTx<'d, Async, T> +impl UartTx<'_, Async, T> where T: Instance, { @@ -2034,7 +1780,7 @@ where } offset = next_offset; - UartTxFuture::new(self.uart.info(), self.uart.state(), TxEvent::TxFiFoEmpty).await; + UartTxFuture::new(self.uart.reborrow(), TxEvent::TxFiFoEmpty).await; } Ok(count) @@ -2048,14 +1794,14 @@ where pub async fn flush_async(&mut self) -> Result<(), Error> { let count = self.tx_fifo_count(); if count > 0 { - UartTxFuture::new(self.uart.info(), self.uart.state(), TxEvent::TxDone).await; + UartTxFuture::new(self.uart.reborrow(), TxEvent::TxDone).await; } Ok(()) } } -impl<'d, T> UartRx<'d, Async, T> +impl UartRx<'_, Async, T> where T: Instance, { @@ -2091,15 +1837,23 @@ where | RxEvent::GlitchDetected | RxEvent::ParityError; - if self.at_cmd_config.is_some() { + let register_block = self.uart.info().register_block(); + if register_block.at_cmd_char().read().char_num().bits() > 0 { events |= RxEvent::CmdCharDetected; } - if self.rx_timeout_config.is_some() { + + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2))] { + let reg_en = register_block.tout_conf(); + } else { + let reg_en = register_block.conf1(); + } + }; + if reg_en.read().rx_tout_en().bit_is_set() { events |= RxEvent::FifoTout; } - let events_happened = - UartRxFuture::new(self.uart.info(), self.uart.state(), events).await; + let events_happened = UartRxFuture::new(self.uart.reborrow(), events).await; // always drain the fifo, if an error has occurred the data is lost let read_bytes = self.drain_fifo(buf); // check error events @@ -2213,7 +1967,7 @@ pub mod lp_uart { use crate::{ gpio::lp_io::{LowPowerInput, LowPowerOutput}, peripherals::{LP_CLKRST, LP_UART}, - uart::config::{self, Config}, + uart::{Config, DataBits, Parity, StopBits}, }; /// LP-UART driver /// @@ -2355,23 +2109,23 @@ pub mod lp_uart { self.rxfifo_reset(); } - fn change_parity(&mut self, parity: config::Parity) -> &mut Self { - if parity != config::Parity::ParityNone { + fn change_parity(&mut self, parity: Parity) -> &mut Self { + if parity != Parity::ParityNone { self.uart .conf0() .modify(|_, w| w.parity().bit((parity as u8 & 0x1) != 0)); } self.uart.conf0().modify(|_, w| match parity { - config::Parity::ParityNone => w.parity_en().clear_bit(), - config::Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), - config::Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), + Parity::ParityNone => w.parity_en().clear_bit(), + Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), + Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), }); self } - fn change_data_bits(&mut self, data_bits: config::DataBits) -> &mut Self { + fn change_data_bits(&mut self, data_bits: DataBits) -> &mut Self { self.uart .conf0() .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); @@ -2381,7 +2135,7 @@ pub mod lp_uart { self } - fn change_stop_bits(&mut self, stop_bits: config::StopBits) -> &mut Self { + fn change_stop_bits(&mut self, stop_bits: StopBits) -> &mut Self { self.uart .conf0() .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); @@ -2401,36 +2155,20 @@ pub mod lp_uart { } } -impl Info { - fn set_interrupt_handler(&self, handler: InterruptHandler) { - for core in crate::Cpu::other() { - crate::interrupt::disable(core, self.interrupt); - } - self.enable_listen(EnumSet::all(), false); - self.clear_interrupts(EnumSet::all()); - unsafe { crate::interrupt::bind_interrupt(self.interrupt, handler.handler()) }; - unwrap!(crate::interrupt::enable(self.interrupt, handler.priority())); - } - - fn disable_interrupts(&self) { - crate::interrupt::disable(crate::Cpu::current(), self.interrupt); - } -} - /// UART Peripheral Instance pub trait Instance: Peripheral

+ PeripheralMarker + Into + 'static { /// Returns the peripheral data and state describing this UART instance. - fn parts(&self) -> (&Info, &State); + fn parts(&self) -> (&'static Info, &'static State); /// Returns the peripheral data describing this UART instance. #[inline(always)] - fn info(&self) -> &Info { + fn info(&self) -> &'static Info { self.parts().0 } /// Returns the peripheral state for this UART instance. #[inline(always)] - fn state(&self) -> &State { + fn state(&self) -> &'static State { self.parts().1 } } @@ -2531,6 +2269,310 @@ impl Info { w }); } + + fn set_interrupt_handler(&self, handler: InterruptHandler) { + for core in crate::Cpu::other() { + crate::interrupt::disable(core, self.interrupt); + } + self.enable_listen(EnumSet::all(), false); + self.clear_interrupts(EnumSet::all()); + unsafe { crate::interrupt::bind_interrupt(self.interrupt, handler.handler()) }; + unwrap!(crate::interrupt::enable(self.interrupt, handler.priority())); + } + + fn disable_interrupts(&self) { + crate::interrupt::disable(crate::Cpu::current(), self.interrupt); + } + + fn apply_config(&self, config: &Config) -> Result<(), Error> { + self.set_rx_fifo_full_threshold(config.rx_fifo_full_threshold)?; + self.set_rx_timeout(config.rx_timeout, config.symbol_length())?; + self.change_baud(config.baudrate, config.clock_source); + self.change_data_bits(config.data_bits); + self.change_parity(config.parity); + self.change_stop_bits(config.stop_bits); + + // Reset Tx/Rx FIFOs + self.rxfifo_reset(); + self.txfifo_reset(); + + Ok(()) + } + + /// Configures the RX-FIFO threshold + /// + /// # Errors + /// `Err(Error::InvalidArgument)` if provided value exceeds maximum value + /// for SOC : + /// - `esp32` **0x7F** + /// - `esp32c6`, `esp32h2` **0xFF** + /// - `esp32c3`, `esp32c2`, `esp32s2` **0x1FF** + /// - `esp32s3` **0x3FF** + fn set_rx_fifo_full_threshold(&self, threshold: u16) -> Result<(), Error> { + #[cfg(esp32)] + const MAX_THRHD: u16 = 0x7F; + #[cfg(any(esp32c6, esp32h2))] + const MAX_THRHD: u16 = 0xFF; + #[cfg(any(esp32c3, esp32c2, esp32s2))] + const MAX_THRHD: u16 = 0x1FF; + #[cfg(esp32s3)] + const MAX_THRHD: u16 = 0x3FF; + + if threshold > MAX_THRHD { + return Err(Error::InvalidArgument); + } + + self.register_block() + .conf1() + .modify(|_, w| unsafe { w.rxfifo_full_thrhd().bits(threshold as _) }); + + Ok(()) + } + + /// Configures the Receive Timeout detection setting + /// + /// # Arguments + /// `timeout` - the number of symbols ("bytes") to wait for before + /// triggering a timeout. Pass None to disable the timeout. + /// + /// # Errors + /// `Err(Error::InvalidArgument)` if the provided value exceeds the maximum + /// value for SOC : + /// - `esp32`: Symbol size is fixed to 8, do not pass a value > **0x7F**. + /// - `esp32c2`, `esp32c3`, `esp32c6`, `esp32h2`, esp32s2`, esp32s3`: The + /// value you pass times the symbol size must be <= **0x3FF** + fn set_rx_timeout(&self, timeout: Option, _symbol_len: u8) -> Result<(), Error> { + cfg_if::cfg_if! { + if #[cfg(esp32)] { + const MAX_THRHD: u8 = 0x7F; // 7 bits + } else { + const MAX_THRHD: u16 = 0x3FF; // 10 bits + } + } + + let register_block = self.register_block(); + + if let Some(timeout) = timeout { + // the esp32 counts directly in number of symbols (symbol len fixed to 8) + #[cfg(esp32)] + let timeout_reg = timeout; + // all other count in bits, so we need to multiply by the symbol len. + #[cfg(not(esp32))] + let timeout_reg = timeout as u16 * _symbol_len as u16; + + if timeout_reg > MAX_THRHD { + return Err(Error::InvalidArgument); + } + + cfg_if::cfg_if! { + if #[cfg(esp32)] { + let reg_thrhd = register_block.conf1(); + } else if #[cfg(any(esp32c6, esp32h2))] { + let reg_thrhd = register_block.tout_conf(); + } else { + let reg_thrhd = register_block.mem_conf(); + } + } + reg_thrhd.modify(|_, w| unsafe { w.rx_tout_thrhd().bits(timeout_reg) }); + } + + cfg_if::cfg_if! { + if #[cfg(any(esp32c6, esp32h2))] { + let reg_en = register_block.tout_conf(); + } else { + let reg_en = register_block.conf1(); + } + } + reg_en.modify(|_, w| w.rx_tout_en().bit(timeout.is_some())); + + self.sync_regs(); + + Ok(()) + } + + #[cfg(any(esp32c2, esp32c3, esp32s3))] + fn change_baud(&self, baudrate: u32, clock_source: ClockSource) { + let clocks = Clocks::get(); + let clk = match clock_source { + ClockSource::Apb => clocks.apb_clock.to_Hz(), + ClockSource::Xtal => clocks.xtal_clock.to_Hz(), + ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.to_Hz(), + }; + + if clock_source == ClockSource::RcFast { + unsafe { crate::peripherals::RTC_CNTL::steal() } + .clk_conf() + .modify(|_, w| w.dig_clk8m_en().variant(true)); + // esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH); + crate::rom::ets_delay_us(5); + } + + let max_div = 0b1111_1111_1111 - 1; + let clk_div = clk.div_ceil(max_div * baudrate); + self.register_block().clk_conf().write(|w| unsafe { + w.sclk_sel().bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }); + w.sclk_div_a().bits(0); + w.sclk_div_b().bits(0); + w.sclk_div_num().bits(clk_div as u8 - 1); + w.rx_sclk_en().bit(true); + w.tx_sclk_en().bit(true) + }); + + let divider = (clk << 4) / (baudrate * clk_div); + let divider_integer = (divider >> 4) as u16; + let divider_frag = (divider & 0xf) as u8; + self.register_block() + .clkdiv() + .write(|w| unsafe { w.clkdiv().bits(divider_integer).frag().bits(divider_frag) }); + } + + fn is_instance(&self, other: impl Instance) -> bool { + self == other.info() + } + + fn sync_regs(&self) { + sync_regs(self.register_block()); + } + + #[cfg(any(esp32c6, esp32h2))] + fn change_baud(&self, baudrate: u32, clock_source: ClockSource) { + let clocks = Clocks::get(); + let clk = match clock_source { + ClockSource::Apb => clocks.apb_clock.to_Hz(), + ClockSource::Xtal => clocks.xtal_clock.to_Hz(), + ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.to_Hz(), + }; + + let max_div = 0b1111_1111_1111 - 1; + let clk_div = clk.div_ceil(max_div * baudrate); + + // UART clocks are configured via PCR + let pcr = unsafe { crate::peripherals::PCR::steal() }; + + if self.is_instance(unsafe { crate::peripherals::UART0::steal() }) { + pcr.uart0_conf() + .modify(|_, w| w.uart0_rst_en().clear_bit().uart0_clk_en().set_bit()); + + pcr.uart0_sclk_conf().modify(|_, w| unsafe { + w.uart0_sclk_div_a().bits(0); + w.uart0_sclk_div_b().bits(0); + w.uart0_sclk_div_num().bits(clk_div as u8 - 1); + w.uart0_sclk_sel().bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }); + w.uart0_sclk_en().set_bit() + }); + } else { + pcr.uart1_conf() + .modify(|_, w| w.uart1_rst_en().clear_bit().uart1_clk_en().set_bit()); + + pcr.uart1_sclk_conf().modify(|_, w| unsafe { + w.uart1_sclk_div_a().bits(0); + w.uart1_sclk_div_b().bits(0); + w.uart1_sclk_div_num().bits(clk_div as u8 - 1); + w.uart1_sclk_sel().bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }); + w.uart1_sclk_en().set_bit() + }); + } + + let clk = clk / clk_div; + let divider = clk / baudrate; + let divider = divider as u16; + + self.register_block() + .clkdiv() + .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); + + self.sync_regs(); + } + + #[cfg(any(esp32, esp32s2))] + fn change_baud(&self, baudrate: u32, clock_source: ClockSource) { + let clk = match clock_source { + ClockSource::Apb => Clocks::get().apb_clock.to_Hz(), + // ESP32(/-S2) TRM, section 3.2.4.2 (6.2.4.2 for S2) + ClockSource::RefTick => crate::soc::constants::REF_TICK.to_Hz(), + }; + + self.register_block() + .conf0() + .modify(|_, w| w.tick_ref_always_on().bit(clock_source == ClockSource::Apb)); + + let divider = clk / baudrate; + + self.register_block() + .clkdiv() + .write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); + } + + fn change_data_bits(&self, data_bits: DataBits) { + self.register_block() + .conf0() + .modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) }); + } + + fn change_parity(&self, parity: Parity) { + self.register_block().conf0().modify(|_, w| match parity { + Parity::ParityNone => w.parity_en().clear_bit(), + Parity::ParityEven => w.parity_en().set_bit().parity().clear_bit(), + Parity::ParityOdd => w.parity_en().set_bit().parity().set_bit(), + }); + } + + fn change_stop_bits(&self, stop_bits: StopBits) { + #[cfg(esp32)] + { + // workaround for hardware issue, when UART stop bit set as 2-bit mode. + if stop_bits == StopBits::STOP2 { + self.register_block() + .rs485_conf() + .modify(|_, w| w.dl1_en().bit(stop_bits == StopBits::STOP2)); + + self.register_block().conf0().modify(|_, w| { + if stop_bits == StopBits::STOP2 { + unsafe { w.stop_bit_num().bits(1) } + } else { + unsafe { w.stop_bit_num().bits(stop_bits as u8) } + } + }); + } + } + + #[cfg(not(esp32))] + self.register_block() + .conf0() + .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8) }); + } + + fn rxfifo_reset(&self) { + fn rxfifo_rst(reg_block: &RegisterBlock, enable: bool) { + reg_block.conf0().modify(|_, w| w.rxfifo_rst().bit(enable)); + sync_regs(reg_block); + } + + rxfifo_rst(self.register_block(), true); + rxfifo_rst(self.register_block(), false); + } + + fn txfifo_reset(&self) { + fn txfifo_rst(reg_block: &RegisterBlock, enable: bool) { + reg_block.conf0().modify(|_, w| w.txfifo_rst().bit(enable)); + sync_regs(reg_block); + } + + txfifo_rst(self.register_block(), true); + txfifo_rst(self.register_block(), false); + } } impl PartialEq for Info { @@ -2544,7 +2586,7 @@ unsafe impl Sync for Info {} macro_rules! impl_instance { ($inst:ident, $txd:ident, $rxd:ident, $cts:ident, $rts:ident) => { impl Instance for crate::peripherals::$inst { - fn parts(&self) -> (&Info, &State) { + fn parts(&self) -> (&'static Info, &'static State) { #[crate::macros::handler] pub(super) fn irq_handler() { intr_handler(&PERIPHERAL, &STATE); @@ -2591,7 +2633,7 @@ crate::any_peripheral! { impl Instance for AnyUart { #[inline] - fn parts(&self) -> (&Info, &State) { + fn parts(&self) -> (&'static Info, &'static State) { match &self.0 { #[cfg(uart0)] AnyUartInner::Uart0(uart) => uart.parts(), diff --git a/examples/src/bin/embassy_serial.rs b/examples/src/bin/embassy_serial.rs index 70728701a..40d25ef0c 100644 --- a/examples/src/bin/embassy_serial.rs +++ b/examples/src/bin/embassy_serial.rs @@ -15,12 +15,7 @@ use esp_backtrace as _; use esp_hal::{ gpio::Io, timer::timg::TimerGroup, - uart::{ - config::{AtCmdConfig, Config}, - Uart, - UartRx, - UartTx, - }, + uart::{AtCmdConfig, Config, Uart, UartRx, UartTx}, Async, }; use static_cell::StaticCell; diff --git a/examples/src/bin/lp_core_uart.rs b/examples/src/bin/lp_core_uart.rs index 4ebe0ff15..9ecb0de75 100644 --- a/examples/src/bin/lp_core_uart.rs +++ b/examples/src/bin/lp_core_uart.rs @@ -22,7 +22,7 @@ use esp_hal::{ }, lp_core::{LpCore, LpCoreWakeupSource}, prelude::*, - uart::{config::Config, lp_uart::LpUart, Uart}, + uart::{lp_uart::LpUart, Config, Uart}, }; use esp_println::println; diff --git a/examples/src/bin/serial_interrupts.rs b/examples/src/bin/serial_interrupts.rs index 1ccfb60ed..daabb9ff2 100644 --- a/examples/src/bin/serial_interrupts.rs +++ b/examples/src/bin/serial_interrupts.rs @@ -15,11 +15,7 @@ use esp_hal::{ delay::Delay, gpio::Io, prelude::*, - uart::{ - config::{AtCmdConfig, Config}, - Uart, - UartInterrupt, - }, + uart::{AtCmdConfig, Config, Uart, UartInterrupt}, Blocking, }; diff --git a/hil-test/tests/uart.rs b/hil-test/tests/uart.rs index 5b1a74cb8..00e8be5f7 100644 --- a/hil-test/tests/uart.rs +++ b/hil-test/tests/uart.rs @@ -9,7 +9,7 @@ use embedded_hal_02::serial::{Read, Write}; use esp_hal::{ gpio::Io, prelude::*, - uart::{ClockSource, Uart}, + uart::{self, ClockSource, Uart}, Blocking, }; use hil_test as _; @@ -91,8 +91,14 @@ mod tests { ]; let mut byte_to_write = 0xA5; - for (baud, clock_source) in &configs { - ctx.uart.change_baud(*baud, *clock_source); + for (baudrate, clock_source) in configs { + ctx.uart + .apply_config(&uart::Config { + baudrate, + clock_source, + ..Default::default() + }) + .unwrap(); ctx.uart.write(byte_to_write).ok(); let read = block!(ctx.uart.read()); assert_eq!(read, Ok(byte_to_write));