update UART clock config, bugfix #23

Merged
muellerr merged 1 commits from update-uart-clock-config-and-bugfix into main 2026-04-25 22:17:49 +02:00
14 changed files with 197 additions and 119 deletions
+7
View File
@@ -0,0 +1,7 @@
[package]
name = "uart-clock-calc"
version = "0.1.0"
edition = "2024"
[dependencies]
arbitrary-int = "2"
+50
View File
@@ -0,0 +1,50 @@
use arbitrary_int::{u6, u18};
#[derive(Debug, Copy, Clone)]
pub struct ClockConfig {
pub frac: u6,
pub int: u18,
}
#[derive(Debug, Copy, Clone)]
pub enum BaudMultiplier {
_8 = 8,
_16 = 16,
}
pub fn uart_clock_calc(ref_clk: u32, baudrate: u32, baud_mult: BaudMultiplier) -> ClockConfig {
// This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating
// point calculations.
let multiplier = baud_mult as u32;
let frac = ((ref_clk % (baudrate * multiplier)) * 64 + (baudrate * (multiplier / 2)))
/ (baudrate * multiplier);
// Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet.
let integer_part = ref_clk / (baudrate * multiplier);
ClockConfig {
frac: u6::new(frac as u8),
int: u18::new(integer_part),
}
}
const SYS_CLK_50_MHZ: u32 = 50_000_000;
fn main() {
println!("UART Clock Configuration App");
let clock_config = uart_clock_calc(SYS_CLK_50_MHZ, 38400, BaudMultiplier::_16);
println!(
"For a reference clock of {} Hz and baud rate of {} bps with multiplier {}, the clock configuration is: {:?}",
SYS_CLK_50_MHZ,
38400,
BaudMultiplier::_16 as u32,
clock_config
);
let clock_config = uart_clock_calc(SYS_CLK_50_MHZ, 38400, BaudMultiplier::_8);
println!(
"For a reference clock of {} Hz and baud rate of {} bps with multiplier {}, the clock configuration is: {:?}",
SYS_CLK_50_MHZ,
38400,
BaudMultiplier::_8 as u32,
clock_config
);
()
}
@@ -65,12 +65,13 @@ async fn main(spawner: Spawner) {
let tx_uart_a = porta.pa9;
let rx_uart_a = porta.pa8;
let clock_config = uart::ClockConfig::calculate(50.MHz(), 115200.Hz(), uart::BaudMode::_16);
let uart_config = uart::Config::new_with_clock_config(clock_config);
let uarta = uart::Uart::new_with_interrupt_uart0(
dp.uarta,
tx_uart_a,
rx_uart_a,
50.MHz(),
115200.Hz().into(),
uart_config,
InterruptConfig::new(pac::Interrupt::OC2, true, true),
);
@@ -81,8 +82,7 @@ async fn main(spawner: Spawner) {
dp.uartb,
tx_uart_b,
rx_uart_b,
50.MHz(),
115200.Hz().into(),
uart_config,
InterruptConfig::new(pac::Interrupt::OC3, true, true),
);
let (mut tx_uart_a, rx_uart_a) = uarta.split();
@@ -52,12 +52,13 @@ async fn main(_spawner: Spawner) {
let tx = porta.pa9;
let rx = porta.pa8;
let clock_config = uart::ClockConfig::calculate(50.MHz(), 115200.Hz(), uart::BaudMode::_16);
let uart_config = uart::Config::new_with_clock_config(clock_config);
let uarta = uart::Uart::new_with_interrupt_uart0(
dp.uarta,
tx,
rx,
50.MHz(),
115200.Hz().into(),
uart_config,
InterruptConfig::new(pac::Interrupt::OC2, true, true),
);
let (tx, _rx) = uarta.split();
@@ -53,12 +53,14 @@ mod app {
let tx = gpioa.pa9;
let rx = gpioa.pa8;
let clock_config =
uart::ClockConfig::calculate(SYSCLK_FREQ, 115200.Hz(), uart::BaudMode::_16);
let uart_config = uart::Config::new_with_clock_config(clock_config);
let irq_uart = uart::Uart::new_with_interrupt_uart0(
dp.uarta,
tx,
rx,
SYSCLK_FREQ,
115200.Hz().into(),
uart_config,
InterruptConfig::new(pac::Interrupt::OC3, true, true),
);
let (tx, rx) = irq_uart.split();
+3 -2
View File
@@ -28,8 +28,9 @@ fn main() -> ! {
let gpioa = PinsA::new(dp.porta);
let tx = gpioa.pa9;
let rx = gpioa.pa8;
let uart =
uart::Uart::new_without_interrupt_uart0(dp.uarta, tx, rx, 50.MHz(), 115200.Hz().into());
let clock_config = uart::ClockConfig::calculate(50.MHz(), 115200.Hz(), uart::BaudMode::_16);
let uart_config = uart::Config::new_with_clock_config(clock_config);
let uart = uart::Uart::new_without_interrupt_uart0(dp.uarta, tx, rx, uart_config);
let (mut tx, mut rx) = uart.split();
writeln!(tx, "Hello World\r").unwrap();
+4 -2
View File
@@ -116,12 +116,14 @@ mod app {
let tx = gpioa.pa9;
let rx = gpioa.pa8;
let clock_config =
uart::ClockConfig::calculate(SYSCLK_FREQ, UART_BAUDRATE.Hz(), uart::BaudMode::_16);
let uart_config = uart::Config::new_with_clock_config(clock_config);
let irq_uart = uart::Uart::new_with_interrupt_uart0(
dp.uarta,
tx,
rx,
SYSCLK_FREQ,
UART_BAUDRATE.Hz().into(),
uart_config,
InterruptConfig::new(pac::Interrupt::OC0, true, true),
);
let (tx, rx) = irq_uart.split();
@@ -61,8 +61,14 @@ async fn main(_spawner: Spawner) {
let portg = PinsG::new(dp.portg);
let mut led = Output::new(portg.pg5, PinState::Low);
let uarta =
uart::Uart::new_for_uart0(dp.uart0, portg.pg0, portg.pg1, &clocks, 115200.Hz().into());
let clock_config = uart::ClockConfig::calculate_with_clocks(
uart::Bank::Uart0,
&clocks,
115200.Hz(),
uart::BaudMode::_16,
);
let uart_config = uart::Config::new_with_clock_config(clock_config);
let uarta = uart::Uart::new_for_uart0(dp.uart0, portg.pg0, portg.pg1, uart_config);
let (mut tx_uart_a, rx_uart_a) = uarta.split();
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
@@ -59,8 +59,14 @@ async fn main(_spawner: Spawner) {
let pinsg = PinsG::new(dp.portg);
let mut led = Output::new(pinsg.pg5, PinState::Low);
let uarta =
uart::Uart::new_for_uart0(dp.uart0, pinsg.pg0, pinsg.pg1, &clocks, 115200.Hz().into());
let clock_config = uart::ClockConfig::calculate_with_clocks(
uart::Bank::Uart0,
&clocks,
115200.Hz(),
uart::BaudMode::_16,
);
let uart_config = uart::Config::new_with_clock_config(clock_config);
let uarta = uart::Uart::new_for_uart0(dp.uart0, pinsg.pg0, pinsg.pg1, uart_config);
let (tx, _rx) = uarta.split();
let mut async_tx = TxAsync::new(tx);
let mut ticker = Ticker::every(Duration::from_secs(1));
@@ -67,13 +67,14 @@ async fn main(spawner: Spawner) {
let portg = PinsG::new(dp.portg);
let uart0 = uart::Uart::new_for_uart0(
dp.uart0,
portg.pg0,
portg.pg1,
let clock_config = uart::ClockConfig::calculate_with_clocks(
uart::Bank::Uart0,
&clocks,
Hertz::from_raw(BAUDRATE).into(),
Hertz::from_raw(BAUDRATE),
uart::BaudMode::_16,
);
let uart_config = uart::Config::new_with_clock_config(clock_config);
let uart0 = uart::Uart::new_for_uart0(dp.uart0, portg.pg0, portg.pg1, uart_config);
let (mut tx, rx) = uart0.split();
let mut rx = rx.into_rx_with_irq();
rx.start();
+6 -5
View File
@@ -30,13 +30,14 @@ fn main() -> ! {
let gpiog = PinsG::new(dp.portg);
let uart0 = uart::Uart::new_for_uart0(
dp.uart0,
gpiog.pg0,
gpiog.pg1,
let clock_config = uart::ClockConfig::calculate_with_clocks(
uart::Bank::Uart0,
&clocks,
Hertz::from_raw(115200).into(),
Hertz::from_raw(115200),
uart::BaudMode::_16,
);
let uart_config = uart::Config::new_with_clock_config(clock_config);
let uart0 = uart::Uart::new_for_uart0(dp.uart0, gpiog.pg0, gpiog.pg1, uart_config);
let (mut tx, mut rx) = uart0.split();
writeln!(tx, "Hello World\n\r").unwrap();
loop {
+8 -5
View File
@@ -116,6 +116,7 @@ mod app {
nvm::Nvm,
pac,
pins::PinsG,
prelude::*,
uart::{self, Uart},
};
@@ -167,13 +168,15 @@ mod app {
let gpiog = PinsG::new(cx.device.portg);
let uart0 = Uart::new_for_uart0(
cx.device.uart0,
gpiog.pg0,
gpiog.pg1,
let clock_config = uart::ClockConfig::calculate_with_clocks(
uart::Bank::Uart0,
&clocks,
Hertz::from_raw(UART_BAUDRATE).into(),
UART_BAUDRATE.Hz(),
uart::BaudMode::_16,
);
let uart_config = uart::Config::new_with_clock_config(clock_config);
let uart0 = Uart::new_for_uart0(cx.device.uart0, gpiog.pg0, gpiog.pg1, uart_config);
let (tx, rx) = uart0.split();
let verif_reporter = VerificationReportCreator::new(u11::new(0));
+3
View File
@@ -21,12 +21,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Improved type level support for resource management for SPI, PWM, UART.
- Renamed `tx_asynch` and `rx_asynch` module name to `*_async`
- Naming improvements in SPI module: replaced `cfg` by `config*`
- UART configuration now expects an explicit clock configuration structure and does not
calculate it itself anymore.
### Fixed
- Removed HW CS pin provider implementation for PA23, PA22 and PA21, which are multi HW CS pins.
- Added missing `AnyPin` trait impl for Multi HW CS pins.
- Expose inner `Input` pin for `InputPinAsync`.
- Bugfix for UART clock calculation with 8x baud mode.
## [v0.2.0] 2025-09-03
+83 -88
View File
@@ -23,7 +23,6 @@ use crate::{
sealed::Sealed,
};
use arbitrary_int::{prelude::*, u6, u18};
use fugit::RateExtU32;
use regs::{ClockScale, Control, Data, Enable, FifoClear, InterruptClear, MmioUart};
use crate::{PeripheralSelect, enable_nvic_interrupt, enable_peripheral_clock, time::Hertz};
@@ -120,6 +119,25 @@ pub enum Event {
TxCts,
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BaudMode {
/// Default 16x baud clock.
#[default]
_16 = 0,
/// Slower 8x baud clock.
_8 = 1,
}
impl BaudMode {
pub const fn multiplier(&self) -> u32 {
match self {
BaudMode::_16 => 16,
BaudMode::_8 => 8,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Parity {
@@ -128,22 +146,74 @@ pub enum Parity {
Even,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClockConfig {
/// Integer divisor.
pub div: u18,
/// Fractional divide value in 1/64 units.
pub frac: u6,
pub baud_mode: BaudMode,
}
impl ClockConfig {
pub const fn calculate(ref_clk: Hertz, baudrate: Hertz, baud_mode: BaudMode) -> Self {
// This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating
// point calculations.
let multiplier = baud_mode.multiplier();
let frac = ((ref_clk.raw() % (baudrate.raw() * multiplier)) * 64
+ (baudrate.raw() * (multiplier / 2)))
/ (baudrate.raw() * multiplier);
// Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet.
let integer_div = ref_clk.raw() / (baudrate.raw() * multiplier);
Self {
frac: u6::new(frac as u8),
div: u18::new(integer_div),
baud_mode,
}
}
#[cfg(feature = "vor4x")]
pub fn calculate_with_clocks(
uart_id: Bank,
clks: &Clocks,
baudrate: Hertz,
baud_mode: BaudMode,
) -> Self {
let clk = if uart_id == Bank::Uart2 {
clks.apb1()
} else {
clks.apb2()
};
Self::calculate(clk, baudrate, baud_mode)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Config {
pub baudrate: Hertz,
pub clock_config: ClockConfig,
pub parity: Parity,
pub stopbits: Stopbits,
// When false, use standard 16x baud clock, other 8x baud clock
pub baud8: bool,
pub wordsize: WordSize,
pub enable_tx: bool,
pub enable_rx: bool,
}
impl Config {
pub fn baudrate(mut self, baudrate: Hertz) -> Self {
self.baudrate = baudrate;
pub fn new_with_clock_config(clock_config: ClockConfig) -> Self {
Config {
clock_config,
parity: Parity::None,
stopbits: Stopbits::One,
wordsize: WordSize::Eight,
enable_tx: true,
enable_rx: true,
}
}
pub fn clock_config(mut self, clock_config: ClockConfig) -> Self {
self.clock_config = clock_config;
self
}
@@ -171,32 +241,6 @@ impl Config {
self.wordsize = wordsize;
self
}
pub fn baud8(mut self, baud: bool) -> Self {
self.baud8 = baud;
self
}
}
impl Default for Config {
fn default() -> Config {
let baudrate = 115_200_u32.Hz();
Config {
baudrate,
parity: Parity::None,
stopbits: Stopbits::One,
baud8: false,
wordsize: WordSize::Eight,
enable_tx: true,
enable_rx: true,
}
}
}
impl From<Hertz> for Config {
fn from(baud: Hertz) -> Self {
Config::default().baudrate(baud)
}
}
//==================================================================================================
@@ -408,11 +452,10 @@ impl Uart {
uart: Uart,
tx_pin: Tx,
rx_pin: Rx,
sys_clk: Hertz,
config: Config,
irq_cfg: InterruptConfig,
) -> Self {
Self::new_for_uart0(uart, tx_pin, rx_pin, sys_clk, config, Some(irq_cfg))
Self::new_for_uart0(uart, tx_pin, rx_pin, config, Some(irq_cfg))
}
/// Calls [Self::new_for_uart1] with the interrupt configuration to some valid value.
@@ -420,11 +463,10 @@ impl Uart {
uart: Uart,
tx_pin: Tx,
rx_pin: Rx,
sys_clk: Hertz,
config: Config,
irq_cfg: InterruptConfig,
) -> Self {
Self::new_for_uart1(uart, tx_pin, rx_pin, sys_clk, config, Some(irq_cfg))
Self::new_for_uart1(uart, tx_pin, rx_pin, config, Some(irq_cfg))
}
/// Calls [Self::new_for_uart0] with the interrupt configuration to [None].
@@ -432,10 +474,9 @@ impl Uart {
uart: Uart,
tx_pin: Tx,
rx_pin: Rx,
sys_clk: Hertz,
config: Config,
) -> Self {
Self::new_for_uart0(uart, tx_pin, rx_pin, sys_clk, config, None)
Self::new_for_uart0(uart, tx_pin, rx_pin, config, None)
}
/// Calls [Self::new_for_uart1] with the interrupt configuration to [None].
@@ -443,10 +484,9 @@ impl Uart {
uart: Uart,
tx_pin: Tx,
rx_pin: Rx,
sys_clk: Hertz,
config: Config,
) -> Self {
Self::new_for_uart1(uart, tx_pin, rx_pin, sys_clk, config, None)
Self::new_for_uart1(uart, tx_pin, rx_pin, config, None)
}
/// Create a new UART peripheral driver with an interrupt configuration.
@@ -465,7 +505,6 @@ impl Uart {
_uart: Uart,
_tx_pin: Tx,
_rx_pin: Rx,
sys_clk: Hertz,
config: Config,
opt_irq_cfg: Option<InterruptConfig>,
) -> Self {
@@ -476,7 +515,6 @@ impl Uart {
Tx::FUNC_SEL,
Rx::ID,
Rx::FUNC_SEL,
sys_clk,
config,
opt_irq_cfg
)
@@ -498,7 +536,6 @@ impl Uart {
_uart: Uart,
_tx_pin: Tx,
_rx_pin: Rx,
sys_clk: Hertz,
config: Config,
opt_irq_cfg: Option<InterruptConfig>,
) -> Self {
@@ -509,7 +546,6 @@ impl Uart {
Tx::FUNC_SEL,
Rx::ID,
Rx::FUNC_SEL,
sys_clk,
config,
opt_irq_cfg
)
@@ -527,14 +563,8 @@ impl Uart {
_uart: Uart,
_tx_pin: Tx,
_rx_pin: Rx,
clks: &Clocks,
config: Config,
) -> Self {
let clk = if Uart::ID == Bank::Uart2 {
clks.apb1()
} else {
clks.apb2()
};
Self::new_internal(
Uart::PERIPH_SEL,
Uart::ID,
@@ -542,7 +572,6 @@ impl Uart {
Tx::FUNC_SEL,
Rx::ID,
Rx::FUNC_SEL,
clk,
config
)
}
@@ -559,14 +588,8 @@ impl Uart {
_uart: Uart,
_tx_pin: Tx,
_rx_pin: Rx,
clks: &Clocks,
config: Config,
) -> Self {
let clk = if Uart::ID == Bank::Uart2 {
clks.apb1()
} else {
clks.apb2()
};
Self::new_internal(
Uart::PERIPH_SEL,
Uart::ID,
@@ -574,7 +597,6 @@ impl Uart {
Tx::FUNC_SEL,
Rx::ID,
Rx::FUNC_SEL,
clk,
config
)
}
@@ -591,14 +613,8 @@ impl Uart {
_uart: Uart,
_tx_pin: Tx,
_rx_pin: Rx,
clks: &Clocks,
config: Config,
) -> Self {
let clk = if Uart::ID == Bank::Uart2 {
clks.apb1()
} else {
clks.apb2()
};
Self::new_internal(
Uart::PERIPH_SEL,
Uart::ID,
@@ -606,7 +622,6 @@ impl Uart {
Tx::FUNC_SEL,
Rx::ID,
Rx::FUNC_SEL,
clk,
config
)
}
@@ -623,7 +638,6 @@ impl Uart {
_uart: Uart,
_tx_pin: Tx,
_rx_pin: Rx,
ref_clk: Hertz,
config: Config,
) -> Self {
Self::new_internal(
@@ -633,7 +647,6 @@ impl Uart {
Tx::FUNC_SEL,
Rx::ID,
Rx::FUNC_SEL,
ref_clk,
config
)
}
@@ -650,7 +663,6 @@ impl Uart {
_uart: Uart,
_tx_pin: Tx,
_rx_pin: Rx,
ref_clk: Hertz,
config: Config,
) -> Self {
Self::new_internal(
@@ -660,7 +672,6 @@ impl Uart {
Tx::FUNC_SEL,
Rx::ID,
Rx::FUNC_SEL,
ref_clk,
config
)
}
@@ -677,7 +688,6 @@ impl Uart {
_uart: Uart,
_tx_pin: Tx,
_rx_pin: Rx,
ref_clk: Hertz,
config: Config,
) -> Self {
Self::new_internal(
@@ -687,7 +697,6 @@ impl Uart {
Tx::FUNC_SEL,
Rx::ID,
Rx::FUNC_SEL,
ref_clk,
config
)
}
@@ -702,7 +711,6 @@ impl Uart {
tx_func_sel: FunctionSelect,
rx_pin_id: DynPinId,
rx_func_sel: FunctionSelect,
ref_clk: Hertz,
config: Config,
#[cfg(feature = "vor1x")] opt_irq_cfg: Option<InterruptConfig>,
) -> Self {
@@ -711,23 +719,10 @@ impl Uart {
enable_peripheral_clock(periph_sel);
let mut reg_block = regs::Uart::new_mmio(uart_bank);
let baud_multiplier = match config.baud8 {
false => 16,
true => 8,
};
// This is the calculation: (64.0 * (x - integer_part as f32) + 0.5) as u32 without floating
// point calculations.
let frac = ((ref_clk.raw() % (config.baudrate.raw() * 16)) * 64
+ (config.baudrate.raw() * 8))
/ (config.baudrate.raw() * 16);
// Calculations here are derived from chapter 4.8.5 (p.79) of the datasheet.
let x = ref_clk.raw() as f32 / (config.baudrate.raw() * baud_multiplier) as f32;
let integer_part = x as u32;
reg_block.write_clkscale(
ClockScale::builder()
.with_int(u18::new(integer_part))
.with_frac(u6::new(frac as u8))
.with_int(config.clock_config.div)
.with_frac(config.clock_config.frac)
.build(),
);
@@ -738,7 +733,7 @@ impl Uart {
};
reg_block.write_ctrl(
Control::builder()
.with_baud8(config.baud8)
.with_baud8(config.clock_config.baud_mode == BaudMode::_8)
.with_auto_rts(false)
.with_def_rts(false)
.with_auto_cts(false)