use crate::{
gpio::{
InputPin,
LpPin,
OutputPin,
lp_io::{LpFunction, low_level},
},
peripherals::{LP_CLKRST, LP_UART, LPWR},
uart::{DataBits, Parity, StopBits},
};
pub trait Tx: LpPin + OutputPin {
#[doc(hidden)]
fn connect_tx(&self);
}
pub trait Rx: LpPin + InputPin {
#[doc(hidden)]
fn connect_rx(&self);
}
#[cfg(lp_io_has_gpio_matrix)]
for_each_lp_function! {
(($_signal:ident, LP_GPIOn, $_pin:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) => {
impl Tx for crate::peripherals::$gpio<'_> {
fn connect_tx(&self) {
crate::gpio::lp_io::connect_output_signal(
self,
crate::gpio::lp_io::LpOutputSignal::LP_UART_TXD,
);
}
}
impl Rx for crate::peripherals::$gpio<'_> {
fn connect_rx(&self) {
crate::gpio::lp_io::connect_input_signal(
self,
crate::gpio::lp_io::LpInputSignal::LP_UART_RXD,
);
}
}
};
}
#[cfg(not(lp_io_has_gpio_matrix))]
for_each_lp_function! {
(LP_UART_TXD, $gpio:ident, $af:ident) => {
impl Tx for crate::peripherals::$gpio<'_> {
fn connect_tx(&self) {
low_level::set_config(self.lp_number(), false, true, LpFunction::$af);
}
}
};
(LP_UART_RXD, $gpio:ident, $af:ident) => {
impl Rx for crate::peripherals::$gpio<'_> {
fn connect_rx(&self) {
low_level::set_config(self.lp_number(), true, true, LpFunction::$af);
}
}
};
}
#[derive(Debug, Clone, Copy, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
baudrate: u32,
data_bits: DataBits,
parity: Parity,
stop_bits: StopBits,
#[builder_lite(unstable)]
clock_source: ClockSource,
}
impl Default for Config {
fn default() -> Config {
Config {
baudrate: 115_200,
data_bits: Default::default(),
parity: Default::default(),
stop_bits: Default::default(),
clock_source: Default::default(),
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
#[instability::unstable]
pub enum ClockSource {
RcFast,
#[default]
Xtal,
}
pub struct LpUart {
uart: LP_UART<'static>,
}
impl LpUart {
pub fn new(
uart: LP_UART<'static>,
config: Config,
tx: impl Tx + 'static,
rx: impl Rx + 'static,
) -> Self {
rx.connect_rx();
tx.connect_tx();
let mut me = Self { uart };
let uart = me.uart.register_block();
uart.conf0().modify(|_, w| unsafe {
w.parity().clear_bit();
w.parity_en().clear_bit();
w.bit_num().bits(0x3);
w.stop_bit_num().bits(0x1)
});
uart.idle_conf()
.modify(|_, w| unsafe { w.tx_idle_num().bits(0) });
uart.hwfc_conf().modify(|_, w| w.rx_flow_en().clear_bit());
LPWR::regs()
.lpperi()
.modify(|_, w| w.lp_uart_clk_sel().clear_bit());
me.change_baud_internal(&config);
me.change_parity(config.parity);
me.change_data_bits(config.data_bits);
me.change_stop_bits(config.stop_bits);
me.change_tx_idle(0);
me.rxfifo_reset();
me.txfifo_reset();
me
}
fn rxfifo_reset(&mut self) {
self.uart
.register_block()
.conf0()
.modify(|_, w| w.rxfifo_rst().set_bit());
self.update();
self.uart
.register_block()
.conf0()
.modify(|_, w| w.rxfifo_rst().clear_bit());
self.update();
}
fn txfifo_reset(&mut self) {
self.uart
.register_block()
.conf0()
.modify(|_, w| w.txfifo_rst().set_bit());
self.update();
self.uart
.register_block()
.conf0()
.modify(|_, w| w.txfifo_rst().clear_bit());
self.update();
}
fn update(&mut self) {
let register_block = self.uart.register_block();
register_block
.reg_update()
.modify(|_, w| w.reg_update().set_bit());
while register_block.reg_update().read().reg_update().bit_is_set() {
}
}
fn change_baud_internal(&mut self, config: &Config) {
let clk = match config.clock_source {
ClockSource::RcFast => crate::soc::clocks::rc_fast_clk_frequency(),
ClockSource::Xtal => crate::soc::clocks::xtal_d2_clk_frequency(),
};
LP_CLKRST::regs().lpperi().modify(|_, w| {
w.lp_uart_clk_sel().bit(match config.clock_source {
ClockSource::RcFast => false,
ClockSource::Xtal => true,
})
});
self.uart.register_block().clk_conf().modify(|_, w| {
w.rx_sclk_en().set_bit();
w.tx_sclk_en().set_bit()
});
let divider = clk / config.baudrate;
let divider = divider as u16;
self.uart
.register_block()
.clkdiv()
.write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) });
self.update();
}
pub fn change_baud(&mut self, config: &Config) {
self.change_baud_internal(config);
self.txfifo_reset();
self.rxfifo_reset();
}
fn change_parity(&mut self, parity: Parity) -> &mut Self {
if parity != Parity::None {
self.uart
.register_block()
.conf0()
.modify(|_, w| w.parity().bit((parity as u8 & 0x1) != 0));
}
self.uart
.register_block()
.conf0()
.modify(|_, w| match parity {
Parity::None => w.parity_en().clear_bit(),
Parity::Even => w.parity_en().set_bit().parity().clear_bit(),
Parity::Odd => w.parity_en().set_bit().parity().set_bit(),
});
self
}
fn change_data_bits(&mut self, data_bits: DataBits) -> &mut Self {
self.uart
.register_block()
.conf0()
.modify(|_, w| unsafe { w.bit_num().bits(data_bits as u8) });
self.update();
self
}
fn change_stop_bits(&mut self, stop_bits: StopBits) -> &mut Self {
self.uart
.register_block()
.conf0()
.modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8 + 1) });
self.update();
self
}
fn change_tx_idle(&mut self, idle_num: u16) -> &mut Self {
self.uart
.register_block()
.idle_conf()
.modify(|_, w| unsafe { w.tx_idle_num().bits(idle_num) });
self.update();
self
}
}