From 6f91367d6c4146926c0efd29c164a7f06b6bbc3a Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov <62840029+playfulFence@users.noreply.github.com> Date: Wed, 17 Apr 2024 17:27:47 +0200 Subject: [PATCH] Add mechanism to configure UART source clock (#1416) * Creating mechanism for setting UART source clock * Format + examples updating * Changelog entry * Smaller fixes (reviews) * Move RC_FAST_CLK constant to soc * Fix REF_TICK value * Add doc comments update doc comments * fmt --- esp-hal/CHANGELOG.md | 1 + esp-hal/src/soc/esp32/mod.rs | 2 + esp-hal/src/soc/esp32c2/mod.rs | 2 + esp-hal/src/soc/esp32c3/mod.rs | 2 + esp-hal/src/soc/esp32c6/mod.rs | 2 + esp-hal/src/soc/esp32h2/mod.rs | 2 + esp-hal/src/soc/esp32s2/mod.rs | 2 + esp-hal/src/soc/esp32s3/mod.rs | 2 + esp-hal/src/uart.rs | 124 ++++++++++++++++++++-------- examples/src/bin/advanced_serial.rs | 21 ++--- examples/src/bin/lp_core_uart.rs | 15 ++-- hil-test/tests/uart.rs | 14 +--- hil-test/tests/uart_async.rs | 10 +-- 13 files changed, 122 insertions(+), 77 deletions(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index dd87b088f82..a66ce451293 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Inherent implementions of GPIO pin `set_low`, `is_low`, etc. - Warn users when attempting to build using the `dev` profile (#1420) - Async uart now reports interrupt errors(overflow, glitch, frame error, parity) back to user of read/write. uart clock decimal part configured for c2,c3,s3 (#1168, #1445) +- Add mechanism to configure UART source clock (#1416) ### Fixed diff --git a/esp-hal/src/soc/esp32/mod.rs b/esp-hal/src/soc/esp32/mod.rs index 4ff316f0fbe..19614314812 100644 --- a/esp-hal/src/soc/esp32/mod.rs +++ b/esp-hal/src/soc/esp32/mod.rs @@ -28,6 +28,8 @@ pub(crate) mod constants { pub const SOC_DRAM_LOW: u32 = 0x3FFA_E000; pub const SOC_DRAM_HIGH: u32 = 0x4000_0000; + + pub const REF_TICK: fugit::HertzU32 = fugit::HertzU32::MHz(1); } /// Function initializes ESP32 specific memories (RTC slow and fast) and diff --git a/esp-hal/src/soc/esp32c2/mod.rs b/esp-hal/src/soc/esp32c2/mod.rs index 42859cbf9d9..bba777bf79c 100644 --- a/esp-hal/src/soc/esp32c2/mod.rs +++ b/esp-hal/src/soc/esp32c2/mod.rs @@ -22,6 +22,8 @@ pub(crate) mod registers { pub(crate) mod constants { pub const SOC_DRAM_LOW: u32 = 0x3FCA_0000; pub const SOC_DRAM_HIGH: u32 = 0x3FCE_0000; + + pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500); } #[export_name = "__post_init"] diff --git a/esp-hal/src/soc/esp32c3/mod.rs b/esp-hal/src/soc/esp32c3/mod.rs index 075a647bd9f..2df42f9a233 100644 --- a/esp-hal/src/soc/esp32c3/mod.rs +++ b/esp-hal/src/soc/esp32c3/mod.rs @@ -34,6 +34,8 @@ pub(crate) mod constants { pub const SOC_DRAM_LOW: u32 = 0x3FC8_0000; pub const SOC_DRAM_HIGH: u32 = 0x3FCE_0000; + + pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500); } #[export_name = "__post_init"] diff --git a/esp-hal/src/soc/esp32c6/mod.rs b/esp-hal/src/soc/esp32c6/mod.rs index 649c915015e..5342226bfbc 100644 --- a/esp-hal/src/soc/esp32c6/mod.rs +++ b/esp-hal/src/soc/esp32c6/mod.rs @@ -40,6 +40,8 @@ pub(crate) mod constants { pub const SOC_DRAM_LOW: u32 = 0x4080_0000; pub const SOC_DRAM_HIGH: u32 = 0x4088_0000; + + pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500); } #[export_name = "__post_init"] diff --git a/esp-hal/src/soc/esp32h2/mod.rs b/esp-hal/src/soc/esp32h2/mod.rs index a6cd8adb0f2..352c9ffa85a 100644 --- a/esp-hal/src/soc/esp32h2/mod.rs +++ b/esp-hal/src/soc/esp32h2/mod.rs @@ -39,6 +39,8 @@ pub(crate) mod constants { pub const SOC_DRAM_LOW: u32 = 0x4080_0000; pub const SOC_DRAM_HIGH: u32 = 0x4085_0000; + + pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500); } #[export_name = "__post_init"] diff --git a/esp-hal/src/soc/esp32s2/mod.rs b/esp-hal/src/soc/esp32s2/mod.rs index c1fb8632244..0ac654e6d57 100644 --- a/esp-hal/src/soc/esp32s2/mod.rs +++ b/esp-hal/src/soc/esp32s2/mod.rs @@ -32,6 +32,8 @@ pub(crate) mod constants { pub const SOC_DRAM_LOW: u32 = 0x3FFB_0000; pub const SOC_DRAM_HIGH: u32 = 0x4000_0000; + + pub const REF_TICK: fugit::HertzU32 = fugit::HertzU32::MHz(1); } /// Function initializes ESP32 specific memories (RTC slow and fast) and diff --git a/esp-hal/src/soc/esp32s3/mod.rs b/esp-hal/src/soc/esp32s3/mod.rs index adbf1569049..e713a4a5343 100644 --- a/esp-hal/src/soc/esp32s3/mod.rs +++ b/esp-hal/src/soc/esp32s3/mod.rs @@ -35,6 +35,8 @@ pub(crate) mod constants { pub const SOC_DRAM_LOW: u32 = 0x3FC8_8000; pub const SOC_DRAM_HIGH: u32 = 0x3FD0_0000; + + pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500); } #[doc(hidden)] diff --git a/esp-hal/src/uart.rs b/esp-hal/src/uart.rs index 24a64c5f827..7c24784c8a7 100644 --- a/esp-hal/src/uart.rs +++ b/esp-hal/src/uart.rs @@ -19,20 +19,14 @@ //! specified. //! //! ```no_run -//! let config = Config { -//! baudrate: 115_200, -//! data_bits: DataBits::DataBits8, -//! parity: Parity::ParityNone, -//! stop_bits: StopBits::STOP1, -//! }; -//! //! let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); //! let pins = TxRxPins::new_tx_rx( //! io.pins.gpio1.into_push_pull_output(), //! io.pins.gpio2.into_floating_input(), //! ); //! -//! let mut uart1 = Uart::new_with_config(peripherals.UART1, config, Some(pins), &clocks); +//! let mut uart1 = +//! Uart::new_with_config(peripherals.UART1, Config::default(), Some(pins), &clocks); //! ``` //! //! ## Usage @@ -92,6 +86,11 @@ use crate::{ const CONSOLE_UART_NUM: usize = 0; const UART_FIFO_SIZE: u16 = 128; +#[cfg(not(any(esp32, esp32s2)))] +use crate::soc::constants::RC_FAST_CLK; +#[cfg(any(esp32, esp32s2))] +use crate::soc::constants::REF_TICK; + /// UART Error #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -123,6 +122,26 @@ impl embedded_io::Error for Error { } } +// (outside of `config` module in order not to "use" it an extra time) +/// UART clock source +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClockSource { + /// APB_CLK clock source (default for UART on all the chips except of + /// esp32c6 and esp32h2) + Apb, + #[cfg(not(any(esp32, esp32s2)))] + /// RC_FAST_CLK clock source (17.5 MHz) + RcFast, + #[cfg(not(any(esp32, esp32s2)))] + /// XTAL_CLK clock source (default for UART on esp32c6 and esp32h2 and + /// LP_UART) + Xtal, + #[cfg(any(esp32, esp32s2))] + /// REF_TICK clock source (derived from XTAL or RC_FAST, 1MHz) + RefTick, +} + /// UART Configuration pub mod config { /// Number of data bits @@ -164,6 +183,7 @@ pub mod config { pub data_bits: DataBits, pub parity: Parity, pub stop_bits: StopBits, + pub clock_source: super::ClockSource, } impl Config { @@ -197,6 +217,11 @@ pub mod config { self } + pub fn clock_source(mut self, source: super::ClockSource) -> Self { + self.clock_source = source; + self + } + pub fn symbol_length(&self) -> u8 { let mut length: u8 = 1; // start bit length += match self.data_bits { @@ -224,6 +249,10 @@ pub mod config { data_bits: DataBits::DataBits8, parity: Parity::ParityNone, stop_bits: StopBits::STOP1, + #[cfg(any(esp32c6, esp32h2, lp_uart))] + clock_source: super::ClockSource::Xtal, + #[cfg(not(any(esp32c6, esp32h2, lp_uart)))] + clock_source: super::ClockSource::Apb, } } } @@ -538,7 +567,7 @@ where symbol_len: config.symbol_length(), }; - serial.change_baud_internal(config.baudrate, clocks); + serial.change_baud_internal(config.baudrate, config.clock_source, clocks); serial.change_data_bits(config.data_bits); serial.change_parity(config.parity); serial.change_stop_bits(config.stop_bits); @@ -869,15 +898,22 @@ where } #[cfg(any(esp32c2, esp32c3, esp32s3))] - fn change_baud_internal(&self, baudrate: u32, clocks: &Clocks) { - // we force the clock source to be APB and don't use the decimal part of the - // divider - let clk = clocks.apb_clock.to_Hz(); - let max_div = 0b1111_1111_1111; // 12 bit clkdiv + fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) { + let clk = match clock_source { + ClockSource::Apb => clocks.apb_clock.to_Hz(), + ClockSource::Xtal => clocks.xtal_clock.to_Hz(), + ClockSource::RcFast => RC_FAST_CLK.to_Hz(), + }; + + let max_div = 0b1111_1111_1111 - 1; let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); T::register_block().clk_conf().write(|w| unsafe { w.sclk_sel() - .bits(1) // APB + .bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }) .sclk_div_a() .bits(0) .sclk_div_b() @@ -899,10 +935,13 @@ where } #[cfg(any(esp32c6, esp32h2))] - fn change_baud_internal(&self, baudrate: u32, clocks: &Clocks) { - // we force the clock source to be XTAL and don't use the decimal part of - // the divider - let clk = clocks.xtal_clock.to_Hz(); + fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) { + let clk = match clock_source { + ClockSource::Apb => clocks.apb_clock.to_Hz(), + ClockSource::Xtal => clocks.xtal_clock.to_Hz(), + ClockSource::RcFast => RC_FAST_CLK.to_Hz(), + }; + let max_div = 0b1111_1111_1111 - 1; let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); @@ -922,7 +961,11 @@ where .uart0_sclk_div_num() .bits(clk_div as u8 - 1) .uart0_sclk_sel() - .bits(0x3) // TODO: this probably shouldn't be hard-coded + .bits(match clock_source { + ClockSource::Apb => 1, + ClockSource::RcFast => 2, + ClockSource::Xtal => 3, + }) .uart0_sclk_en() .set_bit() }); @@ -959,14 +1002,20 @@ where } #[cfg(any(esp32, esp32s2))] - fn change_baud_internal(&self, baudrate: u32, clocks: &Clocks) { - // we force the clock source to be APB and don't use the decimal part of the - // divider - let clk = clocks.apb_clock.to_Hz(); + fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) { + let clk = match clock_source { + ClockSource::Apb => clocks.apb_clock.to_Hz(), + ClockSource::RefTick => REF_TICK.to_Hz(), /* ESP32(/-S2) TRM, section 3.2.4.2 + * (6.2.4.2 for S2) */ + }; + + T::register_block().conf0().modify(|_, w| { + w.tick_ref_always_on().bit(match clock_source { + ClockSource::Apb => true, + ClockSource::RefTick => false, + }) + }); - T::register_block() - .conf0() - .modify(|_, w| w.tick_ref_always_on().bit(true)); let divider = clk / baudrate; T::register_block() @@ -992,8 +1041,8 @@ where } /// Modify UART baud rate and reset TX/RX fifo. - pub fn change_baud(&mut self, baudrate: u32, clocks: &Clocks) { - self.change_baud_internal(baudrate, clocks); + pub fn change_baud(&mut self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) { + self.change_baud_internal(baudrate, clock_source, clocks); self.txfifo_reset(); self.rxfifo_reset(); } @@ -2130,7 +2179,7 @@ pub mod lp_uart { // Override protocol parameters from the configuration // uart_hal_set_baudrate(&hal, cfg->uart_proto_cfg.baud_rate, sclk_freq); - me.change_baud_internal(config.baudrate); + me.change_baud_internal(config.baudrate, config.clock_source); // uart_hal_set_parity(&hal, cfg->uart_proto_cfg.parity); me.change_parity(config.parity); // uart_hal_set_data_bit_num(&hal, cfg->uart_proto_cfg.data_bits); @@ -2172,9 +2221,8 @@ pub mod lp_uart { } } - fn change_baud_internal(&mut self, baudrate: u32) { - // we force the clock source to be XTAL and don't use the decimal part of - // the divider + fn change_baud_internal(&mut self, baudrate: u32, clock_source: super::ClockSource) { + // TODO: Currently it's not possible to use XtalD2Clk let clk = 16_000_000; let max_div = 0b1111_1111_1111 - 1; let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); @@ -2187,7 +2235,11 @@ pub mod lp_uart { .sclk_div_num() .bits(clk_div as u8 - 1) .sclk_sel() - .bits(0x3) // TODO: this probably shouldn't be hard-coded + .bits(match clock_source { + super::ClockSource::Xtal => 3, + super::ClockSource::RcFast => 2, + super::ClockSource::Apb => panic!("Wrong clock source for LP_UART"), + }) .sclk_en() .set_bit() }); @@ -2204,8 +2256,8 @@ pub mod lp_uart { } /// Modify UART baud rate and reset TX/RX fifo. - pub fn change_baud(&mut self, baudrate: u32) { - self.change_baud_internal(baudrate); + pub fn change_baud(&mut self, baudrate: u32, clock_source: super::ClockSource) { + self.change_baud_internal(baudrate, clock_source); self.txfifo_reset(); self.rxfifo_reset(); } diff --git a/examples/src/bin/advanced_serial.rs b/examples/src/bin/advanced_serial.rs index 7570cfa53aa..564086458fb 100644 --- a/examples/src/bin/advanced_serial.rs +++ b/examples/src/bin/advanced_serial.rs @@ -19,11 +19,7 @@ use esp_hal::{ gpio::IO, peripherals::Peripherals, prelude::*, - uart::{ - config::{Config, DataBits, Parity, StopBits}, - TxRxPins, - Uart, - }, + uart::{config::Config, TxRxPins, Uart}, }; use esp_println::println; use nb::block; @@ -34,20 +30,19 @@ fn main() -> ! { let system = peripherals.SYSTEM.split(); let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - let config = Config { - baudrate: 115200, - data_bits: DataBits::DataBits8, - parity: Parity::ParityNone, - stop_bits: StopBits::STOP1, - }; - let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); let pins = TxRxPins::new_tx_rx( io.pins.gpio4.into_push_pull_output(), io.pins.gpio5.into_floating_input(), ); - let mut serial1 = Uart::new_with_config(peripherals.UART1, config, Some(pins), &clocks, None); + let mut serial1 = Uart::new_with_config( + peripherals.UART1, + Config::default(), + Some(pins), + &clocks, + None, + ); let delay = Delay::new(&clocks); diff --git a/examples/src/bin/lp_core_uart.rs b/examples/src/bin/lp_core_uart.rs index d602c866e99..aaa51cdc34b 100644 --- a/examples/src/bin/lp_core_uart.rs +++ b/examples/src/bin/lp_core_uart.rs @@ -36,19 +36,18 @@ fn main() -> ! { // Set up (HP) UART1: - let config = Config { - baudrate: 115_200, - data_bits: DataBits::DataBits8, - parity: Parity::ParityNone, - stop_bits: StopBits::STOP1, - }; - let pins = TxRxPins::new_tx_rx( io.pins.gpio6.into_push_pull_output(), io.pins.gpio7.into_floating_input(), ); - let mut uart1 = Uart::new_with_config(peripherals.UART1, config, Some(pins), &clocks, None); + let mut uart1 = Uart::new_with_config( + peripherals.UART1, + Config::default(), + Some(pins), + &clocks, + None, + ); // Set up (LP) UART: let lp_tx = io.pins.gpio5.into_low_power().into_push_pull_output(); diff --git a/hil-test/tests/uart.rs b/hil-test/tests/uart.rs index 476a603eaec..c442acda81a 100644 --- a/hil-test/tests/uart.rs +++ b/hil-test/tests/uart.rs @@ -17,11 +17,7 @@ use esp_hal::{ gpio::IO, peripherals::{Peripherals, UART0}, prelude::*, - uart::{ - config::{Config, DataBits, Parity, StopBits}, - TxRxPins, - Uart, - }, + uart::{config::Config, TxRxPins, Uart}, Blocking, }; use nb::block; @@ -40,14 +36,8 @@ impl Context { io.pins.gpio2.into_push_pull_output(), io.pins.gpio4.into_floating_input(), ); - let config = Config { - baudrate: 115200, - data_bits: DataBits::DataBits8, - parity: Parity::ParityNone, - stop_bits: StopBits::STOP1, - }; - let uart = Uart::new_with_config(peripherals.UART0, config, Some(pins), &clocks, None); + let uart = Uart::new_with_config(peripherals.UART0, Config::default(), Some(pins), &clocks, None); Context { uart } } diff --git a/hil-test/tests/uart_async.rs b/hil-test/tests/uart_async.rs index 76ec218b729..15496b7e8e1 100644 --- a/hil-test/tests/uart_async.rs +++ b/hil-test/tests/uart_async.rs @@ -17,7 +17,7 @@ use esp_hal::{ peripherals::{Peripherals, UART0}, prelude::*, uart::{ - config::{Config, DataBits, Parity, StopBits}, + config::Config, TxRxPins, Uart, UartRx, @@ -41,14 +41,8 @@ impl Context { io.pins.gpio2.into_push_pull_output(), io.pins.gpio4.into_floating_input(), ); - let config = Config { - baudrate: 115200, - data_bits: DataBits::DataBits8, - parity: Parity::ParityNone, - stop_bits: StopBits::STOP1, - }; - let uart = Uart::new_async_with_config(peripherals.UART0, config, Some(pins), &clocks); + let uart = Uart::new_async_with_config(peripherals.UART0, Config::default(), Some(pins), &clocks); let (tx, rx) = uart.split(); Context { rx, tx }