use atsamd_hal_macros::hal_cfg;
use super::{BaudMode, BitOrder, CharSizeEnum, Flags, Oversampling, Parity, Status, StopBits};
use crate::pac;
use crate::sercom::Sercom;
#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
use pac::sercom0::usart::ctrla::Modeselect;
#[hal_cfg("sercom0-d5x")]
use pac::sercom0::usart_int::ctrla::Modeselect;
use crate::time::Hertz;
pub(super) struct Registers<S: Sercom> {
sercom: S,
}
unsafe impl<S: Sercom> Sync for Registers<S> {}
impl<S: Sercom> Registers<S> {
#[inline]
pub(super) fn new(sercom: S) -> Self {
Self { sercom }
}
#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
#[inline]
fn usart(&self) -> &pac::sercom0::Usart {
self.sercom.usart()
}
#[hal_cfg("sercom0-d5x")]
#[inline]
fn usart(&self) -> &pac::sercom0::UsartInt {
self.sercom.usart_int()
}
#[cfg(feature = "dma")]
pub(super) fn data_ptr<T>(&self) -> *mut T {
self.usart().data().as_ptr() as *mut _
}
#[inline]
pub(super) fn free(self) -> S {
self.sercom
}
#[inline]
pub(super) fn swrst(&mut self) {
self.usart().ctrla().write(|w| w.swrst().set_bit());
while self.usart().syncbusy().read().swrst().bit_is_set() {}
}
#[inline]
pub(super) fn configure_mode(&mut self) {
self.usart()
.ctrla()
.modify(|_, w| w.mode().variant(Modeselect::UsartIntClk));
}
#[inline]
pub(super) fn configure_pads(&mut self, rxpo: u8, txpo: u8) {
self.usart().ctrla().modify(|_, w| unsafe {
w.rxpo().bits(rxpo);
w.txpo().bits(txpo)
});
}
#[inline]
pub(super) fn set_char_size(&mut self, size: CharSizeEnum) {
self.usart()
.ctrlb()
.modify(|_, w| unsafe { w.chsize().bits(size as u8) });
}
#[inline]
pub(super) fn get_char_size(&self) -> CharSizeEnum {
let size = self.usart().ctrlb().read().chsize().bits();
match size {
0x5 => CharSizeEnum::FiveBit,
0x6 => CharSizeEnum::SixBit,
0x7 => CharSizeEnum::SevenBit,
0x0 => CharSizeEnum::EightBit,
0x1 => CharSizeEnum::NineBit,
_ => unreachable!(),
}
}
#[inline]
pub(super) fn set_bit_order(&mut self, bit_order: BitOrder) {
let bits = match bit_order {
BitOrder::MsbFirst => false,
BitOrder::LsbFirst => true,
};
self.usart().ctrla().modify(|_, w| w.dord().bit(bits));
}
#[inline]
pub(super) fn get_bit_order(&self) -> BitOrder {
let bits = self.usart().ctrla().read().dord().bit();
match bits {
false => BitOrder::MsbFirst,
true => BitOrder::LsbFirst,
}
}
#[inline]
pub(super) fn set_parity(&mut self, parity: Parity) {
let enabled = match parity {
Parity::None => false,
Parity::Odd => {
self.usart().ctrlb().modify(|_, w| w.pmode().bit(true));
true
}
Parity::Even => {
self.usart().ctrlb().modify(|_, w| w.pmode().bit(false));
true
}
};
self.usart()
.ctrla()
.modify(|_, w| unsafe { w.form().bits(enabled as u8) });
}
#[inline]
pub(super) fn get_parity(&self) -> Parity {
let form = self.usart().ctrla().read().form().bits();
let enabled = form == 0x1 || form == 0x5;
if !enabled {
return Parity::None;
}
let pmode = self.usart().ctrlb().read().pmode().bit();
match pmode {
false => Parity::Even,
true => Parity::Odd,
}
}
#[inline]
pub(super) fn set_stop_bits(&mut self, stop_bits: StopBits) {
let bits = match stop_bits {
StopBits::OneBit => false,
StopBits::TwoBits => true,
};
self.usart().ctrlb().modify(|_, w| w.sbmode().bit(bits));
}
#[inline]
pub(super) fn get_stop_bits(&self) -> StopBits {
let bits = self.usart().ctrlb().read().sbmode().bit();
match bits {
false => StopBits::OneBit,
true => StopBits::TwoBits,
}
}
#[inline]
pub(super) fn set_start_of_frame_detection(&mut self, enabled: bool) {
self.usart().ctrlb().modify(|_, w| w.sfde().bit(enabled));
}
#[inline]
pub(super) fn get_start_of_frame_detection(&self) -> bool {
self.usart().ctrlb().read().sfde().bit()
}
#[inline]
pub(super) fn set_collision_detection(&mut self, enabled: bool) {
self.usart().ctrlb().modify(|_, w| w.colden().bit(enabled));
}
#[inline]
pub(super) fn get_collision_detection(&self) -> bool {
self.usart().ctrlb().read().colden().bit()
}
#[inline]
pub(super) fn set_baud(&mut self, freq: Hertz, baud: Hertz, mode: BaudMode) {
use BaudMode::*;
use Oversampling::*;
let usart = self.usart();
let sampr = match mode {
Arithmetic(n) => match n {
Bits16 => 0,
Bits8 => 2,
},
Fractional(n) => match n {
Bits16 => 1,
Bits8 => 3,
},
};
usart
.ctrla()
.modify(|_, w| unsafe { w.sampr().bits(sampr) });
match mode {
BaudMode::Arithmetic(n) => {
let baud = calculate_baud_asynchronous_arithm(baud.to_Hz(), freq.to_Hz(), n as u8);
unsafe { usart.baud_usartfp_mode().write(|w| w.baud().bits(baud)) };
}
BaudMode::Fractional(n) => {
let (baud, frac) =
calculate_baud_asynchronous_fractional(baud.to_Hz(), freq.to_Hz(), n as u8);
unsafe {
usart.baud_frac_mode().write(|w| {
w.fp().bits(frac);
w.baud().bits(baud)
});
}
}
};
}
#[inline]
pub(super) fn get_baud(&self) -> (u16, BaudMode) {
use BaudMode::*;
use Oversampling::*;
let baud = self.usart().baud_usartfp_mode().read().bits();
let sampr = self.usart().ctrla().read().sampr().bits();
let mode = match sampr {
0 => Arithmetic(Bits16),
1 => Fractional(Bits16),
2 => Arithmetic(Bits8),
3 => Fractional(Bits8),
_ => unreachable!(),
};
(baud, mode)
}
#[inline]
pub(super) fn set_immediate_overflow_notification(&mut self, set: bool) {
self.usart().ctrla().modify(|_, w| w.ibon().bit(set));
}
#[inline]
pub(super) fn get_immediate_overflow_notification(&self) -> bool {
self.usart().ctrla().read().ibon().bit()
}
#[inline]
pub(super) fn set_run_in_standby(&mut self, set: bool) {
self.usart().ctrla().modify(|_, w| w.runstdby().bit(set));
}
#[inline]
pub(super) fn get_run_in_standby(&self) -> bool {
self.usart().ctrla().read().runstdby().bit()
}
#[inline]
pub(super) fn set_irda_encoding(&mut self, pulse_length: Option<u8>) {
match pulse_length {
Some(l) => {
self.usart().rxpl().write(|w| unsafe { w.rxpl().bits(l) });
self.usart().ctrlb().modify(|_, w| w.enc().bit(true));
}
None => {
self.usart().ctrlb().modify(|_, w| w.enc().bit(false));
}
}
}
#[inline]
pub(super) fn get_irda_encoding(&self) -> Option<u8> {
if self.usart().ctrlb().read().enc().bit() {
Some(self.usart().rxpl().read().bits())
} else {
None
}
}
#[inline]
pub(super) fn clear_flags(&mut self, flags: Flags) {
self.usart()
.intflag()
.modify(|_, w| unsafe { w.bits(flags.bits()) });
}
#[inline]
pub(super) fn read_flags(&self) -> Flags {
Flags::from_bits_truncate(self.usart().intflag().read().bits())
}
#[inline]
pub(super) fn enable_interrupts(&mut self, flags: Flags) {
self.usart()
.intenset()
.write(|w| unsafe { w.bits(flags.bits()) });
}
#[inline]
pub(super) fn disable_interrupts(&mut self, flags: Flags) {
self.usart()
.intenclr()
.write(|w| unsafe { w.bits(flags.bits()) });
}
#[inline]
pub(super) fn clear_status(&mut self, status: Status) {
self.usart()
.status()
.modify(|_, w| unsafe { w.bits(status.bits()) });
}
#[inline]
pub(super) fn read_status(&self) -> Status {
Status::from_bits_truncate(self.usart().status().read().bits())
}
#[inline]
pub(super) unsafe fn read_data(&mut self) -> super::DataReg {
self.usart().data().read().data().bits()
}
#[inline]
pub(super) unsafe fn write_data(&mut self, data: super::DataReg) {
self.usart().data().write(|w| w.data().bits(data))
}
#[inline]
pub(super) fn enable(&mut self, rxen: bool, txen: bool) {
let usart = self.usart();
if rxen {
usart.ctrlb().modify(|_, w| w.rxen().set_bit());
while usart.syncbusy().read().ctrlb().bit_is_set() {}
}
if txen {
usart.ctrlb().modify(|_, w| w.txen().set_bit());
while usart.syncbusy().read().ctrlb().bit_is_set() {}
}
self.enable_peripheral(true);
}
#[inline]
pub(super) fn disable(&mut self) {
let usart = self.usart();
usart.ctrlb().modify(|_, w| w.rxen().clear_bit());
while usart.syncbusy().read().ctrlb().bit_is_set() {}
usart.ctrlb().modify(|_, w| w.txen().clear_bit());
while usart.syncbusy().read().ctrlb().bit_is_set() {}
self.enable_peripheral(false);
}
pub(super) fn enable_peripheral(&mut self, enable: bool) {
self.usart().ctrla().modify(|_, w| w.enable().bit(enable));
while self.usart().syncbusy().read().enable().bit_is_set() {}
}
}
#[inline]
fn calculate_baud_asynchronous_arithm(baudrate: u32, clk_freq: u32, n_samples: u8) -> u16 {
const SHIFT: u8 = 32;
let sample_rate = (n_samples as u64 * baudrate as u64) << SHIFT;
let ratio = sample_rate / clk_freq as u64;
let scale = (1u64 << SHIFT) - ratio;
let baud_calculated = (65536u64 * scale) >> SHIFT;
baud_calculated as u16
}
#[inline]
fn calculate_baud_asynchronous_fractional(
baudrate: u32,
clk_freq: u32,
n_samples: u8,
) -> (u16, u8) {
let baud_mult = (clk_freq * 8) / (n_samples as u32 * baudrate);
((baud_mult / 8) as u16, (baud_mult % 8) as u8)
}