diff --git a/Cargo.lock b/Cargo.lock index d7bcb99..969a145 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -358,6 +358,7 @@ dependencies = [ "cortex-m-rt", "hash2hwaddr", "log", + "nb", "nom", "num-traits", "panic-abort", diff --git a/Cargo.toml b/Cargo.toml index fbee09d..9553af0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ bit_field = "0.10" byteorder = { version = "1", default-features = false } nom = { version = "5", default-features = false } num-traits = { version = "0.2", default-features = false, features = ["libm"] } +nb = "0.1" [patch.crates-io] # TODO: pending https://github.com/stm32-rs/stm32f4xx-hal/pull/125 diff --git a/src/main.rs b/src/main.rs index 84a2b00..3cd87a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,7 @@ mod init_log; use init_log::init_log; mod pins; use pins::Pins; +mod softspi; mod ad7172; mod ad5680; mod net; @@ -89,6 +90,8 @@ fn main() -> ! { wd.start(WATCHDOG_INTERVAL.ms()); wd.feed(); + timer::setup(cp.SYST, clocks); + let pins = Pins::setup( clocks, dp.TIM1, dp.TIM3, dp.GPIOA, dp.GPIOB, dp.GPIOC, dp.GPIOE, dp.GPIOF, dp.GPIOG, @@ -98,8 +101,6 @@ fn main() -> ! { let mut channels = Channels::new(pins); channels.calibrate_dac_value(0); - timer::setup(cp.SYST, clocks); - #[cfg(not(feature = "generate-hwaddr"))] let hwaddr = EthernetAddress(NET_HWADDR); #[cfg(feature = "generate-hwaddr")] diff --git a/src/pins.rs b/src/pins.rs index ff4545f..dd1c773 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -1,6 +1,6 @@ use stm32f4xx_hal::{ adc::Adc, - hal::{blocking::spi::Transfer, digital::v2::OutputPin}, + hal::{blocking::spi::Transfer, digital::v2::{InputPin, OutputPin}}, gpio::{ AF5, Alternate, Analog, gpioa::*, @@ -20,6 +20,20 @@ use stm32f4xx_hal::{ time::U32Ext, }; use crate::channel::{Channel0, Channel1}; +use crate::softspi::SoftSpi; + + +pub struct DummyInputPin; + +impl InputPin for DummyInputPin { + type Error = (); // `Void` + fn is_high(&self) -> Result { + Ok(false) + } + fn is_low(&self) -> Result { + Ok(true) + } +} pub trait ChannelPins { @@ -58,8 +72,8 @@ impl ChannelPins for Channel1 { /// SPI peripheral used for communication with the ADC pub type AdcSpi = Spi>, PB14>, PB15>)>; pub type AdcNss = PB12>; -type Dac0Spi = Spi>, NoMiso, PE6>)>; -type Dac1Spi = Spi>, NoMiso, PF9>)>; +type Dac0Spi = SoftSpi>, PE6>, DummyInputPin>; +type Dac1Spi = SoftSpi>, PF9>, DummyInputPin>; pub type TecUMeasAdc = Adc; @@ -196,14 +210,10 @@ impl Pins { clocks: Clocks, spi4: SPI4, sclk: PE2, sync: PE4, sdin: PE6 ) -> (Dac0Spi, PE4>) { - let sclk = sclk.into_alternate_af5(); - let sdin = sdin.into_alternate_af5(); - let spi = Spi::spi4( - spi4, - (sclk, NoMiso, sdin), - crate::ad5680::SPI_MODE, - crate::ad5680::SPI_CLOCK.into(), - clocks + let sclk = sclk.into_push_pull_output(); + let sdin = sdin.into_push_pull_output(); + let spi = SoftSpi::new( + sclk, sdin, DummyInputPin, ); let sync = sync.into_push_pull_output(); @@ -214,14 +224,10 @@ impl Pins { clocks: Clocks, spi5: SPI5, sclk: PF7, sync: PF6, sdin: PF9 ) -> (Dac1Spi, PF6>) { - let sclk = sclk.into_alternate_af5(); - let sdin = sdin.into_alternate_af5(); - let spi = Spi::spi5( - spi5, - (sclk, NoMiso, sdin), - crate::ad5680::SPI_MODE, - crate::ad5680::SPI_CLOCK.into(), - clocks + let sclk = sclk.into_push_pull_output(); + let sdin = sdin.into_push_pull_output(); + let spi = SoftSpi::new( + sclk, sdin, DummyInputPin, ); let sync = sync.into_push_pull_output(); diff --git a/src/softspi.rs b/src/softspi.rs new file mode 100644 index 0000000..f1fc52c --- /dev/null +++ b/src/softspi.rs @@ -0,0 +1,147 @@ +use stm32f4xx_hal::hal::{ + spi::FullDuplex, + digital::v2::{InputPin, OutputPin}, + blocking::spi::{Transfer, transfer}, +}; +use nb::{block, Error, Error::WouldBlock}; +use crate::timer::now; + +/// Bit-banged Mode3 SPI +pub struct SoftSpi { + sck: SCK, + mosi: MOSI, + miso: MISO, + state: State, + input: Option, +} + +#[derive(PartialEq)] +enum State { + Idle, + Transfer { + clock_phase: bool, + mask: u8, + output: u8, + input: u8, + }, +} + +impl SoftSpi { + pub fn new(mut sck: SCK, mut mosi: MOSI, miso: MISO) -> Self { + let _ = sck.set_high(); + let _ = mosi.set_low(); + SoftSpi { + sck, mosi, miso, + state: State::Idle, + input: None, + } + } + + /// Call this at twice the data rate + pub fn tick(&mut self) { + match self.state { + State::Idle => {} + State::Transfer { clock_phase: false, + mask, output, input } => { + if output & mask != 0 { + let _ = self.mosi.set_high(); + } else { + let _ = self.mosi.set_low(); + } + let _ = self.sck.set_low(); + + self.state = State::Transfer { + clock_phase: true, + mask, output, input, + }; + } + State::Transfer { clock_phase: true, + mask, output, mut input } => { + if self.miso.is_high().unwrap_or(false) { + input |= mask; + } + let _ = self.sck.set_high(); + + if mask != 1 { + self.state = State::Transfer { + clock_phase: false, + mask: mask >> 1, + output, input, + }; + } else { + self.input = Some(input); + self.state = State::Idle; + } + } + } + } + + pub fn run(&mut self) { + while self.state != State::Idle { + self.tick(); + spi_delay(); + } + } + + fn retry(&mut self, f: &F) -> Result + where + F: Fn(&'_ mut SoftSpi) -> Result> + { + loop { + match f(self) { + Ok(r) => return Ok(r), + Err(nb::Error::Other(e)) => return Err(e), + Err(WouldBlock) => self.run(), + } + } + } +} + +impl FullDuplex for SoftSpi { + type Error = (); + + fn read(&mut self) -> Result> { + match self.input.take() { + Some(input) => + Ok(input), + None if self.state == State::Idle => + Err(nb::Error::Other(())), + None => + Err(WouldBlock), + } + } + + fn send(&mut self, output: u8) -> Result<(), nb::Error> { + match self.state { + State::Idle => { + self.state = State::Transfer { + clock_phase: false, + mask: 0x80, + output, + input: 0, + }; + Ok(()) + } + _ => Err(WouldBlock) + } + } +} + +impl Transfer for SoftSpi { + // TODO: proper type + type Error = (); + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + for b in words.iter_mut() { + self.retry(&|spi| spi.send(*b))?; + *b = self.retry(&|spi| spi.read())?; + } + Ok(words) + } +} + +fn spi_delay() { + const DELAY: u32 = 1; + + let start = now(); + while now() - start < DELAY {} +}