Allow more baud rates (#3104)

* merge change_baud impls

* merge further with pac updates

* avoid overflow when calculating uart clk divider

* xfmt

* avoid u64 ops

* changelog

* reword comment
This commit is contained in:
Scott Mabin 2025-02-06 11:05:54 +00:00 committed by GitHub
parent 715e447a47
commit 6f5c48e54c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 79 additions and 134 deletions

View File

@ -47,7 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed an issue that caused LCD_CAM drivers to turn off their clocks unexpectedly (#3007)
- Fixed an issue where DMA-driver peripherals started transferring before the data was ready (#3003)
- Fixed an issue on ESP32 and S2 where short asynchronous Timer delays would never resolve (#3093)
- Fixed an issue setting higher UART baud rates (#3104)
- ESP32-S2: Fixed linker script (#3096)
### Removed

View File

@ -57,13 +57,13 @@ ufmt-write = "0.1.0"
# IMPORTANT:
# Each supported device MUST have its PAC included below along with a
# corresponding feature.
esp32 = { version = "0.35.0", features = ["critical-section", "rt"], optional = true }
esp32c2 = { version = "0.24.0", features = ["critical-section", "rt"], optional = true }
esp32c3 = { version = "0.27.0", features = ["critical-section", "rt"], optional = true }
esp32c6 = { version = "0.18.0", features = ["critical-section", "rt"], optional = true }
esp32h2 = { version = "0.14.0", features = ["critical-section", "rt"], optional = true }
esp32s2 = { version = "0.26.0", features = ["critical-section", "rt"], optional = true }
esp32s3 = { version = "0.30.0", features = ["critical-section", "rt"], optional = true }
esp32 = { version = "0.35.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "cab435a", optional = true }
esp32c2 = { version = "0.24.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "cab435a", optional = true }
esp32c3 = { version = "0.27.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "cab435a", optional = true }
esp32c6 = { version = "0.18.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "cab435a", optional = true }
esp32h2 = { version = "0.14.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "cab435a", optional = true }
esp32s2 = { version = "0.26.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "cab435a", optional = true }
esp32s3 = { version = "0.30.0", features = ["critical-section", "rt"], git = "https://github.com/esp-rs/esp-pacs", rev = "cab435a", optional = true }
[target.'cfg(target_arch = "riscv32")'.dependencies]
riscv = { version = "0.12.1" }

View File

@ -741,15 +741,11 @@ impl PeripheralClockControl {
}
#[cfg(uart0)]
Peripheral::Uart0 => {
system
.uart0_conf()
.modify(|_, w| w.uart0_clk_en().bit(enable));
system.uart(0).conf().modify(|_, w| w.clk_en().bit(enable));
}
#[cfg(uart1)]
Peripheral::Uart1 => {
system
.uart1_conf()
.modify(|_, w| w.uart1_clk_en().bit(enable));
system.uart(1).conf().modify(|_, w| w.clk_en().bit(enable));
}
#[cfg(rsa)]
Peripheral::Rsa => {
@ -934,21 +930,13 @@ impl PeripheralClockControl {
}
#[cfg(uart0)]
Peripheral::Uart0 => {
system
.uart0_conf()
.modify(|_, w| w.uart0_rst_en().set_bit());
system
.uart0_conf()
.modify(|_, w| w.uart0_rst_en().clear_bit());
system.uart(0).conf().modify(|_, w| w.rst_en().set_bit());
system.uart(0).conf().modify(|_, w| w.rst_en().clear_bit());
}
#[cfg(uart1)]
Peripheral::Uart1 => {
system
.uart1_conf()
.modify(|_, w| w.uart1_rst_en().set_bit());
system
.uart1_conf()
.modify(|_, w| w.uart1_rst_en().clear_bit());
system.uart(1).conf().modify(|_, w| w.rst_en().set_bit());
system.uart(1).conf().modify(|_, w| w.rst_en().clear_bit());
}
#[cfg(rsa)]
Peripheral::Rsa => {

View File

@ -2365,48 +2365,6 @@ impl Info {
Ok(())
}
#[cfg(any(esp32c2, esp32c3, esp32s3))]
fn change_baud(&self, config: &Config) {
use crate::peripherals::LPWR;
let clocks = Clocks::get();
let clk = match config.clock_source {
ClockSource::Apb => clocks.apb_clock.as_hz(),
ClockSource::Xtal => clocks.xtal_clock.as_hz(),
ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.as_hz(),
};
if config.clock_source == ClockSource::RcFast {
LPWR::regs()
.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 * config.baudrate);
self.regs().clk_conf().write(|w| unsafe {
w.sclk_sel().bits(match config.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) / (config.baudrate * clk_div);
let divider_integer = (divider >> 4) as u16;
let divider_frag = (divider & 0xf) as u8;
self.regs()
.clkdiv()
.write(|w| unsafe { w.clkdiv().bits(divider_integer).frag().bits(divider_frag) });
}
fn is_instance(&self, other: impl Instance) -> bool {
self == other.info()
}
@ -2415,82 +2373,80 @@ impl Info {
sync_regs(self.regs());
}
#[cfg(any(esp32c6, esp32h2))]
fn change_baud(&self, config: &Config) {
let clocks = Clocks::get();
let clk = match config.clock_source {
ClockSource::Apb => clocks.apb_clock.as_hz(),
#[cfg(not(any(esp32, esp32s2)))]
ClockSource::Xtal => clocks.xtal_clock.as_hz(),
#[cfg(not(any(esp32, esp32s2)))]
ClockSource::RcFast => crate::soc::constants::RC_FAST_CLK.as_hz(),
};
let max_div = 0b1111_1111_1111 - 1;
let clk_div = clk.div_ceil(max_div * config.baudrate);
// UART clocks are configured via PCR
let pcr = crate::peripherals::PCR::regs();
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 config.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 config.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 / config.baudrate;
let divider = divider as u16;
self.regs()
.clkdiv()
.write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) });
self.sync_regs();
}
#[cfg(any(esp32, esp32s2))]
fn change_baud(&self, config: &Config) {
let clk = match config.clock_source {
ClockSource::Apb => Clocks::get().apb_clock.as_hz(),
// ESP32(/-S2) TRM, section 3.2.4.2 (6.2.4.2 for S2)
#[cfg(any(esp32, esp32s2))]
ClockSource::RefTick => crate::soc::constants::REF_TICK.as_hz(),
};
self.regs().conf0().modify(|_, w| {
w.tick_ref_always_on()
.bit(config.clock_source == ClockSource::Apb)
cfg_if::cfg_if! {
if #[cfg(any(esp32c2, esp32c3, esp32s3, esp32c6, esp32h2))] {
const MAX_DIV: u32 = 0b1111_1111_1111 - 1;
let clk_div = (clk.div_ceil(MAX_DIV)).div_ceil(config.baudrate);
// define `conf` in scope for modification below
cfg_if::cfg_if! {
if #[cfg(any(esp32c2, esp32c3, esp32s3))] {
if matches!(config.clock_source, ClockSource::RcFast) {
crate::peripherals::LPWR::regs()
.clk_conf()
.modify(|_, w| w.dig_clk8m_en().variant(true));
// small delay whilst the clock source changes (SOC_DELAY_RC_FAST_DIGI_SWITCH from esp-idf)
crate::rom::ets_delay_us(5);
}
let conf = self.regs().clk_conf();
} else {
// UART clocks are configured via PCR
let pcr = crate::peripherals::PCR::regs();
let conf = if self.is_instance(unsafe { crate::peripherals::UART0::steal() }) {
pcr.uart(0).clk_conf()
} else {
pcr.uart(1).clk_conf()
};
}
};
conf.write(|w| unsafe {
w.sclk_sel().bits(match config.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)
});
let divider = (clk << 4) / (config.baudrate * clk_div);
} else {
self.regs().conf0().modify(|_, w| {
w.tick_ref_always_on()
.bit(config.clock_source == ClockSource::Apb)
});
let divider = (clk << 4) / config.baudrate;
}
}
let divider_integer = divider >> 4;
let divider_frag = (divider & 0xf) as u8;
self.regs().clkdiv().write(|w| unsafe {
w.clkdiv()
.bits(divider_integer as _)
.frag()
.bits(divider_frag)
});
let divider = clk / config.baudrate;
self.regs()
.clkdiv()
.write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) });
self.sync_regs();
}
fn change_data_bits(&self, data_bits: DataBits) {

View File

@ -322,4 +322,4 @@ debug-assertions = true
incremental = false
opt-level = 3
lto = false # LTO (thin or fat) miscompiles some tests on RISC-V
overflow-checks = false
overflow-checks = true

View File

@ -72,6 +72,7 @@ mod tests {
#[cfg(esp32s2)]
(9600, ClockSource::RefTick),
(921_600, ClockSource::Apb),
(2_000_000, ClockSource::Apb),
];
let mut byte_to_write = 0xA5;