hk32-partial-hal 0.1.1

HK32 partial HAL
use crate::pac::spi::{regs, Spi};
use core::{cmp::min, convert::Infallible};
use core::marker::PhantomData;
use embedded_hal::{digital::OutputPin, spi::Operation};

pub trait SpiDev {
    const SPI: Spi;
}

crate::foreach_spi!(
    ($spi_name:ident) => {
        impl SpiDev for $crate::peripherals::$spi_name {
            const SPI: $crate::pac::spi::Spi = $crate::pac::$spi_name;
        }
    };
);

#[derive(Clone, Debug)]
pub struct SpiBus<S> {
    phantom: PhantomData<S>,
}

impl<S: SpiDev> SpiBus<S> {
    /// Initializes a new SPI bus with the given configuration.
    ///
    /// # Returns
    /// A new `SpiBus` instance configured according to the provided parameters.
    pub fn new() -> Self {
        SpiBus {
            phantom: PhantomData,
        }
    }

    /// Converts the SPI bus into a SPI device by associating it with a chip select pin.
    ///
    /// # Arguments
    /// * `nss` - The chip select pin configured as an output.
    ///
    /// # Returns
    /// A `SpiDevice` instance ready to communicate with a specific device.
    pub fn to_device<NSS: OutputPin>(self, nss: NSS) -> SpiDevice<NSS, S> {
        SpiDevice {
            bus: self,
            nss,
        }
    }

    /// Performs a blocking read operation on the SPI bus.
    ///
    /// # Arguments
    /// * `words` - A mutable slice where the read data will be stored.
    ///
    /// # Returns
    /// The number of bytes read or an error code.
    fn blocking_read(&mut self, words: &mut [u8]) {
        for word in words {
            *word = self.transfer_one(0);
        }
    }

    /// Performs a blocking write operation on the SPI bus.
    ///
    /// # Arguments
    /// * `words` - A slice containing the data to be written.
    ///
    /// # Returns
    /// The number of bytes written or an error code.
    fn blocking_write(&mut self, words: &[u8]) {
        for word in words {
            self.transfer_one(*word);
        }
    }

    /// Performs a blocking transfer operation on the SPI bus.
    ///
    /// # Arguments
    /// * `read` - A mutable slice where the received data will be stored.
    /// * `write` - A slice containing the data to be sent.
    ///
    /// # Returns
    /// The number of bytes transferred or an error code.
    fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) {
        let size = min(read.len(), write.len());
        for i in 0..size {
            read[i] = self.transfer_one(write[i]);
        }
    }

    /// Performs a blocking transfer operation in place on the SPI bus.
    ///
    /// # Arguments
    /// * `words` - A mutable slice for both sending and receiving data.
    ///
    /// # Returns
    /// The number of bytes transferred or an error code.
    fn blocking_transfer_in_place(&mut self, words: &mut [u8]) {
        for word in words.iter_mut() {
            *word = self.transfer_one(*word);
        }
    }

    /// Transfers one byte over the SPI bus.
    #[inline]
    fn transfer_one(&mut self, write: u8) -> u8 {
        while !S::SPI.sr().read().txe() {}
        S::SPI.dr().write_value(regs::Dr(write));

        while !S::SPI.sr().read().rxne() {}
        let data = S::SPI.dr().read().0 as u8;

        return data;
    }
}

impl<S: SpiDev> embedded_hal::spi::ErrorType for SpiBus<S> {
    type Error = Infallible;
}

impl<S: SpiDev> embedded_hal::spi::SpiBus for SpiBus<S> {
    fn flush(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }

    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
        self.blocking_read(words);

        Ok(())
    }

    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
        self.blocking_write(words);

        Ok(())
    }

    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
        self.blocking_transfer(read, write);

        Ok(())
    }

    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
        self.blocking_transfer_in_place(words);

        Ok(())
    }
}

pub struct SpiDevice<NSS, S> {
    bus: SpiBus<S>,
    nss: NSS,
}

impl<NSS: OutputPin, S: SpiDev> SpiDevice<NSS, S> {
    /// Creates a new `SpiDevice` instance.
    ///
    /// # Arguments
    /// * `bus` - A reference to the SPI bus this device will communicate over.
    /// * `nss` - A pin configured as an output, used as the chip select line for the device.
    ///
    /// # Returns
    /// A new `SpiDevice` instance ready to communicate with the device over the provided SPI bus.
    pub fn new(bus: SpiBus<S>, nss: NSS) -> Self {
        SpiDevice { bus, nss }
    }
}

impl<NSS: OutputPin, S: SpiDev> embedded_hal::spi::ErrorType for SpiDevice<NSS, S> {
    type Error = Infallible;
}

impl<NSS: OutputPin, S: SpiDev> embedded_hal::spi::SpiDevice for SpiDevice<NSS, S> {
    fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
        self.nss.set_low().ok();
        for op in operations {
            match op {
                Operation::Read(words) => {
                    self.bus.blocking_read(words);
                }
                Operation::Write(words) => {
                    self.bus.blocking_write(words);
                }
                Operation::Transfer(rd_words, wr_words) => {
                    self.bus.blocking_transfer(rd_words, wr_words);
                }
                Operation::TransferInPlace(words) => {
                    self.bus.blocking_transfer_in_place(words);
                }
                Operation::DelayNs(_ns) => {
                    //NOP
                }
            }
        }
        self.nss.set_high().ok();

        Ok(())
    }
}