use avr_oxide::hal::generic::serial::{SerialRxTx, ReadHandlerResult, SerialError, SerialPortMode, SerialPortIdentity, SerialWriteEventCallback, SerialReadEventCallback, SerialErrorEventCallback};
use avr_oxide::event::{EventSink, EventSource, OxideEvent, OxideEventEnvelope};
use core::marker::PhantomData;
use core::cell::RefCell;
use core::ops::DerefMut;
use ufmt::derive::uDebug;
use avr_oxide::devices::internal::StaticShareable;
use oxide_macros::Persist;
use avr_oxide::alloc::boxed::Box;
use avr_oxide::devices::Handle;
use avr_oxide::hal::generic::callback::IsrCallback;
use avr_oxide::hal::generic::port::{Pin, PinMode};
use avr_oxide::panic_if_none;
use avr_oxide::util::{BitField, BitIndex};
pub trait SerialCallback = FnMut(SerialPortIdentity, SerialState) -> ();
#[derive(PartialEq,Eq,Clone,Copy,uDebug,Persist)]
pub enum SerialState {
ReadAvailable,
SerialCommsError,
BreakDetected,
ReadBufferOverflow
}
pub type SerialOptions = BitField<1>;
pub struct SerialPort<'sp,P,T,R,S>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
port: &'static mut P,
tx: &'static T,
rx: &'static R,
phantom: PhantomData<S>,
serial_options: SerialOptions,
on_event: RefCell<Option<Box<dyn SerialCallback + 'sp>>>
}
impl<'sp,P,T,R,S> StaticShareable for SerialPort<'sp,P,T,R,S>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{}
const TX_FLUSH_ON_EOL: BitIndex = BitIndex::bit(0);
const TX_CR_TO_CRLF: BitIndex = BitIndex::bit(1);
const TX_LF_TO_CRLF: BitIndex = BitIndex::bit(2);
const INTERACTIVE: BitIndex = BitIndex::bit(3);
const ECHO: BitIndex = BitIndex::bit(4);
impl SerialOptions {
pub fn set_flush_on_eol(&mut self, flag: bool) {
self.set_or_clr(TX_FLUSH_ON_EOL, flag);
}
pub fn flush_on_eol(self) -> bool {
self.is_set(TX_FLUSH_ON_EOL)
}
pub fn set_cr_to_crlf(&mut self, flag: bool) {
self.set_or_clr(TX_CR_TO_CRLF, flag);
}
pub fn cr_to_crlf(&self) -> bool {
self.is_set(TX_CR_TO_CRLF)
}
pub fn set_lf_to_crlf(&mut self, flag: bool) {
self.set_or_clr(TX_LF_TO_CRLF, flag);
}
pub fn lf_to_crlf(&self) -> bool {
self.is_set(TX_LF_TO_CRLF)
}
pub fn set_interactive(&mut self, flag: bool) {
self.set_or_clr(INTERACTIVE, flag);
}
pub fn interactive(&self) -> bool {
self.is_set(INTERACTIVE)
}
pub fn set_echo(&mut self, flag: bool) {
self.set_or_clr(ECHO, flag)
}
pub fn echo(&self) -> bool {
self.is_set(ECHO)
}
pub fn disable_translations(&mut self) {
self.set_cr_to_crlf(false);
self.set_lf_to_crlf(false);
}
}
impl Default for SerialOptions {
fn default() -> Self {
BitField::with_bits_set(&[TX_FLUSH_ON_EOL])
}
}
impl<P,T,R,S> SerialPort<'_,P,T,R,S>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
pub fn using_port_and_pins(port: &'static mut P, tx: &'static mut T, rx: &'static mut R) -> Self {
rx.listen(IsrCallback::Nop(()));
port.enable_rxtx(true);
tx.set_mode(PinMode::Output);
rx.set_mode(PinMode::InputFloating);
SerialPort {
port, tx, rx,
phantom: PhantomData::default(),
serial_options: SerialOptions::default(),
on_event: RefCell::new(None)
}
}
pub fn mode(self, mode: SerialPortMode) -> Self {
self.tx.set_mode(PinMode::InputFloating);
self.rx.set_mode(PinMode::InputFloating);
self.port.mode(mode);
self.port.enable_rxtx(true);
self.tx.set_mode(PinMode::Output);
self
}
pub fn get_serial_options(&self) -> SerialOptions {
self.serial_options
}
pub fn set_serial_options(&mut self, options: SerialOptions) {
self.serial_options = options;
}
pub fn serial_options(&mut self) -> &mut SerialOptions {
&mut self.serial_options
}
}
impl<P,T,R,S> EventSource for SerialPort<'_,P,T,R,S>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
fn listen(&'static self) {
self.port.set_write_complete_callback(SerialWriteEventCallback::Nop(()));
self.port.set_read_callback(SerialReadEventCallback::WithData(|src,byte, udata|{
S::event(OxideEventEnvelope::to(unsafe {&*(panic_if_none!(udata) as *const SerialPort<P,T,R,S> as *const dyn EventSource)},
OxideEvent::SerialEvent(src, SerialState::ReadAvailable)));
ReadHandlerResult::Buffer(byte)
},self as *const dyn core::any::Any));
self.port.set_error_callback(SerialErrorEventCallback::WithData(|src,error, udata|{
S::event(OxideEventEnvelope::to(unsafe {&*(panic_if_none!(udata) as *const SerialPort<P,T,R,S> as *const dyn EventSource)},
OxideEvent::SerialEvent(src, match error {
SerialError::BufferOverflow => SerialState::ReadBufferOverflow,
SerialError::Break => SerialState::BreakDetected,
_ => SerialState::SerialCommsError
})));
}, self as *const dyn core::any::Any));
}
fn process_event(&self, evt: OxideEvent) {
match (self.on_event.borrow_mut().deref_mut(), evt) {
(Some(f), OxideEvent::SerialEvent(source, state)) => {
(*f)(source,state)
},
_ => {}
}
}
}
impl<P,T,R,S> avr_oxide::io::Read for SerialPort<'_,P,T,R,S>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
fn read(&mut self, buf: &mut [u8]) -> avr_oxide::io::Result<usize> {
let mut cnt = 0usize;
if self.serial_options.interactive() {
self.port.flush();
}
while cnt < buf.len() {
match self.port.try_read_u8() {
Some(c) => {
buf[cnt] = c;
if self.serial_options.echo() {
self.port.write_u8(c);
if self.serial_options.interactive() {
self.port.flush();
}
}
cnt += 1;
},
None => {
break;
}
}
}
Ok(cnt)
}
}
impl<P,T,R,S> avr_oxide::io::Write for SerialPort<'_,P,T,R,S>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
fn write(&mut self, buf: &[u8]) -> avr_oxide::io::Result<usize> {
let cnt : usize = 0;
for byte in buf {
match byte {
b'\n' => {
if self.serial_options.lf_to_crlf() {
self.port.write_u8(b'\r');
}
},
_ => {}
}
self.port.write_u8(*byte);
match byte {
b'\r' => {
if self.serial_options.cr_to_crlf() {
self.port.write_u8(b'\n');
}
if self.serial_options.flush_on_eol() {
self.port.flush();
}
},
b'\n' => {
if self.serial_options.flush_on_eol() {
self.port.flush();
}
},
_ => {}
}
}
Ok(cnt)
}
fn flush(&mut self) -> avr_oxide::io::Result<()> {
self.port.flush();
Ok(())
}
}
impl<P,T,R,S> ufmt_write::uWrite for SerialPort<'_,P,T,R,S>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
type Error = core::convert::Infallible;
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
let _ignore = avr_oxide::io::Write::write(self, s.as_bytes());
Ok(())
}
}
impl<P,T,R,S> avr_oxide::io::Write for Handle<SerialPort<'_,P,T,R,S>>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
fn write(&mut self, buf: &[u8]) -> avr_oxide::io::Result<usize> {
Handle::deref_mut(self).write(buf)
}
fn flush(&mut self) -> avr_oxide::io::Result<()> {
Handle::deref_mut(self).flush()
}
}
impl<P,T,R,S> avr_oxide::io::Read for Handle<SerialPort<'_,P,T,R,S>>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
fn read(&mut self, buf: &mut [u8]) -> avr_oxide::io::Result<usize> {
Handle::deref_mut(self).read(buf)
}
}
impl<P,T,R,S> ufmt_write::uWrite for Handle<SerialPort<'_,P,T,R,S>>
where
P: 'static + SerialRxTx,
T: 'static + Pin,
R: 'static + Pin,
S: EventSink
{
type Error = core::convert::Infallible;
fn write_str(&mut self, s: &str) -> Result<(), Self::Error> {
Handle::deref_mut(self).write_str(s)
}
}