use crate::uart::{
ConfigError,
CtsConfig,
HwFlowControl,
RegisterBlock,
RtsConfig,
StopBits,
SwFlowControl,
low_level::Info,
};
#[inline(always)]
pub(crate) fn enable_register_sync(_register_block: &RegisterBlock) {
#[cfg(not(any(esp32, esp32s2)))]
{
_register_block
.id()
.modify(|_, w| w.high_speed().clear_bit());
}
}
#[inline(always)]
pub(crate) fn sync_regs(_register_block: &RegisterBlock) {
#[cfg(not(any(esp32, esp32s2)))]
{
let update_reg = _register_block.id();
update_reg.write(|w| {
w.high_speed().clear_bit();
w.reg_update().set_bit()
});
while update_reg.read().reg_update().bit_is_set() {
core::hint::spin_loop();
}
}
}
pub(super) fn set_rx_timeout(
info: &Info,
timeout: Option<u8>,
_symbol_len: u8,
) -> Result<(), ConfigError> {
cfg_select! {
esp32 => {
const MAX_THRHD: u8 = 0x7F; }
_ => {
const MAX_THRHD: u16 = 0x3FF; }
}
if let Some(timeout) = timeout {
let timeout_reg = cfg_select! {
esp32 => timeout,
_ => timeout as u16 * _symbol_len as u16,
};
if timeout_reg > MAX_THRHD {
return Err(ConfigError::TimeoutTooLong);
}
let reg_thrhd = cfg_select! {
esp32 => info.regs().conf1(),
_ => info.regs().mem_conf(),
};
reg_thrhd.modify(|_, w| unsafe { w.rx_tout_thrhd().bits(timeout_reg) });
}
info.regs()
.conf1()
.modify(|_, w| w.rx_tout_en().bit(timeout.is_some()));
info.sync_regs();
Ok(())
}
pub(super) fn rx_timeout_enabled(info: &Info) -> bool {
info.regs().conf1().read().rx_tout_en().bit_is_set()
}
pub(super) fn is_tx_idle(info: &Info) -> bool {
let status = cfg_select! {
esp32 => info.regs().status(),
_ => info.regs().fsm_status(),
};
status.read().st_utx_out().bits() == 0x0
}
pub(super) fn change_stop_bits(info: &Info, stop_bits: StopBits) {
#[cfg(esp32)]
{
if stop_bits == StopBits::_2 {
info.regs()
.rs485_conf()
.modify(|_, w| w.dl1_en().bit(stop_bits == StopBits::_2));
info.regs()
.conf0()
.modify(|_, w| unsafe { w.stop_bit_num().bits(1) });
}
}
#[cfg(not(esp32))]
info.regs()
.conf0()
.modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8 + 1) });
}
pub(super) fn change_flow_control(
info: &Info,
sw_flow_ctrl: SwFlowControl,
hw_flow_ctrl: HwFlowControl,
) {
if let SwFlowControl::Enabled {
xon_char,
xoff_char,
xon_threshold,
xoff_threshold,
} = sw_flow_ctrl
{
cfg_select! {
esp32 => {
info.regs().swfc_conf().modify(|_, w| unsafe {
w.xon_threshold().bits(xon_threshold);
w.xoff_threshold().bits(xoff_threshold);
w.xon_char().bits(xon_char);
w.xoff_char().bits(xoff_char)
});
}
_ => {
info.regs().swfc_conf1().modify(|_, w| unsafe {
w.xon_threshold().bits(xon_threshold as u16);
w.xon_char().bits(xon_char)
});
info.regs().swfc_conf0().modify(|_, w| unsafe {
w.xoff_threshold().bits(xoff_threshold as u16);
w.xoff_char().bits(xoff_char)
});
}
}
}
info.regs().flow_conf().modify(|_, w| {
w.xonoff_del()
.bit(matches!(sw_flow_ctrl, SwFlowControl::Enabled { .. }));
w.sw_flow_con_en()
.bit(matches!(sw_flow_ctrl, SwFlowControl::Enabled { .. }))
});
info.regs().conf0().modify(|_, w| {
w.tx_flow_en()
.bit(matches!(hw_flow_ctrl.cts, CtsConfig::Enabled))
});
match hw_flow_ctrl.rts {
RtsConfig::Enabled(threshold) => configure_rts_flow_ctrl(info, true, Some(threshold)),
RtsConfig::Disabled => configure_rts_flow_ctrl(info, false, None),
}
sync_regs(info.regs());
}
#[cfg(sleep_driver_supported)]
pub(super) fn suspend(_info: &Info, _en: bool) {
}
#[cfg(sleep_driver_supported)]
pub(super) fn set_wakeup_edge_threshold(info: &Info, threshold: u16) {
info.regs()
.sleep_conf()
.modify(|_, w| unsafe { w.active_threshold().bits(threshold) });
}
#[cfg(sleep_driver_supported)]
pub(super) fn wait_for_suspended(info: &Info) {
while info.regs().status().read().txfifo_cnt().bits() > 0 {}
let fsm_status = cfg_select! {
esp32 => info.regs().status(),
_ => info.regs().fsm_status(),
};
while fsm_status.read().st_utx_out().bits() != 0 {}
}
fn configure_rts_flow_ctrl(info: &Info, enable: bool, threshold: Option<u8>) {
if let Some(threshold) = threshold {
cfg_select! {
esp32 => {
info.regs()
.conf1()
.modify(|_, w| unsafe { w.rx_flow_thrhd().bits(threshold) });
}
_ => {
info.regs()
.mem_conf()
.modify(|_, w| unsafe { w.rx_flow_thrhd().bits(threshold as u16) });
}
}
}
info.regs()
.conf1()
.modify(|_, w| w.rx_flow_en().bit(enable));
}
pub(super) fn current_symbol_length(info: &Info) -> u8 {
let conf0 = info.regs().conf0().read();
let data_bits = conf0.bit_num().bits() + 5; let parity = conf0.parity_en().bit() as u8;
let mut stop_bits = conf0.stop_bit_num().bits();
match stop_bits {
1 => {
#[cfg(esp32)]
if info.regs().rs485_conf().read().dl1_en().bit_is_set() {
stop_bits = 2;
}
}
_ => stop_bits = 2,
}
1 + data_bits + parity + stop_bits
}
pub(super) fn read_next_from_fifo(info: &Info) -> u8 {
fn access_fifo_register<R>(f: impl Fn() -> R) -> R {
cfg_select! {
esp32 => crate::interrupt::free(f),
_ => f(),
}
}
let fifo_reg = info.regs().fifo();
cfg_select! {
esp32s2 => {
let fifo_reg = unsafe {
&*fifo_reg
.as_ptr()
.cast::<u8>()
.add(0x20C00000)
.cast::<crate::pac::uart0::FIFO>()
};
}
_ => {}
}
access_fifo_register(|| fifo_reg.read().rxfifo_rd_byte().bits())
}
#[allow(clippy::unnecessary_cast)]
pub(super) fn rx_fifo_count(info: &Info) -> u16 {
cfg_select! {
esp32 => {
let fifo_cnt = info.regs().status().read().rxfifo_cnt().bits();
let status = info.regs().mem_rx_status().read();
let rd_addr: u16 = status.mem_rx_rd_addr().bits();
let wr_addr: u16 = status.mem_rx_wr_addr().bits();
if wr_addr > rd_addr {
wr_addr - rd_addr
} else if wr_addr < rd_addr {
(wr_addr + Info::UART_FIFO_SIZE) - rd_addr
} else if fifo_cnt > 0 {
Info::UART_FIFO_SIZE
} else {
0
}
}
_ => info.regs().status().read().rxfifo_cnt().bits() as u16,
}
}