use core::any::Any;
use ufmt::derive::uDebug;
use avr_oxide::concurrency::Isolated;
use oxide_macros::Persist;
use avr_oxide::hal::generic::callback::IsrCallback;
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum SerialPortIdentity {
Usart0,
Usart1,
Usart2,
Usart3
}
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum BaudRate {
Baud300,
Baud600,
Baud1200,
Baud2400,
Baud4800,
Baud9600,
Baud14400,
Baud19200,
Baud28800,
Baud38400,
Baud57600,
Baud76800,
Baud115200,
Auto
}
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum SynchronousMode {
Master(BaudRate),
Slave
}
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum DataBits {
Bits5,
Bits6,
Bits7,
Bits8,
Bits9LE,
Bits9HE
}
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum Parity {
None,
Even,
Odd
}
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum StopBits {
Bits1,
Bits2
}
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum SerialPortMode {
Asynch(BaudRate,DataBits,Parity,StopBits),
Synch(SynchronousMode)
}
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum SerialError {
BufferOverflow,
FrameError,
ParityError,
AutoBaudDetectFail,
Break
}
#[derive(Copy,Clone,Debug)]
pub enum ReadHandlerResult<T> {
Buffer(T),
Discard
}
pub type SerialReadEventHandlerFunction = fn(Isolated, SerialPortIdentity, u8, Option<*const dyn Any>) ->ReadHandlerResult<u8>;
pub type SerialReadEventCallback = IsrCallback<SerialReadEventHandlerFunction,ReadHandlerResult<u8>>;
pub type SerialErrorEventHandlerFunction = fn(Isolated, SerialPortIdentity, SerialError, Option<*const dyn Any>) ->();
pub type SerialErrorEventCallback = IsrCallback<SerialErrorEventHandlerFunction,()>;
pub type SerialWriteEventHandlerFunction = fn(Isolated, SerialPortIdentity, Option<*const dyn Any>) ->();
pub type SerialWriteEventCallback = IsrCallback<SerialWriteEventHandlerFunction,()>;
pub trait SerialRxTx {
fn set_mode(&mut self, mode: SerialPortMode);
fn set_enable_rxtx(&mut self, enable: bool);
fn set_write_complete_callback(&self, handler: SerialWriteEventCallback);
fn set_error_callback(&self, handler: SerialErrorEventCallback);
fn set_read_callback(&self, handler: SerialReadEventCallback);
fn write_u8(&mut self, byte: u8);
fn flush(&mut self);
fn read_u8(&mut self) -> u8;
fn try_read_u8(&mut self) -> Option<u8>;
}
pub(crate) trait SerialNoInterruptTx {
fn can_write(&self) -> bool;
fn blocking_write_u8(&mut self, byte: u8);
fn blocking_write_slice(&mut self, bytes: &[u8]){
for byte in bytes {
self.blocking_write_u8(*byte);
}
}
}
#[allow(dead_code)]
impl BaudRate {
pub fn to_bps(&self) -> u32 {
match self {
BaudRate::Baud300 => 300,
BaudRate::Baud600 => 600,
BaudRate::Baud1200 => 1200,
BaudRate::Baud2400 => 2400,
BaudRate::Baud4800 => 4800,
BaudRate::Baud9600 => 9600,
BaudRate::Baud14400 => 14400,
BaudRate::Baud19200 => 19200,
BaudRate::Baud28800 => 28800,
BaudRate::Baud38400 => 38400,
BaudRate::Baud57600 => 57600,
BaudRate::Baud76800 => 76800,
BaudRate::Baud115200 => 115200,
BaudRate::Auto => 1
}
}
}
#[cfg(target_arch="avr")]
pub mod base {
pub mod zeroseries {
#![allow(dead_code)]
use core::any::Any;
use core::cell::Cell;
use core::arch::asm;
use avr_oxide::sleepctrl;
use avr_oxide::hal::generic::serial::{SerialPortMode, SerialPortIdentity, SynchronousMode, BaudRate, Parity, StopBits, DataBits, SerialRxTx, ReadHandlerResult, SerialNoInterruptTx, SerialReadEventCallback, SerialWriteEventCallback, SerialErrorEventCallback};
use avr_oxide::deviceconsts::clock::{MASTER_CLOCK_HZ, MASTER_CLOCK_PRESCALER};
use core::marker::PhantomData;
use avr_oxide::concurrency::Isolated;
use avr_oxide::hal::generic::cpu::private::PermitStandbyToken;
use avr_oxide::hal::generic::cpu::SleepControl;
use avr_oxide::hal::generic::MuxControl;
use avr_oxide::private::ringq::RingQ;
use avr_oxide::util::datatypes::{BitFieldAccess, BitIndex, Volatile, VolatileBitField};
use avr_oxide::OxideResult::{Ok,Err};
#[repr(C)]
pub struct Atmel0SeriesUsartControl {
pub(crate) rxdatal: Volatile<u8>,
pub(crate) rxdatah: Volatile<u8>,
pub(crate) txdatal: Volatile<u8>,
pub(crate) txdatah: Volatile<u8>,
pub(crate) status: VolatileBitField,
pub(crate) ctrla: VolatileBitField,
pub(crate) ctrlb: VolatileBitField,
pub(crate) ctrlc: Volatile<u8>,
pub(crate) baud: Volatile<u16>,
pub(crate) ctrld: Volatile<u8>,
pub(crate) dbgctrl: Volatile<u8>,
pub(crate) evctrl: Volatile<u8>,
pub(crate) txplctrl: Volatile<u8>,
pub(crate) rxplctrl: Volatile<u8>
}
pub(crate) const CTRLA_DREIE: BitIndex = BitIndex::bit_c(5);
pub(crate) const CTRLB_RXEN : BitIndex = BitIndex::bit_c(7);
pub(crate) const CTRLB_TXEN : BitIndex = BitIndex::bit_c(6);
pub(crate) const CTRLB_SFDEN : BitIndex = BitIndex::bit_c(4);
pub(crate) const CTRLB_ODME : BitIndex = BitIndex::bit_c(3);
pub(crate) const CTRLB_MPCM : BitIndex = BitIndex::bit_c(0);
pub(crate) const STATUS_RXCIF : BitIndex = BitIndex::bit_c(7);
pub(crate) const STATUS_TXCIF : BitIndex = BitIndex::bit_c(6);
pub(crate) const STATUS_DREIF : BitIndex = BitIndex::bit_c(5);
pub(crate) const STATUS_RXSIF : BitIndex = BitIndex::bit_c(4);
pub(crate) const STATUS_ISFIF : BitIndex = BitIndex::bit_c(3);
pub(crate) const STATUS_BDF : BitIndex = BitIndex::bit_c(1);
pub(crate) const STATUS_WFB : BitIndex = BitIndex::bit_c(0);
pub struct AtmelUsart<M,const BUF_SIZE: usize>
where
M: MuxControl
{
pub(crate) control: &'static mut Atmel0SeriesUsartControl,
pub(crate) read_handler: Cell<SerialReadEventCallback>,
pub(crate) write_complete_handler: Cell<SerialWriteEventCallback>,
pub(crate) error_handler: Cell<SerialErrorEventCallback>,
pub(crate) phantom: PhantomData<M>,
pub(crate) rx_buf: RingQ<u8,BUF_SIZE>,
pub(crate) tx_buf: RingQ<u8,BUF_SIZE>,
pub(crate) sleep_inhibitor: Option<PermitStandbyToken>
}
#[inline(never)]
pub(crate) fn read_nop_handler(_isotoken: Isolated, _src: SerialPortIdentity, byte: u8, _udata: Option<*const dyn Any>) -> ReadHandlerResult<u8> {
ReadHandlerResult::Buffer(byte)
}
impl<M,const BUF_SIZE:usize> AtmelUsart<M,BUF_SIZE>
where
M: MuxControl
{
pub fn mux(&mut self, mux: M) -> &mut Self {
mux.route();
self
}
pub(crate) fn dre_int_enable(&mut self, isotoken: Isolated, enabled: bool) {
match enabled {
true => {
self.control.ctrla.set_isolated(isotoken, CTRLA_DREIE);
},
false => {
self.control.ctrla.clr_isolated(isotoken, CTRLA_DREIE);
}
};
}
pub(crate) fn isr_try_send_u8_from_buffer(&mut self, isotoken: Isolated) -> bool {
if self.control.status.is_set(STATUS_DREIF) {
let byte = self.tx_buf.consume(isotoken);
match byte {
Some(byte) => {
self.control.txdatah.write(0x00);
self.control.txdatal.write(byte);
true
},
None => {
false
}
}
} else {
true
}
}
pub(crate) unsafe fn isr_transmission_complete(&mut self, _isotoken: Isolated) {
self.control.status.exc_set(STATUS_TXCIF);
}
pub(crate) unsafe fn isr_try_receive_u8_to_buffer(&mut self, isotoken: Isolated, byte: u8) -> bool {
self.rx_buf.append(isotoken, byte).is_ok()
}
fn mode(&mut self, mode: SerialPortMode) -> &mut Self {
unsafe {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.control.ctrla.clr_all();
self.control.ctrlb.clr_all();
while self.control.status.is_clr(STATUS_DREIF) {
asm!("nop");
}
while self.control.status.is_set(STATUS_RXCIF) {
let _discard = self.control.rxdatah.read();
let _discard = self.control.rxdatal.read();
}
self.control.baud.write(mode.avr_0series_baud_register_value());
self.control.ctrlc.write(mode.avr_0series_ctrlc());
self.control.ctrld.write(0b11000000);
self.control.ctrla.write_byte(0b11000000);
});
}
self
}
fn enable_rxtx(&mut self, enable: bool) -> &mut Self {
avr_oxide::concurrency::interrupt::isolated(|isotoken|{
match enable {
true => {
if self.sleep_inhibitor.is_none() {
self.sleep_inhibitor = Some(sleepctrl!(isotoken).inhibit_standby());
}
self.control.ctrlb.set_isolated(isotoken, CTRLB_RXEN);
self.control.ctrlb.set_isolated(isotoken, CTRLB_TXEN);
},
false => {
match self.sleep_inhibitor.take() {
None => {
},
Some(token) => {
sleepctrl!(isotoken).permit_standby(token);
}
}
self.control.ctrlb.clr_isolated(isotoken, CTRLB_RXEN);
self.control.ctrlb.clr_isolated(isotoken, CTRLB_TXEN);
while self.control.status.is_clr(STATUS_DREIF) {
unsafe {
asm!("nop");
}
}
}
}
});
self
}
}
impl SerialPortMode {
pub fn avr_0series_baud_register_value(&self) -> u16 {
let integer_clk_per = MASTER_CLOCK_HZ / MASTER_CLOCK_PRESCALER as u32;
match self {
SerialPortMode::Asynch(baud, _, _, _) => {
let integer_baud_clock = (integer_clk_per * 64u32) / (16u32 * baud.to_bps() as u32)+1;
integer_baud_clock as u16
},
SerialPortMode::Synch(mode) => {
match mode {
SynchronousMode::Master(baud) => {
let integer_baud_clock = (integer_clk_per / (2u32 * baud.to_bps() as u32)) + 1;
(integer_baud_clock as u16) << 6
}
SynchronousMode::Slave => {
0
}
}
}
}
}
pub fn avr_0series_ctrlb_rxmode(&self) -> u8 {
match self {
SerialPortMode::Asynch(baud, _, _, _) => {
match baud {
BaudRate::Auto => 0x02 << 1,
_ => 0x00
}
},
SerialPortMode::Synch(_) => 0x00
}
}
#[allow(clippy::unusual_byte_groupings)]
pub fn avr_0series_ctrlc(&self) -> u8 {
match self {
SerialPortMode::Asynch(_baud, dbits, parity, sbits) => {
let mut ctrlc: u8 = 0b00_000000; ctrlc |= match parity {
Parity::None => 0b00_00_0000,
Parity::Even => 0b00_10_0000,
Parity::Odd => 0b00_11_0000
};
ctrlc |= match sbits {
StopBits::Bits1 => 0b0000_0_000,
StopBits::Bits2 => 0b0000_1_000
};
ctrlc |= match dbits {
DataBits::Bits5 => 0x00,
DataBits::Bits6 => 0x01,
DataBits::Bits7 => 0x02,
DataBits::Bits8 => 0x03,
DataBits::Bits9LE => 0x06,
DataBits::Bits9HE => 0x07
};
ctrlc
},
SerialPortMode::Synch(_) => {
let ctrlc: u8 = 0b01_000000; ctrlc
}
}
}
}
impl<M,const BUF_SIZE:usize> SerialRxTx for AtmelUsart<M,BUF_SIZE>
where
M: MuxControl
{
fn set_mode(&mut self, mode: SerialPortMode) {
self.mode(mode);
}
fn set_enable_rxtx(&mut self, enable: bool) {
self.enable_rxtx(enable);
}
fn set_write_complete_callback(&self, callback: SerialWriteEventCallback) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write_complete_handler.replace(callback);
});
}
fn set_read_callback(&self, callback: SerialReadEventCallback) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.read_handler.replace(callback);
});
}
fn set_error_callback(&self, callback: SerialErrorEventCallback) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.error_handler.replace(callback);
});
}
fn write_u8(&mut self, byte: u8) {
match avr_oxide::concurrency::interrupt::isolated(|isotoken|{
if self.sleep_inhibitor.is_none() {
self.sleep_inhibitor = Some(sleepctrl!(isotoken).inhibit_standby());
}
self.tx_buf.append(isotoken, byte)
}) {
Ok(()) => {
}
Err(_e) => {
self.flush();
self.tx_buf.append_blocking(byte);
}
}
}
fn flush(&mut self) {
avr_oxide::concurrency::interrupt::isolated(|isotoken|{
self.dre_int_enable(isotoken, true);
})
}
fn read_u8(&mut self) -> u8 {
self.rx_buf.consume_blocking()
}
fn try_read_u8(&mut self) -> Option<u8> {
avr_oxide::concurrency::interrupt::isolated(|isotoken|{self.rx_buf.consume(isotoken)})
}
}
impl<M,const BUF_SIZE:usize> SerialNoInterruptTx for AtmelUsart<M,BUF_SIZE>
where
M: MuxControl
{
fn can_write(&self) -> bool {
self.control.ctrlb.is_set(CTRLB_TXEN)
}
fn blocking_write_u8(&mut self, byte: u8) {
unsafe {
avr_oxide::concurrency::interrupt::isolated(|isotoken|{
let stdbytoken = sleepctrl!(isotoken).inhibit_standby();
while self.control.status.is_clr(STATUS_DREIF) {
asm!("nop")
}
self.control.txdatah.write(0x00);
self.control.txdatal.write(byte);
while self.control.status.is_clr(STATUS_TXCIF) {
asm!("nop")
}
self.control.status.exc_set(STATUS_TXCIF);
sleepctrl!(isotoken).permit_standby(stdbytoken);
})
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! atmel_0series_usart_tpl {
($usartref:expr, $srcref:expr, $implementation:ty, $isr_rx:expr, $isr_tx:expr, $isr_dre:expr) => {
use avr_oxide::hal::generic::serial::{SerialError, ReadHandlerResult,SerialReadEventCallback,SerialWriteEventCallback,SerialErrorEventCallback};
use avr_oxide::hal::generic::serial::base::zeroseries::{ AtmelUsart, read_nop_handler };
use avr_oxide::{mut_singleton_explicit_init,isr_cb_invoke};
use avr_oxide::private::ringq::RingQ;
use avr_oxide::hal::generic::serial::base::zeroseries::{STATUS_RXCIF,STATUS_ISFIF,STATUS_BDF};
use avr_oxide::util::datatypes::BitFieldAccess;
use core::marker::PhantomData;
use core::cell::Cell;
pub type SerialImpl = $implementation;
mut_singleton_explicit_init!(
$implementation,
__INSTANCE,
initialise, instance, instance_isolated,
AtmelUsart {
control: core::mem::transmute($usartref),
read_handler: Cell::new(SerialReadEventCallback::Function(read_nop_handler)),
write_complete_handler: Cell::new(SerialWriteEventCallback::Nop(())),
error_handler: Cell::new(SerialErrorEventCallback::Nop(())),
phantom: PhantomData::default(),
rx_buf: RingQ::new(),
tx_buf: RingQ::new(),
sleep_inhibitor: None
});
#[oxide_macros::interrupt(isr=$isr_rx)]
fn usart_rx_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
let atmelusart = instance_isolated(isotoken);
let trigger = atmelusart.control.status.snapshot();
if trigger.is_set(STATUS_RXCIF) { let byte = atmelusart.control.rxdatal.read();
match isr_cb_invoke!(isotoken, atmelusart.read_handler.get(), $srcref, byte) {
ReadHandlerResult::Buffer(byte) => {
if !atmelusart.isr_try_receive_u8_to_buffer(isotoken, byte) {
isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::BufferOverflow);
}
},
ReadHandlerResult::Discard => {}
}
}
if trigger.is_set(STATUS_ISFIF) { isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::AutoBaudDetectFail);
}
if trigger.is_set(STATUS_BDF) { isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::Break);
}
atmelusart.control.status.write_byte(0b00011010);
}
#[oxide_macros::interrupt(isr=$isr_tx)]
fn usart_tx_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
let atmelusart = instance_isolated(isotoken);
atmelusart.isr_transmission_complete(isotoken);
}
#[oxide_macros::interrupt(isr=$isr_dre)]
fn usart_dre_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
let atmelusart = instance_isolated(isotoken);
match atmelusart.isr_try_send_u8_from_buffer(isotoken) {
true => {
},
false => {
atmelusart.dre_int_enable(isotoken, false);
isr_cb_invoke!(isotoken, atmelusart.write_complete_handler.get(), $srcref);
}
}
}
}
}
}
pub mod simple {
#![allow(dead_code)]
use core::any::Any;
use core::cell::Cell;
use core::arch::asm;
use avr_oxide::hal::generic::serial::{SerialPortMode, SerialPortIdentity, SynchronousMode, Parity, StopBits, DataBits, SerialRxTx, ReadHandlerResult, SerialNoInterruptTx, SerialReadEventCallback, SerialWriteEventCallback, SerialErrorEventCallback};
use avr_oxide::deviceconsts::clock::{MASTER_CLOCK_HZ, MASTER_CLOCK_PRESCALER};
use avr_oxide::concurrency::Isolated;
use avr_oxide::private::ringq::RingQ;
use avr_oxide::util::datatypes::Volatile;
use avr_oxide::OxideResult::{Ok,Err};
const CTRLB_ENABLE_ALL : u8 = 0b1111_1000;
const CTRLB_ENABLE_ALL_XCEPT_DRE: u8 = 0b1101_1000;
#[repr(C)]
pub struct AtmelSimpleUsartControl {
pub(crate) ctrla: Volatile<u8>,
pub(crate) ctrlb: Volatile<u8>,
pub(crate) ctrlc: Volatile<u8>,
pub(crate) _reserved: u8,
pub(crate) baud: Volatile<u16>,
pub(crate) data: Volatile<u8>
}
pub struct AtmelUsart<const BUF_SIZE: usize>
{
pub(crate) control: &'static mut AtmelSimpleUsartControl,
pub(crate) read_handler: Cell<SerialReadEventCallback>,
pub(crate) write_complete_handler: Cell<SerialWriteEventCallback>,
pub(crate) error_handler: Cell<SerialErrorEventCallback>,
pub(crate) rx_buf: RingQ<u8,BUF_SIZE>,
pub(crate) tx_buf: RingQ<u8,BUF_SIZE>
}
#[inline(never)]
pub(crate) fn read_nop_handler(_isotoken: Isolated, _src: SerialPortIdentity, byte: u8, _udata: Option<*const dyn Any>) -> ReadHandlerResult<u8> {
ReadHandlerResult::Buffer(byte)
}
impl<const BUF_SIZE:usize> AtmelUsart<BUF_SIZE> {
pub(crate) unsafe fn dre_int_enable(&mut self, enabled: bool) {
self.control.ctrlb.write(match enabled {
true => CTRLB_ENABLE_ALL,
false => CTRLB_ENABLE_ALL_XCEPT_DRE
})
}
pub(crate) unsafe fn isr_try_send_u8_from_buffer(&mut self, isotoken: Isolated) -> bool {
if (self.control.ctrla.read() & 0b0010_0000) > 0 { let byte = self.tx_buf.consume(isotoken);
match byte {
Some(byte) => {
self.control.data.write(byte);
true
},
None => {
false
}
}
} else {
true
}
}
pub(crate) unsafe fn isr_transmission_complete(&mut self, _isotoken: Isolated) {
self.control.ctrla.write(0b01000000);
}
pub(crate) unsafe fn isr_try_receive_u8_to_buffer(&mut self, isotoken: Isolated, byte: u8) -> bool {
self.rx_buf.append(isotoken, byte).is_ok()
}
fn mode(&mut self, mode: SerialPortMode) -> &mut Self {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.control.ctrlb.write(0x00);
let _discard = self.control.data.read();
self.control.baud.write(mode.avr_simple_baud_register_value());
self.control.ctrlc.write(mode.avr_simple_ctrlc());
self.control.ctrla.write(0b01000000);
self.control.ctrlb.write(0b110_00_000);
});
self
}
fn enable_rxtx(&mut self, enable: bool) -> &mut Self {
match enable {
true => self.control.ctrlb |= 0b000_11_000,
false => self.control.ctrlb &= 0b111_00_111
};
self
}
}
impl SerialPortMode {
pub fn avr_simple_baud_register_value(&self) -> u16 {
let integer_clk_per = MASTER_CLOCK_HZ / MASTER_CLOCK_PRESCALER as u32;
match self {
SerialPortMode::Asynch(baud, _, _, _) => {
let integer_baud_clock = (integer_clk_per / (16u32 * baud.to_bps() as u32))-1;
integer_baud_clock as u16
},
SerialPortMode::Synch(mode) => {
match mode {
SynchronousMode::Master(baud) => {
let integer_baud_clock = (integer_clk_per / (2u32 * baud.to_bps() as u32)) - 1;
integer_baud_clock as u16
}
SynchronousMode::Slave => {
0
}
}
}
}
}
#[allow(clippy::unusual_byte_groupings)]
pub fn avr_simple_ctrlc(&self) -> u8 {
match self {
SerialPortMode::Asynch(_baud, dbits, parity, sbits) => {
let mut ctrlc: u8 = 0b00_000000; ctrlc |= match parity {
Parity::None => 0b00_00_0000,
Parity::Even => 0b00_10_0000,
Parity::Odd => 0b00_11_0000
};
ctrlc |= match sbits {
StopBits::Bits1 => 0b0000_0_000,
StopBits::Bits2 => 0b0000_1_000
};
ctrlc |= match dbits {
DataBits::Bits5 => 0b00000_00_0,
DataBits::Bits6 => 0b00000_01_0,
DataBits::Bits7 => 0b00000_10_0,
DataBits::Bits8 => 0b00000_11_0,
DataBits::Bits9LE => avr_oxide::oserror::halt(avr_oxide::oserror::OsError::BadParams),
DataBits::Bits9HE => avr_oxide::oserror::halt(avr_oxide::oserror::OsError::BadParams)
};
ctrlc
},
SerialPortMode::Synch(_) => {
let ctrlc: u8 = 0b01_000000; ctrlc
}
}
}
}
impl<const BUF_SIZE:usize> SerialRxTx for AtmelUsart<BUF_SIZE> {
fn set_mode(&mut self, mode: SerialPortMode) {
self.mode(mode);
}
fn set_enable_rxtx(&mut self, enable: bool) {
self.enable_rxtx(enable);
}
fn set_write_complete_callback(&self, callback: SerialWriteEventCallback) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.write_complete_handler.replace(callback);
});
}
fn set_read_callback(&self, callback: SerialReadEventCallback) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.read_handler.replace(callback);
});
}
fn set_error_callback(&self, callback: SerialErrorEventCallback) {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
self.error_handler.replace(callback);
});
}
fn write_u8(&mut self, byte: u8) {
match avr_oxide::concurrency::interrupt::isolated(|isotoken|{self.tx_buf.append(isotoken, byte)}) {
Ok(()) => {
}
Err(_e) => {
self.flush();
self.tx_buf.append_blocking(byte);
}
}
}
fn flush(&mut self) {
unsafe {
self.dre_int_enable(true);
}
}
fn read_u8(&mut self) -> u8 {
self.rx_buf.consume_blocking()
}
fn try_read_u8(&mut self) -> Option<u8> {
avr_oxide::concurrency::interrupt::isolated(|isotoken|{self.rx_buf.consume(isotoken)})
}
}
impl<const BUF_SIZE:usize> SerialNoInterruptTx for AtmelUsart<BUF_SIZE> {
fn can_write(&self) -> bool {
(self.control.ctrlb.read() & 0b000_10_000) > 0
}
fn blocking_write_u8(&mut self, byte: u8) {
unsafe {
avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
while (self.control.ctrla.read() & 0b00100000) == 0 {
asm!("nop")
}
self.control.data.write(byte);
while (self.control.ctrla.read() & 0b01000000) == 0 {
asm!("nop")
}
self.control.ctrla.write(0b01000000);
})
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! atmel_simple_usart_tpl {
($usartref:expr, $srcref:expr, $implementation:ty, $isr_rx:expr, $isr_tx:expr, $isr_dre:expr) => {
use avr_oxide::hal::generic::serial::{SerialError, ReadHandlerResult,SerialReadEventCallback,SerialWriteEventCallback,SerialErrorEventCallback};
use avr_oxide::hal::generic::serial::base::simple::{ AtmelUsart, read_nop_handler };
use avr_oxide::{mut_singleton_explicit_init,isr_cb_invoke};
use avr_oxide::private::ringq::RingQ;
use core::marker::PhantomData;
use core::cell::Cell;
pub type SerialImpl = $implementation;
mut_singleton_explicit_init!(
$implementation,
__INSTANCE,
initialise, instance, instance_isolated,
AtmelUsart {
control: core::mem::transmute($usartref),
read_handler: Cell::new(SerialReadEventCallback::Function(read_nop_handler)),
write_complete_handler: Cell::new(SerialWriteEventCallback::Nop(())),
error_handler: Cell::new(SerialErrorEventCallback::Nop(())),
rx_buf: RingQ::new(),
tx_buf: RingQ::new(),
});
#[oxide_macros::interrupt(isr=$isr_rx)]
fn usart_rx_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
let atmelusart = instance_isolated(isotoken);
let trigger = atmelusart.control.ctrla.read();
if (trigger & 0b10000000) > 0 { let byte = atmelusart.control.data.read();
match isr_cb_invoke!(isotoken, atmelusart.read_handler.get(), $srcref, byte) {
ReadHandlerResult::Buffer(byte) => {
if !atmelusart.isr_try_receive_u8_to_buffer(isotoken, byte) {
isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::BufferOverflow);
}
},
ReadHandlerResult::Discard => {}
}
}
if (trigger & 0b00010000) > 0 { isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::Break);
}
if (trigger & 0b00001000) > 0 { isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::BufferOverflow);
}
if (trigger & 0b00000100) > 0 { isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::ParityError);
}
}
#[oxide_macros::interrupt(isr=$isr_tx)]
fn usart_tx_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
let atmelusart = instance_isolated(isotoken);
atmelusart.isr_transmission_complete(isotoken);
}
#[oxide_macros::interrupt(isr=$isr_dre)]
fn usart_dre_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
let atmelusart = instance_isolated(isotoken);
match atmelusart.isr_try_send_u8_from_buffer(isotoken) {
true => {
},
false => {
atmelusart.dre_int_enable(false);
isr_cb_invoke!(isotoken, atmelusart.write_complete_handler.get(), $srcref);
}
}
}
}
}
}
}
#[cfg(not(target_arch="avr"))]
pub mod base {
pub mod zeroseries {
use avr_oxide::hal::generic::serial::{SerialPortMode, SerialPortIdentity, BaudRate, Parity, StopBits, DataBits, SerialRxTx, SerialWriteEventCallback, SerialReadEventCallback, SerialError, ReadHandlerResult, SerialErrorEventCallback};
use avr_oxide::hal::generic::MuxControl;
use core::marker::PhantomData;
use std::fmt::{Display, Formatter};
#[repr(C)]
pub struct DummyUsartControl {
}
#[allow(dead_code)]
pub struct DummyUsart {
pub(crate) control: DummyUsartControl,
}
impl DummyUsart {
pub fn mux<M: MuxControl>(&mut self, mux: M) -> &mut Self {
mux.route();
self
}
}
impl Display for SerialPortMode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
SerialPortMode::Asynch(baud, dbits, parity, stopbits) => {
f.write_str(&format!("{} {}{}{}", baud, dbits, parity, stopbits))
}
SerialPortMode::Synch(_) => {
f.write_str("Synchronous")
}
}
}
}
impl Display for BaudRate {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
BaudRate::Baud300 => "300",
BaudRate::Baud600 => "600",
BaudRate::Baud1200 => "1200",
BaudRate::Baud2400 => "2400",
BaudRate::Baud4800 => "4800",
BaudRate::Baud9600 => "9600",
BaudRate::Baud14400 => "14400",
BaudRate::Baud19200 => "19200",
BaudRate::Baud28800 => "28800",
BaudRate::Baud38400 => "38400",
BaudRate::Baud57600 => "57600",
BaudRate::Baud76800 => "76800",
BaudRate::Baud115200 => "115200",
BaudRate::Auto => "Auto"
})
}
}
impl Display for DataBits {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
DataBits::Bits5 => "5",
DataBits::Bits6 => "6",
DataBits::Bits7 => "7",
DataBits::Bits8 => "8",
DataBits::Bits9LE => "9le",
DataBits::Bits9HE => "9he"
})
}
}
impl Display for Parity {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Parity::None => "N",
Parity::Even => "E",
Parity::Odd => "O"
})
}
}
impl Display for StopBits {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
StopBits::Bits1 => "1",
StopBits::Bits2 => "2"
})
}
}
impl SerialRxTx for DummyUsart {
fn set_mode(&mut self, mode: SerialPortMode) {
println!("*** USART: Set port mode to {}", mode);
}
fn set_enable_rxtx(&mut self, enable: bool) {
println!("*** USART: Rx/Tx enable set to {}", enable);
}
fn set_write_complete_callback(&self, callback: SerialWriteEventCallback) {
println!("*** USART: Write callback set to {:?}", callback);
}
fn set_read_callback(&self, callback: SerialReadEventCallback) {
println!("*** USART: Write callback set to {:?}", callback);
}
fn set_error_callback(&self, callback: SerialErrorEventCallback) {
println!("*** USART: Write callback set to {:?}", callback);
}
fn write_u8(&mut self, byte: u8) {
println!("*** USART: Wrote '{}'", byte as char);
eprint!("{}", byte as char);
}
fn flush(&mut self) {
todo!()
}
fn read_u8(&mut self) -> u8 {
todo!()
}
fn try_read_u8(&mut self) -> Option<u8> {
todo!()
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! atmel_0series_usart_tpl {
($usartref:expr, $srcref:expr, $implementation:ty, $isr_rx:expr, $isr_tx:expr, $isr_dre:expr) => {
use avr_oxide::hal::generic::serial::{SerialError, ReadHandlerResult,SerialReadEventCallback,SerialWriteEventCallback,SerialErrorEventCallback};
use avr_oxide::{mut_singleton_explicit_init,isr_cb_invoke};
use avr_oxide::private::ringq::RingQ;
use core::marker::PhantomData;
use core::cell::Cell;
use avr_oxide::hal::generic::serial::base::zeroseries::{ DummyUsart, DummyUsartControl};
pub type SerialImpl = DummyUsart;
static mut INSTANCE : DummyUsart = DummyUsart {
control: DummyUsartControl {},
};
pub fn initialise() {
}
pub fn instance_isolated(_isotoken: avr_oxide::concurrency::Isolated) -> &'static mut SerialImpl {
unsafe {
&mut INSTANCE
}
}
pub fn instance() -> &'static mut SerialImpl {
unsafe {
&mut INSTANCE
}
}
}
}
}
}