#![no_std] use core::marker::PhantomData; use arduino_hal::{ delay_us, hal::port::{PB4, PB5, PE6}, port::{ mode::{Input, PullUp}, Pin, PinOps, }, }; use avr_device::asm::delay_cycles; use static_pins::StaticPinOps; use ufmt::derive::uDebug; mod structures; 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 = 10; const TX_DELAY_CYCLES: u32 = 22; const MSB: u16 = 0x0800; const PACKET_MASK: u16 = 0x0FFF; #[inline(always)] fn crc_calculate(value: u8) -> u8 { ((value >> 6) | (value >> 4) | (value >> 2) | value) & 0x0F } #[cfg(feature = "ufmt")] #[derive(uDebug)] pub enum PollError { NotFound, NotReady, } pub struct HalfDuplexSerial

{ _pin: PhantomData, P>>, } #[cfg(debug_assertions)] #[inline(always)] fn debug_pb4_low() { PB4::set_low(); } #[cfg(debug_assertions)] #[inline(always)] fn debug_pb4_high() { PB4::set_high(); } #[allow(dead_code)] #[cfg(debug_assertions)] #[inline(always)] fn debug_pb4_pulse_positive() { debug_pb4_low(); debug_pb4_high(); } #[allow(dead_code)] #[cfg(debug_assertions)] #[inline(always)] fn debug_pb4_pulse_negative() { debug_pb4_high(); debug_pb4_low(); } impl

HalfDuplexSerial

where P: PinOps + StaticPinOps, { pub fn new(_pin: Pin, P>) -> Self { if cfg!(debug_assertions) { PB4::into_output_high(); PB5::into_output_high(); PE6::into_output_high(); } Self { _pin: PhantomData {}, } } pub fn poll(&self) -> PollResult { PE6::set_low(); P::into_output(); delay_cycles(2); P::into_pull_up_input(); delay_us(SERIAL_DELAY + FIRST_HALF_SERIAL_DELAY); if P::is_low() { PE6::set_high(); return PollResult::Err(PollError::NotFound); } delay_us(SERIAL_DELAY); if P::is_high() { PE6::set_high(); return PollResult::Err(PollError::NotReady); } while P::is_low() {} PE6::set_high(); PollResult::Ok(()) } pub fn response(&self) { PE6::set_low(); P::into_output_high(); delay_us(FIRST_HALF_SERIAL_DELAY); P::set_low(); delay_us(SERIAL_DELAY); P::set_high(); P::into_pull_up_input(); PE6::set_high(); } #[inline] pub fn sync_transmitter(&self) { if cfg!(debug_assertions) { PB5::set_low(); } P::into_output(); delay_us(SERIAL_DELAY); P::set_high(); if cfg!(debug_assertions) { PB5::set_high(); } } #[inline] pub fn sync_reciever(&self) { if cfg!(debug_assertions) { PB5::set_low(); } while P::is_high() {} while P::is_low() {} if cfg!(debug_assertions) { PB5::set_high(); } } #[inline] pub fn reset(&self) { P::into_pull_up_input(); } } pub trait SoftSerialWriter: SoftSerialByteWriter

where P: PinOps + StaticPinOps, { fn write_bytes(&self, transmit_data: T); } pub trait SoftSerialReader: SoftSerialByteReader

where P: PinOps + StaticPinOps, { fn read_bytes(&self, recieve_data: T) -> Result<(), CorruptedData>; } pub trait SoftSerialByteWriter

where P: PinOps + StaticPinOps, { #[inline(never)] fn write_byte(&self, transmit_data: u8) { let mut data = ((transmit_data as u16) << 4) | (crc_calculate(transmit_data) as u16); for _ in 0..(u8::BITS + 4) { debug_pb4_low(); if data & MSB == 0 { P::set_high(); } else { P::set_low(); } data = (data << 1) & PACKET_MASK; delay_us(SERIAL_DELAY); debug_pb4_high(); } P::set_high(); delay_cycles(TX_DELAY_CYCLES); } } pub trait SoftSerialByteReader

where P: PinOps + StaticPinOps, { #[inline(never)] fn read_byte(&self) -> ReadByteResult { let mut packet = 0u16; for _ in 0..(u8::BITS + 4) { debug_pb4_low(); delay_us(FIRST_HALF_SERIAL_DELAY); delay_cycles(READING_ADJUST); if P::is_low() { packet = (packet << 1) | 1; } else { packet <<= 1; } delay_us(SECOND_HALF_SERIAL_DELAY); debug_pb4_high(); } let data = (packet >> 4) as u8; let received_crc = (packet & 0x000F) as u8; let calculated_crc = crc_calculate(data); if received_crc != calculated_crc { return Err((received_crc, calculated_crc)); } Ok(data) } } impl SoftSerialByteWriter

for HalfDuplexSerial

{} impl SoftSerialByteReader

for HalfDuplexSerial

{} 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(); } } } impl

SoftSerialReader for HalfDuplexSerial

where P: PinOps + StaticPinOps, { fn read_bytes(&self, recieve_data: &mut [u8]) -> Result<(), CorruptedData> { for byte in recieve_data { *byte = self.read_byte()?; self.sync_reciever(); } Ok(()) } }