use core::ops::Deref;
use cfg_if::cfg_if;
#[cfg(any(feature = "f3", feature = "l4"))]
use crate::dma::DmaInput;
#[cfg(not(any(feature = "f4", feature = "l552", feature = "h5")))]
use crate::dma::{self, ChannelCfg, DmaChannel};
#[cfg(feature = "g0")]
use crate::pac::DMA as DMA1;
#[cfg(not(any(feature = "g0", feature = "h5")))]
use crate::pac::DMA1;
use crate::{
clocks::Clocks,
pac::{self, RCC},
util::{BaudPeriph, RccPeriph},
MAX_ITERS,
};
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum StopBits {
S1 = 0b00,
S0_5 = 0b01,
S2 = 0b10,
S1_5 = 0b11,
}
#[derive(Clone, Copy, PartialEq)]
pub enum Parity {
EnabledEven,
EnabledOdd,
Disabled,
}
#[derive(Clone, Copy)]
pub enum WordLen {
W8,
W9,
W7,
}
impl WordLen {
pub fn bits(&self) -> (u8, u8) {
match self {
Self::W8 => (0, 0),
Self::W9 => (0, 1),
Self::W7 => (1, 0),
}
}
}
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum OverSampling {
O16 = 0,
O8 = 1,
}
#[derive(Clone, Copy, PartialEq)]
pub enum IrdaMode {
None,
Normal,
LowPower,
}
#[non_exhaustive]
#[derive(Debug)]
pub enum UartError {
Framing,
Noise,
Overrun,
Parity,
Hardware,
}
#[cfg(not(feature = "f4"))]
#[derive(Clone, Copy)]
pub enum UsartInterrupt {
CharDetect(Option<u8>),
Cts,
EndOfBlock,
Idle,
FramingError,
LineBreak,
Overrun,
ParityError,
ReadNotEmpty,
ReceiverTimeout,
#[cfg(not(any(feature = "f3", feature = "l4")))] Tcbgt,
TransmissionComplete,
TransmitEmpty,
}
pub struct UsartConfig {
pub word_len: WordLen,
pub stop_bits: StopBits,
pub oversampling: OverSampling,
pub parity: Parity,
pub irda_mode: IrdaMode,
#[cfg(any(feature = "g4", feature = "h7"))]
pub fifo_enabled: bool,
#[cfg(not(feature = "f4"))]
pub overrun_disabled: bool,
}
impl Default for UsartConfig {
fn default() -> Self {
Self {
word_len: WordLen::W8,
stop_bits: StopBits::S1,
oversampling: OverSampling::O16,
parity: Parity::Disabled,
irda_mode: IrdaMode::None,
#[cfg(any(feature = "g4", feature = "h7"))]
fifo_enabled: true,
#[cfg(not(feature = "f4"))]
overrun_disabled: false,
}
}
}
#[cfg(feature = "h5")]
macro_rules! cr1 {
($regs:expr) => {
$regs.cr1_enabled()
};
}
#[cfg(not(feature = "h5"))]
macro_rules! cr1 {
($regs:expr) => {
$regs.cr1
};
}
#[cfg(feature = "h5")]
macro_rules! isr {
($regs:expr) => {
$regs.isr_enabled()
};
}
#[cfg(not(feature = "h5"))]
macro_rules! isr {
($regs:expr) => {
$regs.isr
};
}
pub struct Usart<R> {
pub regs: R,
baud: u32,
config: UsartConfig,
}
impl<R> Usart<R>
where
R: Deref<Target = pac::usart1::RegisterBlock> + RccPeriph + BaudPeriph,
{
pub fn new(regs: R, baud: u32, config: UsartConfig, clock_cfg: &Clocks) -> Self {
let rcc = unsafe { &(*RCC::ptr()) };
R::en_reset(rcc);
let mut result = Self { regs, baud, config };
result.disable();
let word_len_bits = result.config.word_len.bits();
cr1!(result.regs).modify(|_, w| {
w.over8().bit(result.config.oversampling as u8 != 0);
w.pce().bit(result.config.parity != Parity::Disabled);
cfg_if! {
if #[cfg(not(any(feature = "f3", feature = "f4", feature = "wl")))] {
w.m1().bit(word_len_bits.0 != 0);
w.m0().bit(word_len_bits.1 != 0);
return w.ps().bit(result.config.parity == Parity::EnabledOdd);
} else {
return w.ps().bit(result.config.parity == Parity::EnabledOdd);
}
}
});
#[cfg(any(feature = "f3", feature = "f4"))]
result.regs.cr1.write(|w| unsafe {
w.bits(
result.regs.cr1.read().bits()
| ((word_len_bits.0 as u32) << 28)
| ((word_len_bits.1 as u32) << 12),
)
});
#[cfg(not(feature = "f4"))]
result
.regs
.cr3
.modify(|_, w| w.ovrdis().bit(result.config.overrun_disabled));
#[cfg(any(feature = "g4", feature = "h7"))]
result
.regs
.cr1
.modify(|_, w| w.fifoen().bit(result.config.fifo_enabled));
result.set_baud(baud, clock_cfg).ok();
result
.regs
.cr2
.modify(|_, w| unsafe { w.stop().bits(result.config.stop_bits as u8) });
result.enable();
cr1!(result.regs).modify(|_, w| {
w.te().set_bit();
w.re().set_bit()
});
match result.config.irda_mode {
IrdaMode::None => (),
_ => {
result.regs.cr2.modify(|_, w| unsafe {
w.linen().clear_bit();
w.stop().bits(0);
w.clken().clear_bit()
});
result.regs.cr3.modify(|_, w| {
w.scen().clear_bit();
w.hdsel().clear_bit();
w.irlp().bit(result.config.irda_mode == IrdaMode::LowPower);
w.iren().set_bit()
});
}
}
result
}
pub fn enable(&mut self) {
cr1!(self.regs).modify(|_, w| w.ue().set_bit());
while cr1!(self.regs).read().ue().bit_is_clear() {}
}
pub fn disable(&mut self) {
cr1!(self.regs).modify(|_, w| w.ue().clear_bit());
while cr1!(self.regs).read().ue().bit_is_set() {}
}
pub fn set_baud(&mut self, baud: u32, clock_cfg: &Clocks) -> Result<(), UartError> {
let originally_enabled = cr1!(self.regs).read().ue().bit_is_set();
if originally_enabled {
cr1!(self.regs).modify(|_, w| w.ue().clear_bit());
let mut i = 0;
while cr1!(self.regs).read().ue().bit_is_set() {
i += 1;
if i >= MAX_ITERS {
return Err(UartError::Hardware);
}
}
}
let fclk = R::baud(clock_cfg);
let usart_div = match self.config.oversampling {
OverSampling::O16 => fclk / baud,
OverSampling::O8 => 2 * fclk / baud,
};
self.regs.brr.write(|w| unsafe { w.bits(usart_div as u32) });
self.baud = baud;
if originally_enabled {
cr1!(self.regs).modify(|_, w| w.ue().set_bit());
}
Ok(())
}
pub fn write(&mut self, data: &[u8]) -> Result<(), UartError> {
cfg_if! {
if #[cfg(not(feature = "f4"))] {
for word in data {
let mut i = 0;
#[cfg(feature = "h5")]
while isr!(self.regs).read().txfe().bit_is_clear() {
i += 1;
if i >= MAX_ITERS {
}
}
#[cfg(not(feature = "h5"))]
while isr!(self.regs).read().txe().bit_is_clear() {
i += 1;
if i >= MAX_ITERS {
return Err(UartError::Hardware);
}
}
self.regs
.tdr
.modify(|_, w| unsafe { w.tdr().bits(*word as u16) });
}
let mut i = 0;
while isr!(self.regs).read().tc().bit_is_clear() {
i += 1;
if i >= MAX_ITERS {
return Err(UartError::Hardware);
}
}
} else {
for word in data {
let mut i = 0;
while self.regs.sr.read().txe().bit_is_clear() {
i += 1;
if i >= MAX_ITERS {
return Err(UartError::Hardware);
}
}
self.regs
.dr
.modify(|_, w| unsafe { w.dr().bits(*word as u16) });
}
let mut i = 0;
while self.regs.sr.read().tc().bit_is_clear() {
i += 1;
if i >= MAX_ITERS {
return Err(UartError::Hardware);
}
}
}
}
Ok(())
}
pub fn write_one(&mut self, word: u8) {
cfg_if! {
if #[cfg(not(feature = "f4"))] {
self.regs
.tdr
.modify(|_, w| unsafe { w.tdr().bits(word as u16) });
} else {
self.regs
.dr
.modify(|_, w| unsafe { w.dr().bits(word as u16) });
}
}
}
pub fn read(&mut self, buf: &mut [u8]) -> Result<(), UartError> {
for i in 0..buf.len() {
let mut i_ = 0;
cfg_if! {
if #[cfg(not(feature = "f4"))] {
#[cfg(feature = "h5")]
while isr!(self.regs).read().rxfne().bit_is_clear() {
i_ += 1;
if i_ >= MAX_ITERS {
return Err(UartError::Hardware);
}
}
#[cfg(not(feature = "h5"))]
while isr!(self.regs).read().rxne().bit_is_clear() {
i_ += 1;
if i_ >= MAX_ITERS {
return Err(UartError::Hardware);
}
}
buf[i] = self.regs.rdr.read().rdr().bits() as u8;
} else {
while self.regs.sr.read().rxne().bit_is_clear() {
i_ += 1;
if i_ >= MAX_ITERS {
return Err(UartError::Hardware);
}
}
buf[i] = self.regs.dr.read().dr().bits() as u8;
}
}
}
Ok(())
}
pub fn read_one(&mut self) -> u8 {
cfg_if! {
if #[cfg(not(feature = "f4"))] {
self.regs.rdr.read().rdr().bits() as u8
} else {
self.regs.dr.read().dr().bits() as u8
}
}
}
#[cfg(not(any(feature = "f4", feature = "l552", feature = "h5")))]
pub unsafe fn write_dma(
&mut self,
buf: &[u8],
channel: DmaChannel,
channel_cfg: ChannelCfg,
dma_periph: dma::DmaPeriph,
) {
let (ptr, len) = (buf.as_ptr(), buf.len());
#[cfg(any(feature = "f3", feature = "l4"))]
let channel = R::write_chan();
#[cfg(feature = "l4")]
let mut dma_regs = unsafe { &(*DMA1::ptr()) }; #[cfg(feature = "l4")]
R::write_sel(&mut dma_regs);
#[cfg(feature = "h7")]
let num_data = len as u32;
#[cfg(not(feature = "h7"))]
let num_data = len as u16;
self.regs.cr3.modify(|_, w| w.dmat().set_bit());
self.regs.icr.write(|w| w.tccf().set_bit());
match dma_periph {
dma::DmaPeriph::Dma1 => {
let mut regs = unsafe { &(*DMA1::ptr()) };
dma::cfg_channel(
&mut regs,
channel,
&self.regs.tdr as *const _ as u32,
ptr as u32,
num_data,
dma::Direction::ReadFromMem,
dma::DataSize::S8,
dma::DataSize::S8,
channel_cfg,
);
}
#[cfg(not(any(feature = "f3x4", feature = "g0", feature = "wb")))]
dma::DmaPeriph::Dma2 => {
let mut regs = unsafe { &(*pac::DMA2::ptr()) };
dma::cfg_channel(
&mut regs,
channel,
&self.regs.tdr as *const _ as u32,
ptr as u32,
num_data,
dma::Direction::ReadFromMem,
dma::DataSize::S8,
dma::DataSize::S8,
channel_cfg,
);
}
}
}
#[cfg(not(any(feature = "f4", feature = "l552", feature = "h5")))]
pub unsafe fn read_dma(
&mut self,
buf: &mut [u8],
channel: DmaChannel,
channel_cfg: ChannelCfg,
dma_periph: dma::DmaPeriph,
) {
let (ptr, len) = (buf.as_mut_ptr(), buf.len());
#[cfg(any(feature = "f3", feature = "l4"))]
let channel = R::read_chan();
#[cfg(feature = "l4")]
let mut dma_regs = unsafe { &(*DMA1::ptr()) }; #[cfg(feature = "l4")]
R::write_sel(&mut dma_regs);
#[cfg(feature = "h7")]
let num_data = len as u32;
#[cfg(not(feature = "h7"))]
let num_data = len as u16;
self.regs.cr3.modify(|_, w| w.dmar().set_bit());
match dma_periph {
dma::DmaPeriph::Dma1 => {
let mut regs = unsafe { &(*DMA1::ptr()) };
dma::cfg_channel(
&mut regs,
channel,
&self.regs.rdr as *const _ as u32,
ptr as u32,
num_data,
dma::Direction::ReadFromPeriph,
dma::DataSize::S8,
dma::DataSize::S8,
channel_cfg,
);
}
#[cfg(not(any(feature = "f3x4", feature = "g0", feature = "wb")))]
dma::DmaPeriph::Dma2 => {
let mut regs = unsafe { &(*pac::DMA2::ptr()) };
dma::cfg_channel(
&mut regs,
channel,
&self.regs.rdr as *const _ as u32,
ptr as u32,
num_data,
dma::Direction::ReadFromPeriph,
dma::DataSize::S8,
dma::DataSize::S8,
channel_cfg,
);
}
}
}
#[cfg(not(feature = "f4"))]
pub fn enable_interrupt(&mut self, interrupt: UsartInterrupt) {
match interrupt {
UsartInterrupt::CharDetect(char_wrapper) => {
if let Some(char) = char_wrapper {
cr1!(self.regs).modify(|_, w| w.ue().clear_bit());
while cr1!(self.regs).read().ue().bit_is_set() {}
cr1!(self.regs).modify(|_, w| w.cmie().set_bit());
self.regs.cr2.modify(|_, w| unsafe {
w.addm7().set_bit();
cfg_if! {
if #[cfg(any(feature = "l5", feature = "g4", feature = "wb"))] {
w.add0_3().bits(char); w.add4_7().bits(char >> 4)
} else {
w.add().bits(char)
}
}
});
cr1!(self.regs).modify(|_, w| w.ue().set_bit());
while cr1!(self.regs).read().ue().bit_is_clear() {}
}
cr1!(self.regs).modify(|_, w| w.cmie().set_bit());
}
UsartInterrupt::Cts => {
self.regs.cr3.modify(|_, w| w.ctsie().set_bit());
}
UsartInterrupt::EndOfBlock => {
cr1!(self.regs).modify(|_, w| w.eobie().set_bit());
}
UsartInterrupt::Idle => {
cr1!(self.regs).modify(|_, w| w.idleie().set_bit());
}
UsartInterrupt::FramingError => {
self.regs.cr3.modify(|_, w| w.eie().set_bit());
}
UsartInterrupt::LineBreak => {
self.regs.cr2.modify(|_, w| w.lbdie().set_bit());
}
UsartInterrupt::Overrun => {
self.regs.cr3.modify(|_, w| w.eie().set_bit());
}
UsartInterrupt::ParityError => {
cr1!(self.regs).modify(|_, w| w.peie().set_bit());
}
UsartInterrupt::ReadNotEmpty => {
#[cfg(feature = "h5")]
cr1!(self.regs).modify(|_, w| w.rxfneie().set_bit());
#[cfg(not(feature = "h5"))]
cr1!(self.regs).modify(|_, w| w.rxneie().set_bit());
}
UsartInterrupt::ReceiverTimeout => {
cr1!(self.regs).modify(|_, w| w.rtoie().set_bit());
}
#[cfg(not(any(feature = "f3", feature = "l4")))]
UsartInterrupt::Tcbgt => {
self.regs.cr3.modify(|_, w| w.tcbgtie().set_bit());
self.regs.cr3.modify(|_, w| w.tcbgtie().set_bit());
}
UsartInterrupt::TransmissionComplete => {
cr1!(self.regs).modify(|_, w| w.tcie().set_bit());
}
UsartInterrupt::TransmitEmpty => {
#[cfg(feature = "h5")]
cr1!(self.regs).modify(|_, w| w.txfeie().set_bit());
#[cfg(not(feature = "h5"))]
cr1!(self.regs).modify(|_, w| w.txeie().set_bit());
}
}
}
#[cfg(not(feature = "f4"))]
pub fn disable_interrupt(&mut self, interrupt: UsartInterrupt) {
match interrupt {
UsartInterrupt::CharDetect(_) => {
cr1!(self.regs).modify(|_, w| w.cmie().clear_bit());
}
UsartInterrupt::Cts => {
self.regs.cr3.modify(|_, w| w.ctsie().clear_bit());
}
UsartInterrupt::EndOfBlock => {
cr1!(self.regs).modify(|_, w| w.eobie().clear_bit());
}
UsartInterrupt::Idle => {
cr1!(self.regs).modify(|_, w| w.idleie().clear_bit());
}
UsartInterrupt::FramingError => {
self.regs.cr3.modify(|_, w| w.eie().clear_bit());
}
UsartInterrupt::LineBreak => {
self.regs.cr2.modify(|_, w| w.lbdie().clear_bit());
}
UsartInterrupt::Overrun => {
self.regs.cr3.modify(|_, w| w.eie().clear_bit());
}
UsartInterrupt::ParityError => {
cr1!(self.regs).modify(|_, w| w.peie().clear_bit());
}
UsartInterrupt::ReadNotEmpty => {
#[cfg(feature = "h5")]
cr1!(self.regs).modify(|_, w| w.rxfneie().clear_bit());
#[cfg(not(feature = "h5"))]
cr1!(self.regs).modify(|_, w| w.rxneie().clear_bit());
}
UsartInterrupt::ReceiverTimeout => {
cr1!(self.regs).modify(|_, w| w.rtoie().clear_bit());
}
#[cfg(not(any(feature = "f3", feature = "l4")))]
UsartInterrupt::Tcbgt => {
self.regs.cr3.modify(|_, w| w.tcbgtie().clear_bit());
self.regs.cr3.modify(|_, w| w.tcbgtie().clear_bit());
}
UsartInterrupt::TransmissionComplete => {
cr1!(self.regs).modify(|_, w| w.tcie().clear_bit());
}
UsartInterrupt::TransmitEmpty => {
#[cfg(feature = "h5")]
cr1!(self.regs).modify(|_, w| w.txfeie().clear_bit());
#[cfg(not(feature = "h5"))]
cr1!(self.regs).modify(|_, w| w.txeie().clear_bit());
}
}
}
#[cfg(not(feature = "f4"))]
pub fn read_status(&self) -> u32 {
unsafe { isr!(self.regs).read().bits() }
}
#[cfg(not(feature = "f4"))]
pub fn clear_interrupt(&mut self, interrupt: UsartInterrupt) {
match interrupt {
UsartInterrupt::CharDetect(_) => self.regs.icr.write(|w| w.cmcf().set_bit()),
UsartInterrupt::Cts => self.regs.icr.write(|w| w.ctscf().set_bit()),
UsartInterrupt::EndOfBlock => self.regs.icr.write(|w| w.eobcf().set_bit()),
UsartInterrupt::Idle => self.regs.icr.write(|w| w.idlecf().set_bit()),
UsartInterrupt::FramingError => self.regs.icr.write(|w| w.fecf().set_bit()),
UsartInterrupt::LineBreak => self.regs.icr.write(|w| w.lbdcf().set_bit()),
UsartInterrupt::Overrun => self.regs.icr.write(|w| w.orecf().set_bit()),
UsartInterrupt::ParityError => self.regs.icr.write(|w| w.pecf().set_bit()),
UsartInterrupt::ReadNotEmpty => self.regs.rqr.write(|w| w.rxfrq().set_bit()),
UsartInterrupt::ReceiverTimeout => self.regs.icr.write(|w| w.rtocf().set_bit()),
#[cfg(not(any(feature = "f3", feature = "l4", feature = "h7")))]
UsartInterrupt::Tcbgt => self.regs.icr.write(|w| w.tcbgtcf().set_bit()),
#[cfg(feature = "h7")]
UsartInterrupt::Tcbgt => self.regs.icr.write(|w| w.tcbgtc().set_bit()),
UsartInterrupt::TransmissionComplete => self.regs.icr.write(|w| w.tccf().set_bit()),
UsartInterrupt::TransmitEmpty => self.regs.rqr.write(|w| w.txfrq().set_bit()),
}
}
#[cfg(not(feature = "f4"))]
pub fn check_status_flag(&mut self, flag: UsartInterrupt) -> bool {
let status = isr!(self.regs).read();
match flag {
UsartInterrupt::CharDetect(_) => status.cmf().bit_is_set(),
UsartInterrupt::Cts => status.cts().bit_is_set(),
UsartInterrupt::EndOfBlock => status.eobf().bit_is_set(),
UsartInterrupt::Idle => status.idle().bit_is_set(),
UsartInterrupt::FramingError => status.fe().bit_is_set(),
UsartInterrupt::LineBreak => status.lbdf().bit_is_set(),
UsartInterrupt::Overrun => status.ore().bit_is_set(),
UsartInterrupt::ParityError => status.pe().bit_is_set(),
#[cfg(feature = "h5")]
UsartInterrupt::ReadNotEmpty => status.rxfne().bit_is_set(),
#[cfg(not(feature = "h5"))]
UsartInterrupt::ReadNotEmpty => status.rxne().bit_is_set(),
UsartInterrupt::ReceiverTimeout => status.rtof().bit_is_set(),
#[cfg(not(any(feature = "f3", feature = "l4")))]
UsartInterrupt::Tcbgt => status.tcbgt().bit_is_set(),
UsartInterrupt::TransmissionComplete => status.tc().bit_is_set(),
#[cfg(feature = "h5")]
UsartInterrupt::TransmitEmpty => status.txfe().bit_is_set(),
#[cfg(not(feature = "h5"))]
UsartInterrupt::TransmitEmpty => status.txe().bit_is_set(),
}
}
}