use crate::clock::{Aclk, Clock, Smclk};
use crate::gpio::{Alternate1, Pin, Pin4, Pin5, Pin6, P1, P2};
use crate::hw_traits::eusci::{EUsciUart, UcaxStatw, Ucssel, UcxCtl0};
use core::marker::PhantomData;
use embedded_hal::serial::{Read, Write};
use msp430fr247x as pac;
#[derive(Clone, Copy)]
pub enum BitOrder {
LsbFirst,
MsbFirst,
}
impl BitOrder {
#[inline(always)]
fn to_bool(self) -> bool {
match self {
BitOrder::LsbFirst => false,
BitOrder::MsbFirst => true,
}
}
}
#[derive(Clone, Copy)]
pub enum BitCount {
EightBits,
SevenBits,
}
impl BitCount {
#[inline(always)]
fn to_bool(self) -> bool {
match self {
BitCount::EightBits => false,
BitCount::SevenBits => true,
}
}
}
#[derive(Clone, Copy)]
pub enum StopBits {
OneStopBit,
TwoStopBits,
}
impl StopBits {
#[inline(always)]
fn to_bool(self) -> bool {
match self {
StopBits::OneStopBit => false,
StopBits::TwoStopBits => true,
}
}
}
#[derive(Clone, Copy)]
pub enum Parity {
NoParity,
OddParity,
EvenParity,
}
impl Parity {
#[inline(always)]
fn ucpen(self) -> bool {
match self {
Parity::NoParity => false,
_ => true,
}
}
#[inline(always)]
fn ucpar(self) -> bool {
match self {
Parity::OddParity => false,
Parity::EvenParity => true,
_ => false,
}
}
}
#[derive(Clone, Copy)]
pub enum Loopback {
NoLoop,
Loopback,
}
impl Loopback {
#[inline(always)]
fn to_bool(self) -> bool {
match self {
Loopback::NoLoop => false,
Loopback::Loopback => true,
}
}
}
pub trait SerialUsci: EUsciUart {
type ClockPin;
type TxPin;
type RxPin;
}
impl SerialUsci for pac::E_USCI_A0 {
type ClockPin = UsciA0ClockPin;
type TxPin = UsciA0TxPin;
type RxPin = UsciA0RxPin;
}
pub struct UsciA0ClockPin;
impl<DIR> Into<UsciA0ClockPin> for Pin<P1, Pin6, Alternate1<DIR>> {
#[inline(always)]
fn into(self) -> UsciA0ClockPin {
UsciA0ClockPin
}
}
pub struct UsciA0TxPin;
impl<DIR> Into<UsciA0TxPin> for Pin<P1, Pin4, Alternate1<DIR>> {
#[inline(always)]
fn into(self) -> UsciA0TxPin {
UsciA0TxPin
}
}
pub struct UsciA0RxPin;
impl<DIR> Into<UsciA0RxPin> for Pin<P1, Pin5, Alternate1<DIR>> {
#[inline(always)]
fn into(self) -> UsciA0RxPin {
UsciA0RxPin
}
}
impl SerialUsci for pac::E_USCI_A1 {
type ClockPin = UsciA1ClockPin;
type TxPin = UsciA1TxPin;
type RxPin = UsciA1RxPin;
}
pub struct UsciA1ClockPin;
impl<DIR> Into<UsciA1ClockPin> for Pin<P2, Pin4, Alternate1<DIR>> {
#[inline(always)]
fn into(self) -> UsciA1ClockPin {
UsciA1ClockPin
}
}
pub struct UsciA1TxPin;
impl<DIR> Into<UsciA1TxPin> for Pin<P2, Pin6, Alternate1<DIR>> {
#[inline(always)]
fn into(self) -> UsciA1TxPin {
UsciA1TxPin
}
}
pub struct UsciA1RxPin;
impl<DIR> Into<UsciA1RxPin> for Pin<P2, Pin5, Alternate1<DIR>> {
#[inline(always)]
fn into(self) -> UsciA1RxPin {
UsciA1RxPin
}
}
pub struct NoClockSet {
baudrate: u32,
}
pub struct ClockSet {
baud_config: BaudConfig,
clksel: Ucssel,
}
pub struct SerialConfig<USCI: SerialUsci, S> {
usci: USCI,
order: BitOrder,
cnt: BitCount,
stopbits: StopBits,
parity: Parity,
loopback: Loopback,
state: S,
}
macro_rules! serial_config {
($conf:expr, $state:expr) => {
SerialConfig {
usci: $conf.usci,
order: $conf.order,
cnt: $conf.cnt,
stopbits: $conf.stopbits,
parity: $conf.parity,
loopback: $conf.loopback,
state: $state,
}
};
}
impl<USCI: SerialUsci> SerialConfig<USCI, NoClockSet> {
#[inline]
pub fn new(
usci: USCI,
order: BitOrder,
cnt: BitCount,
stopbits: StopBits,
parity: Parity,
loopback: Loopback,
baudrate: u32,
) -> Self {
SerialConfig {
order,
cnt,
stopbits,
parity,
loopback,
usci,
state: NoClockSet { baudrate },
}
}
#[inline(always)]
pub fn use_uclk<P: Into<USCI::ClockPin>>(
self,
_clk_pin: P,
freq: u32,
) -> SerialConfig<USCI, ClockSet> {
serial_config!(
self,
ClockSet {
baud_config: calculate_baud_config(freq, self.state.baudrate),
clksel: Ucssel::Uclk,
}
)
}
#[inline(always)]
pub fn use_aclk(self, aclk: &Aclk) -> SerialConfig<USCI, ClockSet> {
serial_config!(
self,
ClockSet {
baud_config: calculate_baud_config(aclk.freq() as u32, self.state.baudrate),
clksel: Ucssel::Aclk,
}
)
}
#[inline(always)]
pub fn use_smclk(self, smclk: &Smclk) -> SerialConfig<USCI, ClockSet> {
serial_config!(
self,
ClockSet {
baud_config: calculate_baud_config(smclk.freq(), self.state.baudrate),
clksel: Ucssel::Smclk,
}
)
}
}
struct BaudConfig {
br: u16,
brs: u8,
brf: u8,
ucos16: bool,
}
#[inline]
fn calculate_baud_config(clk_freq: u32, bps: u32) -> BaudConfig {
let bps = bps.max(1);
let n = (clk_freq / bps).max(1).min(0xFFFF);
let brs = lookup_brs(clk_freq, bps);
if n >= 16 {
let div = bps * 16;
let br = (clk_freq / div) as u16;
let brf = ((clk_freq % div) / bps) as u8;
BaudConfig {
ucos16: true,
br,
brf,
brs,
}
} else {
BaudConfig {
ucos16: false,
br: n as u16,
brf: 0,
brs,
}
}
}
#[inline(always)]
fn lookup_brs(clk_freq: u32, bps: u32) -> u8 {
let modulo = clk_freq % bps;
if modulo * 19 < bps {
0x0
} else if modulo * 14 < bps {
0x1
} else if modulo * 12 < bps {
0x2
} else if modulo * 10 < bps {
0x4
} else if modulo * 8 < bps {
0x8
} else if modulo * 7 < bps {
0x10
} else if modulo * 6 < bps {
0x20
} else if modulo * 5 < bps {
0x11
} else if modulo * 4 < bps {
0x22
} else if modulo * 3 < bps {
0x44
} else if modulo * 11 < bps * 4 {
0x49
} else if modulo * 5 < bps * 2 {
0x4A
} else if modulo * 7 < bps * 3 {
0x92
} else if modulo * 2 < bps {
0x53
} else if modulo * 7 < bps * 4 {
0xAA
} else if modulo * 13 < bps * 8 {
0x6B
} else if modulo * 3 < bps * 2 {
0xAD
} else if modulo * 11 < bps * 8 {
0xD6
} else if modulo * 4 < bps * 3 {
0xBB
} else if modulo * 5 < bps * 4 {
0xDD
} else if modulo * 9 < bps * 8 {
0xEF
} else {
0xFD
}
}
impl<USCI: SerialUsci> SerialConfig<USCI, ClockSet> {
#[inline]
fn config_hw(self) {
let ClockSet {
baud_config,
clksel,
} = self.state;
let usci = self.usci;
usci.ctl0_reset();
usci.brw_settings(baud_config.br);
usci.mctlw_settings(baud_config.ucos16, baud_config.brs, baud_config.brf);
usci.loopback(self.loopback.to_bool());
usci.ctl0_settings(UcxCtl0 {
ucpen: self.parity.ucpen(),
ucpar: self.parity.ucpar(),
ucmsb: self.order.to_bool(),
uc7bit: self.cnt.to_bool(),
ucspb: self.stopbits.to_bool(),
ucssel: clksel,
ucrxeie: true,
});
}
#[inline]
pub fn split<T: Into<USCI::TxPin>, R: Into<USCI::RxPin>>(
self,
_tx: T,
_rx: R,
) -> (Tx<USCI>, Rx<USCI>) {
self.config_hw();
(Tx(PhantomData), Rx(PhantomData))
}
#[inline]
pub fn tx_only<T: Into<USCI::TxPin>>(self, _tx: T) -> Tx<USCI> {
self.config_hw();
Tx(PhantomData)
}
#[inline]
pub fn rx_only<R: Into<USCI::RxPin>>(self, _rx: R) -> Rx<USCI> {
self.config_hw();
Rx(PhantomData)
}
}
pub struct Tx<USCI: SerialUsci>(PhantomData<USCI>);
impl<USCI: SerialUsci> Tx<USCI> {
#[inline(always)]
pub fn enable_tx_interrupts(&mut self) {
let usci = unsafe { USCI::steal() };
usci.txie_set();
}
#[inline(always)]
pub fn disable_tx_interrupts(&mut self) {
let usci = unsafe { USCI::steal() };
usci.txie_clear();
}
}
impl<USCI: SerialUsci> Write<u8> for Tx<USCI> {
type Error = void::Void;
#[inline]
fn flush(&mut self) -> nb::Result<(), Self::Error> {
let usci = unsafe { USCI::steal() };
if usci.txifg_rd() {
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
#[inline]
fn write(&mut self, data: u8) -> nb::Result<(), Self::Error> {
let usci = unsafe { USCI::steal() };
if usci.txifg_rd() {
usci.tx_wr(data);
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
}
impl<USCI: SerialUsci> embedded_hal::blocking::serial::write::Default<u8> for Tx<USCI> {}
pub struct Rx<USCI: SerialUsci>(PhantomData<USCI>);
impl<USCI: SerialUsci> Rx<USCI> {
#[inline(always)]
pub fn enable_rx_interrupts(&mut self) {
let usci = unsafe { USCI::steal() };
usci.rxie_set();
}
#[inline(always)]
pub fn disable_rx_interrupts(&mut self) {
let usci = unsafe { USCI::steal() };
usci.rxie_clear();
}
}
pub enum RecvError {
Framing,
Parity,
Overrun(u8),
}
impl<USCI: SerialUsci> Read<u8> for Rx<USCI> {
type Error = RecvError;
#[inline]
fn read(&mut self) -> nb::Result<u8, Self::Error> {
let usci = unsafe { USCI::steal() };
if usci.rxifg_rd() {
let statw = usci.statw_rd();
let data = usci.rx_rd();
if statw.ucfe() {
Err(nb::Error::Other(RecvError::Framing))
} else if statw.ucpe() {
Err(nb::Error::Other(RecvError::Parity))
} else if statw.ucoe() {
Err(nb::Error::Other(RecvError::Overrun(data)))
} else {
Ok(data)
}
} else {
Err(nb::Error::WouldBlock)
}
}
}