#![no_std] use core::marker::PhantomData; use arduino_hal::{ delay_us, port::{ mode::{Input, PullUp}, Pin, PinOps, }, }; use avr_device::asm::delay_cycles; use static_pins::StaticPinOps; pub type PollResult = Result<(), PollError>; pub type ReadByteResult = Result; pub type CorruptedData = (u8, u8); const SERIAL_DELAY: u32 = 4; const FIRST_HALF_SERIAL_DELAY: u32 = SERIAL_DELAY / 2; const SECOND_HALF_SERIAL_DELAY: u32 = SERIAL_DELAY - FIRST_HALF_SERIAL_DELAY; const READING_ADJUST: u32 = 16; const FIRST_ENTRY_READING: u32 = 30; const LSB: u8 = 0x01; const MSB: u8 = 0x80; pub enum PollError { NotFound, NotReady, } pub struct HalfDuplexSerial

{ _pin: PhantomData, P>>, } impl

HalfDuplexSerial

where P: PinOps + StaticPinOps, { #[inline] pub fn new(_pin: Pin, P>) -> Self { Self { _pin: PhantomData {}, } } } pub trait SoftSerial

where P: PinOps + StaticPinOps, { fn poll(&self) -> PollResult; fn response(&self); fn sync_reciever(&self); fn sync_transmitter(&self); fn write_byte(&self, data: u8); fn read_byte(&self) -> ReadByteResult; #[inline] fn finish_write(&self) { P::into_pull_up_input(); while P::is_low() {} } } impl

SoftSerial

for HalfDuplexSerial

where P: PinOps + StaticPinOps, { fn poll(&self) -> PollResult { P::into_output(); delay_cycles(1); P::into_pull_up_input(); delay_us(SERIAL_DELAY); if P::is_low() { return PollResult::Err(PollError::NotFound); } delay_us(SERIAL_DELAY); if P::is_high() { return PollResult::Err(PollError::NotReady); } while P::is_low() {} PollResult::Ok(()) } fn response(&self) { P::into_output_high(); delay_us(FIRST_HALF_SERIAL_DELAY); P::set_low(); delay_us(SERIAL_DELAY); P::into_pull_up_input(); } #[inline(never)] fn sync_transmitter(&self) { P::into_output(); delay_us(SERIAL_DELAY); P::set_high(); } #[inline(never)] fn sync_reciever(&self) { while P::is_high() {} while P::is_low() {} } pub trait SoftSerialWriter where P: PinOps + StaticPinOps, { #[inline(never)] fn write_byte(&self, data: u8) { let (mut data, mut parity_bit) = (data, 0); for _ in 0..8 { if data & MSB == 0 { P::set_high(); parity_bit ^= 0; } else { P::set_low(); parity_bit ^= 1; } delay_us(SERIAL_DELAY); data <<= 1; } // Hamming code and CRC are very weightful and slow, so I use simple parity check if parity_bit == 0 { P::set_high(); } else { P::set_low(); } delay_us(SERIAL_DELAY); } fn write_bytes(&self, transmit_data: T); } impl

SoftSerialWriter for HalfDuplexSerial

where P: PinOps + StaticPinOps, { fn write_bytes(&self, transmit_data: &[u8]) { for byte in transmit_data { self.write_byte(*byte); self.sync_transmitter(); } } } pub trait SoftSerialReader where P: PinOps + StaticPinOps, { #[inline(never)] fn read_byte(&self) -> ReadByteResult { let (mut data, mut reciever_parity_bit) = (0, 0); delay_cycles(FIRST_ENTRY_READING); for _ in 0..8 { delay_us(FIRST_HALF_SERIAL_DELAY); data <<= 1; if P::is_low() { data |= 1; reciever_parity_bit ^= 1; } else { data |= 0; reciever_parity_bit ^= 0; } delay_cycles(READING_ADJUST); delay_us(SECOND_HALF_SERIAL_DELAY); } delay_us(FIRST_HALF_SERIAL_DELAY); let transmitter_parity_bit = (P::read() >> P::PIN_NUM) & LSB; delay_cycles(READING_ADJUST); delay_us(SECOND_HALF_SERIAL_DELAY); if reciever_parity_bit == transmitter_parity_bit { return Err((data, reciever_parity_bit)); } Ok(data) } fn read_bytes(&self, recieve_data: T); } impl

SoftSerialReader for HalfDuplexSerial

where P: PinOps + StaticPinOps, { fn read_bytes(&self, recieve_data: &mut [u8]) { for byte in recieve_data { if let Ok(data) = self.read_byte() { *byte = data; } self.sync_reciever(); } } }