use crate::pac::{GPIO, SPI0};
use embedded_hal::spi::{Phase, Polarity};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChipSelect {
Cs0 = 0,
Cs1 = 1,
None = 0b11,
}
pub fn divider_for(core_hz: u32, target_hz: u32) -> u16 {
let exact = core_hz.div_ceil(target_hz.max(1));
exact.saturating_add(exact % 2).clamp(2, 65534) as u16
}
pub struct Spi {
spi0: SPI0,
}
impl Spi {
pub fn init(
gpio: &GPIO,
spi0: SPI0,
mode: embedded_hal::spi::Mode,
chip_select: ChipSelect,
clock_divider: u16,
) -> Self {
gpio.gpfsel0().modify(|_, w| w.fsel9().spi0_miso());
gpio.gpfsel1()
.modify(|_, w| w.fsel10().spi0_mosi().fsel11().spi0_sclk());
if chip_select != ChipSelect::None {
gpio.gpfsel0()
.modify(|_, w| w.fsel7().spi0_ce1_n().fsel8().spi0_ce0_n());
}
let cpol = mode.polarity == Polarity::IdleHigh;
let cpha = mode.phase == Phase::CaptureOnSecondTransition;
spi0.cs().write(|w| {
unsafe { w.cs().bits(chip_select as u8) }
.cpol()
.bit(cpol)
.cpha()
.bit(cpha)
.clear()
.both()
.ren()
.clear_bit()
});
spi0.clk()
.write(|w| unsafe { w.cdiv().bits(clock_divider) });
let spi = Self { spi0 };
spi.reset_hw();
spi
}
fn reset_hw(&self) {
const INTR: u32 = 1 << 10;
const INTD: u32 = 1 << 9;
const DMAEN: u32 = 1 << 8;
const TA: u32 = 1 << 7;
const CLEAR_TX: u32 = 1 << 4;
const CLEAR_RX: u32 = 1 << 5;
const DONE: u32 = 1 << 16;
let cs = self.spi0.cs().read().bits();
let cs = (cs & !(INTR | INTD | DMAEN | TA)) | DONE | CLEAR_TX | CLEAR_RX;
unsafe {
self.spi0.cs().write(|w| w.bits(cs));
self.spi0.dlen().write(|w| w.bits(0));
}
}
fn begin_transfer(&self) {
self.spi0.cs().modify(|_, w| w.ta().set_bit());
}
fn end_transfer(&self) {
while self.spi0.cs().read().done().bit_is_clear() {}
self.reset_hw();
}
fn shift(
&mut self,
len: usize,
mut tx: impl FnMut(usize) -> u8,
mut rx: impl FnMut(usize, u8),
) {
self.begin_transfer();
let mut sent = 0;
let mut received = 0;
while received < len {
if sent < len && self.spi0.cs().read().txd().bit_is_set() {
let byte = tx(sent);
unsafe {
self.spi0.fifo().write(|w| w.data().bits(byte as u32));
}
sent += 1;
}
if self.spi0.cs().read().rxd().bit_is_set() {
let byte = self.spi0.fifo().read().data().bits() as u8;
rx(received, byte);
received += 1;
}
}
self.end_transfer();
}
}
impl embedded_hal::spi::ErrorType for Spi {
type Error = core::convert::Infallible;
}
impl embedded_hal::spi::SpiBus<u8> for Spi {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.shift(words.len(), |_| 0, |i, byte| words[i] = byte);
Ok(())
}
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.shift(words.len(), |i| words[i], |_, _| {});
Ok(())
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
let len = read.len().max(write.len());
self.shift(
len,
|i| write.get(i).copied().unwrap_or(0),
|i, byte| {
if let Some(slot) = read.get_mut(i) {
*slot = byte;
}
},
);
Ok(())
}
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.begin_transfer();
let len = words.len();
let mut sent = 0;
let mut received = 0;
while received < len {
if sent < len && self.spi0.cs().read().txd().bit_is_set() {
let byte = words[sent];
unsafe {
self.spi0.fifo().write(|w| w.data().bits(byte as u32));
}
sent += 1;
}
if self.spi0.cs().read().rxd().bit_is_set() {
words[received] = self.spi0.fifo().read().data().bits() as u8;
received += 1;
}
}
self.end_transfer();
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}