hk32-partial-hal 0.3.1

HK32 partial HAL
use crate::pac::spi::{regs, Spi};
use core::marker::PhantomData;
use core::{cmp::min, convert::Infallible};
use embedded_hal::{digital::OutputPin, spi::Operation};
pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};

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;
        }
    };
);

/// SPI bit order
#[derive(Copy, Clone, PartialEq)]
pub enum BitOrder {
    /// Least significant bit first.
    LsbFirst,
    /// Most significant bit first.
    MsbFirst,
}

/// SPI configuration.
#[non_exhaustive]
#[derive(Copy, Clone)]
pub struct Config {
    /// SPI mode.
    pub mode: Mode,
    /// Bit order.
    pub bit_order: BitOrder,
    /// SPI clock prescaler.
    /// 0:fPCLK/2
    /// 1:fPCLK/4
    /// 2:fPCLK/8
    /// 3:fPCLK/16
    /// 4:fPCLK/32
    /// 5:fPCLK/64
    /// 6:fPCLK/128
    /// 7:fPCLK/256
    pub clk_prescaler: u8,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            mode: MODE_0,
            bit_order: BitOrder::MsbFirst,
            clk_prescaler: 3, //4MHz at 64MHz system clock
        }
    }
}

#[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(config: Option<Config>) -> Self {
        let spi_buf = SpiBus {
            phantom: PhantomData,
        };
        if let Some(config) = config {
            spi_buf.init(config);
        }

        spi_buf
    }

    fn init(&self, config: Config) {
        let mode = &config.mode;
        let br = config.clk_prescaler;
        let br = if br > 7 { 7 } else { br };
        let cpha = mode.phase == Phase::CaptureOnSecondTransition;
        let cpol = mode.polarity == Polarity::IdleHigh;
        let lsbfirst = config.bit_order == BitOrder::LsbFirst;

        let regs = S::SPI;

        regs.cr2().modify(|w| {
            w.set_ssoe(false);
            w.set_frxth(true);
        });
        regs.cr1().modify(|w| {
            w.set_cpha(cpha);
            w.set_cpol(cpol);

            w.set_mstr(true);
            w.set_br(br);
            w.set_lsbfirst(lsbfirst);
            w.set_ssi(true);
            w.set_ssm(true);
            w.set_crcen(false);
            w.set_bidimode(false);
            w.set_rxonly(false);
            w.set_spe(true);
        });
    }

    /// 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::new(self, nss)
    }

    pub fn to_tx_only_device<NSS: OutputPin>(self, nss: NSS) -> SpiDeviceTxOnly<NSS, S> {
        SpiDeviceTxOnly::new(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(never)]
    fn transfer_one(&mut self, write: u8) -> u8 {
        let spi_dr = S::SPI.dr();
        let spi_sr = S::SPI.sr();
        while !spi_sr.read().txe() {}
        spi_dr.write_value(regs::Dr(write));

        while !spi_sr.read().rxne() {}
        let data = spi_dr.read().0 as u8;

        return data;
    }

    /// Only Transfers one byte
    #[inline(always)]
    fn transfer_one_tx_only(&mut self, write: u8) {
        let spi_dr = S::SPI.dr();
        let spi_sr = S::SPI.sr();
        while !spi_sr.read().txe() {}
        spi_dr.write_value(regs::Dr(write));
    }

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

    /// Waiting for the RXNE flag to be set.
    #[inline(always)]
    fn tx_only_wait_busy(&self) {
        let spi_sr = S::SPI.sr();

        while spi_sr.read().bsy() {}
    }
}

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(())
    }
}

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

impl<NSS: OutputPin, S: SpiDev> SpiDeviceTxOnly<NSS, S> {
    /// Creates a new `SpiDeviceTxOnly` 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 {
        SpiDeviceTxOnly { bus, nss }
    }
}

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

impl<NSS: OutputPin, S: SpiDev> embedded_hal::spi::SpiDevice for SpiDeviceTxOnly<NSS, S> {
    fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
        self.nss.set_low().ok();
        for op in operations {
            let tx_buf: &[u8] = match op {
                Operation::Write(words) => words,
                Operation::Transfer(_, wr_words) => wr_words,
                Operation::TransferInPlace(words) => words,
                _ => &[],
            };
            self.bus.blocking_write_tx_only(tx_buf);
        }
        self.bus.tx_only_wait_busy();
        self.nss.set_high().ok();

        Ok(())
    }
}