use crate::{Read, Write};
use embedded_hal::digital::v2::{InputPin, OutputPin};
use embedded_hal::timer::{CountDown, Periodic};
use nb::block;
pub struct Mdio<MdioPin, MdcPin, Clk> {
mdio: MdioPin,
mdc: MdcPin,
clk: Clk,
}
impl<MdioPin, MdcPin, Clk, E> Mdio<MdioPin, MdcPin, Clk>
where
MdcPin: OutputPin<Error = E>,
MdioPin: InputPin<Error = E> + OutputPin<Error = E>,
Clk: CountDown + Periodic,
{
const PREAMBLE_BITS: usize = 32;
pub fn new(mdio: MdioPin, mdc: MdcPin, clk: Clk) -> Self {
Self { mdio, mdc, clk }
}
pub fn into_parts(self) -> (MdioPin, MdcPin, Clk) {
let Self { mdio, mdc, clk } = self;
(mdio, mdc, clk)
}
fn wait_for_clk(&mut self) {
block!(self.clk.wait()).ok();
}
fn pulse_clock(&mut self) -> Result<(), E> {
self.wait_for_clk();
self.mdc.set_high()?;
self.wait_for_clk();
self.mdc.set_low()
}
fn preamble(&mut self) -> Result<(), E> {
self.mdio.set_high()?;
for _bit in 0..Self::PREAMBLE_BITS {
self.pulse_clock()?;
}
Ok(())
}
fn write_bit(&mut self, bit: bool) -> Result<(), E> {
if bit {
self.mdio.set_high()?;
} else {
self.mdio.set_low()?;
}
self.pulse_clock()
}
fn write_bits(&mut self, byte: u8, count: usize) -> Result<(), E> {
for bit_offset in 0..core::cmp::min(count, 8) {
self.write_bit(bit_is_set(byte, bit_offset))?;
}
Ok(())
}
fn write_u8(&mut self, byte: u8) -> Result<(), E> {
for bit_offset in 0..8 {
self.write_bit(bit_is_set(byte, bit_offset))?;
}
Ok(())
}
fn write_u16(&mut self, bytes: u16) -> Result<(), E> {
for &byte in &bytes.to_be_bytes() {
self.write_u8(byte)?;
}
Ok(())
}
fn turnaround(&mut self) -> Result<(), E> {
self.pulse_clock()?;
self.pulse_clock()
}
fn read_bit(&mut self) -> Result<bool, E> {
let b = self.mdio.is_high()?;
self.pulse_clock()?;
Ok(b)
}
fn read_byte(&mut self) -> Result<u8, E> {
let mut byte = 0u8;
for bit_offset in 0..8 {
if self.read_bit()? {
set_bit(&mut byte, bit_offset);
}
}
Ok(byte)
}
fn read_u16(&mut self) -> Result<u16, E> {
let a = self.read_byte()?;
let b = self.read_byte()?;
let u = u16::from_be_bytes([a, b]);
Ok(u)
}
}
impl<MdioPin, MdcPin, Clk, E> Read for Mdio<MdioPin, MdcPin, Clk>
where
MdcPin: OutputPin<Error = E>,
MdioPin: InputPin<Error = E> + OutputPin<Error = E>,
Clk: CountDown + Periodic,
{
type Error = E;
fn read(&mut self, ctrl_bits: u16) -> Result<u16, Self::Error> {
self.preamble()?;
let [ctrl_a, ctrl_b] = ctrl_bits.to_be_bytes();
self.write_u8(ctrl_a)?;
self.write_bits(ctrl_b, 6)?;
self.turnaround()?;
self.read_u16()
}
}
impl<MdioPin, MdcPin, Clk, E> Write for Mdio<MdioPin, MdcPin, Clk>
where
MdcPin: OutputPin<Error = E>,
MdioPin: InputPin<Error = E> + OutputPin<Error = E>,
Clk: CountDown + Periodic,
{
type Error = E;
fn write(&mut self, ctrl_bits: u16, data_bits: u16) -> Result<(), Self::Error> {
self.preamble()?;
self.write_u16(ctrl_bits)?;
self.write_u16(data_bits)
}
}
fn bit_is_set(byte: u8, bit_index: usize) -> bool {
let out_bit = (byte >> (7 - bit_index)) & 0b1;
out_bit == 1
}
fn set_bit(byte: &mut u8, bit_index: usize) {
*byte |= 1 << (7 - bit_index);
}