Skip to content

Commit

Permalink
Add mechanism to configure UART source clock (#1416)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
playfulFence authored Apr 17, 2024
1 parent 111dcda commit 6f91367
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 77 deletions.
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions esp-hal/src/soc/esp32/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions esp-hal/src/soc/esp32c2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 2 additions & 0 deletions esp-hal/src/soc/esp32c3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 2 additions & 0 deletions esp-hal/src/soc/esp32c6/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 2 additions & 0 deletions esp-hal/src/soc/esp32h2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 2 additions & 0 deletions esp-hal/src/soc/esp32s2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions esp-hal/src/soc/esp32s3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
124 changes: 88 additions & 36 deletions esp-hal/src/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
}
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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()
Expand All @@ -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);

Expand All @@ -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()
});
Expand Down Expand Up @@ -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()
Expand All @@ -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();
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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()
});
Expand All @@ -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();
}
Expand Down
21 changes: 8 additions & 13 deletions examples/src/bin/advanced_serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);

Expand Down
15 changes: 7 additions & 8 deletions examples/src/bin/lp_core_uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading

0 comments on commit 6f91367

Please sign in to comment.