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
This commit is contained in:
Kirill Mikhailov 2024-04-17 17:27:47 +02:00 committed by GitHub
parent 111dcda103
commit 6f91367d6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 122 additions and 77 deletions

View File

@ -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. - Inherent implementions of GPIO pin `set_low`, `is_low`, etc.
- Warn users when attempting to build using the `dev` profile (#1420) - 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) - 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 ### Fixed

View File

@ -28,6 +28,8 @@ pub(crate) mod constants {
pub const SOC_DRAM_LOW: u32 = 0x3FFA_E000; pub const SOC_DRAM_LOW: u32 = 0x3FFA_E000;
pub const SOC_DRAM_HIGH: u32 = 0x4000_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 /// Function initializes ESP32 specific memories (RTC slow and fast) and

View File

@ -22,6 +22,8 @@ pub(crate) mod registers {
pub(crate) mod constants { pub(crate) mod constants {
pub const SOC_DRAM_LOW: u32 = 0x3FCA_0000; pub const SOC_DRAM_LOW: u32 = 0x3FCA_0000;
pub const SOC_DRAM_HIGH: u32 = 0x3FCE_0000; pub const SOC_DRAM_HIGH: u32 = 0x3FCE_0000;
pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500);
} }
#[export_name = "__post_init"] #[export_name = "__post_init"]

View File

@ -34,6 +34,8 @@ pub(crate) mod constants {
pub const SOC_DRAM_LOW: u32 = 0x3FC8_0000; pub const SOC_DRAM_LOW: u32 = 0x3FC8_0000;
pub const SOC_DRAM_HIGH: u32 = 0x3FCE_0000; pub const SOC_DRAM_HIGH: u32 = 0x3FCE_0000;
pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500);
} }
#[export_name = "__post_init"] #[export_name = "__post_init"]

View File

@ -40,6 +40,8 @@ pub(crate) mod constants {
pub const SOC_DRAM_LOW: u32 = 0x4080_0000; pub const SOC_DRAM_LOW: u32 = 0x4080_0000;
pub const SOC_DRAM_HIGH: u32 = 0x4088_0000; pub const SOC_DRAM_HIGH: u32 = 0x4088_0000;
pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500);
} }
#[export_name = "__post_init"] #[export_name = "__post_init"]

View File

@ -39,6 +39,8 @@ pub(crate) mod constants {
pub const SOC_DRAM_LOW: u32 = 0x4080_0000; pub const SOC_DRAM_LOW: u32 = 0x4080_0000;
pub const SOC_DRAM_HIGH: u32 = 0x4085_0000; pub const SOC_DRAM_HIGH: u32 = 0x4085_0000;
pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500);
} }
#[export_name = "__post_init"] #[export_name = "__post_init"]

View File

@ -32,6 +32,8 @@ pub(crate) mod constants {
pub const SOC_DRAM_LOW: u32 = 0x3FFB_0000; pub const SOC_DRAM_LOW: u32 = 0x3FFB_0000;
pub const SOC_DRAM_HIGH: u32 = 0x4000_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 /// Function initializes ESP32 specific memories (RTC slow and fast) and

View File

@ -35,6 +35,8 @@ pub(crate) mod constants {
pub const SOC_DRAM_LOW: u32 = 0x3FC8_8000; pub const SOC_DRAM_LOW: u32 = 0x3FC8_8000;
pub const SOC_DRAM_HIGH: u32 = 0x3FD0_0000; pub const SOC_DRAM_HIGH: u32 = 0x3FD0_0000;
pub const RC_FAST_CLK: fugit::HertzU32 = fugit::HertzU32::kHz(17500);
} }
#[doc(hidden)] #[doc(hidden)]

View File

@ -19,20 +19,14 @@
//! specified. //! specified.
//! //!
//! ```no_run //! ```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 io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
//! let pins = TxRxPins::new_tx_rx( //! let pins = TxRxPins::new_tx_rx(
//! io.pins.gpio1.into_push_pull_output(), //! io.pins.gpio1.into_push_pull_output(),
//! io.pins.gpio2.into_floating_input(), //! 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 //! ## Usage
@ -92,6 +86,11 @@ use crate::{
const CONSOLE_UART_NUM: usize = 0; const CONSOLE_UART_NUM: usize = 0;
const UART_FIFO_SIZE: u16 = 128; 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 /// UART Error
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[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 /// UART Configuration
pub mod config { pub mod config {
/// Number of data bits /// Number of data bits
@ -164,6 +183,7 @@ pub mod config {
pub data_bits: DataBits, pub data_bits: DataBits,
pub parity: Parity, pub parity: Parity,
pub stop_bits: StopBits, pub stop_bits: StopBits,
pub clock_source: super::ClockSource,
} }
impl Config { impl Config {
@ -197,6 +217,11 @@ pub mod config {
self self
} }
pub fn clock_source(mut self, source: super::ClockSource) -> Self {
self.clock_source = source;
self
}
pub fn symbol_length(&self) -> u8 { pub fn symbol_length(&self) -> u8 {
let mut length: u8 = 1; // start bit let mut length: u8 = 1; // start bit
length += match self.data_bits { length += match self.data_bits {
@ -224,6 +249,10 @@ pub mod config {
data_bits: DataBits::DataBits8, data_bits: DataBits::DataBits8,
parity: Parity::ParityNone, parity: Parity::ParityNone,
stop_bits: StopBits::STOP1, 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(), 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_data_bits(config.data_bits);
serial.change_parity(config.parity); serial.change_parity(config.parity);
serial.change_stop_bits(config.stop_bits); serial.change_stop_bits(config.stop_bits);
@ -869,15 +898,22 @@ where
} }
#[cfg(any(esp32c2, esp32c3, esp32s3))] #[cfg(any(esp32c2, esp32c3, esp32s3))]
fn change_baud_internal(&self, baudrate: u32, clocks: &Clocks) { fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) {
// we force the clock source to be APB and don't use the decimal part of the let clk = match clock_source {
// divider ClockSource::Apb => clocks.apb_clock.to_Hz(),
let clk = clocks.apb_clock.to_Hz(); ClockSource::Xtal => clocks.xtal_clock.to_Hz(),
let max_div = 0b1111_1111_1111; // 12 bit clkdiv 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); let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate);
T::register_block().clk_conf().write(|w| unsafe { T::register_block().clk_conf().write(|w| unsafe {
w.sclk_sel() w.sclk_sel()
.bits(1) // APB .bits(match clock_source {
ClockSource::Apb => 1,
ClockSource::RcFast => 2,
ClockSource::Xtal => 3,
})
.sclk_div_a() .sclk_div_a()
.bits(0) .bits(0)
.sclk_div_b() .sclk_div_b()
@ -899,10 +935,13 @@ where
} }
#[cfg(any(esp32c6, esp32h2))] #[cfg(any(esp32c6, esp32h2))]
fn change_baud_internal(&self, baudrate: u32, clocks: &Clocks) { fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) {
// we force the clock source to be XTAL and don't use the decimal part of let clk = match clock_source {
// the divider ClockSource::Apb => clocks.apb_clock.to_Hz(),
let clk = clocks.xtal_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 max_div = 0b1111_1111_1111 - 1;
let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate);
@ -922,7 +961,11 @@ where
.uart0_sclk_div_num() .uart0_sclk_div_num()
.bits(clk_div as u8 - 1) .bits(clk_div as u8 - 1)
.uart0_sclk_sel() .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() .uart0_sclk_en()
.set_bit() .set_bit()
}); });
@ -959,14 +1002,20 @@ where
} }
#[cfg(any(esp32, esp32s2))] #[cfg(any(esp32, esp32s2))]
fn change_baud_internal(&self, baudrate: u32, clocks: &Clocks) { fn change_baud_internal(&self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) {
// we force the clock source to be APB and don't use the decimal part of the let clk = match clock_source {
// divider ClockSource::Apb => clocks.apb_clock.to_Hz(),
let clk = 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; let divider = clk / baudrate;
T::register_block() T::register_block()
@ -992,8 +1041,8 @@ where
} }
/// Modify UART baud rate and reset TX/RX fifo. /// Modify UART baud rate and reset TX/RX fifo.
pub fn change_baud(&mut self, baudrate: u32, clocks: &Clocks) { pub fn change_baud(&mut self, baudrate: u32, clock_source: ClockSource, clocks: &Clocks) {
self.change_baud_internal(baudrate, clocks); self.change_baud_internal(baudrate, clock_source, clocks);
self.txfifo_reset(); self.txfifo_reset();
self.rxfifo_reset(); self.rxfifo_reset();
} }
@ -2130,7 +2179,7 @@ pub mod lp_uart {
// Override protocol parameters from the configuration // Override protocol parameters from the configuration
// uart_hal_set_baudrate(&hal, cfg->uart_proto_cfg.baud_rate, sclk_freq); // 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); // uart_hal_set_parity(&hal, cfg->uart_proto_cfg.parity);
me.change_parity(config.parity); me.change_parity(config.parity);
// uart_hal_set_data_bit_num(&hal, cfg->uart_proto_cfg.data_bits); // 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) { fn change_baud_internal(&mut self, baudrate: u32, clock_source: super::ClockSource) {
// we force the clock source to be XTAL and don't use the decimal part of // TODO: Currently it's not possible to use XtalD2Clk
// the divider
let clk = 16_000_000; let clk = 16_000_000;
let max_div = 0b1111_1111_1111 - 1; let max_div = 0b1111_1111_1111 - 1;
let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate);
@ -2187,7 +2235,11 @@ pub mod lp_uart {
.sclk_div_num() .sclk_div_num()
.bits(clk_div as u8 - 1) .bits(clk_div as u8 - 1)
.sclk_sel() .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() .sclk_en()
.set_bit() .set_bit()
}); });
@ -2204,8 +2256,8 @@ pub mod lp_uart {
} }
/// Modify UART baud rate and reset TX/RX fifo. /// Modify UART baud rate and reset TX/RX fifo.
pub fn change_baud(&mut self, baudrate: u32) { pub fn change_baud(&mut self, baudrate: u32, clock_source: super::ClockSource) {
self.change_baud_internal(baudrate); self.change_baud_internal(baudrate, clock_source);
self.txfifo_reset(); self.txfifo_reset();
self.rxfifo_reset(); self.rxfifo_reset();
} }

View File

@ -19,11 +19,7 @@ use esp_hal::{
gpio::IO, gpio::IO,
peripherals::Peripherals, peripherals::Peripherals,
prelude::*, prelude::*,
uart::{ uart::{config::Config, TxRxPins, Uart},
config::{Config, DataBits, Parity, StopBits},
TxRxPins,
Uart,
},
}; };
use esp_println::println; use esp_println::println;
use nb::block; use nb::block;
@ -34,20 +30,19 @@ fn main() -> ! {
let system = peripherals.SYSTEM.split(); let system = peripherals.SYSTEM.split();
let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); 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 io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
let pins = TxRxPins::new_tx_rx( let pins = TxRxPins::new_tx_rx(
io.pins.gpio4.into_push_pull_output(), io.pins.gpio4.into_push_pull_output(),
io.pins.gpio5.into_floating_input(), 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); let delay = Delay::new(&clocks);

View File

@ -36,19 +36,18 @@ fn main() -> ! {
// Set up (HP) UART1: // 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( let pins = TxRxPins::new_tx_rx(
io.pins.gpio6.into_push_pull_output(), io.pins.gpio6.into_push_pull_output(),
io.pins.gpio7.into_floating_input(), 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: // Set up (LP) UART:
let lp_tx = io.pins.gpio5.into_low_power().into_push_pull_output(); let lp_tx = io.pins.gpio5.into_low_power().into_push_pull_output();

View File

@ -17,11 +17,7 @@ use esp_hal::{
gpio::IO, gpio::IO,
peripherals::{Peripherals, UART0}, peripherals::{Peripherals, UART0},
prelude::*, prelude::*,
uart::{ uart::{config::Config, TxRxPins, Uart},
config::{Config, DataBits, Parity, StopBits},
TxRxPins,
Uart,
},
Blocking, Blocking,
}; };
use nb::block; use nb::block;
@ -40,14 +36,8 @@ impl Context {
io.pins.gpio2.into_push_pull_output(), io.pins.gpio2.into_push_pull_output(),
io.pins.gpio4.into_floating_input(), 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 } Context { uart }
} }

View File

@ -17,7 +17,7 @@ use esp_hal::{
peripherals::{Peripherals, UART0}, peripherals::{Peripherals, UART0},
prelude::*, prelude::*,
uart::{ uart::{
config::{Config, DataBits, Parity, StopBits}, config::Config,
TxRxPins, TxRxPins,
Uart, Uart,
UartRx, UartRx,
@ -41,14 +41,8 @@ impl Context {
io.pins.gpio2.into_push_pull_output(), io.pins.gpio2.into_push_pull_output(),
io.pins.gpio4.into_floating_input(), 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(); let (tx, rx) = uart.split();
Context { rx, tx } Context { rx, tx }